aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2020-08-05 23:05:44 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-08-05 23:05:44 +0000
commit548f841b745404224698a3eafc6d4cb190d0ca8c (patch)
tree8a0d6fd78eb610653f12ded770595c80f1e89a61
parent1d3aa42aef5ab74bf9b13fb5a7e18c73e137f58c (diff)
parenta5e4f4d2969520f4563ec0d66cbe469c49ee38bc (diff)
downloadchromium-trace-548f841b745404224698a3eafc6d4cb190d0ca8c.tar.gz
Merge changes Ie9f01eed,I3acb8a0d am: fb769a1607 am: 316160b179 am: 3fd616f492 am: 5338649baa am: a5e4f4d296
Original change: https://android-review.googlesource.com/c/platform/external/chromium-trace/+/1392438 Change-Id: I7a428aa7cb0cdad8a449e25867e8c4bb514c2d2d
-rw-r--r--UPSTREAM_REVISION2
-rwxr-xr-xcatapult/common/bin/update_chrome_reference_binaries229
-rwxr-xr-xcatapult/common/bin/update_chrome_reference_binaries.py393
-rw-r--r--catapult/common/node_runner/node_runner/node_binaries.json6
-rw-r--r--catapult/common/node_runner/node_runner/package.json3
-rw-r--r--catapult/common/py_trace_event/py_trace_event/trace_event.py30
-rw-r--r--catapult/common/py_trace_event/py_trace_event/trace_event_impl/log.py94
-rw-r--r--catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_proto_classes.py143
-rw-r--r--catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer.py129
-rw-r--r--catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer_unittest.py70
-rw-r--r--catapult/common/py_trace_event/py_trace_event/trace_event_unittest.py10
-rw-r--r--catapult/common/py_trace_event/third_party/protobuf/README.chromium2
-rw-r--r--catapult/common/py_trace_event/third_party/protobuf/encoder.py82
-rw-r--r--catapult/common/py_utils/py_utils/chrome_binaries.json128
-rw-r--r--catapult/common/py_utils/py_utils/cloud_storage.py74
-rw-r--r--catapult/common/py_utils/py_utils/cloud_storage_unittest.py27
-rw-r--r--catapult/common/py_utils/py_utils/constants/__init__.py0
-rw-r--r--catapult/common/py_utils/py_utils/constants/exit_codes.py13
-rw-r--r--catapult/common/py_utils/py_utils/discover.py6
-rw-r--r--catapult/common/py_utils/py_utils/discover_unittest.py3
-rw-r--r--catapult/common/py_utils/py_utils/expectations_parser_unittest.py3
-rw-r--r--catapult/common/py_utils/py_utils/lock_unittest.py3
-rw-r--r--catapult/common/py_utils/py_utils/refactor/annotated_symbol/base_symbol.py4
-rw-r--r--catapult/common/py_utils/py_utils/refactor/annotated_symbol/import_statement.py3
-rw-r--r--catapult/common/py_utils/py_utils/refactor/annotated_symbol/reference.py7
-rw-r--r--catapult/common/py_utils/py_utils/slots_metaclass_unittest.py3
-rw-r--r--catapult/common/py_utils/py_utils/ts_proxy_server.py222
-rw-r--r--catapult/common/py_utils/py_utils/ts_proxy_server_unittest.py56
-rw-r--r--catapult/common/py_utils/py_utils/webpagereplay_go_server.py402
-rw-r--r--catapult/common/py_vulcanize/py_vulcanize/generate.py26
-rw-r--r--catapult/dependency_manager/dependency_manager/dependency_manager_unittest.py2
-rw-r--r--catapult/devil/.style.yapf7
-rw-r--r--catapult/devil/PRESUBMIT.py46
-rw-r--r--catapult/devil/README.md8
-rwxr-xr-xcatapult/devil/bin/generate_md_docs22
-rwxr-xr-xcatapult/devil/bin/run_py_devicetests26
-rwxr-xr-xcatapult/devil/bin/run_py_tests8
-rw-r--r--catapult/devil/build/cipd.yaml14
-rw-r--r--catapult/devil/devil/android/apk_helper.py391
-rwxr-xr-xcatapult/devil/devil/android/apk_helper_test.py273
-rw-r--r--catapult/devil/devil/android/app_ui.py11
-rw-r--r--catapult/devil/devil/android/app_ui_test.py48
-rw-r--r--catapult/devil/devil/android/battery_utils.py143
-rwxr-xr-xcatapult/devil/devil/android/battery_utils_test.py508
-rw-r--r--catapult/devil/devil/android/constants/chrome.py72
-rw-r--r--catapult/devil/devil/android/constants/webapk.py1
-rw-r--r--catapult/devil/devil/android/cpu_temperature.py3
-rw-r--r--catapult/devil/devil/android/cpu_temperature_test.py10
-rw-r--r--catapult/devil/devil/android/crash_handler.py3
-rwxr-xr-xcatapult/devil/devil/android/crash_handler_devicetest.py27
-rw-r--r--catapult/devil/devil/android/decorators.py47
-rw-r--r--catapult/devil/devil/android/decorators_test.py44
-rw-r--r--catapult/devil/devil/android/device_blacklist.py79
-rw-r--r--catapult/devil/devil/android/device_blacklist_test.py38
-rw-r--r--catapult/devil/devil/android/device_denylist.py79
-rw-r--r--catapult/devil/devil/android/device_denylist_test.py37
-rw-r--r--catapult/devil/devil/android/device_errors.py64
-rwxr-xr-xcatapult/devil/devil/android/device_errors_test.py21
-rw-r--r--catapult/devil/devil/android/device_list.py5
-rw-r--r--catapult/devil/devil/android/device_signal.py2
-rw-r--r--catapult/devil/devil/android/device_temp_file.py12
-rw-r--r--catapult/devil/devil/android/device_test_case.py4
-rw-r--r--catapult/devil/devil/android/device_utils.py1522
-rwxr-xr-xcatapult/devil/devil/android/device_utils_devicetest.py54
-rwxr-xr-xcatapult/devil/devil/android/device_utils_test.py2761
-rw-r--r--catapult/devil/devil/android/fastboot_utils.py282
-rwxr-xr-xcatapult/devil/devil/android/fastboot_utils_test.py356
-rw-r--r--catapult/devil/devil/android/flag_changer.py14
-rw-r--r--catapult/devil/devil/android/flag_changer_devicetest.py32
-rwxr-xr-xcatapult/devil/devil/android/flag_changer_test.py48
-rw-r--r--catapult/devil/devil/android/forwarder.py141
-rw-r--r--catapult/devil/devil/android/install_commands.py25
-rw-r--r--catapult/devil/devil/android/logcat_monitor.py30
-rwxr-xr-xcatapult/devil/devil/android/logcat_monitor_test.py96
-rw-r--r--catapult/devil/devil/android/md5sum.py59
-rwxr-xr-xcatapult/devil/devil/android/md5sum_test.py126
-rw-r--r--catapult/devil/devil/android/ndk/abis.py1
-rw-r--r--catapult/devil/devil/android/perf/perf_control.py267
-rw-r--r--catapult/devil/devil/android/perf/perf_control_devicetest.py2
-rw-r--r--catapult/devil/devil/android/perf/perf_control_test.py15
-rw-r--r--catapult/devil/devil/android/perf/surface_stats_collector.py7
-rw-r--r--catapult/devil/devil/android/perf/surface_stats_collector_test.py12
-rw-r--r--catapult/devil/devil/android/perf/thermal_throttle.py18
-rw-r--r--catapult/devil/devil/android/ports.py31
-rw-r--r--catapult/devil/devil/android/sdk/aapt.py2
-rw-r--r--catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py32
-rw-r--r--catapult/devil/devil/android/sdk/adb_wrapper.py268
-rwxr-xr-xcatapult/devil/devil/android/sdk/adb_wrapper_devicetest.py4
-rwxr-xr-xcatapult/devil/devil/android/sdk/adb_wrapper_test.py20
-rw-r--r--catapult/devil/devil/android/sdk/build_tools.py12
-rw-r--r--catapult/devil/devil/android/sdk/bundletool.py62
-rw-r--r--catapult/devil/devil/android/sdk/dexdump.py2
-rw-r--r--catapult/devil/devil/android/sdk/fastboot.py32
-rw-r--r--catapult/devil/devil/android/sdk/gce_adb_wrapper.py39
-rw-r--r--catapult/devil/devil/android/sdk/intent.py14
-rw-r--r--catapult/devil/devil/android/sdk/keyevent.py1
-rw-r--r--catapult/devil/devil/android/sdk/shared_prefs.py33
-rwxr-xr-xcatapult/devil/devil/android/sdk/shared_prefs_test.py101
-rw-r--r--catapult/devil/devil/android/sdk/split_select.py14
-rw-r--r--catapult/devil/devil/android/sdk/version_codes.py2
-rw-r--r--catapult/devil/devil/android/settings.py239
-rwxr-xr-xcatapult/devil/devil/android/tools/adb_run_shell_cmd.py13
-rwxr-xr-xcatapult/devil/devil/android/tools/cpufreq.py14
-rwxr-xr-xcatapult/devil/devil/android/tools/device_monitor.py73
-rwxr-xr-xcatapult/devil/devil/android/tools/device_monitor_test.py92
-rwxr-xr-xcatapult/devil/devil/android/tools/device_recovery.py137
-rwxr-xr-xcatapult/devil/devil/android/tools/device_status.py186
-rwxr-xr-xcatapult/devil/devil/android/tools/flash_device.py48
-rwxr-xr-xcatapult/devil/devil/android/tools/keyboard.py88
-rwxr-xr-xcatapult/devil/devil/android/tools/provision_devices.py320
-rwxr-xr-xcatapult/devil/devil/android/tools/screenshot.py27
-rw-r--r--catapult/devil/devil/android/tools/script_common.py52
-rwxr-xr-xcatapult/devil/devil/android/tools/script_common_test.py36
-rwxr-xr-xcatapult/devil/devil/android/tools/system_app.py147
-rwxr-xr-xcatapult/devil/devil/android/tools/system_app_devicetest.py18
-rw-r--r--catapult/devil/devil/android/tools/system_app_test.py38
-rw-r--r--catapult/devil/devil/android/tools/unlock_bootloader.py28
-rwxr-xr-xcatapult/devil/devil/android/tools/video_recorder.py79
-rwxr-xr-xcatapult/devil/devil/android/tools/wait_for_devices.py20
-rwxr-xr-xcatapult/devil/devil/android/tools/webview_app.py45
-rw-r--r--catapult/devil/devil/android/valgrind_tools/base_tool.py1
-rw-r--r--catapult/devil/devil/base_error.py1
-rw-r--r--catapult/devil/devil/constants/exit_codes.py1
-rw-r--r--catapult/devil/devil/devil_dependencies.json40
-rw-r--r--catapult/devil/devil/devil_env.py59
-rwxr-xr-xcatapult/devil/devil/devil_env_test.py23
-rw-r--r--catapult/devil/devil/utils/cmd_helper.py153
-rwxr-xr-xcatapult/devil/devil/utils/cmd_helper_test.py91
-rw-r--r--catapult/devil/devil/utils/file_utils.py2
-rwxr-xr-xcatapult/devil/devil/utils/find_usb_devices.py40
-rwxr-xr-xcatapult/devil/devil/utils/find_usb_devices_test.py104
-rw-r--r--catapult/devil/devil/utils/geometry.py1
-rw-r--r--catapult/devil/devil/utils/geometry_test.py3
-rw-r--r--catapult/devil/devil/utils/host_utils.py5
-rw-r--r--catapult/devil/devil/utils/lazy/weak_constant.py6
-rw-r--r--catapult/devil/devil/utils/lazy/weak_constant_test.py20
-rw-r--r--catapult/devil/devil/utils/logging_common.py11
-rw-r--r--catapult/devil/devil/utils/lsusb.py24
-rwxr-xr-xcatapult/devil/devil/utils/lsusb_test.py224
-rwxr-xr-xcatapult/devil/devil/utils/markdown.py66
-rwxr-xr-xcatapult/devil/devil/utils/markdown_test.py9
-rw-r--r--catapult/devil/devil/utils/mock_calls.py32
-rwxr-xr-xcatapult/devil/devil/utils/mock_calls_test.py21
-rw-r--r--catapult/devil/devil/utils/parallelizer.py23
-rw-r--r--catapult/devil/devil/utils/parallelizer_test.py32
-rw-r--r--catapult/devil/devil/utils/reraiser_thread.py7
-rw-r--r--catapult/devil/devil/utils/reraiser_thread_unittest.py9
-rwxr-xr-xcatapult/devil/devil/utils/reset_usb.py22
-rw-r--r--catapult/devil/devil/utils/run_tests_helper.py7
-rw-r--r--catapult/devil/devil/utils/timeout_retry.py31
-rwxr-xr-xcatapult/devil/devil/utils/timeout_retry_unittest.py30
-rwxr-xr-xcatapult/devil/devil/utils/update_dependencies.py218
-rw-r--r--catapult/devil/devil/utils/usb_hubs.py47
-rw-r--r--catapult/devil/devil/utils/watchdog_timer.py1
-rw-r--r--catapult/devil/devil/utils/zip_utils.py23
-rw-r--r--catapult/devil/devil/utils/zip_utils_test.py13
-rw-r--r--catapult/devil/docs/adb_wrapper.md335
-rw-r--r--catapult/devil/docs/device_denylist.md (renamed from catapult/devil/docs/device_blacklist.md)36
-rw-r--r--catapult/devil/docs/device_utils.md1183
-rw-r--r--catapult/devil/docs/markdown.md69
-rw-r--r--catapult/systrace/profile_chrome/chrome_tracing_agent.py3
-rw-r--r--catapult/systrace/profile_chrome/perf_tracing_agent.py2
-rw-r--r--catapult/systrace/systrace/monitor_unittest.py2
-rw-r--r--catapult/systrace/systrace/output_generator.py18
-rw-r--r--catapult/systrace/systrace/output_generator_unittest.py9
-rw-r--r--catapult/systrace/systrace/prefix.html.template (renamed from catapult/systrace/systrace/prefix.html)6
-rwxr-xr-xcatapult/systrace/systrace/run_systrace.py3
-rw-r--r--catapult/systrace/systrace/systrace_runner.py5
-rw-r--r--catapult/systrace/systrace/systrace_trace_viewer.html831
-rw-r--r--catapult/systrace/systrace/test_data/cgroup_dump16
-rw-r--r--catapult/systrace/systrace/tracing_agents/android_cgroup_agent.py96
-rw-r--r--catapult/systrace/systrace/tracing_agents/atrace_agent.py14
-rw-r--r--catapult/systrace/systrace/util.py21
-rw-r--r--catapult/third_party/polymer/.bowerrc3
-rw-r--r--catapult/third_party/polymer/LICENSE.polymer27
-rw-r--r--catapult/third_party/polymer/README.chromium26
-rw-r--r--catapult/third_party/polymer/bower.json34
-rw-r--r--catapult/third_party/polymer/components/app-route/.bower.json44
-rw-r--r--catapult/third_party/polymer/components/app-route/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/app-route/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/app-route/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/app-route/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/app-route/README.md217
-rw-r--r--catapult/third_party/polymer/components/app-route/app-location.html194
-rw-r--r--catapult/third_party/polymer/components/app-route/app-route-converter-behavior.html112
-rw-r--r--catapult/third_party/polymer/components/app-route/app-route-converter.html79
-rw-r--r--catapult/third_party/polymer/components/app-route/app-route.html421
-rw-r--r--catapult/third_party/polymer/components/app-route/bower.json35
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/data-loading-demo.html35
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-image-page.html107
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-demo.html66
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-page.html107
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/index.html214
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/simple-demo.html117
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/youtube-demo/route-info.html48
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/youtube-demo/search-results.html63
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/youtube-demo/video-viewer.html133
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-lite.html204
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-search.html103
-rw-r--r--catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-toolbar.html181
-rw-r--r--catapult/third_party/polymer/components/app-route/index.html27
-rw-r--r--catapult/third_party/polymer/components/app-route/test/app-example-1.html45
-rw-r--r--catapult/third_party/polymer/components/app-route/test/app-location.html168
-rw-r--r--catapult/third_party/polymer/components/app-route/test/app-route-converter.html58
-rw-r--r--catapult/third_party/polymer/components/app-route/test/app-route.html488
-rw-r--r--catapult/third_party/polymer/components/app-route/test/index.html29
-rw-r--r--catapult/third_party/polymer/components/app-route/test/observer-tester.html47
-rw-r--r--catapult/third_party/polymer/components/app-route/test/redirection.html44
-rw-r--r--catapult/third_party/polymer/components/app-route/test/test-app-example-1.html137
-rw-r--r--catapult/third_party/polymer/components/app-route/test/test-observer-app.html67
-rw-r--r--catapult/third_party/polymer/components/core-tooltip/README.md4
-rw-r--r--catapult/third_party/polymer/components/core-tooltip/bower.json13
-rw-r--r--catapult/third_party/polymer/components/core-tooltip/core-tooltip.css104
-rw-r--r--catapult/third_party/polymer/components/core-tooltip/core-tooltip.html217
-rw-r--r--catapult/third_party/polymer/components/core-tooltip/demo.html211
-rw-r--r--catapult/third_party/polymer/components/core-tooltip/index.html23
-rw-r--r--catapult/third_party/polymer/components/core-tooltip/metadata.html20
-rw-r--r--catapult/third_party/polymer/components/font-roboto/.bower.json31
-rw-r--r--catapult/third_party/polymer/components/font-roboto/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/font-roboto/README.md1
-rw-r--r--catapult/third_party/polymer/components/font-roboto/bower.json22
-rw-r--r--catapult/third_party/polymer/components/font-roboto/package.json19
-rw-r--r--catapult/third_party/polymer/components/font-roboto/roboto.html10
-rw-r--r--catapult/third_party/polymer/components/google-apis/.bower.json56
-rw-r--r--catapult/third_party/polymer/components/google-apis/LICENSE13
-rw-r--r--catapult/third_party/polymer/components/google-apis/README.md4
-rw-r--r--catapult/third_party/polymer/components/google-apis/bower.json47
-rw-r--r--catapult/third_party/polymer/components/google-apis/demo/index.html51
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-apis.html16
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-client-loader.html232
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-js-api.html63
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-legacy-loader.html55
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-maps-api.html150
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-plusone-api.html54
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-realtime-api.html57
-rw-r--r--catapult/third_party/polymer/components/google-apis/google-youtube-api.html61
-rw-r--r--catapult/third_party/polymer/components/google-apis/index.html15
-rw-r--r--catapult/third_party/polymer/components/google-signin/.bower.json63
-rw-r--r--catapult/third_party/polymer/components/google-signin/LICENSE13
-rw-r--r--catapult/third_party/polymer/components/google-signin/README.md4
-rw-r--r--catapult/third_party/polymer/components/google-signin/bower.json54
-rw-r--r--catapult/third_party/polymer/components/google-signin/demo/index.html208
-rw-r--r--catapult/third_party/polymer/components/google-signin/google-icons.html36
-rw-r--r--catapult/third_party/polymer/components/google-signin/google-signin-aware.html824
-rw-r--r--catapult/third_party/polymer/components/google-signin/google-signin-styles.html267
-rw-r--r--catapult/third_party/polymer/components/google-signin/google-signin.html596
-rw-r--r--catapult/third_party/polymer/components/google-signin/index.html15
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/.bower.json43
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/README.md55
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/bower.json33
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/demo/index.html38
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/demo/x-announces.html60
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/index.html28
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/iron-a11y-announcer.html122
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-announcer/test/iron-a11y-announcer.html56
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/.bower.json43
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/.travis.yml25
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/README.md58
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/bower.json33
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/x-key-aware.html105
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html496
-rw-r--r--catapult/third_party/polymer/components/iron-a11y-keys-behavior/test/basic-test.html445
-rwxr-xr-xcatapult/third_party/polymer/components/iron-a11y-keys-behavior/test/index.html28
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/.bower.json65
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/.gitignore2
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/.travis.yml26
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/README.md111
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/bower.json56
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/demo/index.html87
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/hero.svg33
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/iron-ajax.html580
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/iron-request.html471
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/test/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/test/iron-ajax.html968
-rw-r--r--catapult/third_party/polymer/components/iron-ajax/test/iron-request.html368
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/.bower.json48
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/README.md41
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/bower.json39
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/demo/index.html111
-rwxr-xr-xcatapult/third_party/polymer/components/iron-autogrow-textarea/hero.svg33
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/iron-autogrow-textarea.html373
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/test/basic.html190
-rw-r--r--catapult/third_party/polymer/components/iron-autogrow-textarea/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/.bower.json42
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/README.md22
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/bower.json32
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/demo/index.html48
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/demo/simple-button.html66
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/iron-button-state.html236
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/iron-control-state.html110
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/test/active-state.html290
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/test/disabled-state.html82
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/test/focused-state.html161
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/test/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-behaviors/test/test-elements.html91
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/.bower.json43
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/README.md27
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/bower.json34
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/demo/index.html39
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/demo/simple-checkbox.html65
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/iron-checked-element-behavior.html120
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/test/basic.html152
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/iron-checked-element-behavior/test/simple-checkbox.html26
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/.bower.json43
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/README.md66
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/bower.json34
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/demo/index.html104
-rwxr-xr-xcatapult/third_party/polymer/components/iron-collapse/hero.svg23
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/index.html31
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/iron-collapse.html302
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/test/a11y.html74
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/test/basic.html199
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/test/flex.html152
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/test/horizontal.html97
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/test/index.html35
-rw-r--r--catapult/third_party/polymer/components/iron-collapse/test/nested.html131
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/.bower.json47
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/README.md46
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/bower.json38
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/demo/grow-height-animation.html36
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/demo/index.html160
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/demo/x-select.html80
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/iron-dropdown-scroll-manager.html372
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html353
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/test/index.html28
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown-scroll-manager.html187
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown.html597
-rw-r--r--catapult/third_party/polymer/components/iron-dropdown/test/x-scrollable-element.html53
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/.bower.json41
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/README.md57
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/bower.json31
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/demo/index.html166
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/demo/simple-fit.html41
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html611
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/test/iron-fit-behavior.html1029
-rw-r--r--catapult/third_party/polymer/components/iron-fit-behavior/test/test-fit.html41
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/.bower.json44
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/.gitignore2
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/GUIDE.md1265
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/README.md67
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/bower.json34
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/classes/iron-flex-layout.html317
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/classes/iron-shadow-flex-layout.html313
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/demo/index.html396
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout-classes.html437
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout.html418
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout-classes.html412
-rw-r--r--catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout.html434
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/.bower.json41
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/README.md25
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/bower.json31
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/demo/index.html67
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-element.html27
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-form.html53
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/iron-form-element-behavior.html86
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/test/basic.html71
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-element.html23
-rw-r--r--catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-form.html19
-rw-r--r--catapult/third_party/polymer/components/iron-form/.bower.json78
-rw-r--r--catapult/third_party/polymer/components/iron-form/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-form/.gitignore2
-rw-r--r--catapult/third_party/polymer/components/iron-form/.travis.yml26
-rw-r--r--catapult/third_party/polymer/components/iron-form/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-form/README.md52
-rw-r--r--catapult/third_party/polymer/components/iron-form/bower.json69
-rw-r--r--catapult/third_party/polymer/components/iron-form/demo/cats-only.html65
-rw-r--r--catapult/third_party/polymer/components/iron-form/demo/index.html250
-rw-r--r--catapult/third_party/polymer/components/iron-form/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-form/iron-form.html462
-rw-r--r--catapult/third_party/polymer/components/iron-form/test/basic.html923
-rw-r--r--catapult/third_party/polymer/components/iron-form/test/index.html22
-rw-r--r--catapult/third_party/polymer/components/iron-icon/.bower.json45
-rw-r--r--catapult/third_party/polymer/components/iron-icon/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-icon/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-icon/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-icon/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-icon/README.md22
-rw-r--r--catapult/third_party/polymer/components/iron-icon/bower.json35
-rw-r--r--catapult/third_party/polymer/components/iron-icon/demo/async.html62
-rw-r--r--catapult/third_party/polymer/components/iron-icon/demo/index.html48
-rw-r--r--catapult/third_party/polymer/components/iron-icon/demo/location.pngbin0 -> 324 bytes
-rwxr-xr-xcatapult/third_party/polymer/components/iron-icon/hero.svg19
-rw-r--r--catapult/third_party/polymer/components/iron-icon/index.html26
-rw-r--r--catapult/third_party/polymer/components/iron-icon/iron-icon.html204
-rw-r--r--catapult/third_party/polymer/components/iron-icon/test/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-icon/test/iron-icon.html246
-rw-r--r--catapult/third_party/polymer/components/iron-icons/.bower.json47
-rw-r--r--catapult/third_party/polymer/components/iron-icons/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-icons/.gitignore3
-rw-r--r--catapult/third_party/polymer/components/iron-icons/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-icons/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-icons/README.md49
-rw-r--r--catapult/third_party/polymer/components/iron-icons/av-icons.html96
-rw-r--r--catapult/third_party/polymer/components/iron-icons/bower.json38
-rw-r--r--catapult/third_party/polymer/components/iron-icons/communication-icons.html66
-rw-r--r--catapult/third_party/polymer/components/iron-icons/demo/index.html116
-rw-r--r--catapult/third_party/polymer/components/iron-icons/device-icons.html95
-rw-r--r--catapult/third_party/polymer/components/iron-icons/editor-icons.html84
-rw-r--r--catapult/third_party/polymer/components/iron-icons/hardware-icons.html64
-rwxr-xr-xcatapult/third_party/polymer/components/iron-icons/hero.svg35
-rw-r--r--catapult/third_party/polymer/components/iron-icons/image-icons.html171
-rw-r--r--catapult/third_party/polymer/components/iron-icons/index.html26
-rw-r--r--catapult/third_party/polymer/components/iron-icons/iron-icons.html353
-rw-r--r--catapult/third_party/polymer/components/iron-icons/maps-icons.html84
-rw-r--r--catapult/third_party/polymer/components/iron-icons/notification-icons.html70
-rw-r--r--catapult/third_party/polymer/components/iron-icons/places-icons.html35
-rw-r--r--catapult/third_party/polymer/components/iron-icons/social-icons.html46
-rw-r--r--catapult/third_party/polymer/components/iron-icons/test/index.html25
-rw-r--r--catapult/third_party/polymer/components/iron-icons/test/iron-icons.html81
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/.bower.json44
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/README.md56
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/bower.json34
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/demo/index.html70
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/demo/svg-sample-icons.html81
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/index.html26
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/iron-iconset-svg.html242
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/test/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-iconset-svg/test/iron-iconset-svg.html233
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/.github/ISSUE_TEMPLATE.md33
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/.gitignore1
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/.travis.yml23
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/CONTRIBUTING.md77
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/README.md86
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/bower.json33
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/demo/index.html266
-rw-r--r--catapult/third_party/polymer/components/iron-image/demo/loading.pngbin0 -> 2183 bytes
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/demo/polymer.svg175
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/index.html24
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/iron-image.html403
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/test/index.html23
-rwxr-xr-xcatapult/third_party/polymer/components/iron-image/test/iron-image.html338
-rw-r--r--catapult/third_party/polymer/components/iron-input/.bower.json44
-rw-r--r--catapult/third_party/polymer/components/iron-input/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-input/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-input/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-input/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-input/README.md59
-rw-r--r--catapult/third_party/polymer/components/iron-input/bower.json35
-rw-r--r--catapult/third_party/polymer/components/iron-input/demo/index.html87
-rwxr-xr-xcatapult/third_party/polymer/components/iron-input/hero.svg19
-rw-r--r--catapult/third_party/polymer/components/iron-input/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-input/iron-input.html311
-rw-r--r--catapult/third_party/polymer/components/iron-input/test/disabled-input.html32
-rw-r--r--catapult/third_party/polymer/components/iron-input/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-input/test/iron-input.html281
-rw-r--r--catapult/third_party/polymer/components/iron-input/test/letters-only.html30
-rw-r--r--catapult/third_party/polymer/components/iron-jsonp-library/.bower.json66
-rw-r--r--catapult/third_party/polymer/components/iron-jsonp-library/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-jsonp-library/README.md51
-rw-r--r--catapult/third_party/polymer/components/iron-jsonp-library/bower.json57
-rw-r--r--catapult/third_party/polymer/components/iron-jsonp-library/demo/index.html133
-rwxr-xr-xcatapult/third_party/polymer/components/iron-jsonp-library/hero.svg31
-rw-r--r--catapult/third_party/polymer/components/iron-jsonp-library/index.html26
-rw-r--r--catapult/third_party/polymer/components/iron-jsonp-library/iron-jsonp-library.html271
-rw-r--r--catapult/third_party/polymer/components/iron-location/.bower.json49
-rw-r--r--catapult/third_party/polymer/components/iron-location/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-location/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-location/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-location/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-location/README.md58
-rw-r--r--catapult/third_party/polymer/components/iron-location/bower.json40
-rw-r--r--catapult/third_party/polymer/components/iron-location/demo/index.html126
-rw-r--r--catapult/third_party/polymer/components/iron-location/demo/iron-query-params.html119
-rw-r--r--catapult/third_party/polymer/components/iron-location/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-location/iron-location.html333
-rw-r--r--catapult/third_party/polymer/components/iron-location/iron-query-params.html89
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/initialization-cases.html277
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/initialization-iframe.html61
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/initialization-tests.html145
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/integration.html135
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/iron-location.html499
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/iron-query-params.html102
-rw-r--r--catapult/third_party/polymer/components/iron-location/test/redirection.html68
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/.bower.json46
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/README.md30
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/bower.json37
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/demo/index.html118
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menu.html48
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menubar.html52
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/iron-menu-behavior.html396
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/iron-menubar-behavior.html81
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/test/index.html33
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menu-behavior.html617
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menubar-behavior.html162
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/test/test-menu.html46
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/test/test-menubar.html46
-rw-r--r--catapult/third_party/polymer/components/iron-menu-behavior/test/test-nested-menu.html44
-rw-r--r--catapult/third_party/polymer/components/iron-meta/.bower.json40
-rw-r--r--catapult/third_party/polymer/components/iron-meta/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-meta/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-meta/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-meta/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-meta/README.md110
-rw-r--r--catapult/third_party/polymer/components/iron-meta/bower.json30
-rw-r--r--catapult/third_party/polymer/components/iron-meta/demo/index.html76
-rwxr-xr-xcatapult/third_party/polymer/components/iron-meta/hero.svg33
-rw-r--r--catapult/third_party/polymer/components/iron-meta/index.html27
-rw-r--r--catapult/third_party/polymer/components/iron-meta/iron-meta.html333
-rw-r--r--catapult/third_party/polymer/components/iron-meta/test/basic.html48
-rw-r--r--catapult/third_party/polymer/components/iron-meta/test/index.html29
-rw-r--r--catapult/third_party/polymer/components/iron-meta/test/iron-meta.html195
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/.bower.json47
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/README.md78
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/bower.json37
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/demo/index.html188
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/demo/simple-overlay.html46
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/iron-focusables-helper.html220
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-backdrop.html168
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-behavior.html637
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-manager.html366
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/index.html33
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-focusables-helper.html182
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-backdrop.html85
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-behavior.html1282
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons-wrapper.html38
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons.html34
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/test-menu-button.html38
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay.html101
-rw-r--r--catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay2.html52
-rw-r--r--catapult/third_party/polymer/components/iron-pages/.bower.json44
-rw-r--r--catapult/third_party/polymer/components/iron-pages/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-pages/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-pages/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-pages/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-pages/README.md42
-rw-r--r--catapult/third_party/polymer/components/iron-pages/bower.json34
-rw-r--r--catapult/third_party/polymer/components/iron-pages/demo/index.html74
-rwxr-xr-xcatapult/third_party/polymer/components/iron-pages/hero.svg22
-rw-r--r--catapult/third_party/polymer/components/iron-pages/index.html25
-rw-r--r--catapult/third_party/polymer/components/iron-pages/iron-pages.html88
-rw-r--r--catapult/third_party/polymer/components/iron-pages/test/attr-for-selected.html92
-rw-r--r--catapult/third_party/polymer/components/iron-pages/test/basic.html98
-rw-r--r--catapult/third_party/polymer/components/iron-pages/test/index.html34
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/.bower.json39
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/README.md24
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/bower.json29
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/demo/index.html80
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/iron-range-behavior.html121
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/test/basic.html180
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/iron-range-behavior/test/x-progressbar.html19
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/.bower.json41
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/README.md36
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/bower.json31
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/demo/index.html29
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/demo/src/x-app.html105
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/index.html25
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/iron-resizable-behavior.html195
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/test/basic.html223
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/test/index.html29
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/test/iron-resizable-behavior.html88
-rw-r--r--catapult/third_party/polymer/components/iron-resizable-behavior/test/test-elements.html194
-rw-r--r--catapult/third_party/polymer/components/iron-selector/.bower.json47
-rw-r--r--catapult/third_party/polymer/components/iron-selector/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-selector/.gitignore2
-rw-r--r--catapult/third_party/polymer/components/iron-selector/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-selector/CONTRIBUTING.md77
-rwxr-xr-xcatapult/third_party/polymer/components/iron-selector/README.md91
-rwxr-xr-xcatapult/third_party/polymer/components/iron-selector/bower.json38
-rw-r--r--catapult/third_party/polymer/components/iron-selector/demo/index.html101
-rwxr-xr-xcatapult/third_party/polymer/components/iron-selector/index.html28
-rw-r--r--catapult/third_party/polymer/components/iron-selector/iron-multi-selectable.html154
-rw-r--r--catapult/third_party/polymer/components/iron-selector/iron-selectable.html388
-rw-r--r--catapult/third_party/polymer/components/iron-selector/iron-selection.html119
-rw-r--r--catapult/third_party/polymer/components/iron-selector/iron-selector.html87
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/activate-event.html150
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/attr-for-selected-elements.html30
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/attr-for-selected.html229
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/basic.html263
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/content-element.html44
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/content.html169
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/excluded-local-names.html96
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/index.html43
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/multi.html375
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/next-previous.html135
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/numeric-ids.html73
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/selected-attribute.html129
-rw-r--r--catapult/third_party/polymer/components/iron-selector/test/template-repeat.html104
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/.bower.json42
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/README.md42
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/bower.json33
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/demo/cats-only.html46
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/demo/index.html71
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/demo/validatable-input.html46
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/index.html30
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/iron-validatable-behavior.html149
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/test/cats-only.html30
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/test/dogs-only.html30
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/test/index.html32
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/test/iron-validatable-behavior.html88
-rw-r--r--catapult/third_party/polymer/components/iron-validatable-behavior/test/test-validatable.html29
-rw-r--r--catapult/third_party/polymer/components/neon-animation/.bower.json61
-rw-r--r--catapult/third_party/polymer/components/neon-animation/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/neon-animation/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/neon-animation/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/neon-animation/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/neon-animation/README.md306
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/cascaded-animation.html95
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/fade-in-animation.html49
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/fade-out-animation.html49
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/hero-animation.html83
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/opaque-animation.html46
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/reverse-ripple-animation.html87
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/ripple-animation.html93
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/scale-down-animation.html65
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/scale-up-animation.html65
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-down-animation.html59
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-from-bottom-animation.html59
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-from-left-animation.html60
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-from-right-animation.html60
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-from-top-animation.html59
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-left-animation.html59
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-right-animation.html59
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/slide-up-animation.html59
-rw-r--r--catapult/third_party/polymer/components/neon-animation/animations/transform-animation.html70
-rw-r--r--catapult/third_party/polymer/components/neon-animation/bower.json52
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/card/index.html166
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/card/x-card.html94
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/card/x-cards-list.html75
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/declarative/index.html132
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/doc/index.html70
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/doc/my-animatable.html68
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/doc/my-dialog.html94
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/dropdown/animated-dropdown.html90
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/dropdown/index.html54
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/grid/animated-grid.html164
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/grid/fullsize-page-with-card.html122
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/grid/index.html64
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/index.html45
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/list/full-view.html118
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/list/index.html35
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/list/list-demo.html102
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/list/list-view.html124
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/load/animated-grid.html146
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/load/full-page.html82
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/load/index.html48
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/reprojection/animated-grid.html167
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/reprojection/fullsize-page-with-card.html120
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/reprojection/index.html63
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/reprojection/reprojected-pages.html45
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/shared-styles.html47
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/tiles/circles-page.html107
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/tiles/index.html70
-rw-r--r--catapult/third_party/polymer/components/neon-animation/demo/tiles/squares-page.html100
-rw-r--r--catapult/third_party/polymer/components/neon-animation/guides/neon-animation.md314
-rw-r--r--catapult/third_party/polymer/components/neon-animation/index.html30
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-animatable-behavior.html150
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-animatable.html54
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-animated-pages.html220
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-animation-behavior.html86
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-animation-runner-behavior.html129
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-animation.html18
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-animations.html29
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-shared-element-animatable-behavior.html43
-rw-r--r--catapult/third_party/polymer/components/neon-animation/neon-shared-element-animation-behavior.html72
-rw-r--r--catapult/third_party/polymer/components/neon-animation/test/index.html28
-rw-r--r--catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-descendant-selection.html118
-rw-r--r--catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-lazy.html73
-rw-r--r--catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages.html101
-rw-r--r--catapult/third_party/polymer/components/neon-animation/test/test-resizable-pages.html58
-rw-r--r--catapult/third_party/polymer/components/neon-animation/web-animations.html11
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/.bower.json51
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/README.md44
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/bower.json42
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/demo/index.html45
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/demo/paper-button.html64
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/demo/paper-radio-button.html112
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/index.html26
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/paper-button-behavior.html97
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/paper-checked-element-behavior.html57
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/paper-inky-focus-behavior.html51
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/paper-ripple-behavior.html126
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/index.html29
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/paper-button-behavior.html113
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/paper-checked-element-behavior.html94
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/paper-radio-button-behavior.html58
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/paper-ripple-behavior.html335
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/shadowed-ripple.html55
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/test-button.html34
-rw-r--r--catapult/third_party/polymer/components/paper-behaviors/test/test-radio-button.html41
-rw-r--r--catapult/third_party/polymer/components/paper-button/.bower.json50
-rw-r--r--catapult/third_party/polymer/components/paper-button/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-button/.gitignore2
-rw-r--r--catapult/third_party/polymer/components/paper-button/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-button/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-button/README.md73
-rw-r--r--catapult/third_party/polymer/components/paper-button/bower.json41
-rw-r--r--catapult/third_party/polymer/components/paper-button/demo/index.html138
-rw-r--r--catapult/third_party/polymer/components/paper-button/index.html26
-rw-r--r--catapult/third_party/polymer/components/paper-button/package.json9
-rw-r--r--catapult/third_party/polymer/components/paper-button/paper-button.html186
-rw-r--r--catapult/third_party/polymer/components/paper-button/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-button/test/paper-button.html108
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/.gitignore1
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/.travis.yml23
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/CONTRIBUTING.md77
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/README.md65
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/bower.json41
-rw-r--r--catapult/third_party/polymer/components/paper-card/demo/cafe.pngbin0 -> 256315 bytes
-rw-r--r--catapult/third_party/polymer/components/paper-card/demo/donuts.pngbin0 -> 395250 bytes
-rw-r--r--catapult/third_party/polymer/components/paper-card/demo/house.pngbin0 -> 376570 bytes
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/demo/index.html323
-rw-r--r--catapult/third_party/polymer/components/paper-card/demo/travel.pngbin0 -> 364923 bytes
-rw-r--r--catapult/third_party/polymer/components/paper-card/demo/trip.pngbin0 -> 283091 bytes
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/index.html28
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/paper-card.html226
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/test/basic.html87
-rwxr-xr-xcatapult/third_party/polymer/components/paper-card/test/index.html26
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/.bower.json49
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/.eslintrc.json18
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/.travis.yml25
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/README.md63
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/bower.json40
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/demo/index.html116
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/paper-checkbox.html310
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/test/.eslintrc.json14
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/test/basic.html275
-rw-r--r--catapult/third_party/polymer/components/paper-checkbox/test/index.html28
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/.bower.json49
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/README.md54
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/bower.json40
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/demo/index.html102
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/demo/simple-dialog.html39
-rwxr-xr-xcatapult/third_party/polymer/components/paper-dialog-behavior/hero.svg51
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-behavior.html139
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-common.css57
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-shared-styles.html83
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/test/paper-dialog-behavior.html408
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/test/test-buttons.html30
-rw-r--r--catapult/third_party/polymer/components/paper-dialog-behavior/test/test-dialog.html39
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/.bower.json49
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/.travis.yml23
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/README.md75
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/bower.json40
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/demo/index.html226
-rwxr-xr-xcatapult/third_party/polymer/components/paper-dialog/hero.svg58
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/paper-dialog.html117
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/paper-dialog/test/paper-dialog.html92
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/.bower.json57
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/README.md44
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/bower.json48
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/demo/index.html295
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-icons.html17
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html597
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-shared-styles.html78
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.html415
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/test/index.html26
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu-light.html219
-rw-r--r--catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu.html220
-rw-r--r--catapult/third_party/polymer/components/paper-fab/.bower.json48
-rw-r--r--catapult/third_party/polymer/components/paper-fab/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-fab/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-fab/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-fab/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-fab/README.md53
-rw-r--r--catapult/third_party/polymer/components/paper-fab/bower.json39
-rw-r--r--catapult/third_party/polymer/components/paper-fab/demo/index.html90
-rw-r--r--catapult/third_party/polymer/components/paper-fab/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-fab/paper-fab.html187
-rw-r--r--catapult/third_party/polymer/components/paper-fab/test/a11y.html67
-rw-r--r--catapult/third_party/polymer/components/paper-fab/test/basic.html141
-rw-r--r--catapult/third_party/polymer/components/paper-fab/test/index.html26
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/.bower.json50
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/README.md95
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/bower.json40
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/demo/index.html103
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/demo/paper-icon-button-light.html57
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/index.html23
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/paper-icon-button-light.html98
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/paper-icon-button.html176
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/test/a11y.html92
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/test/basic.html75
-rw-r--r--catapult/third_party/polymer/components/paper-icon-button/test/index.html26
-rw-r--r--catapult/third_party/polymer/components/paper-input/.bower.json60
-rw-r--r--catapult/third_party/polymer/components/paper-input/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-input/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-input/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-input/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-input/README.md38
-rw-r--r--catapult/third_party/polymer/components/paper-input/all-imports.html12
-rw-r--r--catapult/third_party/polymer/components/paper-input/bower.json51
-rw-r--r--catapult/third_party/polymer/components/paper-input/demo/index.html155
-rw-r--r--catapult/third_party/polymer/components/paper-input/demo/ssn-input.html96
-rw-r--r--catapult/third_party/polymer/components/paper-input/demo/ssn-validator.html27
-rwxr-xr-xcatapult/third_party/polymer/components/paper-input/hero.svg19
-rw-r--r--catapult/third_party/polymer/components/paper-input/index.html28
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-input-addon-behavior.html47
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-input-behavior.html569
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-input-char-counter.html99
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-input-container.html653
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-input-error.html94
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-input.html183
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-textarea.html145
-rw-r--r--catapult/third_party/polymer/components/paper-input/test/index.html32
-rw-r--r--catapult/third_party/polymer/components/paper-input/test/letters-only.html30
-rw-r--r--catapult/third_party/polymer/components/paper-input/test/paper-input-char-counter.html109
-rw-r--r--catapult/third_party/polymer/components/paper-input/test/paper-input-container.html333
-rw-r--r--catapult/third_party/polymer/components/paper-input/test/paper-input-error.html68
-rw-r--r--catapult/third_party/polymer/components/paper-input/test/paper-input.html398
-rw-r--r--catapult/third_party/polymer/components/paper-input/test/paper-textarea.html233
-rw-r--r--catapult/third_party/polymer/components/paper-item/.bower.json52
-rw-r--r--catapult/third_party/polymer/components/paper-item/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-item/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-item/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-item/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-item/README.md163
-rw-r--r--catapult/third_party/polymer/components/paper-item/all-imports.html13
-rw-r--r--catapult/third_party/polymer/components/paper-item/bower.json43
-rw-r--r--catapult/third_party/polymer/components/paper-item/demo/index.html191
-rw-r--r--catapult/third_party/polymer/components/paper-item/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-item/paper-icon-item.html86
-rw-r--r--catapult/third_party/polymer/components/paper-item/paper-item-behavior.html36
-rw-r--r--catapult/third_party/polymer/components/paper-item/paper-item-body.html83
-rw-r--r--catapult/third_party/polymer/components/paper-item/paper-item-shared-styles.html70
-rw-r--r--catapult/third_party/polymer/components/paper-item/paper-item.html111
-rw-r--r--catapult/third_party/polymer/components/paper-item/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/paper-item/test/paper-item.html218
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/.bower.json44
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/.travis.yml22
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/README.md70
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/bower.json35
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/demo/index.html93
-rwxr-xr-xcatapult/third_party/polymer/components/paper-listbox/hero.svg35
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/paper-listbox.html96
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/test/index.html34
-rw-r--r--catapult/third_party/polymer/components/paper-listbox/test/paper-listbox.html64
-rw-r--r--catapult/third_party/polymer/components/paper-material/.bower.json44
-rw-r--r--catapult/third_party/polymer/components/paper-material/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-material/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-material/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-material/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-material/README.md35
-rw-r--r--catapult/third_party/polymer/components/paper-material/bower.json35
-rw-r--r--catapult/third_party/polymer/components/paper-material/demo/index.html84
-rw-r--r--catapult/third_party/polymer/components/paper-material/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-material/paper-material-shared-styles.html42
-rw-r--r--catapult/third_party/polymer/components/paper-material/paper-material.html81
-rw-r--r--catapult/third_party/polymer/components/paper-material/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-material/test/paper-material.html88
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/.bower.json56
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/.travis.yml23
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/README.md69
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/bower.json47
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/demo/index.html203
-rwxr-xr-xcatapult/third_party/polymer/components/paper-menu-button/hero.svg39
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/paper-menu-button-animations.html109
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/paper-menu-button.html479
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-menu-button/test/paper-menu-button.html199
-rw-r--r--catapult/third_party/polymer/components/paper-menu/.bower.json48
-rw-r--r--catapult/third_party/polymer/components/paper-menu/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-menu/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-menu/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-menu/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-menu/README.md113
-rw-r--r--catapult/third_party/polymer/components/paper-menu/bower.json39
-rw-r--r--catapult/third_party/polymer/components/paper-menu/demo/index.html149
-rwxr-xr-xcatapult/third_party/polymer/components/paper-menu/hero.svg35
-rw-r--r--catapult/third_party/polymer/components/paper-menu/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-menu/paper-menu-shared-styles.html51
-rw-r--r--catapult/third_party/polymer/components/paper-menu/paper-menu.html100
-rw-r--r--catapult/third_party/polymer/components/paper-menu/paper-submenu.html223
-rw-r--r--catapult/third_party/polymer/components/paper-menu/test/index.html37
-rw-r--r--catapult/third_party/polymer/components/paper-menu/test/paper-menu.html64
-rw-r--r--catapult/third_party/polymer/components/paper-menu/test/paper-submenu.html285
-rw-r--r--catapult/third_party/polymer/components/paper-progress/.bower.json43
-rw-r--r--catapult/third_party/polymer/components/paper-progress/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-progress/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-progress/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-progress/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-progress/README.md49
-rw-r--r--catapult/third_party/polymer/components/paper-progress/bower.json33
-rw-r--r--catapult/third_party/polymer/components/paper-progress/demo/index.html127
-rwxr-xr-xcatapult/third_party/polymer/components/paper-progress/hero.svg21
-rw-r--r--catapult/third_party/polymer/components/paper-progress/index.html28
-rw-r--r--catapult/third_party/polymer/components/paper-progress/paper-progress.html354
-rw-r--r--catapult/third_party/polymer/components/paper-progress/test/basic.html148
-rw-r--r--catapult/third_party/polymer/components/paper-progress/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/.bower.json48
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/README.md56
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/bower.json38
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/demo/index.html79
-rwxr-xr-xcatapult/third_party/polymer/components/paper-radio-button/hero.svg22
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/paper-radio-button.html247
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/test/basic.html207
-rw-r--r--catapult/third_party/polymer/components/paper-radio-button/test/index.html28
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/.bower.json48
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/README.md59
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/bower.json38
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/demo/index.html76
-rwxr-xr-xcatapult/third_party/polymer/components/paper-radio-group/hero.svg25
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/paper-radio-group.html185
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/test/basic.html249
-rw-r--r--catapult/third_party/polymer/components/paper-radio-group/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/.bower.json45
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/.travis.yml23
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/README.md40
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/bower.json35
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/demo/index.html415
-rwxr-xr-xcatapult/third_party/polymer/components/paper-ripple/hero.svg30
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/index.html27
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/paper-ripple.html763
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-ripple/test/paper-ripple.html253
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/.bower.json47
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/.travis.yml23
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/README.md49
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/bower.json38
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/demo/index.html97
-rwxr-xr-xcatapult/third_party/polymer/components/paper-spinner/hero.svg27
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/index.html30
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/paper-spinner-behavior.html87
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/paper-spinner-lite.html71
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/paper-spinner-styles.html341
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/paper-spinner.html91
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/paper-spinner/test/paper-spinner.html86
-rw-r--r--catapult/third_party/polymer/components/paper-styles/.bower.json42
-rw-r--r--catapult/third_party/polymer/components/paper-styles/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-styles/README.md49
-rw-r--r--catapult/third_party/polymer/components/paper-styles/bower.json33
-rw-r--r--catapult/third_party/polymer/components/paper-styles/classes/global.html96
-rw-r--r--catapult/third_party/polymer/components/paper-styles/classes/shadow-layout.html307
-rw-r--r--catapult/third_party/polymer/components/paper-styles/classes/shadow.html52
-rw-r--r--catapult/third_party/polymer/components/paper-styles/classes/typography.html169
-rw-r--r--catapult/third_party/polymer/components/paper-styles/color.html333
-rw-r--r--catapult/third_party/polymer/components/paper-styles/default-theme.html72
-rw-r--r--catapult/third_party/polymer/components/paper-styles/demo-pages.html72
-rw-r--r--catapult/third_party/polymer/components/paper-styles/demo.css25
-rw-r--r--catapult/third_party/polymer/components/paper-styles/demo/index.html339
-rw-r--r--catapult/third_party/polymer/components/paper-styles/element-styles/paper-item-styles.html90
-rw-r--r--catapult/third_party/polymer/components/paper-styles/element-styles/paper-material-styles.html78
-rw-r--r--catapult/third_party/polymer/components/paper-styles/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-styles/paper-styles-classes.html14
-rw-r--r--catapult/third_party/polymer/components/paper-styles/paper-styles.html44
-rw-r--r--catapult/third_party/polymer/components/paper-styles/shadow.html76
-rw-r--r--catapult/third_party/polymer/components/paper-styles/typography.html169
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/.bower.json54
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/.travis.yml23
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/README.md46
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/bower.json44
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/demo/index.html338
-rwxr-xr-xcatapult/third_party/polymer/components/paper-tabs/hero.svg23
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/index.html25
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/paper-tab.html175
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/paper-tabs-icons.html18
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/paper-tabs.html661
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/test/attr-for-selected.html82
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/test/basic.html394
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/paper-tabs/test/links.html163
-rw-r--r--catapult/third_party/polymer/components/paper-toast/.bower.json45
-rw-r--r--catapult/third_party/polymer/components/paper-toast/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-toast/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-toast/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-toast/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-toast/README.md72
-rw-r--r--catapult/third_party/polymer/components/paper-toast/bower.json35
-rw-r--r--catapult/third_party/polymer/components/paper-toast/demo/index.html96
-rwxr-xr-xcatapult/third_party/polymer/components/paper-toast/hero.svg20
-rw-r--r--catapult/third_party/polymer/components/paper-toast/index.html26
-rw-r--r--catapult/third_party/polymer/components/paper-toast/paper-toast.html323
-rw-r--r--catapult/third_party/polymer/components/paper-toast/test/basic.html243
-rw-r--r--catapult/third_party/polymer/components/paper-toast/test/index.html31
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/.bower.json45
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/.github/ISSUE_TEMPLATE.md33
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/.gitignore1
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/.travis.yml24
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/README.md62
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/bower.json36
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/demo/index.html133
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/demo/test-button.html37
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/index.html28
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/paper-tooltip.html407
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/test/basic.html552
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/test/index.html24
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/test/test-button.html43
-rw-r--r--catapult/third_party/polymer/components/paper-tooltip/test/test-icon.html43
-rw-r--r--catapult/third_party/polymer/components/polymer/.bower.json45
-rw-r--r--catapult/third_party/polymer/components/polymer/LICENSE.txt27
-rw-r--r--catapult/third_party/polymer/components/polymer/bower.json35
-rw-r--r--catapult/third_party/polymer/components/polymer/build.log576
-rw-r--r--catapult/third_party/polymer/components/polymer/polymer-micro.html821
-rw-r--r--catapult/third_party/polymer/components/polymer/polymer-mini.html2246
-rw-r--r--catapult/third_party/polymer/components/polymer/polymer.html5555
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/.bower.json40
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/CONTRIBUTING.md77
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/Gruntfile.js49
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/LICENSE20
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/Promise-Statics.js49
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/Promise.js137
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/Promise.min.js12
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/README.md16
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/bower.json31
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/package.json35
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/promise-polyfill-lite.html16
-rw-r--r--catapult/third_party/polymer/components/promise-polyfill/promise-polyfill.html11
-rw-r--r--catapult/third_party/polymer/components/shadycss/.bower.json14
-rw-r--r--catapult/third_party/polymer/components/shadycss/.eslintignore1
-rw-r--r--catapult/third_party/polymer/components/shadycss/.eslintrc.json14
-rw-r--r--catapult/third_party/polymer/components/shadycss/.gitattributes1
-rw-r--r--catapult/third_party/polymer/components/shadycss/.github/CODEOWNERS1
-rw-r--r--catapult/third_party/polymer/components/shadycss/.gitignore2
-rw-r--r--catapult/third_party/polymer/components/shadycss/.travis.yml17
-rw-r--r--catapult/third_party/polymer/components/shadycss/LICENSE.md19
-rw-r--r--catapult/third_party/polymer/components/shadycss/README.md428
-rw-r--r--catapult/third_party/polymer/components/shadycss/apply-shim.html10
-rw-r--r--catapult/third_party/polymer/components/shadycss/apply-shim.min.js32
-rw-r--r--catapult/third_party/polymer/components/shadycss/apply-shim.min.js.map1
-rw-r--r--catapult/third_party/polymer/components/shadycss/custom-style-interface.html10
-rw-r--r--catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js15
-rw-r--r--catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js.map1
-rw-r--r--catapult/third_party/polymer/components/shadycss/entrypoints/apply-shim.js222
-rw-r--r--catapult/third_party/polymer/components/shadycss/entrypoints/custom-style-interface.js81
-rw-r--r--catapult/third_party/polymer/components/shadycss/entrypoints/scoping-shim.js106
-rw-r--r--catapult/third_party/polymer/components/shadycss/examples/custom-style-element.js42
-rw-r--r--catapult/third_party/polymer/components/shadycss/examples/document-style-lib.js54
-rw-r--r--catapult/third_party/polymer/components/shadycss/externs/shadycss-externs.js64
-rw-r--r--catapult/third_party/polymer/components/shadycss/gulpfile.js107
-rw-r--r--catapult/third_party/polymer/components/shadycss/package-lock.json12144
-rw-r--r--catapult/third_party/polymer/components/shadycss/package.json71
-rw-r--r--catapult/third_party/polymer/components/shadycss/scoping-shim.min.js58
-rw-r--r--catapult/third_party/polymer/components/shadycss/scoping-shim.min.js.map1
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/apply-shim-utils.js149
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/apply-shim.js525
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/common-regex.js19
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/common-utils.js59
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/css-parse.js264
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/custom-style-interface.js164
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/document-wait.js45
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/document-watcher.js198
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/scoping-shim.js570
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/style-cache.js52
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/style-info.js75
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/style-placeholder.js55
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/style-properties.js608
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/style-settings.js53
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/style-transformer.js487
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/style-util.js411
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/template-map.js17
-rw-r--r--catapult/third_party/polymer/components/shadycss/src/unscoped-style-handler.js40
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/.eslintrc.json11
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/apply-shim.html343
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/async-loading.html16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/chrome-devtools.html28
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/complicated-mixin-ordering.html104
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/css-parse.html188
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/custom-style-import.html49
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/custom-style-late.html89
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/custom-style-only.html76
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/custom-style.html100
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/deferred-apply.html82
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/dynamic-scoping.html296
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/html-imports/custom-style-import.html16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/lazy-init.html157
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/media-query.html69
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/mixin-fallbacks.html72
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/mixin-ordering.html143
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/css-parse.js17
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/custom-style-element.js15
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/make-element.js35
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/style-cache.js16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/style-info.js16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/style-placeholder.js15
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/style-properties.js16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/style-settings.js16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/style-transformer.js16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/style-util.js16
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/module/svg-in-shadow.js43
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-late.html76
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-only.html75
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style.html68
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/apply-shim.html309
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/complicated-mixin-ordering.html94
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-late.html79
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-only.html76
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style.html80
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/mixin-ordering.html141
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/ordering.html173
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/placeholder-ordering.html66
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/runner.html113
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/scoping-api.html131
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/scoping.html1058
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/settings.html61
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/style-transformer.html299
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/svg.html118
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/test-flags.js54
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/wc-1.html42
-rw-r--r--catapult/third_party/polymer/components/shadycss/tests/workarounds.html58
-rw-r--r--catapult/third_party/polymer/components/shadycss/wct.conf.json14
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/.bower.json40
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/CONTRIBUTING.md123
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/COPYING202
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/History.md265
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/README.md80
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/bower.json30
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/docs/examples.md241
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/docs/experimental.md58
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/docs/support.md69
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/externs/README.md10
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/externs/web-animations-next.js113
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/externs/web-animations.js126
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/animation.js279
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/apply-preserving-inline-style.js239
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/apply.js25
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/box-handler.js57
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/color-handler.js63
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/deprecation.js47
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/dev.js16
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/dimension-handler.js240
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/effect-callback.js98
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/element-animatable.js23
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/font-weight-handler.js42
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/group-constructors.js204
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/handler-utils.js177
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/interpolation.js49
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect-constructor.js183
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect.js57
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/keyframe-interpolations.js123
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/matrix-decomposition.js440
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/matrix-interpolation.js130
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/normalize-keyframes.js321
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/number-handler.js97
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/position-handler.js117
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/property-interpolation.js127
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/property-names.js40
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/scope.js20
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/shadow-handler.js108
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/shape-handler.js85
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/tick.js181
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/timeline.js101
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/timing-utilities.js420
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/transform-handler.js275
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/visibility-handler.js29
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-cancel-events.js83
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-object-form-keyframes.js63
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js383
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.html1
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js16
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js.map1
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.html1
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js16
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js.map1
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations.html50
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations.min.html1
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations.min.js16
-rw-r--r--catapult/third_party/polymer/components/web-animations-js/web-animations.min.js.map1
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/.bower.json30
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/CustomElements.js1029
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/CustomElements.min.js11
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.js1163
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.min.js11
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.js350
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.min.js11
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/README.md155
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.js4496
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.min.js13
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/bower.json21
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/build.log507
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/package.json31
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.js2505
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.min.js12
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/webcomponents.js7209
-rw-r--r--catapult/third_party/polymer/components/webcomponentsjs/webcomponents.min.js14
-rw-r--r--catapult/tracing/tracing/trace_data/trace_data.py79
-rw-r--r--catapult/tracing/tracing/trace_data/trace_data_unittest.py17
-rw-r--r--catapult/tracing/tracing_project.py6
-rwxr-xr-xupdate.py1
1307 files changed, 154999 insertions, 6204 deletions
diff --git a/UPSTREAM_REVISION b/UPSTREAM_REVISION
index 2856b7ab..63a5c44e 100644
--- a/UPSTREAM_REVISION
+++ b/UPSTREAM_REVISION
@@ -1 +1 @@
-cad35e22dcad126c6a20663ded101565e6326d82
+6dacd170b8de6d4a17d8b836d5f2f22c50e06154
diff --git a/catapult/common/bin/update_chrome_reference_binaries b/catapult/common/bin/update_chrome_reference_binaries
deleted file mode 100755
index e148c747..00000000
--- a/catapult/common/bin/update_chrome_reference_binaries
+++ /dev/null
@@ -1,229 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Updates the Chrome reference builds.
-
-Usage:
- $ /path/to/update_reference_build.py
- $ git commit -a
- $ git cl upload
-"""
-
-import collections
-import logging
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-import urllib2
-import zipfile
-
-sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'py_utils'))
-
-from py_utils import cloud_storage
-from dependency_manager import base_config
-
-
-def BuildNotFoundError(error_string):
- raise ValueError(error_string)
-
-
-_CHROME_BINARIES_CONFIG = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), '..', '..', 'common',
- 'py_utils', 'py_utils', 'chrome_binaries.json')
-
-CHROME_GS_BUCKET = 'chrome-unsigned'
-
-
-# Remove a platform name from this list to disable updating it.
-# Add one to enable updating it. (Must also update _PLATFORM_MAP.)
-_PLATFORMS_TO_UPDATE = ['mac_x86_64', 'win_x86', 'win_AMD64', 'linux_x86_64',
- 'android_k_armeabi-v7a', 'android_l_arm64-v8a',
- 'android_l_armeabi-v7a', 'android_n_armeabi-v7a',
- 'android_n_arm64-v8a']
-
-# Remove a channel name from this list to disable updating it.
-# Add one to enable updating it.
-_CHANNELS_TO_UPDATE = ['stable', 'canary', 'dev']
-
-
-# Omaha is Chrome's autoupdate server. It reports the current versions used
-# by each platform on each channel.
-_OMAHA_PLATFORMS = { 'stable': ['mac', 'linux', 'win', 'android'],
- 'dev': ['linux'], 'canary': ['mac', 'win']}
-
-
-# All of the information we need to update each platform.
-# omaha: name omaha uses for the platforms.
-# zip_name: name of the zip file to be retrieved from cloud storage.
-# gs_build: name of the Chrome build platform used in cloud storage.
-# destination: Name of the folder to download the reference build to.
-UpdateInfo = collections.namedtuple('UpdateInfo',
- 'omaha, gs_folder, gs_build, zip_name')
-_PLATFORM_MAP = {'mac_x86_64': UpdateInfo(omaha='mac',
- gs_folder='desktop-*',
- gs_build='mac64',
- zip_name='chrome-mac.zip'),
- 'win_x86': UpdateInfo(omaha='win',
- gs_folder='desktop-*',
- gs_build='win-clang',
- zip_name='chrome-win-clang.zip'),
- 'win_AMD64': UpdateInfo(omaha='win',
- gs_folder='desktop-*',
- gs_build='win64-clang',
- zip_name='chrome-win64-clang.zip'),
- 'linux_x86_64': UpdateInfo(omaha='linux',
- gs_folder='desktop-*',
- gs_build='linux64',
- zip_name='chrome-linux64.zip'),
- 'android_k_armeabi-v7a': UpdateInfo(omaha='android',
- gs_folder='android-*',
- gs_build='arm',
- zip_name='Chrome.apk'),
- 'android_l_arm64-v8a': UpdateInfo(omaha='android',
- gs_folder='android-*',
- gs_build='arm_64',
- zip_name='ChromeModern.apk'),
- 'android_l_armeabi-v7a': UpdateInfo(omaha='android',
- gs_folder='android-*',
- gs_build='arm',
- zip_name='Chrome.apk'),
- 'android_n_armeabi-v7a': UpdateInfo(omaha='android',
- gs_folder='android-*',
- gs_build='arm',
- zip_name='Monochrome.apk'),
- 'android_n_arm64-v8a': UpdateInfo(omaha='android',
- gs_folder='android-*',
- gs_build='arm_64',
- zip_name='Monochrome.apk'),
-
-}
-
-
-def _ChannelVersionsMap(channel):
- rows = _OmahaReportVersionInfo(channel)
- omaha_versions_map = _OmahaVersionsMap(rows, channel)
- channel_versions_map = {}
- for platform in _PLATFORMS_TO_UPDATE:
- omaha_platform = _PLATFORM_MAP[platform].omaha
- if omaha_platform in omaha_versions_map:
- channel_versions_map[platform] = omaha_versions_map[omaha_platform]
- return channel_versions_map
-
-
-def _OmahaReportVersionInfo(channel):
- url ='https://omahaproxy.appspot.com/all?channel=%s' % channel
- lines = urllib2.urlopen(url).readlines()
- return [l.split(',') for l in lines]
-
-
-def _OmahaVersionsMap(rows, channel):
- platforms = _OMAHA_PLATFORMS.get(channel, [])
- if (len(rows) < 1 or
- not rows[0][0:3] == ['os', 'channel', 'current_version']):
- raise ValueError(
- 'Omaha report is not in the expected form: %s.' % rows)
- versions_map = {}
- for row in rows[1:]:
- if row[1] != channel:
- raise ValueError(
- 'Omaha report contains a line with the channel %s' % row[1])
- if row[0] in platforms:
- versions_map[row[0]] = row[2]
- logging.warn('versions map: %s' % versions_map)
- if not all(platform in versions_map for platform in platforms):
- raise ValueError(
- 'Omaha report did not contain all desired platforms for channel %s' % channel)
- return versions_map
-
-
-def _QueuePlatformUpdate(platform, version, config, channel):
- """ platform: the name of the platform for the browser to
- be downloaded & updated from cloud storage. """
- platform_info = _PLATFORM_MAP[platform]
- filename = platform_info.zip_name
- # remote_path example: desktop-*/30.0.1595.0/precise32/chrome-precise32.zip
- remote_path = '%s/%s/%s/%s' % (
- platform_info.gs_folder, version, platform_info.gs_build, filename)
- if not cloud_storage.Exists(CHROME_GS_BUCKET, remote_path):
- cloud_storage_path = 'gs://%s/%s' % (CHROME_GS_BUCKET, remote_path)
- raise BuildNotFoundError(
- 'Failed to find %s build for version %s at path %s.' % (
- platform, version, cloud_storage_path))
- reference_builds_folder = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), 'chrome_telemetry_build',
- 'reference_builds', channel)
- if not os.path.exists(reference_builds_folder):
- os.makedirs(reference_builds_folder)
- local_dest_path = os.path.join(reference_builds_folder, filename)
- cloud_storage.Get(CHROME_GS_BUCKET, remote_path, local_dest_path)
- _ModifyBuildIfNeeded(local_dest_path, platform)
- config.AddCloudStorageDependencyUpdateJob(
- 'chrome_%s' % channel, platform, local_dest_path, version=version,
- execute_job=False)
-
-
-def _ModifyBuildIfNeeded(location, platform):
- """Hook to modify the build before saving it for Telemetry to use.
-
- This can be used to remove various utilities that cause noise in a
- test environment. Right now, it is just used to remove Keystone,
- which is a tool used to autoupdate Chrome.
- """
- if platform == 'mac_x86_64':
- _RemoveKeystoneFromBuild(location)
- return
-
- if 'mac' in platform:
- raise NotImplementedError(
- 'Platform <%s> sounds like it is an OSX version. If so, we may need to '
- 'remove Keystone from it per crbug.com/932615. Please edit this script'
- ' and teach it what needs to be done :).')
-
-
-def _RemoveKeystoneFromBuild(location):
- """Removes the Keystone autoupdate binary from the chrome mac zipfile."""
- logging.info('Removing keystone from mac build at %s' % location)
- temp_folder = tempfile.mkdtemp(prefix='RemoveKeystoneFromBuild')
- try:
- subprocess.check_call(['unzip', '-q', location, '-d', temp_folder])
- keystone_folder = os.path.join(
- temp_folder, 'chrome-mac', 'Google Chrome.app', 'Contents',
- 'Frameworks', 'Google Chrome Framework.framework', 'Frameworks',
- 'KeystoneRegistration.framework')
- shutil.rmtree(keystone_folder)
- os.remove(location)
- subprocess.check_call(['zip', '--quiet', '--recurse-paths', '--symlinks',
- location, 'chrome-mac'],
- cwd=temp_folder)
- finally:
- shutil.rmtree(temp_folder)
-
-
-def UpdateBuilds():
- config = base_config.BaseConfig(_CHROME_BINARIES_CONFIG, writable=True)
- for channel in _CHANNELS_TO_UPDATE:
- channel_versions_map = _ChannelVersionsMap(channel)
- for platform in channel_versions_map:
- print 'Downloading Chrome (%s channel) on %s' % (channel, platform)
- current_version = config.GetVersion('chrome_%s' % channel, platform)
- channel_version = channel_versions_map.get(platform)
- print 'current: %s, channel: %s' % (current_version, channel_version)
- if current_version and current_version == channel_version:
- continue
- _QueuePlatformUpdate(platform, channel_version, config, channel)
-
- print 'Updating chrome builds with downloaded binaries'
- config.ExecuteUpdateJobs(force=True)
-
-
-def main():
- logging.getLogger().setLevel(logging.DEBUG)
- UpdateBuilds()
-
-if __name__ == '__main__':
- main()
diff --git a/catapult/common/bin/update_chrome_reference_binaries.py b/catapult/common/bin/update_chrome_reference_binaries.py
new file mode 100755
index 00000000..86a1d7fe
--- /dev/null
+++ b/catapult/common/bin/update_chrome_reference_binaries.py
@@ -0,0 +1,393 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Updates the Chrome reference builds.
+
+Usage:
+ $ /path/to/update_reference_build.py
+ $ git commit -a
+ $ git cl upload
+"""
+
+import argparse
+import collections
+import logging
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import urllib2
+import zipfile
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'py_utils'))
+
+from py_utils import cloud_storage
+from dependency_manager import base_config
+
+
+_CHROME_BINARIES_CONFIG = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), '..', '..', 'common',
+ 'py_utils', 'py_utils', 'chrome_binaries.json')
+
+_CHROME_GS_BUCKET = 'chrome-unsigned'
+_CHROMIUM_GS_BUCKET = 'chromium-browser-snapshots'
+
+# How many commit positions to search below and above omaha branch position to
+# find closest chromium build snapshot. The value 10 is chosen because it looks
+# more than sufficient from manual inspection of the bucket.
+_CHROMIUM_SNAPSHOT_SEARCH_WINDOW = 10
+
+# Remove a platform name from this list to disable updating it.
+# Add one to enable updating it. (Must also update _PLATFORM_MAP.)
+_PLATFORMS_TO_UPDATE = ['mac_x86_64', 'win_x86', 'win_AMD64', 'linux_x86_64',
+ 'android_k_armeabi-v7a', 'android_l_arm64-v8a',
+ 'android_l_armeabi-v7a', 'android_n_armeabi-v7a',
+ 'android_n_arm64-v8a', 'android_n_bundle_armeabi-v7a',
+ 'android_n_bundle_arm64-v8a']
+
+# Add platforms here if you also want to update chromium binary for it.
+# Must add chromium_info for it in _PLATFORM_MAP.
+_CHROMIUM_PLATFORMS = ['mac_x86_64', 'win_x86', 'win_AMD64', 'linux_x86_64']
+
+# Remove a channel name from this list to disable updating it.
+# Add one to enable updating it.
+_CHANNELS_TO_UPDATE = ['stable', 'canary', 'dev']
+
+
+# Omaha is Chrome's autoupdate server. It reports the current versions used
+# by each platform on each channel.
+_OMAHA_PLATFORMS = { 'stable': ['mac', 'linux', 'win', 'android'],
+ 'dev': ['linux'], 'canary': ['mac', 'win']}
+
+
+# All of the information we need to update each platform.
+# omaha: name omaha uses for the platforms.
+# zip_name: name of the zip file to be retrieved from cloud storage.
+# gs_build: name of the Chrome build platform used in cloud storage.
+# chromium_info: information needed to update chromium (optional).
+# destination: Name of the folder to download the reference build to.
+UpdateInfo = collections.namedtuple('UpdateInfo',
+ 'omaha, gs_folder, gs_build, chromium_info, zip_name')
+# build_dir: name of the build directory in _CHROMIUM_GS_BUCKET.
+# zip_name: name of the zip file to be retrieved from cloud storage.
+ChromiumInfo = collections.namedtuple('ChromiumInfo', 'build_dir, zip_name')
+_PLATFORM_MAP = {'mac_x86_64': UpdateInfo(
+ omaha='mac',
+ gs_folder='desktop-*',
+ gs_build='mac64',
+ chromium_info=ChromiumInfo(
+ build_dir='Mac',
+ zip_name='chrome-mac.zip'),
+ zip_name='chrome-mac.zip'),
+ 'win_x86': UpdateInfo(
+ omaha='win',
+ gs_folder='desktop-*',
+ gs_build='win-clang',
+ chromium_info=ChromiumInfo(
+ build_dir='Win',
+ zip_name='chrome-win.zip'),
+ zip_name='chrome-win-clang.zip'),
+ 'win_AMD64': UpdateInfo(
+ omaha='win',
+ gs_folder='desktop-*',
+ gs_build='win64-clang',
+ chromium_info=ChromiumInfo(
+ build_dir='Win_x64',
+ zip_name='chrome-win.zip'),
+ zip_name='chrome-win64-clang.zip'),
+ 'linux_x86_64': UpdateInfo(
+ omaha='linux',
+ gs_folder='desktop-*',
+ gs_build='linux64',
+ chromium_info=ChromiumInfo(
+ build_dir='Linux_x64',
+ zip_name='chrome-linux.zip'),
+ zip_name='chrome-linux64.zip'),
+ 'android_k_armeabi-v7a': UpdateInfo(
+ omaha='android',
+ gs_folder='android-*',
+ gs_build='arm',
+ chromium_info=None,
+ zip_name='Chrome.apk'),
+ 'android_l_arm64-v8a': UpdateInfo(
+ omaha='android',
+ gs_folder='android-*',
+ gs_build='arm_64',
+ chromium_info=None,
+ zip_name='ChromeModern.apk'),
+ 'android_l_armeabi-v7a': UpdateInfo(
+ omaha='android',
+ gs_folder='android-*',
+ gs_build='arm',
+ chromium_info=None,
+ zip_name='Chrome.apk'),
+ 'android_n_armeabi-v7a': UpdateInfo(
+ omaha='android',
+ gs_folder='android-*',
+ gs_build='arm',
+ chromium_info=None,
+ zip_name='Monochrome.apk'),
+ 'android_n_arm64-v8a': UpdateInfo(
+ omaha='android',
+ gs_folder='android-*',
+ gs_build='arm_64',
+ chromium_info=None,
+ zip_name='Monochrome.apk'),
+ 'android_n_bundle_armeabi-v7a': UpdateInfo(
+ omaha='android',
+ gs_folder='android-*',
+ gs_build='arm',
+ chromium_info=None,
+ zip_name='Monochrome.apks'),
+ 'android_n_bundle_arm64-v8a': UpdateInfo(
+ omaha='android',
+ gs_folder='android-*',
+ gs_build='arm_64',
+ chromium_info=None,
+ zip_name='Monochrome.apks')
+
+}
+
+
+VersionInfo = collections.namedtuple('VersionInfo',
+ 'version, branch_base_position')
+
+
+def _ChannelVersionsMap(channel):
+ rows = _OmahaReportVersionInfo(channel)
+ omaha_versions_map = _OmahaVersionsMap(rows, channel)
+ channel_versions_map = {}
+ for platform in _PLATFORMS_TO_UPDATE:
+ omaha_platform = _PLATFORM_MAP[platform].omaha
+ if omaha_platform in omaha_versions_map:
+ channel_versions_map[platform] = omaha_versions_map[omaha_platform]
+ return channel_versions_map
+
+
+def _OmahaReportVersionInfo(channel):
+ url ='https://omahaproxy.appspot.com/all?channel=%s' % channel
+ lines = urllib2.urlopen(url).readlines()
+ return [l.split(',') for l in lines]
+
+
+def _OmahaVersionsMap(rows, channel):
+ platforms = _OMAHA_PLATFORMS.get(channel, [])
+ if (len(rows) < 1 or
+ rows[0][0:3] != ['os', 'channel', 'current_version'] or
+ rows[0][7] != 'branch_base_position'):
+ raise ValueError(
+ 'Omaha report is not in the expected form: %s.' % rows)
+ versions_map = {}
+ for row in rows[1:]:
+ if row[1] != channel:
+ raise ValueError(
+ 'Omaha report contains a line with the channel %s' % row[1])
+ if row[0] in platforms:
+ versions_map[row[0]] = VersionInfo(version=row[2],
+ branch_base_position=int(row[7]))
+ logging.warn('versions map: %s' % versions_map)
+ if not all(platform in versions_map for platform in platforms):
+ raise ValueError(
+ 'Omaha report did not contain all desired platforms '
+ 'for channel %s' % channel)
+ return versions_map
+
+
+RemotePath = collections.namedtuple('RemotePath', 'bucket, path')
+
+
+def _ResolveChromeRemotePath(platform_info, version_info):
+ # Path example: desktop-*/30.0.1595.0/precise32/chrome-precise32.zip
+ return RemotePath(bucket=_CHROME_GS_BUCKET,
+ path=('%s/%s/%s/%s' % (platform_info.gs_folder,
+ version_info.version,
+ platform_info.gs_build,
+ platform_info.zip_name)))
+
+
+def _FindClosestChromiumSnapshot(base_position, build_dir):
+ """Returns the closest chromium snapshot available in cloud storage.
+
+ Chromium snapshots are pulled from _CHROMIUM_BUILD_DIR in CHROMIUM_GS_BUCKET.
+
+ Continuous chromium snapshots do not always contain the exact release build.
+ This function queries the storage bucket and find the closest snapshot within
+ +/-_CHROMIUM_SNAPSHOT_SEARCH_WINDOW to find the closest build.
+ """
+ min_position = base_position - _CHROMIUM_SNAPSHOT_SEARCH_WINDOW
+ max_position = base_position + _CHROMIUM_SNAPSHOT_SEARCH_WINDOW
+
+ # Getting the full list of objects in cloud storage bucket is prohibitively
+ # slow. It's faster to list objects with a prefix. Assuming we're looking at
+ # +/- 10 commit positions, for commit position 123456, we want to look at
+ # positions between 123446 an 123466. We do this by getting all snapshots
+ # with prefix 12344*, 12345*, and 12346*. This may get a few more snapshots
+ # that we intended, but that's fine since we take the min distance anyways.
+ min_position_prefix = min_position / 10;
+ max_position_prefix = max_position / 10;
+
+ available_positions = []
+ for position_prefix in range(min_position_prefix, max_position_prefix + 1):
+ query = '%s/%d*' % (build_dir, position_prefix)
+ try:
+ ls_results = cloud_storage.ListDirs(_CHROMIUM_GS_BUCKET, query)
+ except cloud_storage.NotFoundError:
+ # It's fine if there is no chromium snapshot available for one prefix.
+ # We will look at the rest of the prefixes.
+ continue
+
+ for entry in ls_results:
+ # entry looks like '/Linux_x64/${commit_position}/'.
+ position = int(entry.split('/')[2])
+ available_positions.append(position)
+
+ if len(available_positions) == 0:
+ raise ValueError('No chromium build found +/-%d commit positions of %d' %
+ (_CHROMIUM_SNAPSHOT_SEARCH_WINDOW, base_position))
+
+ distance_function = lambda position: abs(position - base_position)
+ min_distance_snapshot = min(available_positions, key=distance_function)
+ return min_distance_snapshot
+
+
+def _ResolveChromiumRemotePath(channel, platform, version_info):
+ platform_info = _PLATFORM_MAP[platform]
+ branch_base_position = version_info.branch_base_position
+ omaha_version = version_info.version
+ build_dir = platform_info.chromium_info.build_dir
+ # Look through chromium-browser-snapshots for closest match.
+ closest_snapshot = _FindClosestChromiumSnapshot(
+ branch_base_position, build_dir)
+ if closest_snapshot != branch_base_position:
+ print ('Channel %s corresponds to commit position ' % channel +
+ '%d on %s, ' % (branch_base_position, platform) +
+ 'but closest chromium snapshot available on ' +
+ '%s is %d' % (_CHROMIUM_GS_BUCKET, closest_snapshot))
+ return RemotePath(bucket=_CHROMIUM_GS_BUCKET,
+ path = ('%s/%s/%s' % (build_dir, closest_snapshot,
+ platform_info.chromium_info.zip_name)))
+
+
+def _QueuePlatformUpdate(binary, platform, version_info, config, channel):
+ """ platform: the name of the platform for the browser to
+ be downloaded & updated from cloud storage. """
+ platform_info = _PLATFORM_MAP[platform]
+
+ if binary == 'chrome':
+ remote_path = _ResolveChromeRemotePath(platform_info, version_info)
+ elif binary == 'chromium':
+ remote_path = _ResolveChromiumRemotePath(channel, platform, version_info)
+ else:
+ raise ValueError('binary must be \'chrome\' or \'chromium\'')
+
+ if not cloud_storage.Exists(remote_path.bucket, remote_path.path):
+ cloud_storage_path = 'gs://%s/%s' % (remote_path.bucket, remote_path.path)
+ logging.warn('Failed to find %s build for version %s at path %s.' % (
+ platform, version_info.version, cloud_storage_path))
+ logging.warn('Skipping this update for this platform/channel.')
+ return
+
+ reference_builds_folder = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), 'chrome_telemetry_build',
+ 'reference_builds', binary, channel)
+ if not os.path.exists(reference_builds_folder):
+ os.makedirs(reference_builds_folder)
+ local_dest_path = os.path.join(reference_builds_folder,
+ platform,
+ platform_info.zip_name)
+ cloud_storage.Get(remote_path.bucket, remote_path.path, local_dest_path)
+ _ModifyBuildIfNeeded(binary, local_dest_path, platform)
+ config.AddCloudStorageDependencyUpdateJob('%s_%s' % (binary, channel),
+ platform, local_dest_path, version=version_info.version,
+ execute_job=False)
+
+
+def _ModifyBuildIfNeeded(binary, location, platform):
+ """Hook to modify the build before saving it for Telemetry to use.
+
+ This can be used to remove various utilities that cause noise in a
+ test environment. Right now, it is just used to remove Keystone,
+ which is a tool used to autoupdate Chrome.
+ """
+ if binary != 'chrome':
+ return
+
+ if platform == 'mac_x86_64':
+ _RemoveKeystoneFromBuild(location)
+ return
+
+ if 'mac' in platform:
+ raise NotImplementedError(
+ 'Platform <%s> sounds like it is an OSX version. If so, we may need to '
+ 'remove Keystone from it per crbug.com/932615. Please edit this script'
+ ' and teach it what needs to be done :).')
+
+
+def _RemoveKeystoneFromBuild(location):
+ """Removes the Keystone autoupdate binary from the chrome mac zipfile."""
+ logging.info('Removing keystone from mac build at %s' % location)
+ temp_folder = tempfile.mkdtemp(prefix='RemoveKeystoneFromBuild')
+ try:
+ subprocess.check_call(['unzip', '-q', location, '-d', temp_folder])
+ keystone_folder = os.path.join(
+ temp_folder, 'chrome-mac', 'Google Chrome.app', 'Contents',
+ 'Frameworks', 'Google Chrome Framework.framework', 'Frameworks',
+ 'KeystoneRegistration.framework')
+ shutil.rmtree(keystone_folder)
+ os.remove(location)
+ subprocess.check_call(['zip', '--quiet', '--recurse-paths', '--symlinks',
+ location, 'chrome-mac'],
+ cwd=temp_folder)
+ finally:
+ shutil.rmtree(temp_folder)
+
+
+def _NeedsUpdate(config, binary, channel, platform, version_info):
+ channel_version = version_info.version
+ print 'Checking %s (%s channel) on %s' % (binary, channel, platform)
+ current_version = config.GetVersion('%s_%s' % (binary, channel), platform)
+ print 'current: %s, channel: %s' % (current_version, channel_version)
+ if current_version and current_version == channel_version:
+ print 'Already up to date.'
+ return False
+ return True
+
+
+def UpdateBuilds(args):
+ config = base_config.BaseConfig(_CHROME_BINARIES_CONFIG, writable=True)
+ for channel in _CHANNELS_TO_UPDATE:
+ channel_versions_map = _ChannelVersionsMap(channel)
+ for platform in channel_versions_map:
+ version_info = channel_versions_map.get(platform)
+ if args.update_chrome:
+ if _NeedsUpdate(config, 'chrome', channel, platform, version_info):
+ _QueuePlatformUpdate('chrome', platform, version_info, config,
+ channel)
+ if args.update_chromium and platform in _CHROMIUM_PLATFORMS:
+ if _NeedsUpdate(config, 'chromium', channel, platform, version_info):
+ _QueuePlatformUpdate('chromium', platform, version_info,
+ config, channel)
+
+ print 'Updating builds with downloaded binaries'
+ config.ExecuteUpdateJobs(force=True)
+
+
+def main():
+ logging.getLogger().setLevel(logging.DEBUG)
+ parser = argparse.ArgumentParser(
+ description='Update reference binaries used by perf bots.')
+ parser.add_argument('--no-update-chrome', action='store_false',
+ dest='update_chrome', default=True,
+ help='do not update chrome binaries')
+ parser.add_argument('--no-update-chromium', action='store_false',
+ dest='update_chromium', default=True,
+ help='do not update chromium binaries')
+ args = parser.parse_args()
+ UpdateBuilds(args)
+
+if __name__ == '__main__':
+ main()
diff --git a/catapult/common/node_runner/node_runner/node_binaries.json b/catapult/common/node_runner/node_runner/node_binaries.json
index 3a17db02..d5e1cc5d 100644
--- a/catapult/common/node_runner/node_runner/node_binaries.json
+++ b/catapult/common/node_runner/node_runner/node_binaries.json
@@ -31,19 +31,19 @@
"file_info": {
"linux_x86_64": {
"cloud_storage_hash": "5750e968975e7f5ab8cb694f5e92a34a890e129d",
- "download_path": "bin/node/node-linux64.zip",
+ "download_path": "bin/node/npm-linux64.zip",
"path_within_archive": "node-v6.7.0-linux-x64/lib/node_modules/npm/bin/npm-cli.js",
"version_in_cs": "6.7.0"
},
"mac_x86_64": {
"cloud_storage_hash": "1af7c221e530165af8a6ab8ff7ccb1f2dd54036d",
- "download_path": "bin/node/node-mac64.zip",
+ "download_path": "bin/node/npm-mac64.zip",
"path_within_archive": "node-v6.7.0-darwin-x64/lib/node_modules/npm/bin/npm-cli.js",
"version_in_cs": "6.7.0"
},
"win_AMD64": {
"cloud_storage_hash": "23f21bfb2edf874a8b6bdb6c1acb408bc7edeced",
- "download_path": "bin/node/node-win64.zip",
+ "download_path": "bin/node/npm-win64.zip",
"path_within_archive": "node-v6.7.0-win-x64\\node_modules\\npm\\bin\\npm-cli.js",
"version_in_cs": "6.7.0"
}
diff --git a/catapult/common/node_runner/node_runner/package.json b/catapult/common/node_runner/node_runner/package.json
index 526650d7..9f6b4878 100644
--- a/catapult/common/node_runner/node_runner/package.json
+++ b/catapult/common/node_runner/node_runner/package.json
@@ -59,6 +59,7 @@
"sinon": "^7.2.3",
"vulcanize": "^1.16.0",
"webpack": "^4.16.1",
- "webpack-command": "^0.4.1"
+ "webpack-command": "^0.4.1",
+ "polymer-bundler": "^4.0.10"
}
}
diff --git a/catapult/common/py_trace_event/py_trace_event/trace_event.py b/catapult/common/py_trace_event/py_trace_event/trace_event.py
index f87c278f..88eef21e 100644
--- a/catapult/common/py_trace_event/py_trace_event/trace_event.py
+++ b/catapult/common/py_trace_event/py_trace_event/trace_event.py
@@ -102,6 +102,9 @@ if trace_event_impl:
def trace_add_benchmark_metadata(*args, **kwargs):
trace_event_impl.trace_add_benchmark_metadata(*args, **kwargs)
+ def trace_set_clock_snapshot(*args, **kwargs):
+ trace_event_impl.trace_set_clock_snapshot(*args, **kwargs)
+
def trace(name, **kwargs):
return trace_event_impl.trace(name, **kwargs)
@@ -110,21 +113,8 @@ if trace_event_impl:
def traced(fn):
return trace_event_impl.traced(fn)
- def clock_sync(sync_id, issue_ts=None):
- '''
- Add a clock sync event to the trace log.
-
- Args:
- sync_id: ID of clock sync event.
- issue_ts: Time at which clock sync was issued, in microseconds.
- '''
- time_stamp = trace_time.Now()
- args_to_log = {'sync_id': sync_id}
- if issue_ts: # Issuer if issue_ts is set, else reciever.
- assert issue_ts <= time_stamp
- args_to_log['issue_ts'] = issue_ts
- trace_event_impl.add_trace_event(
- "c", time_stamp, "python", "clock_sync", args_to_log)
+ def clock_sync(*args, **kwargs):
+ return trace_event_impl.clock_sync(*args, **kwargs)
def is_tracing_controllable():
return trace_event_impl.is_tracing_controllable()
@@ -268,7 +258,8 @@ trace.__doc__ = """Traces a block of code using a with statement.
If tracing an entire function call, prefer the @traced decorator.
"""
-traced.__doc__ = """
+traced.__doc__ = """Traces the provided function.
+
Traces the provided function, using the function name for the actual generated
event.
@@ -284,12 +275,15 @@ traced.__doc__ = """
urllib2.urlopen(url).read()
"""
-clock_sync.__doc__ = """
- Issues a clock sync marker event.
+clock_sync.__doc__ = """Issues a clock sync marker event.
Clock sync markers are used to synchronize the clock domains of different
traces so that they can be used together. It takes a sync_id, and if it is
the issuer of a clock sync event it will also require an issue_ts. The
issue_ts is a timestamp from when the clocksync was first issued. This is used
to calculate the time difference between clock domains.
+
+ This function has no effect if trace format is proto and
+ trace_set_clock_snapshot was called before trace start. The synchronization
+ will be performed in trace_processor using clock snapshots in this case.
"""
diff --git a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/log.py b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/log.py
index 7af86daf..130d1683 100644
--- a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/log.py
+++ b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/log.py
@@ -42,6 +42,24 @@ _log_file = None
_cur_events = [] # events that have yet to be buffered
_benchmark_metadata = {}
+# Default timestamp values for clock snapshot.
+# If a ClockSnapshot message with these default values is emitted, Telemetry
+# events' time will not be translated by trace processor, because both
+# TELEMETRY and BOOTTIME timestamps are the same. This allows the old-style
+# synchronization (using clock_sync events) to take place in catapult.
+# If we want to actually synchronize Telemetry with other trace producers
+# via clock snapshots in trace processor, we should set _boottime_ts to the
+# actual BOOTTIME of the device and _emit_clock_sync to False. In this case,
+# trace processor will translate both Chrome's and telemetry's timestamps
+# to the device time (BOOTTIME) during proto-to-json conversion, and catapult's
+# clock synchronization will not take place because we do not emit the
+# clock_sync event.
+# Note that we can't use both synchronization methods at the same time
+# because that will lead to wrong results.
+_telemetry_ts = trace_time.Now()
+_boottime_ts = _telemetry_ts
+_emit_clock_sync = True
+
_tls = threading.local() # tls used to detect forking/etc
_atexit_regsitered_for_pid = None
@@ -72,7 +90,7 @@ def _disallow_tracing_control():
_control_allowed = False
def trace_enable(log_file=None, format=None):
- """ Enable tracing.
+ """Enable tracing.
Args:
log_file: file to write trace into. Can be a file-like object,
@@ -80,17 +98,24 @@ def trace_enable(log_file=None, format=None):
from executable name.
format: trace file format. See trace_event.py for available options.
"""
+ global _emit_clock_sync
if format is None:
format = JSON
+ # Can only write clock snapshots in protobuf format. In all other formats
+ # should use clock_syncs.
+ if format != PROTOBUF:
+ _emit_clock_sync = True
_trace_enable(log_file, format)
def _write_header():
- tid = threading.current_thread().ident
- if not tid:
- tid = os.getpid()
-
if _format == PROTOBUF:
tid = threading.current_thread().ident
+ perfetto_trace_writer.write_clock_snapshot(
+ output=_log_file,
+ tid=tid,
+ telemetry_ts=_telemetry_ts,
+ boottime_ts=_boottime_ts,
+ )
perfetto_trace_writer.write_thread_descriptor_event(
output=_log_file,
pid=os.getpid(),
@@ -103,7 +128,7 @@ def _write_header():
category="process_argv",
name="process_argv",
ts=trace_time.Now(),
- args=sys.argv,
+ args={"argv": sys.argv},
tid=tid,
)
else:
@@ -183,6 +208,9 @@ def trace_disable():
return
_enabled = False
_flush(close=True)
+ # Clear the collected interned data so that the next trace session
+ # could start from a clean state.
+ perfetto_trace_writer.reset_global_state()
multiprocessing.Process = _original_multiprocessing_process
def _write_cur_events():
@@ -289,9 +317,8 @@ def trace_add_benchmark_metadata(
story_tags,
story_run_index,
label=None,
- had_failures=None,
):
- """ Add benchmark metadata to be written to trace file.
+ """Add benchmark metadata to be written to trace file.
Args:
benchmark_start_time_us: Benchmark start time in microseconds.
@@ -317,8 +344,12 @@ def trace_add_benchmark_metadata(
story_tags=story_tags,
story_run_index=story_run_index,
label=label,
- had_failures=had_failures,
)
+ if _emit_clock_sync:
+ perfetto_trace_writer.write_chrome_metadata(
+ output=_log_file,
+ clock_domain="TELEMETRY",
+ )
elif _format == JSON_WITH_METADATA:
# Store metadata to write it in the footer.
telemetry_metadata_for_json = {
@@ -332,9 +363,8 @@ def trace_add_benchmark_metadata(
}
if label:
telemetry_metadata_for_json["labels"] = [label]
- if had_failures:
- telemetry_metadata_for_json["hadFailures"] = [had_failures]
+ assert _emit_clock_sync
_benchmark_metadata = {
# TODO(crbug.com/948633): For right now, we use "TELEMETRY" as the
# clock domain to guarantee that Telemetry is given its own clock
@@ -356,6 +386,48 @@ def trace_add_benchmark_metadata(
else:
raise TraceException("Unknown format: %s" % _format)
+def trace_set_clock_snapshot(telemetry_ts, boottime_ts):
+ """Set timestamps to be written in a ClockSnapshot message.
+
+ This function must be called before the trace start. When trace starts,
+ a ClockSnapshot message with given timestamps will be written in protobuf
+ format. In json format, nothing will happen. Use clock_sync function
+ for clock synchronization in json format.
+
+ Args:
+ telemetry_ts: BOOTTIME of the device where Telemetry runs.
+ boottime_ts: BOOTTIME of the device where the tested browser runs.
+ """
+ global _telemetry_ts
+ global _boottime_ts
+ global _emit_clock_sync
+ global _enabled
+ if _enabled:
+ raise TraceException("Can't set the clock snapshot after trace started.")
+ _telemetry_ts = telemetry_ts
+ _boottime_ts = boottime_ts
+ _emit_clock_sync = False
+
+def clock_sync(sync_id, issue_ts=None):
+ """Add a clock sync event to the trace log.
+
+ Adds a clock sync event if the TBMv2-style synchronization is enabled.
+ It's enabled in two cases:
+ 1) Trace format is json.
+ 2) The clock snapshot was not set before the trace start.
+
+ Args:
+ sync_id: ID of clock sync event.
+ issue_ts: Time at which clock sync was issued, in microseconds.
+ """
+ if _emit_clock_sync:
+ time_stamp = trace_time.Now()
+ args_to_log = {"sync_id": sync_id}
+ if issue_ts: # Issuer if issue_ts is set, else reciever.
+ assert issue_ts <= time_stamp
+ args_to_log["issue_ts"] = issue_ts
+ add_trace_event("c", time_stamp, "python", "clock_sync", args_to_log)
+
def _trace_disable_atexit():
trace_disable()
diff --git a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_proto_classes.py b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_proto_classes.py
index 2da179be..5be4a8e1 100644
--- a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_proto_classes.py
+++ b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_proto_classes.py
@@ -21,15 +21,32 @@ import wire_format
class TracePacket(object):
def __init__(self):
+ self.clock_snapshot = None
+ self.timestamp = None
+ self.timestamp_clock_id = None
self.interned_data = None
self.thread_descriptor = None
self.incremental_state_cleared = None
+ self.chrome_event = None
self.track_event = None
self.trusted_packet_sequence_id = None
self.chrome_benchmark_metadata = None
def encode(self):
parts = []
+ if self.chrome_event is not None:
+ tag = encoder.TagBytes(5, wire_format.WIRETYPE_LENGTH_DELIMITED)
+ data = self.chrome_event.encode()
+ length = encoder._VarintBytes(len(data))
+ parts += [tag, length, data]
+ if self.clock_snapshot is not None:
+ tag = encoder.TagBytes(6, wire_format.WIRETYPE_LENGTH_DELIMITED)
+ data = self.clock_snapshot.encode()
+ length = encoder._VarintBytes(len(data))
+ parts += [tag, length, data]
+ if self.timestamp is not None:
+ writer = encoder.UInt64Encoder(8, False, False)
+ writer(parts.append, self.timestamp)
if self.trusted_packet_sequence_id is not None:
writer = encoder.UInt32Encoder(10, False, False)
writer(parts.append, self.trusted_packet_sequence_id)
@@ -56,6 +73,9 @@ class TracePacket(object):
data = self.chrome_benchmark_metadata.encode()
length = encoder._VarintBytes(len(data))
parts += [tag, length, data]
+ if self.timestamp_clock_id is not None:
+ writer = encoder.UInt32Encoder(58, False, False)
+ writer(parts.append, self.timestamp_clock_id)
return b"".join(parts)
@@ -106,11 +126,9 @@ class ThreadDescriptor(object):
def __init__(self):
self.pid = None
self.tid = None
- self.reference_timestamp_us = None
def encode(self):
- if (self.pid is None or self.tid is None or
- self.reference_timestamp_us is None):
+ if (self.pid is None or self.tid is None):
raise RuntimeError("Missing mandatory fields.")
parts = []
@@ -118,35 +136,46 @@ class ThreadDescriptor(object):
writer(parts.append, self.pid)
writer = encoder.UInt32Encoder(2, False, False)
writer(parts.append, self.tid)
- writer = encoder.Int64Encoder(6, False, False)
- writer(parts.append, self.reference_timestamp_us)
+
+ return b"".join(parts)
+
+
+class ChromeEventBundle(object):
+ def __init__(self):
+ self.metadata = []
+
+ def encode(self):
+ parts = []
+ for item in self.metadata:
+ tag = encoder.TagBytes(2, wire_format.WIRETYPE_LENGTH_DELIMITED)
+ data = item.encode()
+ length = encoder._VarintBytes(len(data))
+ parts += [tag, length, data]
return b"".join(parts)
class TrackEvent(object):
def __init__(self):
- self.timestamp_absolute_us = None
- self.timestamp_delta_us = None
self.legacy_event = None
self.category_iids = None
+ self.debug_annotations = []
def encode(self):
parts = []
- if self.timestamp_delta_us is not None:
- writer = encoder.Int64Encoder(1, False, False)
- writer(parts.append, self.timestamp_delta_us)
if self.category_iids is not None:
writer = encoder.UInt32Encoder(3, is_repeated=True, is_packed=False)
writer(parts.append, self.category_iids)
+ for annotation in self.debug_annotations:
+ tag = encoder.TagBytes(4, wire_format.WIRETYPE_LENGTH_DELIMITED)
+ data = annotation.encode()
+ length = encoder._VarintBytes(len(data))
+ parts += [tag, length, data]
if self.legacy_event is not None:
tag = encoder.TagBytes(6, wire_format.WIRETYPE_LENGTH_DELIMITED)
data = self.legacy_event.encode()
length = encoder._VarintBytes(len(data))
parts += [tag, length, data]
- if self.timestamp_absolute_us is not None:
- writer = encoder.Int64Encoder(16, False, False)
- writer(parts.append, self.timestamp_absolute_us)
return b"".join(parts)
@@ -178,7 +207,6 @@ class ChromeBenchmarkMetadata(object):
self.story_tags = None
self.story_run_index = None
self.label = None
- self.had_failures = None
def encode(self):
parts = []
@@ -206,9 +234,6 @@ class ChromeBenchmarkMetadata(object):
if self.story_run_index is not None:
writer = encoder.Int32Encoder(8, False, False)
writer(parts.append, self.story_run_index)
- if self.had_failures is not None:
- writer = encoder.BoolEncoder(9, False, False)
- writer(parts.append, self.had_failures)
return b"".join(parts)
@@ -220,3 +245,87 @@ def write_trace_packet(output, trace_packet):
encoder._EncodeVarint(output.write, len(binary_data))
output.write(binary_data)
+
+class DebugAnnotation(object):
+ def __init__(self):
+ self.name = None
+ self.int_value = None
+ self.double_value = None
+ self.string_value = None
+
+ def encode(self):
+ if self.name is None:
+ raise RuntimeError("DebugAnnotation must have a name.")
+ if ((self.string_value is not None) +
+ (self.int_value is not None) +
+ (self.double_value is not None)) != 1:
+ raise RuntimeError("DebugAnnotation must have exactly one value.")
+
+ parts = []
+ writer = encoder.StringEncoder(10, False, False)
+ writer(parts.append, self.name)
+ if self.int_value is not None:
+ writer = encoder.Int64Encoder(4, False, False)
+ writer(parts.append, self.int_value)
+ if self.double_value is not None:
+ writer = encoder.DoubleEncoder(5, False, False)
+ writer(parts.append, self.double_value)
+ if self.string_value is not None:
+ writer = encoder.StringEncoder(6, False, False)
+ writer(parts.append, self.string_value)
+
+ return b"".join(parts)
+
+
+class ChromeMetadata(object):
+ def __init__(self):
+ self.name = None
+ self.string_value = None
+
+ def encode(self):
+ if self.name is None or self.string_value is None:
+ raise RuntimeError("ChromeMetadata must have a name and a value.")
+
+ parts = []
+ writer = encoder.StringEncoder(1, False, False)
+ writer(parts.append, self.name)
+ writer = encoder.StringEncoder(2, False, False)
+ writer(parts.append, self.string_value)
+
+ return b"".join(parts)
+
+
+class Clock(object):
+ def __init__(self):
+ self.clock_id = None
+ self.timestamp = None
+
+ def encode(self):
+ if self.clock_id is None or self.timestamp is None:
+ raise RuntimeError("Clock must have a clock_id and a timestamp.")
+
+ parts = []
+ writer = encoder.UInt32Encoder(1, False, False)
+ writer(parts.append, self.clock_id)
+ writer = encoder.UInt64Encoder(2, False, False)
+ writer(parts.append, self.timestamp)
+
+ return b"".join(parts)
+
+
+class ClockSnapshot(object):
+ def __init__(self):
+ self.clocks = []
+
+ def encode(self):
+ if len(self.clocks) < 2:
+ raise RuntimeError("ClockSnapshot must have at least two clocks.")
+
+ parts = []
+ for clock in self.clocks:
+ tag = encoder.TagBytes(1, wire_format.WIRETYPE_LENGTH_DELIMITED)
+ data = clock.encode()
+ length = encoder._VarintBytes(len(data))
+ parts += [tag, length, data]
+
+ return b"".join(parts)
diff --git a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer.py b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer.py
index 37809538..584352cf 100644
--- a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer.py
+++ b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer.py
@@ -9,24 +9,31 @@ import collections
import perfetto_proto_classes as proto
+CLOCK_BOOTTIME = 6
+CLOCK_TELEMETRY = 64
-# Dicts of strings for interning.
-# Note that each thread has its own interning index.
-_interned_categories_by_tid = collections.defaultdict(dict)
-_interned_event_names_by_tid = collections.defaultdict(dict)
+def reset_global_state():
+ global _interned_categories_by_tid
+ global _interned_event_names_by_tid
+ global _next_sequence_id
+ global _sequence_ids
+
+ # Dicts of strings for interning.
+ # Note that each thread has its own interning index.
+ _interned_categories_by_tid = collections.defaultdict(dict)
+ _interned_event_names_by_tid = collections.defaultdict(dict)
-# Trusted sequence ids from telemetry should not overlap with
-# trusted sequence ids from other trace producers. Chrome assigns
-# sequence ids incrementally starting from 1 and we expect all its ids
-# to be well below 10000. Starting from 2^20 will give us enough
-# confidence that it will not overlap.
-_next_sequence_id = 1<<20
-_sequence_ids = {}
+ # Trusted sequence ids from telemetry should not overlap with
+ # trusted sequence ids from other trace producers. Chrome assigns
+ # sequence ids incrementally starting from 1 and we expect all its ids
+ # to be well below 10000. Starting from 2^20 will give us enough
+ # confidence that it will not overlap.
+ _next_sequence_id = 1<<20
+ _sequence_ids = {}
-# Timestamp of the last event from each thread. Used for delta-encoding
-# of timestamps.
-_last_timestamps = {}
+
+reset_global_state()
def _get_sequence_id(tid):
@@ -67,7 +74,7 @@ def _intern_event_name(event_name, trace_packet, tid):
def write_thread_descriptor_event(output, pid, tid, ts):
- """ Write the first event in a sequence.
+ """Write the first event in a sequence.
Call this function before writing any other events.
Note that this function is NOT thread-safe.
@@ -78,12 +85,11 @@ def write_thread_descriptor_event(output, pid, tid, ts):
tid: thread ID.
ts: timestamp in microseconds.
"""
- global _last_timestamps
- ts_us = int(ts)
- _last_timestamps[tid] = ts_us
-
thread_descriptor_packet = proto.TracePacket()
thread_descriptor_packet.trusted_packet_sequence_id = _get_sequence_id(tid)
+ thread_descriptor_packet.timestamp = int(ts * 1e3)
+ thread_descriptor_packet.timestamp_clock_id = CLOCK_TELEMETRY
+
thread_descriptor_packet.thread_descriptor = proto.ThreadDescriptor()
thread_descriptor_packet.thread_descriptor.pid = pid
# Thread ID from threading module doesn't fit into int32.
@@ -91,14 +97,13 @@ def write_thread_descriptor_event(output, pid, tid, ts):
# distinguish one thread from another. We assume that the last 31 bits
# will do for that purpose.
thread_descriptor_packet.thread_descriptor.tid = tid & 0x7FFFFFFF
- thread_descriptor_packet.thread_descriptor.reference_timestamp_us = ts_us
thread_descriptor_packet.incremental_state_cleared = True;
proto.write_trace_packet(output, thread_descriptor_packet)
def write_event(output, ph, category, name, ts, args, tid):
- """ Write a trace event.
+ """Write a trace event.
Note that this function is NOT thread-safe.
@@ -108,30 +113,49 @@ def write_event(output, ph, category, name, ts, args, tid):
category: category of event.
name: event name.
ts: timestamp in microseconds.
- args: this argument is currently ignored.
+ args: dict of arbitrary key-values to be stored as DebugAnnotations.
tid: thread ID.
"""
- del args # TODO(khokhlov): Encode args as DebugAnnotations.
-
- global _last_timestamps
- ts_us = int(ts)
- delta_ts = ts_us - _last_timestamps[tid]
-
packet = proto.TracePacket()
packet.trusted_packet_sequence_id = _get_sequence_id(tid)
- packet.track_event = proto.TrackEvent()
-
- if delta_ts >= 0:
- packet.track_event.timestamp_delta_us = delta_ts
- _last_timestamps[tid] = ts_us
- else:
- packet.track_event.timestamp_absolute_us = ts_us
+ packet.timestamp = int(ts * 1e3)
+ packet.timestamp_clock_id = CLOCK_TELEMETRY
+ packet.track_event = proto.TrackEvent()
packet.track_event.category_iids = [_intern_category(category, packet, tid)]
legacy_event = proto.LegacyEvent()
legacy_event.phase = ord(ph)
legacy_event.name_iid = _intern_event_name(name, packet, tid)
packet.track_event.legacy_event = legacy_event
+
+ for name, value in args.iteritems():
+ debug_annotation = proto.DebugAnnotation()
+ debug_annotation.name = name
+ if isinstance(value, int):
+ debug_annotation.int_value = value
+ elif isinstance(value, float):
+ debug_annotation.double_value = value
+ else:
+ debug_annotation.string_value = str(value)
+ packet.track_event.debug_annotations.append(debug_annotation)
+
+ proto.write_trace_packet(output, packet)
+
+
+def write_chrome_metadata(output, clock_domain):
+ """Write a chrome trace event with metadata.
+
+ Args:
+ output: a file-like object to write events into.
+ clock_domain: a string representing the trace clock domain.
+ """
+ chrome_metadata = proto.ChromeMetadata()
+ chrome_metadata.name = 'clock-domain'
+ chrome_metadata.string_value = clock_domain
+ chrome_event = proto.ChromeEventBundle()
+ chrome_event.metadata.append(chrome_metadata)
+ packet = proto.TracePacket()
+ packet.chrome_event = chrome_event
proto.write_trace_packet(output, packet)
@@ -145,8 +169,8 @@ def write_metadata(
story_tags,
story_run_index,
label=None,
- had_failures=None,
):
+ """Write a ChromeBenchmarkMetadata message."""
metadata = proto.ChromeBenchmarkMetadata()
metadata.benchmark_start_time_us = int(benchmark_start_time_us)
metadata.story_run_time_us = int(story_run_time_us)
@@ -157,10 +181,39 @@ def write_metadata(
metadata.story_run_index = int(story_run_index)
if label is not None:
metadata.label = label
- if had_failures is not None:
- metadata.had_failures = had_failures
packet = proto.TracePacket()
packet.chrome_benchmark_metadata = metadata
proto.write_trace_packet(output, packet)
+
+def write_clock_snapshot(
+ output,
+ tid,
+ telemetry_ts=None,
+ boottime_ts=None,
+):
+ """Write a ClockSnapshot message.
+
+ Note that this function is NOT thread-safe.
+
+ Args:
+ output: a file-like object to write events into.
+ telemetry_ts: host BOOTTIME timestamp in microseconds.
+ boottime_ts: device BOOTTIME timestamp in microseconds.
+ """
+ clock_snapshot = proto.ClockSnapshot()
+ if telemetry_ts is not None:
+ clock = proto.Clock()
+ clock.clock_id = CLOCK_TELEMETRY
+ clock.timestamp = int(telemetry_ts * 1e3)
+ clock_snapshot.clocks.append(clock)
+ if boottime_ts is not None:
+ clock = proto.Clock()
+ clock.clock_id = CLOCK_BOOTTIME
+ clock.timestamp = int(boottime_ts * 1e3)
+ clock_snapshot.clocks.append(clock)
+ packet = proto.TracePacket()
+ packet.trusted_packet_sequence_id = _get_sequence_id(tid)
+ packet.clock_snapshot = clock_snapshot
+ proto.write_trace_packet(output, packet)
diff --git a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer_unittest.py b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer_unittest.py
index e49a0a4b..00dafa46 100644
--- a/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer_unittest.py
+++ b/catapult/common/py_trace_event/py_trace_event/trace_event_impl/perfetto_trace_writer_unittest.py
@@ -15,7 +15,8 @@ class PerfettoTraceWriterTest(unittest.TestCase):
TODO(crbug.com/944078): Switch to using python-protobuf library
and implement proper protobuf parsing then.
"""
-
+ def setUp(self):
+ perfetto_trace_writer.reset_global_state()
def testWriteThreadDescriptorEvent(self):
result = StringIO.StringIO()
@@ -26,8 +27,8 @@ class PerfettoTraceWriterTest(unittest.TestCase):
ts=1556716807306000,
)
expected_output = (
- '\n\x17P\x80\x80@\xc8\x02\x01\xe2\x02\r\x08\x01\x10'
- '\x020\x90\xf6\xc2\x82\xb6\xfa\xe1\x02'
+ '\n\x1b@\x80\xec\xea\xda\x83\xb6\xa4\xcd\x15P\x80\x80@'
+ '\xc8\x02\x01\xe2\x02\x04\x08\x01\x10\x02\xd0\x03@'
)
self.assertEqual(expected_output, result.getvalue())
@@ -49,10 +50,11 @@ class PerfettoTraceWriterTest(unittest.TestCase):
tid=2,
)
expected_output = (
- '\n\x17P\x80\x80@\xc8\x02\x01\xe2\x02\r\x08\x01\x10'
- '\x020\x90\xf6\xc2\x82\xb6\xfa\xe1\x02\n2P\x80\x80@Z\x0c\x08'
- '\xa0\x8d\x06\x18\x012\x04\x08\x01\x10Mb\x1e\n\x0c\x08\x01'
- '\x12\x08category\x12\x0e\x08\x01\x12\nevent_name'
+ '\n\x1b@\x80\xec\xea\xda\x83\xb6\xa4\xcd\x15P\x80\x80@'
+ '\xc8\x02\x01\xe2\x02\x04\x08\x01\x10\x02\xd0\x03@\n;@'
+ '\x80\xb0\xc2\x8a\x84\xb6\xa4\xcd\x15P\x80\x80@Z\x08'
+ '\x18\x012\x04\x08\x01\x10Mb\x1e\n\x0c\x08\x01\x12\x08'
+ 'category\x12\x0e\x08\x01\x12\nevent_name\xd0\x03@'
)
self.assertEqual(expected_output, result.getvalue())
@@ -68,13 +70,61 @@ class PerfettoTraceWriterTest(unittest.TestCase):
story_tags=["foo", "bar"],
story_run_index=0,
label="label",
- had_failures=False,
)
expected_output = (
- '\nI\x82\x03F\x08\x90\xf6\xc2\x82\xb6\xfa\xe1'
+ '\nG\x82\x03D\x08\x90\xf6\xc2\x82\xb6\xfa\xe1'
'\x02\x10\xb0\x83\xc9\x82\xb6\xfa\xe1\x02\x1a\tbenchmark"'
- '\x0bdescription*\x05label2\x05story:\x03foo:\x03bar@\x00H\x00'
+ '\x0bdescription*\x05label2\x05story:\x03foo:\x03bar@\x00'
+ )
+ self.assertEqual(expected_output, result.getvalue())
+
+ def testWriteArgs(self):
+ result = StringIO.StringIO()
+ perfetto_trace_writer.write_thread_descriptor_event(
+ output=result,
+ pid=1,
+ tid=2,
+ ts=0,
+ )
+ perfetto_trace_writer.write_event(
+ output=result,
+ ph="M",
+ category="",
+ name="",
+ ts=0,
+ args={'int': 123, 'double': 1.23, 'string': 'onetwothree'},
+ tid=2,
+ )
+ expected_output = (
+ '\n\x13@\x00P\x80\x80@\xc8\x02\x01\xe2\x02\x04\x08\x01\x10'
+ '\x02\xd0\x03@\nT@\x00P\x80\x80@Z;\x18\x01"\x07R\x03int {"\x11'
+ 'R\x06double)\xaeG\xe1z\x14\xae\xf3?"\x15R\x06string2\x0bonetwothree2'
+ '\x04\x08\x01\x10Mb\x0c\n\x04\x08\x01\x12\x00\x12\x04\x08\x01\x12\x00'
+ '\xd0\x03@'
)
self.assertEqual(expected_output, result.getvalue())
+ def testWriteChromeMetadata(self):
+ result = StringIO.StringIO()
+ perfetto_trace_writer.write_chrome_metadata(
+ output=result,
+ clock_domain='FOO',
+ )
+ expected_output = (
+ '\n\x17*\x15\x12\x13\n\x0cclock-domain\x12\x03FOO'
+ )
+ self.assertEqual(expected_output, result.getvalue())
+ def testWriteClockSnapshot(self):
+ result = StringIO.StringIO()
+ perfetto_trace_writer.write_clock_snapshot(
+ output=result,
+ tid=1,
+ telemetry_ts=1234.567,
+ boottime_ts=7654.321,
+ )
+ expected_output = (
+ '\n\x172\x11\n\x06\x08@\x10\x87\xadK\n\x07\x08\x06\x10'
+ '\xb1\x97\xd3\x03P\x80\x80@'
+ )
+ self.assertEqual(expected_output, result.getvalue())
diff --git a/catapult/common/py_trace_event/py_trace_event/trace_event_unittest.py b/catapult/common/py_trace_event/py_trace_event/trace_event_unittest.py
index 9916c71d..de7b5947 100644
--- a/catapult/common/py_trace_event/py_trace_event/trace_event_unittest.py
+++ b/catapult/common/py_trace_event/py_trace_event/trace_event_unittest.py
@@ -512,6 +512,16 @@ class TraceEventTests(unittest.TestCase):
story_run_index=0,
)
+ def testSetClockSnapshotProtobuf(self):
+ trace_event.trace_set_clock_snapshot(
+ telemetry_ts=1234.5678,
+ boottime_ts=8765.4321,
+ )
+ with self._test_trace(format=trace_event.PROTOBUF):
+ trace_event.trace_disable()
+ with open(self._log_path, 'r') as f:
+ self.assertGreater(len(f.read()), 0)
+
if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
diff --git a/catapult/common/py_trace_event/third_party/protobuf/README.chromium b/catapult/common/py_trace_event/third_party/protobuf/README.chromium
index f22d684e..adf4cb8a 100644
--- a/catapult/common/py_trace_event/third_party/protobuf/README.chromium
+++ b/catapult/common/py_trace_event/third_party/protobuf/README.chromium
@@ -9,4 +9,4 @@ extensible mechanism for serializing structured data.
Local Modifications:
Removed pretty much everything except functions necessary to write
-bools, ints, and strings.
+bools, ints, floats and strings.
diff --git a/catapult/common/py_trace_event/third_party/protobuf/encoder.py b/catapult/common/py_trace_event/third_party/protobuf/encoder.py
index 18aaccdc..50d10465 100644
--- a/catapult/common/py_trace_event/third_party/protobuf/encoder.py
+++ b/catapult/common/py_trace_event/third_party/protobuf/encoder.py
@@ -29,6 +29,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import six
+import struct
import wire_format
@@ -155,12 +156,93 @@ def _SimpleEncoder(wire_type, encode_value, compute_value_size):
return SpecificEncoder
+def _FloatingPointEncoder(wire_type, format):
+ """Return a constructor for an encoder for float fields.
+
+ This is like StructPackEncoder, but catches errors that may be due to
+ passing non-finite floating-point values to struct.pack, and makes a
+ second attempt to encode those values.
+
+ Args:
+ wire_type: The field's wire type, for encoding tags.
+ format: The format string to pass to struct.pack().
+ """
+
+ value_size = struct.calcsize(format)
+ if value_size == 4:
+ def EncodeNonFiniteOrRaise(write, value):
+ # Remember that the serialized form uses little-endian byte order.
+ if value == _POS_INF:
+ write(b'\x00\x00\x80\x7F')
+ elif value == _NEG_INF:
+ write(b'\x00\x00\x80\xFF')
+ elif value != value: # NaN
+ write(b'\x00\x00\xC0\x7F')
+ else:
+ raise
+ elif value_size == 8:
+ def EncodeNonFiniteOrRaise(write, value):
+ if value == _POS_INF:
+ write(b'\x00\x00\x00\x00\x00\x00\xF0\x7F')
+ elif value == _NEG_INF:
+ write(b'\x00\x00\x00\x00\x00\x00\xF0\xFF')
+ elif value != value: # NaN
+ write(b'\x00\x00\x00\x00\x00\x00\xF8\x7F')
+ else:
+ raise
+ else:
+ raise ValueError('Can\'t encode floating-point values that are '
+ '%d bytes long (only 4 or 8)' % value_size)
+
+ def SpecificEncoder(field_number, is_repeated, is_packed):
+ local_struct_pack = struct.pack
+ if is_packed:
+ tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
+ local_EncodeVarint = _EncodeVarint
+ def EncodePackedField(write, value):
+ write(tag_bytes)
+ local_EncodeVarint(write, len(value) * value_size)
+ for element in value:
+ # This try/except block is going to be faster than any code that
+ # we could write to check whether element is finite.
+ try:
+ write(local_struct_pack(format, element))
+ except SystemError:
+ EncodeNonFiniteOrRaise(write, element)
+ return EncodePackedField
+ elif is_repeated:
+ tag_bytes = TagBytes(field_number, wire_type)
+ def EncodeRepeatedField(write, value):
+ for element in value:
+ write(tag_bytes)
+ try:
+ write(local_struct_pack(format, element))
+ except SystemError:
+ EncodeNonFiniteOrRaise(write, element)
+ return EncodeRepeatedField
+ else:
+ tag_bytes = TagBytes(field_number, wire_type)
+ def EncodeField(write, value):
+ write(tag_bytes)
+ try:
+ write(local_struct_pack(format, value))
+ except SystemError:
+ EncodeNonFiniteOrRaise(write, value)
+ return EncodeField
+
+ return SpecificEncoder
+
+
Int32Encoder = Int64Encoder = EnumEncoder = _SimpleEncoder(
wire_format.WIRETYPE_VARINT, _EncodeSignedVarint, _SignedVarintSize)
UInt32Encoder = UInt64Encoder = _SimpleEncoder(
wire_format.WIRETYPE_VARINT, _EncodeVarint, _VarintSize)
+FloatEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED32, '<f')
+
+DoubleEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED64, '<d')
+
def BoolEncoder(field_number, is_repeated, is_packed):
"""Returns an encoder for a boolean field."""
diff --git a/catapult/common/py_utils/py_utils/chrome_binaries.json b/catapult/common/py_utils/py_utils/chrome_binaries.json
index 437cbb38..badb3642 100644
--- a/catapult/common/py_utils/py_utils/chrome_binaries.json
+++ b/catapult/common/py_utils/py_utils/chrome_binaries.json
@@ -6,22 +6,22 @@
"cloud_storage_bucket": "chrome-telemetry",
"file_info": {
"mac_x86_64": {
- "cloud_storage_hash": "381a491e14ab523b8db4cdf3c993713678237af8",
+ "cloud_storage_hash": "841d45e806a296ce21f7f783cc7a2ae5f857924c",
"download_path": "bin/reference_builds/chrome-mac64.zip",
"path_within_archive": "chrome-mac/Google Chrome.app/Contents/MacOS/Google Chrome",
- "version_in_cs": "77.0.3822.0"
+ "version_in_cs": "85.0.4169.0"
},
"win_AMD64": {
- "cloud_storage_hash": "600ee522c410efe1de2f593c0efc32ae113a7d99",
+ "cloud_storage_hash": "c0b02f47afb6bacdd8d12eda6f03488583404b60",
"download_path": "bin\\reference_build\\chrome-win64-clang.zip",
"path_within_archive": "chrome-win64-clang\\chrome.exe",
- "version_in_cs": "77.0.3822.0"
+ "version_in_cs": "85.0.4169.0"
},
"win_x86": {
- "cloud_storage_hash": "5b79a181bfbd94d8288529b0da1defa3ef097197",
+ "cloud_storage_hash": "5879958eda4edc5b0eaffeda4c7369a07937af73",
"download_path": "bin\\reference_build\\chrome-win32-clang.zip",
"path_within_archive": "chrome-win32-clang\\chrome.exe",
- "version_in_cs": "77.0.3822.0"
+ "version_in_cs": "85.0.4169.0"
}
}
},
@@ -30,10 +30,10 @@
"cloud_storage_bucket": "chrome-telemetry",
"file_info": {
"linux_x86_64": {
- "cloud_storage_hash": "61d68a6b00f25c964f5162f5251962468c886f3a",
+ "cloud_storage_hash": "1b21a60f8e83a43d01e2ddbe83c5df56c8159d56",
"download_path": "bin/reference_build/chrome-linux64.zip",
"path_within_archive": "chrome-linux64/chrome",
- "version_in_cs": "76.0.3809.21"
+ "version_in_cs": "85.0.4164.2"
}
}
},
@@ -42,83 +42,129 @@
"cloud_storage_bucket": "chrome-telemetry",
"file_info": {
"android_k_armeabi-v7a": {
- "cloud_storage_hash": "28b913c720d56a30c092625c7862f00175a316c7",
+ "cloud_storage_hash": "cf89ebbcf302fab3d4cd80b5566c5246cdf1504a",
"download_path": "bin/reference_build/android_k_armeabi-v7a/ChromeStable.apk",
- "version_in_cs": "75.0.3770.67"
+ "version_in_cs": "83.0.4103.101"
},
"android_l_arm64-v8a": {
- "cloud_storage_hash": "4b953c33c61f94c2198e8001d0d8142c6504a875",
+ "cloud_storage_hash": "8d3d5fe99655f61a088d4b4d51cad028b1a60f9c",
"download_path": "bin/reference_build/android_l_arm64-v8a/ChromeStable.apk",
- "version_in_cs": "75.0.3770.67"
+ "version_in_cs": "83.0.4103.101"
},
"android_l_armeabi-v7a": {
- "cloud_storage_hash": "28b913c720d56a30c092625c7862f00175a316c7",
+ "cloud_storage_hash": "cf89ebbcf302fab3d4cd80b5566c5246cdf1504a",
"download_path": "bin/reference_build/android_l_armeabi-v7a/ChromeStable.apk",
- "version_in_cs": "75.0.3770.67"
+ "version_in_cs": "83.0.4103.101"
},
"android_n_arm64-v8a": {
- "cloud_storage_hash": "84152ba8f7a25cacc79d588ed827ea75f0e4ab94",
+ "cloud_storage_hash": "8a4a0e3be7c1043c63446b74c210c3bd1387c1f8",
"download_path": "bin/reference_build/android_n_arm64-v8a/Monochrome.apk",
- "version_in_cs": "75.0.3770.67"
+ "version_in_cs": "83.0.4103.101"
},
"android_n_armeabi-v7a": {
- "cloud_storage_hash": "656bb9e3982d0d35decd5347ced2c320a7267f33",
+ "cloud_storage_hash": "3c80e59b2c0fede2a85a6b79dc71a5c787e01d30",
"download_path": "bin/reference_build/android_n_armeabi-v7a/Monochrome.apk",
- "version_in_cs": "75.0.3770.67"
+ "version_in_cs": "83.0.4103.101"
+ },
+ "android_n_bundle_arm64-v8a": {
+ "cloud_storage_hash": "b369c1a955d49d246b52e93b8c5692a80d5356b1",
+ "download_path": "bin/reference_build/android_n_bundle_arm64-v8a/Monochrome.apks",
+ "version_in_cs": "83.0.4103.101"
+ },
+ "android_n_bundle_armeabi-v7a": {
+ "cloud_storage_hash": "8529aa69c987bcc731589e54543a870263dfa431",
+ "download_path": "bin/reference_build/android_n_bundle_armeabi-v7a/Monochrome.apks",
+ "version_in_cs": "83.0.4103.101"
},
"linux_x86_64": {
- "cloud_storage_hash": "dee8469e8dcd8453efd33f3a00d7ea302a126a4b",
+ "cloud_storage_hash": "2eb0469024c127a4b99d6b20ac673e2be7ab1b2d",
"download_path": "bin/reference_build/chrome-linux64.zip",
"path_within_archive": "chrome-linux64/chrome",
- "version_in_cs": "75.0.3770.80"
+ "version_in_cs": "83.0.4103.97"
},
"mac_x86_64": {
- "cloud_storage_hash": "16a43a1e794bb99ec1ebcd40569084985b3c6626",
+ "cloud_storage_hash": "012c5a5e7b1e69a0960462e7ba7f77f90464a10f",
"download_path": "bin/reference_builds/chrome-mac64.zip",
"path_within_archive": "chrome-mac/Google Chrome.app/Contents/MacOS/Google Chrome",
- "version_in_cs": "75.0.3770.80"
+ "version_in_cs": "83.0.4103.97"
},
"win_AMD64": {
- "cloud_storage_hash": "1ec52bd4164f2d93c53113a093dae9e041eb2d73",
+ "cloud_storage_hash": "e6515496ebab0d02a5294a008fe8bbf9dd3dbc0c",
"download_path": "bin\\reference_build\\chrome-win64-clang.zip",
"path_within_archive": "chrome-win64-clang\\chrome.exe",
- "version_in_cs": "75.0.3770.80"
+ "version_in_cs": "83.0.4103.97"
},
"win_x86": {
- "cloud_storage_hash": "0f9eb991ba618dc61f2063ea252f44be94c2252e",
+ "cloud_storage_hash": "1749fac72241bc48e4adaf881a9278811b7ee406",
"download_path": "bin\\reference_build\\chrome-win-clang.zip",
"path_within_archive": "chrome-win-clang\\chrome.exe",
- "version_in_cs": "75.0.3770.80"
+ "version_in_cs": "83.0.4103.97"
+ }
+ }
+ },
+ "chromium_canary": {
+ "cloud_storage_base_folder": "binary_dependencies",
+ "cloud_storage_bucket": "chrome-telemetry",
+ "file_info": {
+ "mac_x86_64": {
+ "cloud_storage_hash": "0e1a89d8aabd37e971aca506130d8ef7a1997b14",
+ "download_path": "bin/reference_builds/chrome-mac.zip",
+ "path_within_archive": "chrome-mac/Chromium.app/Contents/MacOS/Chromium",
+ "version_in_cs": "85.0.4169.0"
+ },
+ "win_AMD64": {
+ "cloud_storage_hash": "fed673ad3f4744160b4997af461573be85153a1a",
+ "download_path": "bin\\reference_build\\chrome-win.zip",
+ "path_within_archive": "chrome-win\\chrome.exe",
+ "version_in_cs": "85.0.4169.0"
+ },
+ "win_x86": {
+ "cloud_storage_hash": "e413191b7355f9bb8daad2cfc26cca924f48c44a",
+ "download_path": "bin\\reference_build\\chrome-win32-clang.zip",
+ "path_within_archive": "chrome-win32-clang\\chrome.exe",
+ "version_in_cs": "85.0.4169.0"
}
}
},
- "chrome_m72": {
+ "chromium_dev": {
"cloud_storage_base_folder": "binary_dependencies",
"cloud_storage_bucket": "chrome-telemetry",
"file_info": {
"linux_x86_64": {
- "cloud_storage_hash": "537c19346b20340cc6807242e1eb6d82dfcfa2e8",
- "download_path": "bin/reference_build/chrome-linux64.zip",
- "path_within_archive": "chrome-linux64/chrome",
- "version_in_cs": "72.0.3626.119"
+ "cloud_storage_hash": "b8b8aeaf5174ba09598ee637353b87f25fe9926e",
+ "download_path": "bin/reference_build/chrome-linux.zip",
+ "path_within_archive": "chrome-linux/chrome",
+ "version_in_cs": "85.0.4164.2"
+ }
+ }
+ },
+ "chromium_stable": {
+ "cloud_storage_base_folder": "binary_dependencies",
+ "cloud_storage_bucket": "chrome-telemetry",
+ "file_info": {
+ "linux_x86_64": {
+ "cloud_storage_hash": "9d7903d1e29589c9a9731b856893a0b6ee4de760",
+ "download_path": "bin/reference_build/chrome-linux.zip",
+ "path_within_archive": "chrome-linux/chrome",
+ "version_in_cs": "83.0.4103.97"
},
"mac_x86_64": {
- "cloud_storage_hash": "7f6a931f696f57561703538c6f799781d6e22e7e",
- "download_path": "bin/reference_builds/chrome-mac64.zip",
- "path_within_archive": "chrome-mac/Google Chrome.app/Contents/MacOS/Google Chrome",
- "version_in_cs": "72.0.3626.119"
+ "cloud_storage_hash": "d1ddbcd3b1d2efd44c5a5030ae871308cca26000",
+ "download_path": "bin/reference_builds/chrome-mac.zip",
+ "path_within_archive": "chrome-mac/Chromium.app/Contents/MacOS/Chromium",
+ "version_in_cs": "83.0.4103.97"
},
"win_AMD64": {
- "cloud_storage_hash": "563d7985c85bfe77e92b8253d0389ff8551018c7",
- "download_path": "bin\\reference_build\\chrome-win64-clang.zip",
- "path_within_archive": "chrome-win64-clang\\chrome.exe",
- "version_in_cs": "72.0.3626.119"
+ "cloud_storage_hash": "5e503f1bfeae37061ddc80ae1660a1c41594b188",
+ "download_path": "bin\\reference_build\\chrome-win.zip",
+ "path_within_archive": "chrome-win\\chrome.exe",
+ "version_in_cs": "83.0.4103.97"
},
"win_x86": {
- "cloud_storage_hash": "1802179da16e44b83bd3f0b296f9e5b0b053d59c",
+ "cloud_storage_hash": "66e8c83cc4b57c38d71a56f6928bcafe7da17d80",
"download_path": "bin\\reference_build\\chrome-win-clang.zip",
"path_within_archive": "chrome-win-clang\\chrome.exe",
- "version_in_cs": "72.0.3626.119"
+ "version_in_cs": "83.0.4103.97"
}
}
}
diff --git a/catapult/common/py_utils/py_utils/cloud_storage.py b/catapult/common/py_utils/py_utils/cloud_storage.py
index b4988c58..a359a065 100644
--- a/catapult/common/py_utils/py_utils/cloud_storage.py
+++ b/catapult/common/py_utils/py_utils/cloud_storage.py
@@ -198,6 +198,37 @@ def List(bucket):
return [url[len(query):] for url in stdout.splitlines()]
+def ListDirs(bucket, path=''):
+ """Returns only directories matching the given path in bucket.
+
+ Args:
+ bucket: Name of cloud storage bucket to look at.
+ path: Path within the bucket to filter to. Path can include wildcards.
+ path = 'foo*' will return ['mybucket/foo1/', 'mybucket/foo2/, ... ] but
+ not mybucket/foo1/file.txt or mybucket/foo-file.txt.
+
+ Returns:
+ A list of directories. All returned path are relative to the bucket root
+ directory. For example, List('my-bucket', path='foo/') will returns results
+ of the form ['/foo/123', '/foo/124', ...], as opposed to ['123', '124',
+ ...].
+ """
+ bucket_prefix = 'gs://%s' % bucket
+ full_path = '%s/%s' % (bucket_prefix, path)
+ # Note that -d only ensures we don't recurse into subdirectories
+ # unnecessarily. It still lists all non directory files matching the path
+ # following by a blank line. Adding -d here is a performance optimization.
+ stdout = _RunCommand(['ls', '-d', full_path])
+ dirs = []
+ for url in stdout.splitlines():
+ if len(url) == 0:
+ continue
+ # The only way to identify directories is by filtering for trailing slash.
+ # See https://github.com/GoogleCloudPlatform/gsutil/issues/466
+ if url[-1] == '/':
+ dirs.append(url[len(bucket_prefix):])
+ return dirs
+
def Exists(bucket, remote_path):
try:
_RunCommand(['ls', 'gs://%s/%s' % (bucket, remote_path)])
@@ -362,7 +393,8 @@ def _GetLocked(bucket, remote_path, local_path):
def Insert(bucket, remote_path, local_path, publicly_readable=False):
- """ Upload file in |local_path| to cloud storage.
+ """Upload file in |local_path| to cloud storage.
+
Args:
bucket: the google cloud storage bucket name.
remote_path: the remote file path in |bucket|.
@@ -373,6 +405,43 @@ def Insert(bucket, remote_path, local_path, publicly_readable=False):
Returns:
The url where the file is uploaded to.
"""
+ cloud_filepath = Upload(bucket, remote_path, local_path, publicly_readable)
+ return cloud_filepath.view_url
+
+
+class CloudFilepath(object):
+ def __init__(self, bucket, remote_path):
+ self.bucket = bucket
+ self.remote_path = remote_path
+
+ @property
+ def view_url(self):
+ """Get a human viewable url for the cloud file."""
+ return 'https://console.developers.google.com/m/cloudstorage/b/%s/o/%s' % (
+ self.bucket, self.remote_path)
+
+ @property
+ def fetch_url(self):
+ """Get a machine fetchable url for the cloud file."""
+ return 'gs://%s/%s' % (self.bucket, self.remote_path)
+
+
+def Upload(bucket, remote_path, local_path, publicly_readable=False):
+ """Upload file in |local_path| to cloud storage.
+
+ Newer version of 'Insert()' returns an object instead of a string.
+
+ Args:
+ bucket: the google cloud storage bucket name.
+ remote_path: the remote file path in |bucket|.
+ local_path: path of the local file to be uploaded.
+ publicly_readable: whether the uploaded file has publicly readable
+ permission.
+
+ Returns:
+ A CloudFilepath object providing the location of the object in various
+ formats.
+ """
url = 'gs://%s/%s' % (bucket, remote_path)
command_and_args = ['cp']
extra_info = ''
@@ -382,8 +451,7 @@ def Insert(bucket, remote_path, local_path, publicly_readable=False):
command_and_args += [local_path, url]
logger.info('Uploading %s to %s%s', local_path, url, extra_info)
_RunCommand(command_and_args)
- return 'https://console.developers.google.com/m/cloudstorage/b/%s/o/%s' % (
- bucket, remote_path)
+ return CloudFilepath(bucket, remote_path)
def GetIfHashChanged(cs_path, download_path, bucket, file_hash):
diff --git a/catapult/common/py_utils/py_utils/cloud_storage_unittest.py b/catapult/common/py_utils/py_utils/cloud_storage_unittest.py
index 7648db6b..2e663a76 100644
--- a/catapult/common/py_utils/py_utils/cloud_storage_unittest.py
+++ b/catapult/common/py_utils/py_utils/cloud_storage_unittest.py
@@ -102,6 +102,22 @@ class CloudStorageFakeFsUnitTest(BaseFakeFsUnitTest):
finally:
cloud_storage._RunCommand = orig_run_command
+ def testUploadCreatesValidCloudUrls(self):
+ orig_run_command = cloud_storage._RunCommand
+ try:
+ cloud_storage._RunCommand = self._FakeRunCommand
+ remote_path = 'test-remote-path.html'
+ local_path = 'test-local-path.html'
+ cloud_filepath = cloud_storage.Upload(
+ cloud_storage.PUBLIC_BUCKET, remote_path, local_path)
+ self.assertEqual('https://console.developers.google.com/m/cloudstorage'
+ '/b/chromium-telemetry/o/test-remote-path.html',
+ cloud_filepath.view_url)
+ self.assertEqual('gs://chromium-telemetry/test-remote-path.html',
+ cloud_filepath.fetch_url)
+ finally:
+ cloud_storage._RunCommand = orig_run_command
+
@mock.patch('py_utils.cloud_storage.subprocess')
def testExistsReturnsFalse(self, subprocess_mock):
p_mock = mock.Mock()
@@ -154,6 +170,17 @@ class CloudStorageFakeFsUnitTest(BaseFakeFsUnitTest):
finally:
cloud_storage._RunCommand = orig_run_command
+ @mock.patch('py_utils.cloud_storage._RunCommand')
+ def testListDirs(self, mock_run_command):
+ mock_run_command.return_value = '\n'.join(['gs://bucket/foo-file.txt',
+ '',
+ 'gs://bucket/foo1/',
+ 'gs://bucket/foo2/',
+ 'gs://bucket/foo1/file.txt'])
+
+ self.assertEqual(cloud_storage.ListDirs('bucket', 'foo*'),
+ ['/foo1/', '/foo2/'])
+
@mock.patch('py_utils.cloud_storage.subprocess.Popen')
def testSwarmingUsesExistingEnv(self, mock_popen):
os.environ['SWARMING_HEADLESS'] = '1'
diff --git a/catapult/common/py_utils/py_utils/constants/__init__.py b/catapult/common/py_utils/py_utils/constants/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/catapult/common/py_utils/py_utils/constants/__init__.py
diff --git a/catapult/common/py_utils/py_utils/constants/exit_codes.py b/catapult/common/py_utils/py_utils/constants/exit_codes.py
new file mode 100644
index 00000000..9eaac5c6
--- /dev/null
+++ b/catapult/common/py_utils/py_utils/constants/exit_codes.py
@@ -0,0 +1,13 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# Lint as: python3
+"""Common exit codes for catapult tools."""
+
+SUCCESS = 0
+TEST_FAILURE = 1
+FATAL_ERROR = 2
+# See crbug.com/1019139#c8 for history on this exit code.
+# Note that some test runners (for example: typ) may continue to return SUCCESS
+# for cases where all tests are skipped.
+ALL_TESTS_SKIPPED = 111
diff --git a/catapult/common/py_utils/py_utils/discover.py b/catapult/common/py_utils/py_utils/discover.py
index ae8ba87d..a9333e2d 100644
--- a/catapult/common/py_utils/py_utils/discover.py
+++ b/catapult/common/py_utils/py_utils/discover.py
@@ -101,20 +101,18 @@ def DiscoverClasses(start_dir,
for module in modules:
new_classes = DiscoverClassesInModule(
module, base_class, index_by_class_name, directly_constructable)
- # TODO(nednguyen): we should remove index_by_class_name once
+ # TODO(crbug.com/548652): we should remove index_by_class_name once
# benchmark_smoke_unittest in chromium/src/tools/perf no longer relied
# naming collisions to reduce the number of smoked benchmark tests.
- # crbug.com/548652
if index_by_class_name:
AssertNoKeyConflicts(classes, new_classes)
classes = dict(list(classes.items()) + list(new_classes.items()))
return classes
-# TODO(nednguyen): we should remove index_by_class_name once
+# TODO(crbug.com/548652): we should remove index_by_class_name once
# benchmark_smoke_unittest in chromium/src/tools/perf no longer relied
# naming collisions to reduce the number of smoked benchmark tests.
-# crbug.com/548652
def DiscoverClassesInModule(module,
base_class,
index_by_class_name=False,
diff --git a/catapult/common/py_utils/py_utils/discover_unittest.py b/catapult/common/py_utils/py_utils/discover_unittest.py
index 2d4fd270..bdc50b2f 100644
--- a/catapult/common/py_utils/py_utils/discover_unittest.py
+++ b/catapult/common/py_utils/py_utils/discover_unittest.py
@@ -8,9 +8,10 @@ from __future__ import print_function
import os
import unittest
-from py_utils import discover
import six
+from py_utils import discover
+
class DiscoverTest(unittest.TestCase):
diff --git a/catapult/common/py_utils/py_utils/expectations_parser_unittest.py b/catapult/common/py_utils/py_utils/expectations_parser_unittest.py
index 523e8711..82debc5d 100644
--- a/catapult/common/py_utils/py_utils/expectations_parser_unittest.py
+++ b/catapult/common/py_utils/py_utils/expectations_parser_unittest.py
@@ -9,9 +9,10 @@ from __future__ import print_function
import unittest
-from py_utils import expectations_parser
from six.moves import range # pylint: disable=redefined-builtin
+from py_utils import expectations_parser
+
class TestExpectationParserTest(unittest.TestCase):
diff --git a/catapult/common/py_utils/py_utils/lock_unittest.py b/catapult/common/py_utils/py_utils/lock_unittest.py
index 7e17e552..2ba288bd 100644
--- a/catapult/common/py_utils/py_utils/lock_unittest.py
+++ b/catapult/common/py_utils/py_utils/lock_unittest.py
@@ -12,8 +12,9 @@ import tempfile
import time
import unittest
+from six.moves import range # pylint: disable=redefined-builtin
+
from py_utils import lock
-from six.moves import range # pylint: disable=redefined-builtin
def _AppendTextToFile(file_name):
diff --git a/catapult/common/py_utils/py_utils/refactor/annotated_symbol/base_symbol.py b/catapult/common/py_utils/py_utils/refactor/annotated_symbol/base_symbol.py
index 5e473bcf..bdaec61b 100644
--- a/catapult/common/py_utils/py_utils/refactor/annotated_symbol/base_symbol.py
+++ b/catapult/common/py_utils/py_utils/refactor/annotated_symbol/base_symbol.py
@@ -5,9 +5,11 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
-from py_utils.refactor import snippet
+
from six.moves import range # pylint: disable=redefined-builtin
+from py_utils.refactor import snippet
+
class AnnotatedSymbol(snippet.Symbol):
def __init__(self, symbol_type, children):
diff --git a/catapult/common/py_utils/py_utils/refactor/annotated_symbol/import_statement.py b/catapult/common/py_utils/py_utils/refactor/annotated_symbol/import_statement.py
index 6318eff6..54a3935c 100644
--- a/catapult/common/py_utils/py_utils/refactor/annotated_symbol/import_statement.py
+++ b/catapult/common/py_utils/py_utils/refactor/annotated_symbol/import_statement.py
@@ -10,9 +10,10 @@ import keyword
import symbol
import token
+from six.moves import zip_longest # pylint: disable=redefined-builtin
+
from py_utils.refactor import snippet
from py_utils.refactor.annotated_symbol import base_symbol
-from six.moves import zip_longest # pylint: disable=redefined-builtin
__all__ = [
diff --git a/catapult/common/py_utils/py_utils/refactor/annotated_symbol/reference.py b/catapult/common/py_utils/py_utils/refactor/annotated_symbol/reference.py
index 9a273d87..493176e6 100644
--- a/catapult/common/py_utils/py_utils/refactor/annotated_symbol/reference.py
+++ b/catapult/common/py_utils/py_utils/refactor/annotated_symbol/reference.py
@@ -9,10 +9,11 @@ from __future__ import print_function
import symbol
import token
-from py_utils.refactor import snippet
+from six.moves import range # pylint: disable=redefined-builtin
+from six.moves import zip_longest # pylint: disable=redefined-builtin
+
from py_utils.refactor.annotated_symbol import base_symbol
-from six.moves import range # pylint: disable=redefined-builtin
-from six.moves import zip_longest # pylint: disable=redefined-builtin
+from py_utils.refactor import snippet
__all__ = [
diff --git a/catapult/common/py_utils/py_utils/slots_metaclass_unittest.py b/catapult/common/py_utils/py_utils/slots_metaclass_unittest.py
index fe21b27c..702371a7 100644
--- a/catapult/common/py_utils/py_utils/slots_metaclass_unittest.py
+++ b/catapult/common/py_utils/py_utils/slots_metaclass_unittest.py
@@ -8,9 +8,10 @@ from __future__ import print_function
import unittest
-from py_utils import slots_metaclass
import six
+from py_utils import slots_metaclass
+
class SlotsMetaclassUnittest(unittest.TestCase):
diff --git a/catapult/common/py_utils/py_utils/ts_proxy_server.py b/catapult/common/py_utils/py_utils/ts_proxy_server.py
new file mode 100644
index 00000000..b71d143d
--- /dev/null
+++ b/catapult/common/py_utils/py_utils/ts_proxy_server.py
@@ -0,0 +1,222 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Start and stop tsproxy."""
+
+import logging
+import os
+import re
+import signal
+import subprocess
+import sys
+import time
+
+try:
+ import fcntl
+except ImportError:
+ fcntl = None
+
+import py_utils
+from py_utils import retry_util
+from py_utils import atexit_with_log
+
+_TSPROXY_PATH = os.path.join(
+ py_utils.GetCatapultDir(), 'third_party', 'tsproxy', 'tsproxy.py')
+
+class TsProxyServerError(Exception):
+ """Catch-all exception for tsProxy Server."""
+ pass
+
+def ParseTsProxyPortFromOutput(output_line):
+ port_re = re.compile(
+ r'Started Socks5 proxy server on '
+ r'(?P<host>[^:]*):'
+ r'(?P<port>\d+)')
+ m = port_re.match(output_line)
+ if m:
+ return int(m.group('port'))
+
+
+class TsProxyServer(object):
+ """Start and stop tsproxy.
+
+ TsProxy provides basic latency, download and upload traffic shaping. This
+ class provides a programming API to the tsproxy script in
+ catapult/third_party/tsproxy/tsproxy.py
+
+ This class can be used as a context manager.
+ """
+
+ def __init__(self, host_ip=None, http_port=None, https_port=None):
+ """
+ Initialize TsProxyServer.
+
+ Args:
+
+ host_ip: A string of the host ip address.
+ http_port: A decimal of the port used for http traffic.
+ https_port: a decimal of the port used for https traffic.
+
+ """
+ self._proc = None
+ self._port = None
+ self._is_running = False
+ self._host_ip = host_ip
+ assert bool(http_port) == bool(https_port)
+ self._http_port = http_port
+ self._https_port = https_port
+ self._non_blocking = False
+ self._rtt = None
+ self._inbkps = None
+ self._outkbps = None
+
+ @property
+ def port(self):
+ return self._port
+
+ @retry_util.RetryOnException(TsProxyServerError, retries=3)
+ def StartServer(self, timeout=10, retries=None):
+ """Start TsProxy server and verify that it started."""
+ del retries # Handled by decorator.
+ cmd_line = [sys.executable, _TSPROXY_PATH]
+ # Use port 0 so tsproxy picks a random available port.
+ cmd_line.extend(['--port=0'])
+ if self._host_ip:
+ cmd_line.append('--desthost=%s' % self._host_ip)
+ if self._http_port:
+ cmd_line.append(
+ '--mapports=443:%s,*:%s' % (self._https_port, self._http_port))
+ logging.info('Tsproxy commandline: %s', cmd_line)
+ self._proc = subprocess.Popen(
+ cmd_line, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE, bufsize=1)
+ self._non_blocking = False
+ if fcntl:
+ logging.info('fcntl is supported, trying to set '
+ 'non blocking I/O for the ts_proxy process')
+ fd = self._proc.stdout.fileno()
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+ self._non_blocking = True
+
+ atexit_with_log.Register(self.StopServer)
+ try:
+ py_utils.WaitFor(self._IsStarted, timeout)
+ logging.info('TsProxy port: %s', self._port)
+ self._is_running = True
+ except py_utils.TimeoutException:
+ err = self.StopServer()
+ if err:
+ logging.error('Error stopping WPR server:\n%s', err)
+ raise TsProxyServerError(
+ 'Error starting tsproxy: timed out after %s seconds' % timeout)
+
+ def _IsStarted(self):
+ assert not self._is_running
+ assert self._proc
+ if self._proc.poll() is not None:
+ return False
+ self._proc.stdout.flush()
+ output_line = self._ReadLineTsProxyStdout(timeout=5)
+ logging.debug('TsProxy output: %s', output_line)
+ self._port = ParseTsProxyPortFromOutput(output_line)
+ return self._port != None
+
+ def _ReadLineTsProxyStdout(self, timeout):
+ def ReadSingleLine():
+ try:
+ return self._proc.stdout.readline().strip()
+ except IOError:
+ # Add a sleep to avoid trying to read self._proc.stdout too often.
+ if self._non_blocking:
+ time.sleep(0.5)
+ return None
+ return py_utils.WaitFor(ReadSingleLine, timeout)
+
+ @retry_util.RetryOnException(TsProxyServerError, retries=3)
+ def _IssueCommand(self, command_string, timeout, retries=None):
+ del retries # handled by the decorator
+ logging.info('Issuing command to ts_proxy_server: %s', command_string)
+ command_output = []
+ self._proc.stdin.write('%s\n' % command_string)
+ def CommandStatusIsRead():
+ self._proc.stdin.flush()
+ self._proc.stdout.flush()
+ command_output.append(self._ReadLineTsProxyStdout(timeout))
+ return command_output[-1] == 'OK' or command_output[-1] == 'ERROR'
+
+ py_utils.WaitFor(CommandStatusIsRead, timeout)
+
+ success = 'OK' in command_output
+ logging.log(logging.DEBUG if success else logging.ERROR,
+ 'TsProxy output:\n%s', '\n'.join(command_output))
+ if not success:
+ raise TsProxyServerError('Failed to execute command: %s', command_string)
+
+ def UpdateOutboundPorts(self, http_port, https_port, timeout=5):
+ assert http_port and https_port
+ assert http_port != https_port
+ assert isinstance(http_port, int) and isinstance(https_port, int)
+ assert 1 <= http_port <= 65535
+ assert 1 <= https_port <= 65535
+ self._IssueCommand('set mapports 443:%i,*:%i' % (https_port, http_port),
+ timeout)
+
+ def UpdateTrafficSettings(
+ self, round_trip_latency_ms=None,
+ download_bandwidth_kbps=None, upload_bandwidth_kbps=None, timeout=20):
+ """Update traffic settings of the proxy server.
+
+ Notes that this method only updates the specified parameter.
+ """
+ # Memorize the traffic settings & only execute the command if the traffic
+ # settings are different.
+ if round_trip_latency_ms is not None and self._rtt != round_trip_latency_ms:
+ self._IssueCommand('set rtt %s' % round_trip_latency_ms, timeout)
+ self._rtt = round_trip_latency_ms
+
+ if (download_bandwidth_kbps is not None and
+ self._inbkps != download_bandwidth_kbps):
+ self._IssueCommand('set inkbps %s' % download_bandwidth_kbps, timeout)
+ self._inbkps = download_bandwidth_kbps
+
+ if (upload_bandwidth_kbps is not None and
+ self._outkbps != upload_bandwidth_kbps):
+ self._IssueCommand('set outkbps %s' % upload_bandwidth_kbps, timeout)
+ self._outkbps = upload_bandwidth_kbps
+
+ def StopServer(self):
+ """Stop TsProxy Server."""
+ if not self._is_running:
+ logging.debug('Attempting to stop TsProxy server that is not running.')
+ return
+ if not self._proc:
+ return
+ try:
+ py_utils.WaitFor(lambda: self._proc.poll() is not None, 10)
+ except py_utils.TimeoutException:
+ try:
+ # Use a SIGNINT so that it can do graceful cleanup
+ self._proc.send_signal(signal.SIGINT)
+ except ValueError:
+ logging.warning('Unable to stop ts_proxy_server gracefully.\n')
+ self._proc.terminate()
+ _, err = self._proc.communicate()
+
+ self._proc = None
+ self._port = None
+ self._is_running = False
+ self._rtt = None
+ self._inbkps = None
+ self._outkbps = None
+ return err
+
+ def __enter__(self):
+ """Add support for with-statement."""
+ self.StartServer()
+ return self
+
+ def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
+ """Add support for with-statement."""
+ self.StopServer()
diff --git a/catapult/common/py_utils/py_utils/ts_proxy_server_unittest.py b/catapult/common/py_utils/py_utils/ts_proxy_server_unittest.py
new file mode 100644
index 00000000..4bb75c8b
--- /dev/null
+++ b/catapult/common/py_utils/py_utils/ts_proxy_server_unittest.py
@@ -0,0 +1,56 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from py_utils import ts_proxy_server
+
+class TsProxyServerTest(unittest.TestCase):
+ def testParseTsProxyPort(self):
+ self.assertEquals(
+ ts_proxy_server.ParseTsProxyPortFromOutput(
+ 'Started Socks5 proxy server on 127.0.0.1:54430 \n'),
+ 54430)
+ self.assertEquals(
+ ts_proxy_server.ParseTsProxyPortFromOutput(
+ 'Started Socks5 proxy server on foo.bar.com:430 \n'),
+ 430)
+ self.assertEquals(
+ ts_proxy_server.ParseTsProxyPortFromOutput(
+ 'Failed to start sock5 proxy.'),
+ None)
+
+ def testSmokeStartingTsProxyServer(self):
+ with ts_proxy_server.TsProxyServer() as server:
+ self.assertIsNotNone(server.port)
+ with ts_proxy_server.TsProxyServer(None, 37124, 37125) as server:
+ self.assertIsNotNone(server.port)
+
+ def testSmokeUpdatingOutboundPorts(self):
+ with ts_proxy_server.TsProxyServer() as server:
+ self.assertIsNotNone(server.port)
+ server.UpdateOutboundPorts(31242, 14220)
+
+ def testSmokeUpdateOutboundPortsInvalid(self):
+ with ts_proxy_server.TsProxyServer() as server:
+ self.assertIsNotNone(server.port)
+ with self.assertRaises(AssertionError):
+ server.UpdateOutboundPorts(31242, 'abcde')
+
+ def testSmokeUpdateTrafficSettings(self):
+ with ts_proxy_server.TsProxyServer() as server:
+ server.UpdateTrafficSettings(round_trip_latency_ms=100)
+ server.UpdateTrafficSettings(download_bandwidth_kbps=5000)
+ server.UpdateTrafficSettings(upload_bandwidth_kbps=2000)
+
+ self.assertEquals(server._rtt, 100)
+ self.assertEquals(server._inbkps, 5000)
+ self.assertEquals(server._outkbps, 2000)
+
+ server.UpdateTrafficSettings(
+ round_trip_latency_ms=200, download_bandwidth_kbps=500,
+ upload_bandwidth_kbps=2000)
+ self.assertEquals(server._rtt, 200)
+ self.assertEquals(server._inbkps, 500)
+ self.assertEquals(server._outkbps, 2000)
diff --git a/catapult/common/py_utils/py_utils/webpagereplay_go_server.py b/catapult/common/py_utils/py_utils/webpagereplay_go_server.py
new file mode 100644
index 00000000..950e8adc
--- /dev/null
+++ b/catapult/common/py_utils/py_utils/webpagereplay_go_server.py
@@ -0,0 +1,402 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Start and stop Web Page Replay."""
+
+import logging
+import os
+import re
+import signal
+import subprocess
+import sys
+import tempfile
+import urllib
+
+import py_utils
+from py_utils import atexit_with_log
+from py_utils import binary_manager
+
+
+_WPR_DIR = os.path.abspath(os.path.join(
+ py_utils.GetCatapultDir(), 'web_page_replay_go'))
+
+TELEMETRY_PROJECT_CONFIG = os.path.join(
+ py_utils.GetCatapultDir(), 'telemetry', 'telemetry',
+ 'binary_dependencies.json')
+
+CHROME_BINARY_CONFIG = os.path.join(
+ py_utils.GetCatapultDir(), 'common', 'py_utils', 'py_utils',
+ 'chrome_binaries.json')
+
+RECORD = '--record'
+INJECT_SCRIPTS = '--inject_scripts='
+
+class ReplayError(Exception):
+ """Catch-all exception for the module."""
+ pass
+
+
+class ReplayNotFoundError(ReplayError):
+ def __init__(self, label, path):
+ """
+ Create a ReplayNotFoundError instance.
+
+ Args:
+
+ label: A string of label of this error.
+ path: A string of the path in this error.
+
+ """
+ super(ReplayNotFoundError, self).__init__()
+ self.args = (label, path)
+
+ def __str__(self):
+ label, path = self.args
+ return 'Path does not exist for %s: %s' % (label, path)
+
+
+class ReplayNotStartedError(ReplayError):
+ pass
+
+
+class ReplayServer(object):
+ """Start and Stop Web Page Replay.
+
+ Web Page Replay is a proxy that can record and "replay" web pages with
+ simulated network characteristics -- without having to edit the pages
+ by hand. With WPR, tests can use "real" web content, and catch
+ performance issues that may result from introducing network delays and
+ bandwidth throttling.
+
+ This class could be used as a context manager.
+
+ Example:
+ with ReplayServer(archive_path):
+ self.NavigateToURL(start_url)
+ self.WaitUntil(...)
+ """
+
+ _go_binary_path = None
+
+ def __init__(self, archive_path, replay_host, http_port, https_port,
+ replay_options, binary_downloader=None):
+ """Initialize ReplayServer.
+
+ Args:
+ archive_path: a path to a specific WPR archive.
+ replay_host: the hostname to serve traffic.
+ http_port: an integer port on which to serve HTTP traffic. May be zero
+ to let the OS choose an available port.
+ https_port: an integer port on which to serve HTTPS traffic. May be zero
+ to let the OS choose an available port.
+ replay_options: an iterable of options strings to forward to replay.py.
+ binary_downloader: a function to be used to fetch binary. May be None to
+ use py_utils.binary_manager.FetchPath as default downloader.
+ """
+ self.archive_path = archive_path
+ self._replay_host = replay_host
+ self._started_ports = {} # a dict such as {'http': 80, 'https': 443}
+
+ # A temporary path for storing stdout & stderr of the webpagereplay
+ # subprocess.
+ self._temp_log_file_path = None
+
+ # Assign the downloader func and binary_manager
+ downloader = None
+ if binary_downloader:
+ downloader = binary_downloader
+ else:
+ configs = [CHROME_BINARY_CONFIG, TELEMETRY_PROJECT_CONFIG]
+ downloader = binary_manager.BinaryManager(configs).FetchPath
+
+ self._cmd_line = self._GetCommandLine(
+ self._GetGoBinaryPath(downloader=downloader), http_port, https_port,
+ replay_options, archive_path)
+
+ if RECORD in replay_options or 'record' in replay_options:
+ self._AssertPathExists('archive directory',
+ os.path.dirname(self.archive_path))
+ elif not os.path.exists(self.archive_path):
+ self._AssertPathExists('archive file', self.archive_path)
+
+ self.replay_process = None
+
+ @classmethod
+ def _GetGoBinaryPath(cls, downloader):
+ if not cls._go_binary_path:
+ cls._go_binary_path = downloader(
+ 'wpr_go', py_utils.GetHostOsName(), py_utils.GetHostArchName())
+ return cls._go_binary_path
+
+ @classmethod
+ def SetGoBinaryPath(cls, go_binary_path):
+ """Overrides the _go_binary_path.
+
+ This allows the server to use WPRGO files retrieved from somewhere
+ other than GCS, such as CIPD."""
+ cls._go_binary_path = go_binary_path
+
+ @property
+ def http_port(self):
+ return self._started_ports['http']
+
+ @property
+ def https_port(self):
+ return self._started_ports['https']
+
+ @staticmethod
+ def _GetCommandLine(go_binary_path, http_port, https_port,
+ options, archive_path):
+ """Set WPR command-line arguments. Can be overridden if needed.
+
+ Keyword arguments:
+
+ * go_binary_path: A string of the path to the wpr.go binary.
+ * http_port: A decimal of the port that handles http requests.
+ * https_port: A decimal of the port that handles https requests.
+ * options: A list of options, such as '--record',
+ '--inject_scripts', etc.
+ * archive_path: A string of the path to the archive file.
+
+ """
+ bad_options = []
+ for option in options:
+ if option not in [RECORD, INJECT_SCRIPTS]:
+ bad_options.append(option)
+ if len(bad_options) > 0:
+ raise ValueError("Invalid replay options %s" % bad_options)
+
+ cmd_line = [go_binary_path]
+ if RECORD in options:
+ cmd_line.append('record')
+ else:
+ cmd_line.append('replay')
+ key_file = os.path.join(_WPR_DIR, 'wpr_key.pem')
+ cert_file = os.path.join(_WPR_DIR, 'wpr_cert.pem')
+ inject_script = os.path.join(_WPR_DIR, 'deterministic.js')
+ cmd_line.extend([
+ '--http_port=%s' % http_port,
+ '--https_port=%s' % https_port,
+ '--https_key_file=%s' % key_file,
+ '--https_cert_file=%s' % cert_file])
+ if INJECT_SCRIPTS in options:
+ cmd_line.append(INJECT_SCRIPTS)
+ else:
+ cmd_line.append('--inject_scripts=%s' % inject_script)
+ cmd_line.append(archive_path)
+ return cmd_line
+
+ def _AssertPathExists(self, label, path):
+ if not os.path.exists(path):
+ raise ReplayNotFoundError(label, path)
+
+ def _OpenLogFile(self):
+ """Opens the log file for writing."""
+ log_dir = os.path.dirname(self._temp_log_file_path)
+ if not os.path.isdir(log_dir):
+ os.makedirs(log_dir)
+ return open(self._temp_log_file_path, 'w')
+
+ def _LogLines(self):
+ """Yields any log lines that have been writtent to disk."""
+ if (not self._temp_log_file_path or
+ not os.path.isfile(self._temp_log_file_path)):
+ yield '(N/A)'
+ return
+ with open(self._temp_log_file_path) as f:
+ for line in f:
+ yield line
+
+ def _IsStarted(self):
+ """Returns true if the server is up and running."""
+ if not self._IsReplayProcessStarted():
+ return False
+
+ def HasIncompleteStartedPorts():
+ return ('http' not in self._started_ports or
+ 'https' not in self._started_ports)
+
+ if HasIncompleteStartedPorts():
+ self._started_ports = self._ParseLogFilePorts(self._LogLines())
+ if HasIncompleteStartedPorts():
+ return False
+
+ try:
+ # HTTPS may require SNI (which urllib does not speak), so only check
+ # that HTTP responds.
+ return self._UrlOpen('web-page-replay-generate-200').getcode() == 200
+ except IOError:
+ return False
+
+ @staticmethod
+ def _ParseLogFilePorts(log_lines):
+ """Returns the ports on which replay listens as reported in its log file.
+
+ Only matches HTTP, HTTPS, and DNS. One call may return only some
+ of the ports depending on what has been written to the log file.
+
+ Example log lines:
+ 2014-09-03 17:04:27,978 Starting server on http://:51673
+ 2014-09-03 17:04:27,978 Starting server on https://:35270
+
+ Returns:
+ a dict with ports available in log_lines. For example,
+ {} # no ports found
+ {'http': 1234, 'https': 2345, 'dns': 3456}
+ """
+ ports = {}
+ port_re = re.compile(
+ r'.*Starting server on '
+ r'(?P<protocol>http|https)://'
+ r'(?P<host>[^:]*):'
+ r'(?P<port>\d+)')
+ for line in log_lines:
+ m = port_re.match(line.strip())
+ if m:
+ protocol = m.group('protocol').lower()
+ ports[protocol] = int(m.group('port'))
+ return ports
+
+ def StartServer(self):
+ """Start Web Page Replay and verify that it started.
+
+ Returns:
+ A dictionary mapping the keys 'http', 'https', and (if used) 'dns'
+ to the respective ports of the replay server.
+ Raises:
+ ReplayNotStartedError: if Replay start-up fails.
+ """
+ is_posix = sys.platform.startswith('linux') or sys.platform == 'darwin'
+ logging.info('Starting Web-Page-Replay: %s', self._cmd_line)
+ self._CreateTempLogFilePath()
+ with self._OpenLogFile() as log_fh:
+ self.replay_process = subprocess.Popen(
+ self._cmd_line, stdout=log_fh, stderr=subprocess.STDOUT,
+ preexec_fn=(_ResetInterruptHandler if is_posix else None))
+ try:
+ # TODO(crbug.com/805418): consider changing this to wait with I/O timeout.
+ # The 120s timeout is based on past failures (e.g: crbug.com/812639).
+ py_utils.WaitFor(self._IsStarted, timeout=120)
+ logging.info('WPR ports: %s', self._started_ports)
+ atexit_with_log.Register(self.StopServer)
+ return dict(self._started_ports)
+ except Exception:
+ self.StopServer(logging.ERROR)
+ raise ReplayNotStartedError('Web Page Replay failed to start.')
+
+ def _IsReplayProcessStarted(self):
+ if not self.replay_process:
+ return False
+ return self.replay_process and self.replay_process.poll() is None
+
+ def StopServer(self, log_level=logging.DEBUG):
+ """Stop Web Page Replay.
+
+ This also attempts to return stdout/stderr logs of wpr process if there is
+ any. If there is none, '(N/A)' string is returned (see _LogLines()
+ implementation).
+ """
+ if self._IsReplayProcessStarted():
+ self._StopReplayProcess()
+ self._CleanUpTempLogFilePath(log_level)
+ self._started_ports = {}
+
+ def _StopReplayProcess(self):
+ if not self.replay_process:
+ return
+ logging.debug('Trying to stop Web-Page-Replay gracefully')
+ try:
+ if self._started_ports:
+ self._UrlOpen('web-page-replay-command-exit').close()
+ except IOError:
+ # IOError is possible because the server might exit without response.
+ pass
+ try:
+ py_utils.WaitFor(lambda: self.replay_process.poll() is not None, 10)
+ except py_utils.TimeoutException:
+ try:
+ # Use a SIGINT so that it can do graceful cleanup.
+ self.replay_process.send_signal(signal.SIGINT)
+ except Exception: # pylint: disable=broad-except
+ # On Windows, we are left with no other option than terminate().
+ is_primary_nameserver_changed_by_replay = (
+ self._replay_host == '127.0.0.1')
+ if is_primary_nameserver_changed_by_replay:
+ # Replay changes the DNS nameserver configuration so that DNS
+ # requests are resolved by replay's own DNS server. It resolves
+ # all DNS requests to it own IP address to it can server the
+ # HTTP and HTTPS requests.
+ # If the replay host is not '127.0.0.1', then replay skips the
+ # nameserver change because it assumes a different mechanism
+ # will be used to route DNS requests to replay's DNS server.
+ logging.warning(
+ 'Unable to stop Web-Page-Replay gracefully.\n'
+ 'Replay changed the DNS nameserver configuration to make replay '
+ 'the primary nameserver. That might not be restored!')
+ self.replay_process.terminate()
+ self.replay_process.communicate()
+ finally:
+ self.replay_process = None
+
+ def _CreateTempLogFilePath(self):
+ assert self._temp_log_file_path is None
+ handle, self._temp_log_file_path = tempfile.mkstemp()
+ os.close(handle)
+
+ def _CleanUpTempLogFilePath(self, log_level):
+ if not self._temp_log_file_path:
+ return ''
+ if logging.getLogger('').isEnabledFor(log_level):
+ with open(self._temp_log_file_path, 'r') as f:
+ wpr_log_output = f.read()
+ logging.log(log_level, '\n'.join([
+ '************************** WPR LOG *****************************',
+ wpr_log_output,
+ '************************** END OF WPR LOG **********************']))
+ os.remove(self._temp_log_file_path)
+ self._temp_log_file_path = None
+
+ def __enter__(self):
+ """Add support for with-statement."""
+ self.StartServer()
+ return self
+
+ def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
+ """Add support for with-statement."""
+ self.StopServer()
+
+ def _UrlOpen(self, url_path, protocol='http'):
+ """Open a Replay URL.
+
+ For matching requests in the archive, Replay relies on the "Host:" header.
+ For Replay command URLs, the "Host:" header is not needed.
+
+ Args:
+ url_path: WPR server request path.
+ protocol: 'http' or 'https'
+ Returns:
+ a file-like object from urllib.urlopen
+ """
+ url = '%s://%s:%s/%s' % (
+ protocol, self._replay_host, self._started_ports[protocol], url_path)
+ return urllib.urlopen(url, proxies={})
+
+def _ResetInterruptHandler():
+ """Reset the interrupt handler back to the default.
+
+ The replay process is stopped gracefully by making an HTTP request
+ ('web-page-replay-command-exit'). The graceful exit is important for
+ restoring the DNS configuration. If the HTTP request fails, the fallback
+ is to send SIGINT to the process.
+
+ On posix system, running this function before starting replay fixes a
+ bug that shows up when Telemetry is run as a background command from a
+ script. https://crbug.com/254572.
+
+ Background: Signal masks on Linux are inherited from parent
+ processes. If anything invoking us accidentally masks SIGINT
+ (e.g. by putting a process in the background from a shell script),
+ sending a SIGINT to the child will fail to terminate it.
+ """
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
diff --git a/catapult/common/py_vulcanize/py_vulcanize/generate.py b/catapult/common/py_vulcanize/py_vulcanize/generate.py
index 484c705a..8af37310 100644
--- a/catapult/common/py_vulcanize/py_vulcanize/generate.py
+++ b/catapult/common/py_vulcanize/py_vulcanize/generate.py
@@ -52,6 +52,22 @@ css_warning_message = """
*/
"""
+origin_trial_tokens = [
+ # WebComponent V0 origin trial token for googleusercontent.com + subdomains.
+ # This is the domain from which traces in cloud storage are served.
+ # Expires Nov 5, 2020. See https://crbug.com/1021137
+ "AnYuQDtUf6OrWCmR9Okd67JhWVTbmnRedvPi1TEvAxac8+1p6o9q08FoDO6oCbLD0xEqev+SkZFiIhFSzlY9HgUAAABxeyJvcmlnaW4iOiJodHRwczovL2dvb2dsZXVzZXJjb250ZW50LmNvbTo0NDMiLCJmZWF0dXJlIjoiV2ViQ29tcG9uZW50c1YwIiwiZXhwaXJ5IjoxNjA0NjE0NTM4LCJpc1N1YmRvbWFpbiI6dHJ1ZX0=",
+ # This is for chromium-build-stats.appspot.com (ukai@)
+ # Expires Feb 2, 2021. see https://crbug.com/1050215
+ "AkFXw3wHnOs/XXYqFXpc3diDLrRFd9PTgGs/gs43haZmngI/u1g8L4bDnSKLZkB6fecjmjTwcAMQFCpWMAoHSQEAAAB8eyJvcmlnaW4iOiJodHRwczovL2Nocm9taXVtLWJ1aWxkLXN0YXRzLmFwcHNwb3QuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJDb21wb25lbnRzVjAiLCJleHBpcnkiOjE2MTIyMjM5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==",
+ # This is for chromium-build-stats-staging.appspot.com (ukai@)
+ # Expires Feb 2, 2021, see https://crbug.com/1050215
+ "AtQY4wpX9+nj+Vn27cTgygzIPbtB2WoAoMQR5jK9mCm/H2gRIDH6MmGVAaziv9XnYTDKjhBnQYtecbTiIHCQiAIAAACEeyJvcmlnaW4iOiJodHRwczovL2Nocm9taXVtLWJ1aWxkLXN0YXRzLXN0YWdpbmcuYXBwc3BvdC5jb206NDQzIiwiZmVhdHVyZSI6IldlYkNvbXBvbmVudHNWMCIsImV4cGlyeSI6MTYxMjIyMzk5OSwiaXNTdWJkb21haW4iOnRydWV9"
+ #
+ # Add more tokens here if traces are served from other domains.
+ # WebComponent V0 origin tiral token is generated on
+ # https://developers.chrome.com/origintrials/#/trials/active
+]
def _AssertIsUTF8(f):
if isinstance(f, StringIO):
@@ -74,13 +90,13 @@ def _MinifyJS(input_js):
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
- res = p.communicate(input=input_js)
+ res = p.communicate(input=input_js.encode('utf-8'))
errorcode = p.wait()
if errorcode != 0:
sys.stderr.write('rJSmin exited with error code %d' % errorcode)
sys.stderr.write(res[1])
raise Exception('Failed to minify, omgah')
- return res[0]
+ return res[0].decode('utf-8')
def GenerateJS(load_sequence,
@@ -206,6 +222,11 @@ def GenerateStandaloneHTMLAsString(*args, **kwargs):
GenerateStandaloneHTMLToFile(f, *args, **kwargs)
return f.getvalue()
+def _WriteOriginTrialTokens(output_file):
+ for token in origin_trial_tokens:
+ output_file.write(' <meta http-equiv="origin-trial" content="')
+ output_file.write(token)
+ output_file.write('">\n')
def GenerateStandaloneHTMLToFile(output_file,
load_sequence,
@@ -231,6 +252,7 @@ def GenerateStandaloneHTMLToFile(output_file,
' <head i18n-values="dir:textdirection;">\n'
' <meta http-equiv="Content-Type" content="text/html;'
'charset=utf-8">\n')
+ _WriteOriginTrialTokens(output_file)
if title:
output_file.write(' <title>%s</title>\n ' % title)
else:
diff --git a/catapult/dependency_manager/dependency_manager/dependency_manager_unittest.py b/catapult/dependency_manager/dependency_manager/dependency_manager_unittest.py
index 86d17f79..52a6d207 100644
--- a/catapult/dependency_manager/dependency_manager/dependency_manager_unittest.py
+++ b/catapult/dependency_manager/dependency_manager/dependency_manager_unittest.py
@@ -29,7 +29,7 @@ class DependencyManagerTest(fake_filesystem_unittest.TestCase):
def tearDown(self):
self.tearDownPyfakefs()
- # TODO(nednguyen): add a test that construct
+ # TODO(crbug.com/1111556): add a test that construct
# dependency_manager.DependencyManager from a list of DependencyInfo.
def testErrorInit(self):
with self.assertRaises(ValueError):
diff --git a/catapult/devil/.style.yapf b/catapult/devil/.style.yapf
new file mode 100644
index 00000000..95091f08
--- /dev/null
+++ b/catapult/devil/.style.yapf
@@ -0,0 +1,7 @@
+[style]
+based_on_style = pep8
+
+allow_multiline_lambdas = True
+column_limit = 80
+indent_dictionary_value = True
+indent_width = 2
diff --git a/catapult/devil/PRESUBMIT.py b/catapult/devil/PRESUBMIT.py
index 289a5c65..0b49eb80 100644
--- a/catapult/devil/PRESUBMIT.py
+++ b/catapult/devil/PRESUBMIT.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Presubmit script for devil.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
@@ -10,39 +9,42 @@ details on the presubmit API built into depot_tools.
def _RunPylint(input_api, output_api):
- return input_api.RunTests(input_api.canned_checks.RunPylint(
- input_api, output_api, pylintrc='pylintrc'))
+ return input_api.RunTests(
+ input_api.canned_checks.RunPylint(
+ input_api, output_api, pylintrc='pylintrc'))
def _RunUnitTests(input_api, output_api):
def J(*dirs):
"""Returns a path relative to presubmit directory."""
- return input_api.os_path.join(
- input_api.PresubmitLocalPath(), 'devil', *dirs)
+ return input_api.os_path.join(input_api.PresubmitLocalPath(), 'devil',
+ *dirs)
test_env = dict(input_api.environ)
test_env.update({
- 'PYTHONDONTWRITEBYTECODE': '1',
- 'PYTHONPATH': ':'.join([J(), J('..')]),
+ 'PYTHONDONTWRITEBYTECODE': '1',
+ 'PYTHONPATH': ':'.join([J(), J('..')]),
})
- message_type = (output_api.PresubmitError if input_api.is_committing
- else output_api.PresubmitPromptWarning)
+ message_type = (output_api.PresubmitError if input_api.is_committing else
+ output_api.PresubmitPromptWarning)
return input_api.RunTests([
input_api.Command(
name='devil/bin/run_py_tests',
cmd=[
- input_api.os_path.join(
- input_api.PresubmitLocalPath(), 'bin', 'run_py_tests')],
+ input_api.os_path.join(input_api.PresubmitLocalPath(), 'bin',
+ 'run_py_tests')
+ ],
kwargs={'env': test_env},
- message=message_type)])
+ message=message_type)
+ ])
def _EnsureNoPylibUse(input_api, output_api):
def other_python_files(f):
- this_presubmit_file = input_api.os_path.join(
- input_api.PresubmitLocalPath(), 'PRESUBMIT.py')
+ this_presubmit_file = input_api.os_path.join(input_api.PresubmitLocalPath(),
+ 'PRESUBMIT.py')
return (f.LocalPath().endswith('.py')
and not f.AbsoluteLocalPath() == this_presubmit_file)
@@ -52,15 +54,16 @@ def _EnsureNoPylibUse(input_api, output_api):
errors = []
for f in changed_files:
- errors.extend(
- '%s:%d' % (f.LocalPath(), line_number)
- for line_number, line_text in f.ChangedContents()
- if import_error_re.search(line_text))
+ errors.extend('%s:%d' % (f.LocalPath(), line_number)
+ for line_number, line_text in f.ChangedContents()
+ if import_error_re.search(line_text))
if errors:
- return [output_api.PresubmitError(
- 'pylib modules should not be imported from devil modules.',
- items=errors)]
+ return [
+ output_api.PresubmitError(
+ 'pylib modules should not be imported from devil modules.',
+ items=errors)
+ ]
return []
@@ -78,4 +81,3 @@ def CheckChangeOnUpload(input_api, output_api):
def CheckChangeOnCommit(input_api, output_api):
return CommonChecks(input_api, output_api)
-
diff --git a/catapult/devil/README.md b/catapult/devil/README.md
index 9953e6ae..83f4ba09 100644
--- a/catapult/devil/README.md
+++ b/catapult/devil/README.md
@@ -27,11 +27,9 @@ devil also provides command-line utilities:
## Constraints and Caveats
-devil is used with python 2.7. Its compatibility with python 3 has not been
-tested, and neither achieving nor maintaining said compatibility is currently
-a priority.
+devil supports python 2.7. Python 3 compatibility is currently a work in
+progress (see https://crbug.com/1007101).
## Contributing
-Please see the [contributor's guide](https://github.com/catapult-project/catapult/blob/master/CONTRIBUTING.md).
-
+Please see [our contributor's guide](/CONTRIBUTING.md)
diff --git a/catapult/devil/bin/generate_md_docs b/catapult/devil/bin/generate_md_docs
index 634e14a5..1cca44c1 100755
--- a/catapult/devil/bin/generate_md_docs
+++ b/catapult/devil/bin/generate_md_docs
@@ -3,25 +3,25 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import logging
import os
import sys
-_DEVIL_PATH = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..'))
-_DEVIL_URL = (
- 'https://github.com/catapult-project/catapult/blob/master/devil/')
+_DEVIL_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+_DEVIL_URL = ('https://github.com/catapult-project/catapult/blob/master/devil/')
sys.path.append(_DEVIL_PATH)
from devil.utils import cmd_helper
_FILES_TO_DOC = {
- 'devil/android/sdk/adb_wrapper.py': 'docs/adb_wrapper.md',
- 'devil/android/device_utils.py': 'docs/device_utils.md',
- 'devil/utils/markdown.py': 'docs/markdown.md',
+ 'devil/android/sdk/adb_wrapper.py': 'docs/adb_wrapper.md',
+ 'devil/android/device_utils.py': 'docs/device_utils.md',
+ 'devil/utils/markdown.py': 'docs/markdown.md',
}
_MARKDOWN_SCRIPT = os.path.join(_DEVIL_PATH, 'devil', 'utils', 'markdown.py')
+
def main():
failed = False
for k, v in _FILES_TO_DOC.iteritems():
@@ -29,9 +29,10 @@ def main():
module_link = _DEVIL_URL + k
doc_path = os.path.join(_DEVIL_PATH, v)
- status, stdout = cmd_helper.GetCmdStatusAndOutput(
- [sys.executable, _MARKDOWN_SCRIPT, module_path,
- '--module-link', module_link])
+ status, stdout = cmd_helper.GetCmdStatusAndOutput([
+ sys.executable, _MARKDOWN_SCRIPT, module_path, '--module-link',
+ module_link
+ ])
if status:
logging.error('Failed to update doc for %s' % module_path)
failed = True
@@ -41,5 +42,6 @@ def main():
return 1 if failed else 0
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/catapult/devil/bin/run_py_devicetests b/catapult/devil/bin/run_py_devicetests
index 656bedf2..6a6da188 100755
--- a/catapult/devil/bin/run_py_devicetests
+++ b/catapult/devil/bin/run_py_devicetests
@@ -3,13 +3,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import logging
import os
import sys
-_CATAPULT_PATH = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..'))
-_DEVIL_PATH = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..'))
+_CATAPULT_PATH = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
+_DEVIL_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
_TYP_PATH = os.path.abspath(os.path.join(_CATAPULT_PATH, 'third_party', 'typ'))
sys.path.append(_TYP_PATH)
@@ -19,7 +19,24 @@ sys.path.append(_DEVIL_PATH)
from devil.android import device_test_case
+def _SetUpLogging():
+ parsed_args = typ.arg_parser.ArgumentParser().parse_args(args=sys.argv[1:])
+ verbosity = parsed_args.verbose
+ level = None
+ if verbosity == 0:
+ level = logging.WARNING
+ elif verbosity == 1:
+ level = logging.INFO
+ elif verbosity >= 2:
+ level = logging.DEBUG
+ else:
+ raise RuntimeError(
+ 'Logging verbosity of {} is not allowed.'.format(verbosity))
+ logging.basicConfig(level=level)
+
+
def main():
+ _SetUpLogging()
runner = typ.Runner()
runner.setup_fn = device_test_case.PrepareDevices
return runner.main(
@@ -28,5 +45,6 @@ def main():
suffixes=['*_devicetest.py'],
top_level_dir=_DEVIL_PATH)
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/catapult/devil/bin/run_py_tests b/catapult/devil/bin/run_py_tests
index a74fa838..112451c6 100755
--- a/catapult/devil/bin/run_py_tests
+++ b/catapult/devil/bin/run_py_tests
@@ -6,10 +6,9 @@
import os
import sys
-_CATAPULT_PATH = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..'))
-_DEVIL_PATH = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..'))
+_CATAPULT_PATH = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
+_DEVIL_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(_CATAPULT_PATH)
from catapult_build import run_with_typ
@@ -24,5 +23,6 @@ def main():
return run_with_typ.Run(top_level_dir=_DEVIL_PATH)
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/catapult/devil/build/cipd.yaml b/catapult/devil/build/cipd.yaml
new file mode 100644
index 00000000..fcb4db3b
--- /dev/null
+++ b/catapult/devil/build/cipd.yaml
@@ -0,0 +1,14 @@
+package: chromium/third_party/catapult/devil/${platform}
+
+description: All of devil along with its dependencies in catapult.
+
+platforms:
+ - linux-amd64
+
+root: ../../
+
+data:
+ - dir: common/py_utils
+ - dir: dependency_manager
+ - dir: devil
+ - dir: third_party/zipfile
diff --git a/catapult/devil/devil/android/apk_helper.py b/catapult/devil/devil/android/apk_helper.py
index abdf9071..fdece072 100644
--- a/catapult/devil/devil/android/apk_helper.py
+++ b/catapult/devil/devil/android/apk_helper.py
@@ -1,42 +1,97 @@
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Module containing utilities for apk packages."""
+import contextlib
+import logging
+import os
import re
-import xml.etree.ElementTree
+import shutil
+import tempfile
import zipfile
from devil import base_error
from devil.android.ndk import abis
from devil.android.sdk import aapt
+from devil.android.sdk import bundletool
+from devil.android.sdk import split_select
from devil.utils import cmd_helper
+_logger = logging.getLogger(__name__)
-_MANIFEST_ATTRIBUTE_RE = re.compile(
- r'\s*A: ([^\(\)= ]*)(?:\([^\(\)= ]*\))?='
- r'(?:"(.*)" \(Raw: .*\)|\(type.*?\)(.*))$')
+_MANIFEST_ATTRIBUTE_RE = re.compile(r'\s*A: ([^\(\)= ]*)(?:\([^\(\)= ]*\))?='
+ r'(?:"(.*)" \(Raw: .*\)|\(type.*?\)(.*))$')
_MANIFEST_ELEMENT_RE = re.compile(r'\s*(?:E|N): (\S*) .*$')
+_BASE_APK_APKS_RE = re.compile(r'^splits/base-master.*\.apk$')
+
+
+class ApkHelperError(base_error.BaseError):
+ """Exception for APK helper failures."""
+
+ def __init__(self, message):
+ super(ApkHelperError, self).__init__(message)
+
+
+@contextlib.contextmanager
+def _DeleteHelper(files, to_delete):
+ """Context manager that returns |files| and deletes |to_delete| on exit."""
+ try:
+ yield files
+ finally:
+ paths = to_delete if isinstance(to_delete, list) else [to_delete]
+ for path in paths:
+ if os.path.isfile(path):
+ os.remove(path)
+ elif os.path.isdir(path):
+ shutil.rmtree(path)
+ else:
+ raise ApkHelperError('Cannot delete %s' % path)
+
+
+@contextlib.contextmanager
+def _NoopFileHelper(files):
+ """Context manager that returns |files|."""
+ yield files
def GetPackageName(apk_path):
"""Returns the package name of the apk."""
- return ApkHelper(apk_path).GetPackageName()
+ return ToHelper(apk_path).GetPackageName()
# TODO(jbudorick): Deprecate and remove this function once callers have been
# converted to ApkHelper.GetInstrumentationName
def GetInstrumentationName(apk_path):
"""Returns the name of the Instrumentation in the apk."""
- return ApkHelper(apk_path).GetInstrumentationName()
+ return ToHelper(apk_path).GetInstrumentationName()
def ToHelper(path_or_helper):
"""Creates an ApkHelper unless one is already given."""
- if isinstance(path_or_helper, basestring):
+ if not isinstance(path_or_helper, basestring):
+ return path_or_helper
+ elif path_or_helper.endswith('.apk'):
return ApkHelper(path_or_helper)
- return path_or_helper
+ elif path_or_helper.endswith('.apks'):
+ return ApksHelper(path_or_helper)
+ elif path_or_helper.endswith('_bundle'):
+ return BundleScriptHelper(path_or_helper)
+
+ raise ApkHelperError('Unrecognized APK format %s' % path_or_helper)
+
+
+def ToSplitHelper(path_or_helper, split_apks):
+ if isinstance(path_or_helper, SplitApkHelper):
+ if sorted(path_or_helper.split_apk_paths) != sorted(split_apks):
+ raise ApkHelperError('Helper has different split APKs')
+ return path_or_helper
+ elif (isinstance(path_or_helper, basestring)
+ and path_or_helper.endswith('.apk')):
+ return SplitApkHelper(path_or_helper, split_apks)
+
+ raise ApkHelperError(
+ 'Unrecognized APK format %s, %s' % (path_or_helper, split_apks))
# To parse the manifest, the function uses a node stack where at each level of
@@ -48,8 +103,8 @@ def ToHelper(path_or_helper):
# matches the height of the stack). Each line parsed (either an attribute or an
# element) is added to the node at the top of the stack (after the stack has
# been popped/pushed due to indentation).
-def _ParseManifestFromApk(apk):
- aapt_output = aapt.Dump('xmltree', apk.path, 'AndroidManifest.xml')
+def _ParseManifestFromApk(apk_path):
+ aapt_output = aapt.Dump('xmltree', apk_path, 'AndroidManifest.xml')
parsed_manifest = {}
node_stack = [parsed_manifest]
indent = ' '
@@ -69,7 +124,8 @@ def _ParseManifestFromApk(apk):
# If namespaces are stripped, aapt still outputs the full url to the
# namespace and appends it to the attribute names.
- line = line.replace('http://schemas.android.com/apk/res/android:', 'android:')
+ line = line.replace('http://schemas.android.com/apk/res/android:',
+ 'android:')
indent_depth = 0
while line[(len(indent) * indent_depth):].startswith(indent):
@@ -97,9 +153,9 @@ def _ParseManifestFromApk(apk):
if m:
manifest_key = m.group(1)
if manifest_key in node:
- raise base_error.BaseError(
- "A single attribute should have one key and one value: {}"
- .format(line))
+ raise ApkHelperError(
+ "A single attribute should have one key and one value: {}".format(
+ line))
else:
node[manifest_key] = m.group(2) or m.group(3)
continue
@@ -107,47 +163,6 @@ def _ParseManifestFromApk(apk):
return parsed_manifest
-def _ParseManifestFromBundle(bundle):
- cmd = [bundle.path, 'dump-manifest']
- status, stdout, stderr = cmd_helper.GetCmdStatusOutputAndError(cmd)
- if status != 0:
- raise Exception('Failed running {} with output\n{}\n{}'.format(
- ' '.join(cmd), stdout, stderr))
- return ParseManifestFromXml(stdout)
-
-
-def ParseManifestFromXml(xml_str):
- """Parse an android bundle manifest.
-
- As ParseManifestFromAapt, but uses the xml output from bundletool. Each
- element is a dict, mapping attribute or children by name. Attributes map to
- a dict (as they are unique), children map to a list of dicts (as there may
- be multiple children with the same name).
-
- Args:
- xml_str (str) An xml string that is an android manifest.
-
- Returns:
- A dict holding the parsed manifest, as with ParseManifestFromAapt.
- """
- root = xml.etree.ElementTree.fromstring(xml_str)
- return {root.tag: [_ParseManifestXMLNode(root)]}
-
-
-def _ParseManifestXMLNode(node):
- out = {}
- for name, value in node.attrib.items():
- cleaned_name = name.replace(
- '{http://schemas.android.com/apk/res/android}',
- 'android:').replace(
- '{http://schemas.android.com/tools}',
- 'tools:')
- out[cleaned_name] = value
- for child in node:
- out.setdefault(child.tag, []).append(_ParseManifestXMLNode(child))
- return out
-
-
def _ParseNumericKey(obj, key, default=0):
val = obj.get(key)
if val is None:
@@ -155,6 +170,13 @@ def _ParseNumericKey(obj, key, default=0):
return int(val, 0)
+def _SplitLocaleString(locale):
+ split_locale = locale.split('-')
+ if len(split_locale) != 2:
+ raise ApkHelperError('Locale has incorrect format: {}'.format(locale))
+ return tuple(split_locale)
+
+
class _ExportedActivity(object):
def __init__(self, name):
self.name = name
@@ -186,26 +208,32 @@ def _IterateExportedActivities(manifest_info):
yield activity
-class ApkHelper(object):
+class BaseApkHelper(object):
+ """Abstract base class representing an installable Android app."""
- def __init__(self, path):
- self._apk_path = path
+ def __init__(self):
self._manifest = None
@property
def path(self):
- return self._apk_path
+ raise NotImplementedError()
- @property
- def is_bundle(self):
- return self._apk_path.endswith('_bundle')
+ def __repr__(self):
+ return '%s(%s)' % (self.__class__.__name__, self.path)
+
+ def _GetBaseApkPath(self):
+ """Returns context manager providing path to this app's base APK.
+
+ Must be implemented by subclasses.
+ """
+ raise NotImplementedError()
def GetActivityName(self):
"""Returns the name of the first launcher Activity in the apk."""
manifest_info = self._GetManifest()
for activity in _IterateExportedActivities(manifest_info):
- if ('android.intent.action.MAIN' in activity.actions and
- 'android.intent.category.LAUNCHER' in activity.categories):
+ if ('android.intent.action.MAIN' in activity.actions
+ and 'android.intent.category.LAUNCHER' in activity.categories):
return self._ResolveName(activity.name)
return None
@@ -213,23 +241,23 @@ class ApkHelper(object):
"""Returns name of the first action=View Activity that can handle http."""
manifest_info = self._GetManifest()
for activity in _IterateExportedActivities(manifest_info):
- if ('android.intent.action.VIEW' in activity.actions and
- 'http' in activity.schemes):
+ if ('android.intent.action.VIEW' in activity.actions
+ and 'http' in activity.schemes):
return self._ResolveName(activity.name)
return None
- def GetInstrumentationName(
- self, default='android.test.InstrumentationTestRunner'):
+ def GetInstrumentationName(self,
+ default='android.test.InstrumentationTestRunner'):
"""Returns the name of the Instrumentation in the apk."""
all_instrumentations = self.GetAllInstrumentations(default=default)
if len(all_instrumentations) != 1:
- raise base_error.BaseError(
+ raise ApkHelperError(
'There is more than one instrumentation. Expected one.')
else:
return self._ResolveName(all_instrumentations[0]['android:name'])
- def GetAllInstrumentations(
- self, default='android.test.InstrumentationTestRunner'):
+ def GetAllInstrumentations(self,
+ default='android.test.InstrumentationTestRunner'):
"""Returns a list of all Instrumentations in the apk."""
try:
return self._GetManifest()['manifest'][0]['instrumentation']
@@ -242,13 +270,15 @@ class ApkHelper(object):
try:
return manifest_info['manifest'][0]['package']
except KeyError:
- raise Exception('Failed to determine package name of %s' % self._apk_path)
+ raise ApkHelperError('Failed to determine package name of %s' % self.path)
def GetPermissions(self):
manifest_info = self._GetManifest()
try:
- return [p['android:name'] for
- p in manifest_info['manifest'][0]['uses-permission']]
+ return [
+ p['android:name']
+ for p in manifest_info['manifest'][0]['uses-permission']
+ ]
except KeyError:
return []
@@ -323,7 +353,9 @@ class ApkHelper(object):
def GetTargetSdkVersion(self):
"""Returns the targetSdkVersion as a string, or None if not available.
- Note: this cannot always be cast to an integer."""
+ Note: this cannot always be cast to an integer. If this application targets
+ a pre-release SDK, this returns the SDK codename instead (ex. "R").
+ """
manifest_info = self._GetManifest()
try:
uses_sdk = manifest_info['manifest'][0]['uses-sdk'][0]
@@ -343,11 +375,8 @@ class ApkHelper(object):
def _GetManifest(self):
if not self._manifest:
- app = ToHelper(self._apk_path)
- if app.is_bundle:
- self._manifest = _ParseManifestFromBundle(app)
- else:
- self._manifest = _ParseManifestFromApk(app)
+ with self._GetBaseApkPath() as base_apk_path:
+ self._manifest = _ParseManifestFromApk(base_apk_path)
return self._manifest
def _ResolveName(self, name):
@@ -357,8 +386,9 @@ class ApkHelper(object):
return name
def _ListApkPaths(self):
- with zipfile.ZipFile(self._apk_path) as z:
- return z.namelist()
+ with self._GetBaseApkPath() as base_apk_path:
+ with zipfile.ZipFile(base_apk_path) as z:
+ return z.namelist()
def GetAbis(self):
"""Returns a list of ABIs in the apk (empty list if no native code)."""
@@ -381,4 +411,197 @@ class ApkHelper(object):
output.add(abi)
return sorted(output)
except KeyError:
- raise base_error.BaseError('Unexpected ABI in lib/* folder.')
+ raise ApkHelperError('Unexpected ABI in lib/* folder.')
+
+ def GetApkPaths(self,
+ device,
+ modules=None,
+ allow_cached_props=False,
+ additional_locales=None):
+ """Returns context manager providing list of split APK paths for |device|.
+
+ The paths may be deleted when the context manager exits. Must be implemented
+ by subclasses.
+
+ args:
+ device: The device for which to return split APKs.
+ modules: Extra feature modules to install.
+ allow_cached_props: Allow using cache when querying propery values from
+ |device|.
+ """
+ # pylint: disable=unused-argument
+ raise NotImplementedError()
+
+ @staticmethod
+ def SupportsSplits():
+ return False
+
+
+class ApkHelper(BaseApkHelper):
+ """Represents a single APK Android app."""
+
+ def __init__(self, apk_path):
+ super(ApkHelper, self).__init__()
+ self._apk_path = apk_path
+
+ @property
+ def path(self):
+ return self._apk_path
+
+ def _GetBaseApkPath(self):
+ return _NoopFileHelper(self._apk_path)
+
+ def GetApkPaths(self,
+ device,
+ modules=None,
+ allow_cached_props=False,
+ additional_locales=None):
+ if modules:
+ raise ApkHelperError('Cannot install modules when installing single APK')
+ return _NoopFileHelper([self._apk_path])
+
+
+class SplitApkHelper(BaseApkHelper):
+ """Represents a multi APK Android app."""
+
+ def __init__(self, base_apk_path, split_apk_paths):
+ super(SplitApkHelper, self).__init__()
+ self._base_apk_path = base_apk_path
+ self._split_apk_paths = split_apk_paths
+
+ @property
+ def path(self):
+ return self._base_apk_path
+
+ @property
+ def split_apk_paths(self):
+ return self._split_apk_paths
+
+ def __repr__(self):
+ return '%s(%s, %s)' % (self.__class__.__name__, self.path,
+ self.split_apk_paths)
+
+ def _GetBaseApkPath(self):
+ return _NoopFileHelper(self._base_apk_path)
+
+ def GetApkPaths(self,
+ device,
+ modules=None,
+ allow_cached_props=False,
+ additional_locales=None):
+ if modules:
+ raise ApkHelperError('Cannot install modules when installing single APK')
+ splits = split_select.SelectSplits(
+ device,
+ self.path,
+ self.split_apk_paths,
+ allow_cached_props=allow_cached_props)
+ if len(splits) == 1:
+ _logger.warning('split-select did not select any from %s', splits)
+ return _NoopFileHelper([self._base_apk_path] + splits)
+
+ #override
+ @staticmethod
+ def SupportsSplits():
+ return True
+
+
+class BaseBundleHelper(BaseApkHelper):
+ """Abstract base class representing an Android app bundle."""
+
+ def _GetApksPath(self):
+ """Returns context manager providing path to the bundle's APKS archive.
+
+ Must be implemented by subclasses.
+ """
+ raise NotImplementedError()
+
+ def _GetBaseApkPath(self):
+ try:
+ base_apk_path = tempfile.mkdtemp()
+ with self._GetApksPath() as apks_path:
+ with zipfile.ZipFile(apks_path) as z:
+ base_apks = [s for s in z.namelist() if _BASE_APK_APKS_RE.match(s)]
+ if len(base_apks) < 1:
+ raise ApkHelperError('Cannot find base APK in %s' % self.path)
+ z.extract(base_apks[0], base_apk_path)
+ return _DeleteHelper(
+ os.path.join(base_apk_path, base_apks[0]), base_apk_path)
+ except:
+ shutil.rmtree(base_apk_path)
+ raise
+
+ def GetApkPaths(self,
+ device,
+ modules=None,
+ allow_cached_props=False,
+ additional_locales=None):
+ locales = [device.GetLocale()]
+ if additional_locales:
+ locales.extend(_SplitLocaleString(l) for l in additional_locales)
+ with self._GetApksPath() as apks_path:
+ try:
+ split_dir = tempfile.mkdtemp()
+ # TODO(tiborg): Support all locales.
+ bundletool.ExtractApks(split_dir, apks_path,
+ device.product_cpu_abis, locales,
+ device.GetFeatures(), device.pixel_density,
+ device.build_version_sdk, modules)
+ splits = [os.path.join(split_dir, p) for p in os.listdir(split_dir)]
+ return _DeleteHelper(splits, split_dir)
+ except:
+ shutil.rmtree(split_dir)
+ raise
+
+ #override
+ @staticmethod
+ def SupportsSplits():
+ return True
+
+
+class ApksHelper(BaseBundleHelper):
+ """Represents a bundle's APKS archive."""
+
+ def __init__(self, apks_path):
+ super(ApksHelper, self).__init__()
+ self._apks_path = apks_path
+
+ @property
+ def path(self):
+ return self._apks_path
+
+ def _GetApksPath(self):
+ return _NoopFileHelper(self._apks_path)
+
+
+class BundleScriptHelper(BaseBundleHelper):
+ """Represents a bundle install script."""
+
+ def __init__(self, bundle_script_path):
+ super(BundleScriptHelper, self).__init__()
+ self._bundle_script_path = bundle_script_path
+
+ @property
+ def path(self):
+ return self._bundle_script_path
+
+ def _GetApksPath(self):
+ apks_path = None
+ try:
+ fd, apks_path = tempfile.mkstemp(suffix='.apks')
+ os.close(fd)
+ cmd = [
+ self._bundle_script_path,
+ 'build-bundle-apks',
+ '--output-apks',
+ apks_path,
+ ]
+ status, stdout, stderr = cmd_helper.GetCmdStatusOutputAndError(cmd)
+ if status != 0:
+ raise ApkHelperError('Failed running {} with output\n{}\n{}'.format(
+ ' '.join(cmd), stdout, stderr))
+ return _DeleteHelper(apks_path, apks_path)
+ except:
+ if apks_path:
+ os.remove(apks_path)
+ raise
diff --git a/catapult/devil/devil/android/apk_helper_test.py b/catapult/devil/devil/android/apk_helper_test.py
index 3258bb01..6ac7fde2 100755
--- a/catapult/devil/devil/android/apk_helper_test.py
+++ b/catapult/devil/devil/android/apk_helper_test.py
@@ -7,7 +7,6 @@ import collections
import os
import unittest
-from devil import base_error
from devil import devil_env
from devil.android import apk_helper
from devil.android.ndk import abis
@@ -16,7 +15,6 @@ from devil.utils import mock_calls
with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
-
# pylint: disable=line-too-long
_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android
E: manifest (line=1)
@@ -135,6 +133,7 @@ _NO_NAMESPACE_MANIFEST_DUMP = """E: manifest (line=1)
A: http://schemas.android.com/apk/res/android:name(0x01010003)="org.chromium.RandomTestRunner" (Raw: "org.chromium.RandomTestRunner")
A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge")
"""
+
# pylint: enable=line-too-long
@@ -143,30 +142,70 @@ def _MockAaptDump(manifest_dump):
'devil.android.sdk.aapt.Dump',
mock.Mock(side_effect=None, return_value=manifest_dump.split('\n')))
+
def _MockListApkPaths(files):
- return mock.patch(
- 'devil.android.apk_helper.ApkHelper._ListApkPaths',
- mock.Mock(side_effect=None, return_value=files))
+ return mock.patch('devil.android.apk_helper.ApkHelper._ListApkPaths',
+ mock.Mock(side_effect=None, return_value=files))
+
+
+class _MockDeviceUtils(object):
+ def __init__(self):
+ self.product_cpu_abi = abis.ARM_64
+ self.product_cpu_abis = [abis.ARM_64, abis.ARM]
+ self.pixel_density = 500
+ self.build_version_sdk = 28
+
+ def GetLocale(self):
+ # pylint: disable=no-self-use
+ return ('en', 'US')
+
+ def GetFeatures(self):
+ # pylint: disable=no-self-use
+ return [
+ 'android.hardware.wifi',
+ 'android.hardware.nfc',
+ ]
+
class ApkHelperTest(mock_calls.TestCase):
+ def testToHelperApk(self):
+ apk = apk_helper.ToHelper('abc.apk')
+ self.assertTrue(isinstance(apk, apk_helper.ApkHelper))
+
+ def testToHelperApks(self):
+ apk = apk_helper.ToHelper('abc.apks')
+ self.assertTrue(isinstance(apk, apk_helper.ApksHelper))
+
+ def testToHelperBundleScript(self):
+ apk = apk_helper.ToHelper('abc_bundle')
+ self.assertTrue(isinstance(apk, apk_helper.BundleScriptHelper))
+
+ def testToHelperSplitApk(self):
+ apk = apk_helper.ToSplitHelper('abc.apk', ['a.apk', 'b.apk'])
+ self.assertTrue(isinstance(apk, apk_helper.SplitApkHelper))
+
+ def testToHelperSplitException(self):
+ with self.assertRaises(apk_helper.ApkHelperError):
+ apk_helper.ToSplitHelper(
+ apk_helper.ToHelper('abc.apk'), ['a.apk', 'b.apk'])
def testGetInstrumentationName(self):
with _MockAaptDump(_MANIFEST_DUMP):
helper = apk_helper.ApkHelper('')
- with self.assertRaises(base_error.BaseError):
+ with self.assertRaises(apk_helper.ApkHelperError):
helper.GetInstrumentationName()
def testGetActivityName(self):
with _MockAaptDump(_MANIFEST_DUMP):
helper = apk_helper.ApkHelper('')
- self.assertEquals(
- helper.GetActivityName(), 'org.chromium.abc.MainActivity')
+ self.assertEquals(helper.GetActivityName(),
+ 'org.chromium.abc.MainActivity')
def testGetViewActivityName(self):
with _MockAaptDump(_MANIFEST_DUMP):
helper = apk_helper.ApkHelper('')
- self.assertEquals(
- helper.GetViewActivityName(), 'org.chromium.ViewActivity')
+ self.assertEquals(helper.GetViewActivityName(),
+ 'org.chromium.ViewActivity')
def testGetAllInstrumentations(self):
with _MockAaptDump(_MANIFEST_DUMP):
@@ -275,107 +314,131 @@ class ApkHelperTest(mock_calls.TestCase):
def testGetArchitectures(self):
AbiPair = collections.namedtuple('AbiPair', ['abi32bit', 'abi64bit'])
- for abi_pair in [AbiPair('lib/' + abis.ARM, 'lib/' + abis.ARM_64),
- AbiPair('lib/' + abis.X86, 'lib/' + abis.X86_64)]:
+ for abi_pair in [
+ AbiPair('lib/' + abis.ARM, 'lib/' + abis.ARM_64),
+ AbiPair('lib/' + abis.X86, 'lib/' + abis.X86_64)
+ ]:
with _MockListApkPaths([abi_pair.abi32bit]):
helper = apk_helper.ApkHelper('')
- self.assertEquals(set([os.path.basename(abi_pair.abi32bit),
- os.path.basename(abi_pair.abi64bit)]),
- set(helper.GetAbis()))
+ self.assertEquals(
+ set([
+ os.path.basename(abi_pair.abi32bit),
+ os.path.basename(abi_pair.abi64bit)
+ ]), set(helper.GetAbis()))
with _MockListApkPaths([abi_pair.abi32bit, abi_pair.abi64bit]):
helper = apk_helper.ApkHelper('')
- self.assertEquals(set([os.path.basename(abi_pair.abi32bit),
- os.path.basename(abi_pair.abi64bit)]),
- set(helper.GetAbis()))
+ self.assertEquals(
+ set([
+ os.path.basename(abi_pair.abi32bit),
+ os.path.basename(abi_pair.abi64bit)
+ ]), set(helper.GetAbis()))
with _MockListApkPaths([abi_pair.abi64bit]):
helper = apk_helper.ApkHelper('')
- self.assertEquals(set([os.path.basename(abi_pair.abi64bit)]),
- set(helper.GetAbis()))
-
- def testParseXmlManifest(self):
- self.assertEquals({
- 'manifest': [
- {'android:compileSdkVersion': '28',
- 'android:versionCode': '2',
- 'uses-sdk': [
- {'android:minSdkVersion': '24',
- 'android:targetSdkVersion': '28'}],
- 'uses-permission': [
- {'android:name':
- 'android.permission.ACCESS_COARSE_LOCATION'},
- {'android:name':
- 'android.permission.ACCESS_NETWORK_STATE'}],
- 'application': [
- {'android:allowBackup': 'true',
- 'android:extractNativeLibs': 'false',
- 'android:fullBackupOnly': 'false',
- 'meta-data': [
- {'android:name': 'android.allow_multiple',
- 'android:value': 'true'},
- {'android:name': 'multiwindow',
- 'android:value': 'true'}],
- 'activity': [
- {'android:configChanges': '0x00001fb3',
- 'android:excludeFromRecents': 'true',
- 'android:name': 'ChromeLauncherActivity',
- 'intent-filter': [
- {'action': [
- {'android:name': 'dummy.action'}],
- 'category': [
- {'android:name': 'DAYDREAM'},
- {'android:name': 'CARDBOARD'}]}]},
- {'android:enabled': 'false',
- 'android:name': 'MediaLauncherActivity',
- 'intent-filter': [
- {'tools:ignore': 'AppLinkUrlError',
- 'action': [{'android:name': 'VIEW'}],
- 'category': [{'android:name': 'DEFAULT'}],
- 'data': [
- {'android:mimeType': 'audio/*'},
- {'android:mimeType': 'image/*'},
- {'android:mimeType': 'video/*'},
- {'android:scheme': 'file'},
- {'android:scheme': 'content'}]}]}]}]}]},
- apk_helper.ParseManifestFromXml("""
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:compileSdkVersion="28" android:versionCode="2">
- <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="28"/>
- <uses-permission
- android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <application android:allowBackup="true"
- android:extractNativeLibs="false"
- android:fullBackupOnly="false">
- <meta-data android:name="android.allow_multiple"
- android:value="true"/>
- <meta-data android:name="multiwindow"
- android:value="true"/>
- <activity android:configChanges="0x00001fb3"
- android:excludeFromRecents="true"
- android:name="ChromeLauncherActivity">
- <intent-filter>
- <action android:name="dummy.action"/>
- <category android:name="DAYDREAM"/>
- <category android:name="CARDBOARD"/>
- </intent-filter>
- </activity>
- <activity android:enabled="false"
- android:name="MediaLauncherActivity">
- <intent-filter tools:ignore="AppLinkUrlError">
- <action android:name="VIEW"/>
-
- <category android:name="DEFAULT"/>
-
- <data android:mimeType="audio/*"/>
- <data android:mimeType="image/*"/>
- <data android:mimeType="video/*"/>
- <data android:scheme="file"/>
- <data android:scheme="content"/>
- </intent-filter>
- </activity>
- </application>
- </manifest>"""))
+ self.assertEquals(
+ set([os.path.basename(abi_pair.abi64bit)]), set(helper.GetAbis()))
+
+ def testGetSplitsApk(self):
+ apk = apk_helper.ToHelper('abc.apk')
+ with apk.GetApkPaths(_MockDeviceUtils()) as apk_paths:
+ self.assertEquals(apk_paths, ['abc.apk'])
+
+ def testGetSplitsApkModulesException(self):
+ apk = apk_helper.ToHelper('abc.apk')
+ with self.assertRaises(apk_helper.ApkHelperError):
+ apk.GetApkPaths(None, modules=['a'])
+
+ def testGetSplitsApks(self):
+ apk = apk_helper.ToHelper('abc.apks')
+ with self.assertCalls(
+ (mock.call.tempfile.mkdtemp(),
+ '/tmp'),
+ (mock.call.devil.android.sdk.bundletool.ExtractApks(
+ '/tmp', 'abc.apks', ['arm64-v8a', 'armeabi-v7a'], [('en', 'US')],
+ ['android.hardware.wifi', 'android.hardware.nfc'], 500, 28, None)),
+ (mock.call.os.listdir('/tmp'), ['base-master.apk', 'foo-master.apk']),
+ (mock.call.shutil.rmtree('/tmp')),
+ ),\
+ apk.GetApkPaths(_MockDeviceUtils()) as apk_paths:
+ self.assertEquals(apk_paths,
+ ['/tmp/base-master.apk', '/tmp/foo-master.apk'])
+
+ def testGetSplitsApksWithModules(self):
+ apk = apk_helper.ToHelper('abc.apks')
+ with self.assertCalls(
+ (mock.call.tempfile.mkdtemp(),
+ '/tmp'),
+ (mock.call.devil.android.sdk.bundletool.ExtractApks(
+ '/tmp', 'abc.apks', ['arm64-v8a', 'armeabi-v7a'], [('en', 'US')],
+ ['android.hardware.wifi', 'android.hardware.nfc'], 500, 28,
+ ['bar'])),
+ (mock.call.os.listdir('/tmp'),
+ ['base-master.apk', 'foo-master.apk', 'bar-master.apk']),
+ (mock.call.shutil.rmtree('/tmp')),
+ ),\
+ apk.GetApkPaths(_MockDeviceUtils(), ['bar']) as apk_paths:
+ self.assertEquals(apk_paths, [
+ '/tmp/base-master.apk', '/tmp/foo-master.apk', '/tmp/bar-master.apk'
+ ])
+
+ def testGetSplitsApksWithAdditionalLocales(self):
+ apk = apk_helper.ToHelper('abc.apks')
+ with self.assertCalls(
+ (mock.call.tempfile.mkdtemp(),
+ '/tmp'),
+ (mock.call.devil.android.sdk.bundletool.ExtractApks(
+ '/tmp', 'abc.apks', ['arm64-v8a', 'armeabi-v7a'],
+ [('en', 'US'), ('es', 'ES'), ('fr', 'CA')],
+ ['android.hardware.wifi', 'android.hardware.nfc'], 500, 28, None)),
+ (mock.call.os.listdir('/tmp'),
+ ['base-master.apk', 'base-es.apk', 'base-fr.apk']),
+ (mock.call.shutil.rmtree('/tmp')),
+ ),\
+ apk.GetApkPaths(_MockDeviceUtils(),
+ additional_locales=['es-ES', 'fr-CA']) as apk_paths:
+ self.assertEquals(
+ apk_paths,
+ ['/tmp/base-master.apk', '/tmp/base-es.apk', '/tmp/base-fr.apk'])
+
+ def testGetSplitsApksWithAdditionalLocalesIncorrectFormat(self):
+ apk = apk_helper.ToHelper('abc.apks')
+ with self.assertRaises(apk_helper.ApkHelperError):
+ apk.GetApkPaths(_MockDeviceUtils(), additional_locales=['es'])
+
+ def testGetSplitsSplitApk(self):
+ apk = apk_helper.ToSplitHelper('base.apk',
+ ['split1.apk', 'split2.apk', 'split3.apk'])
+ device = _MockDeviceUtils()
+ with self.assertCalls(
+ (mock.call.devil.android.sdk.split_select.SelectSplits(
+ device,
+ 'base.apk', ['split1.apk', 'split2.apk', 'split3.apk'],
+ allow_cached_props=False), ['split2.apk'])),\
+ apk.GetApkPaths(device) as apk_paths:
+ self.assertEquals(apk_paths, ['base.apk', 'split2.apk'])
+
+ def testGetSplitsBundleScript(self):
+ apk = apk_helper.ToHelper('abc_bundle')
+ device = _MockDeviceUtils()
+ with self.assertCalls(
+ (mock.call.tempfile.mkstemp(suffix='.apks'), (0, '/tmp/abc.apks')),
+ (mock.call.devil.utils.cmd_helper.GetCmdStatusOutputAndError([
+ 'abc_bundle', 'build-bundle-apks', '--output-apks', '/tmp/abc.apks'
+ ]), (0, '', '')),
+ (mock.call.tempfile.mkdtemp(), '/tmp2'),
+ (mock.call.devil.android.sdk.bundletool.ExtractApks(
+ '/tmp2', '/tmp/abc.apks', ['arm64-v8a', 'armeabi-v7a'],
+ [('en', 'US')], ['android.hardware.wifi', 'android.hardware.nfc'],
+ 500, 28, ['bar'])),
+ (mock.call.os.listdir('/tmp2'), ['base-master.apk', 'bar-master.apk']),
+ (mock.call.os.path.isfile('/tmp/abc.apks'), True),
+ (mock.call.os.remove('/tmp/abc.apks')),
+ (mock.call.os.path.isfile('/tmp2'), False),
+ (mock.call.os.path.isdir('/tmp2'), True),
+ (mock.call.shutil.rmtree('/tmp2')),
+ ),\
+ apk.GetApkPaths(device, modules=['bar']) as apk_paths:
+ self.assertEquals(apk_paths,
+ ['/tmp2/base-master.apk', '/tmp2/bar-master.apk'])
if __name__ == '__main__':
diff --git a/catapult/devil/devil/android/app_ui.py b/catapult/devil/devil/android/app_ui.py
index 2b04e8b8..399c2ee3 100644
--- a/catapult/devil/devil/android/app_ui.py
+++ b/catapult/devil/devil/android/app_ui.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Provides functionality to interact with UI elements of an Android app."""
import collections
@@ -24,7 +23,6 @@ _RE_BOUNDS = re.compile(
class _UiNode(object):
-
def __init__(self, device, xml_node, package=None):
"""Object to interact with a UI node from an xml snapshot.
@@ -148,13 +146,11 @@ class _UiNode(object):
def _NodeMatcher(self, kwargs):
# Auto-complete resource-id's using the package name if available.
resource_id = kwargs.get('resource_id')
- if (resource_id is not None
- and self._package is not None
+ if (resource_id is not None and self._package is not None
and ':id/' not in resource_id):
kwargs['resource_id'] = '%s:id/%s' % (self._package, resource_id)
- criteria = [(k.replace('_', '-'), v)
- for k, v in kwargs.iteritems()
+ criteria = [(k.replace('_', '-'), v) for k, v in kwargs.iteritems()
if v is not None]
if not criteria:
raise TypeError('At least one search criteria should be specified')
@@ -198,7 +194,7 @@ class AppUi(object):
"""
with device_temp_file.DeviceTempFile(self._device.adb) as dtemp:
self._device.RunShellCommand(['uiautomator', 'dump', dtemp.name],
- check_return=True)
+ check_return=True)
xml_node = element_tree.fromstring(
self._device.ReadFile(dtemp.name, force_pull=True))
return _UiNode(self._device, xml_node, package=self._package)
@@ -237,6 +233,7 @@ class AppUi(object):
device_errors.CommandTimeoutError if the node is not found before the
timeout.
"""
+
def node_found():
return self.GetUiNode(**kwargs)
diff --git a/catapult/devil/devil/android/app_ui_test.py b/catapult/devil/devil/android/app_ui_test.py
index 34729851..938fd408 100644
--- a/catapult/devil/devil/android/app_ui_test.py
+++ b/catapult/devil/devil/android/app_ui_test.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Unit tests for the app_ui module."""
import unittest
@@ -15,7 +14,6 @@ from devil.utils import geometry
with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
-
MOCK_XML_LOADING = '''
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation="0">
@@ -24,7 +22,6 @@ MOCK_XML_LOADING = '''
</hierarchy>
'''.strip()
-
MOCK_XML_LOADED = '''
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation="0">
@@ -56,7 +53,6 @@ MOCK_XML_LOADED = '''
class UiAppTest(unittest.TestCase):
-
def setUp(self):
self.device = mock.Mock()
self.device.pixel_density = 320 # Each dp pixel is 2 real pixels.
@@ -69,12 +65,13 @@ class UiAppTest(unittest.TestCase):
Each time the method is called it will return a UI node for each string
given in |xml_docs|, or rise a time out error when the list is exhausted.
"""
+
# pylint: disable=protected-access
def get_mock_root_ui_node(value):
if isinstance(value, Exception):
raise value
- return app_ui._UiNode(
- self.device, element_tree.fromstring(value), self.app.package)
+ return app_ui._UiNode(self.device, element_tree.fromstring(value),
+ self.app.package)
xml_docs.append(device_errors.CommandTimeoutError('Timed out!'))
@@ -92,20 +89,22 @@ class UiAppTest(unittest.TestCase):
def testFind_byText(self):
node = self.app.GetUiNode(text='Primary')
- self.assertNodeHasAttribs(node, {
- 'text': 'Primary',
- 'content-desc': None,
- 'resource-id': 'com.example.app:id/actionbar_title',
- })
+ self.assertNodeHasAttribs(
+ node, {
+ 'text': 'Primary',
+ 'content-desc': None,
+ 'resource-id': 'com.example.app:id/actionbar_title',
+ })
self.assertEquals(node.bounds, geometry.Rectangle([121, 50], [1424, 178]))
def testFind_byContentDesc(self):
node = self.app.GetUiNode(content_desc='Social')
- self.assertNodeHasAttribs(node, {
- 'text': None,
- 'content-desc': 'Social',
- 'resource-id': 'com.example.app:id/image_view',
- })
+ self.assertNodeHasAttribs(
+ node, {
+ 'text': None,
+ 'content-desc': 'Social',
+ 'resource-id': 'com.example.app:id/image_view',
+ })
self.assertEquals(node.bounds, geometry.Rectangle([16, 466], [128, 578]))
def testFind_byResourceId_autocompleted(self):
@@ -123,12 +122,13 @@ class UiAppTest(unittest.TestCase):
})
def testFind_byMultiple(self):
- node = self.app.GetUiNode(resource_id='image_view',
- content_desc='Promotions')
- self.assertNodeHasAttribs(node, {
- 'content-desc': 'Promotions',
- 'resource-id': 'com.example.app:id/image_view',
- })
+ node = self.app.GetUiNode(
+ resource_id='image_view', content_desc='Promotions')
+ self.assertNodeHasAttribs(
+ node, {
+ 'content-desc': 'Promotions',
+ 'resource-id': 'com.example.app:id/image_view',
+ })
self.assertEquals(node.bounds, geometry.Rectangle([16, 578], [128, 690]))
def testFind_notFound(self):
@@ -142,8 +142,8 @@ class UiAppTest(unittest.TestCase):
def testGetChildren(self):
node = self.app.GetUiNode(resource_id='mini_drawer')
- self.assertNodeHasAttribs(
- node[0], {'resource-id': 'com.example.app:id/avatar'})
+ self.assertNodeHasAttribs(node[0],
+ {'resource-id': 'com.example.app:id/avatar'})
self.assertNodeHasAttribs(node[1], {'content-desc': 'Primary'})
self.assertNodeHasAttribs(node[2], {'content-desc': 'Social'})
self.assertNodeHasAttribs(node[3], {'content-desc': 'Promotions'})
diff --git a/catapult/devil/devil/android/battery_utils.py b/catapult/devil/devil/android/battery_utils.py
index c41c19a2..e8134d2b 100644
--- a/catapult/devil/devil/android/battery_utils.py
+++ b/catapult/devil/devil/android/battery_utils.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Provides a variety of device interactions with power.
"""
# pylint: disable=unused-argument
@@ -25,7 +24,7 @@ _DEFAULT_RETRIES = 3
_DEVICE_PROFILES = [
- {
+ {
'name': ['Nexus 4'],
'enable_command': (
'echo 0 > /sys/module/pm8921_charger/parameters/disabled && '
@@ -36,8 +35,8 @@ _DEVICE_PROFILES = [
'charge_counter': None,
'voltage': None,
'current': None,
- },
- {
+ },
+ {
'name': ['Nexus 5'],
# Nexus 5
# Setting the HIZ bit of the bq24192 causes the charger to actually ignore
@@ -56,8 +55,8 @@ _DEVICE_PROFILES = [
'charge_counter': None,
'voltage': None,
'current': None,
- },
- {
+ },
+ {
'name': ['Nexus 6'],
'enable_command': (
'echo 1 > /sys/class/power_supply/battery/charging_enabled && '
@@ -69,8 +68,8 @@ _DEVICE_PROFILES = [
'/sys/class/power_supply/max170xx_battery/charge_counter_ext'),
'voltage': '/sys/class/power_supply/max170xx_battery/voltage_now',
'current': '/sys/class/power_supply/max170xx_battery/current_now',
- },
- {
+ },
+ {
'name': ['Nexus 9'],
'enable_command': (
'echo Disconnected > '
@@ -83,8 +82,8 @@ _DEVICE_PROFILES = [
'charge_counter': '/sys/class/power_supply/battery/charge_counter_ext',
'voltage': '/sys/class/power_supply/battery/voltage_now',
'current': '/sys/class/power_supply/battery/current_now',
- },
- {
+ },
+ {
'name': ['Nexus 10'],
'enable_command': None,
'disable_command': None,
@@ -92,8 +91,8 @@ _DEVICE_PROFILES = [
'voltage': '/sys/class/power_supply/ds2784-fuelgauge/voltage_now',
'current': '/sys/class/power_supply/ds2784-fuelgauge/current_now',
- },
- {
+ },
+ {
'name': ['Nexus 5X'],
'enable_command': (
'echo 1 > /sys/class/power_supply/battery/charging_enabled && '
@@ -104,8 +103,8 @@ _DEVICE_PROFILES = [
'charge_counter': None,
'voltage': None,
'current': None,
- },
- { # Galaxy s5
+ },
+ { # Galaxy s5
'name': ['SM-G900H'],
'enable_command': (
'chmod 644 /sys/class/power_supply/battery/test_mode && '
@@ -122,8 +121,8 @@ _DEVICE_PROFILES = [
'charge_counter': None,
'voltage': '/sys/class/power_supply/sec-fuelgauge/voltage_now',
'current': '/sys/class/power_supply/sec-charger/current_now',
- },
- { # Galaxy s6, Galaxy s6, Galaxy s6 edge
+ },
+ { # Galaxy s6, Galaxy s6, Galaxy s6 edge
'name': ['SM-G920F', 'SM-G920V', 'SM-G925V'],
'enable_command': (
'chmod 644 /sys/class/power_supply/battery/test_mode && '
@@ -140,8 +139,8 @@ _DEVICE_PROFILES = [
'charge_counter': None,
'voltage': '/sys/class/power_supply/max77843-fuelgauge/voltage_now',
'current': '/sys/class/power_supply/max77843-charger/current_now',
- },
- { # Cherry Mobile One
+ },
+ { # Cherry Mobile One
'name': ['W6210 (4560MMX_b fingerprint)'],
'enable_command': (
'echo "0 0" > /proc/mtk_battery_cmd/current_cmd && '
@@ -152,7 +151,7 @@ _DEVICE_PROFILES = [
'charge_counter': None,
'voltage': None,
'current': None,
-},
+ },
]
# The list of useful dumpsys columns.
@@ -178,8 +177,9 @@ _MAX_CHARGE_ERROR = 20
class BatteryUtils(object):
-
- def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
+ def __init__(self,
+ device,
+ default_timeout=_DEFAULT_TIMEOUT,
default_retries=_DEFAULT_RETRIES):
"""BatteryUtils constructor.
@@ -215,7 +215,7 @@ class BatteryUtils(object):
"""
self._DiscoverDeviceProfile()
return (self._cache['profile']['enable_command'] != None
- and self._cache['profile']['charge_counter'] != None)
+ and self._cache['profile']['charge_counter'] != None)
@decorators.WithTimeoutAndRetriesFromInstance()
def GetFuelGaugeChargeCounter(self, timeout=None, retries=None):
@@ -235,10 +235,9 @@ class BatteryUtils(object):
device_errors.CommandFailedError: If fuel gauge chip not found.
"""
if self.SupportsFuelGauge():
- return int(self._device.ReadFile(
- self._cache['profile']['charge_counter']))
- raise device_errors.CommandFailedError(
- 'Unable to find fuel gauge.')
+ return int(
+ self._device.ReadFile(self._cache['profile']['charge_counter']))
+ raise device_errors.CommandFailedError('Unable to find fuel gauge.')
@decorators.WithTimeoutAndRetriesFromInstance()
def GetPowerData(self, timeout=None, retries=None):
@@ -264,8 +263,7 @@ class BatteryUtils(object):
if 'uids' not in self._cache:
self._cache['uids'] = {}
dumpsys_output = self._device.RunShellCommand(
- ['dumpsys', 'batterystats', '-c'],
- check_return=True, large_output=True)
+ ['dumpsys', 'batterystats', '-c'], check_return=True, large_output=True)
csvreader = csv.reader(dumpsys_output)
pwi_entries = collections.defaultdict(list)
system_total = None
@@ -273,32 +271,37 @@ class BatteryUtils(object):
if entry[_DUMP_VERSION_INDEX] not in ['8', '9']:
# Wrong dumpsys version.
raise device_errors.DeviceVersionError(
- 'Dumpsys version must be 8 or 9. "%s" found.'
- % entry[_DUMP_VERSION_INDEX])
+ 'Dumpsys version must be 8 or 9. "%s" found.' %
+ entry[_DUMP_VERSION_INDEX])
if _ROW_TYPE_INDEX < len(entry) and entry[_ROW_TYPE_INDEX] == 'uid':
current_package = entry[_PACKAGE_NAME_INDEX]
if (self._cache['uids'].get(current_package)
- and self._cache['uids'].get(current_package)
- != entry[_PACKAGE_UID_INDEX]):
+ and self._cache['uids'].get(current_package) !=
+ entry[_PACKAGE_UID_INDEX]):
raise device_errors.CommandFailedError(
- 'Package %s found multiple times with different UIDs %s and %s'
- % (current_package, self._cache['uids'][current_package],
+ 'Package %s found multiple times with different UIDs %s and %s' %
+ (current_package, self._cache['uids'][current_package],
entry[_PACKAGE_UID_INDEX]))
self._cache['uids'][current_package] = entry[_PACKAGE_UID_INDEX]
elif (_PWI_POWER_CONSUMPTION_INDEX < len(entry)
- and entry[_ROW_TYPE_INDEX] == 'pwi'
- and entry[_PWI_AGGREGATION_INDEX] == 'l'):
+ and entry[_ROW_TYPE_INDEX] == 'pwi'
+ and entry[_PWI_AGGREGATION_INDEX] == 'l'):
pwi_entries[entry[_PWI_UID_INDEX]].append(
float(entry[_PWI_POWER_CONSUMPTION_INDEX]))
elif (_PWS_POWER_CONSUMPTION_INDEX < len(entry)
- and entry[_ROW_TYPE_INDEX] == 'pws'
- and entry[_PWS_AGGREGATION_INDEX] == 'l'):
+ and entry[_ROW_TYPE_INDEX] == 'pws'
+ and entry[_PWS_AGGREGATION_INDEX] == 'l'):
# This entry should only appear once.
assert system_total is None
system_total = float(entry[_PWS_POWER_CONSUMPTION_INDEX])
- per_package = {p: {'uid': uid, 'data': pwi_entries[uid]}
- for p, uid in self._cache['uids'].iteritems()}
+ per_package = {
+ p: {
+ 'uid': uid,
+ 'data': pwi_entries[uid]
+ }
+ for p, uid in self._cache['uids'].iteritems()
+ }
return {'system_total': system_total, 'per_package': per_package}
@decorators.WithTimeoutAndRetriesFromInstance()
@@ -314,8 +317,8 @@ class BatteryUtils(object):
"""
result = {}
# Skip the first line, which is just a header.
- for line in self._device.RunShellCommand(
- ['dumpsys', 'battery'], check_return=True)[1:]:
+ for line in self._device.RunShellCommand(['dumpsys', 'battery'],
+ check_return=True)[1:]:
# If usb charging has been disabled, an extra line of header exists.
if 'UPDATES STOPPED' in line:
logger.warning('Dumpsys battery not receiving updates. '
@@ -337,15 +340,16 @@ class BatteryUtils(object):
Returns:
True if the device is charging, false otherwise.
"""
+
# Wrapper function so that we can use `RetryOnSystemCrash`.
def GetBatteryInfoHelper(device):
return self.GetBatteryInfo()
- battery_info = crash_handler.RetryOnSystemCrash(
- GetBatteryInfoHelper, self._device)
+ battery_info = crash_handler.RetryOnSystemCrash(GetBatteryInfoHelper,
+ self._device)
for k in ('AC powered', 'USB powered', 'Wireless powered'):
- if (k in battery_info and
- battery_info[k].lower() in ('true', '1', 'yes')):
+ if (k in battery_info
+ and battery_info[k].lower() in ('true', '1', 'yes')):
return True
return False
@@ -364,6 +368,7 @@ class BatteryUtils(object):
reset power values.
device_errors.DeviceVersionError: If device is not L or higher.
"""
+
def battery_updates_disabled():
return self.GetCharging() is False
@@ -386,6 +391,7 @@ class BatteryUtils(object):
Raises:
device_errors.DeviceVersionError: If device is not L or higher.
"""
+
def battery_updates_enabled():
return (self.GetCharging()
or not bool('UPDATES STOPPED' in self._device.RunShellCommand(
@@ -437,15 +443,16 @@ class BatteryUtils(object):
"""
battery_level = int(self.GetBatteryInfo().get('level'))
if not 0 < percent < 100:
- raise ValueError('Discharge amount(%s) must be between 1 and 99'
- % percent)
+ raise ValueError(
+ 'Discharge amount(%s) must be between 1 and 99' % percent)
if battery_level is None:
logger.warning('Unable to find current battery level. Cannot discharge.')
return
# Do not discharge if it would make battery level too low.
if percent >= battery_level - 10:
- logger.warning('Battery is too low or discharge amount requested is too '
- 'high. Cannot discharge phone %s percent.', percent)
+ logger.warning(
+ 'Battery is too low or discharge amount requested is too '
+ 'high. Cannot discharge phone %s percent.', percent)
return
self._HardwareSetCharging(False)
@@ -471,10 +478,8 @@ class BatteryUtils(object):
device_errors.DeviceChargingError: If error while charging is detected.
"""
self.SetCharging(True)
- charge_status = {
- 'charge_failure_count': 0,
- 'last_charge_value': 0
- }
+ charge_status = {'charge_failure_count': 0, 'last_charge_value': 0}
+
def device_charged():
battery_level = self.GetBatteryInfo().get('level')
if battery_level is None:
@@ -494,8 +499,8 @@ class BatteryUtils(object):
if (not battery_level >= level
and charge_status['charge_failure_count'] >= _MAX_CHARGE_ERROR):
raise device_errors.DeviceChargingError(
- 'Device not charging properly. Current level:%s Previous level:%s'
- % (battery_level, charge_status['last_charge_value']))
+ 'Device not charging properly. Current level:%s Previous level:%s' %
+ (battery_level, charge_status['last_charge_value']))
return battery_level >= level
timeout_retry.WaitFor(device_charged, wait_period=wait_period)
@@ -506,6 +511,7 @@ class BatteryUtils(object):
temp: maximum temperature to allow in tenths of degrees c.
wait_period: time in seconds to wait between checking.
"""
+
def cool_device():
temp = self.GetBatteryInfo().get('temperature')
if temp is None:
@@ -554,7 +560,7 @@ class BatteryUtils(object):
self._HardwareSetCharging(enabled)
else:
logger.info('Unable to disable charging via hardware. '
- 'Falling back to software disabling.')
+ 'Falling back to software disabling.')
self.DisableBatteryUpdates()
def _HardwareSetCharging(self, enabled, timeout=None, retries=None):
@@ -575,8 +581,8 @@ class BatteryUtils(object):
raise device_errors.CommandFailedError(
'Unable to find charging commands.')
- command = (self._cache['profile']['enable_command'] if enabled
- else self._cache['profile']['disable_command'])
+ command = (self._cache['profile']['enable_command']
+ if enabled else self._cache['profile']['disable_command'])
def verify_charging():
return self.GetCharging() == enabled
@@ -629,17 +635,18 @@ class BatteryUtils(object):
'Cannot clear power data.')
return False
- self._device.RunShellCommand(
- ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True)
- self._device.RunShellCommand(
- ['dumpsys', 'battery', 'set', 'ac', '1'], check_return=True)
+ self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '1'],
+ check_return=True)
+ self._device.RunShellCommand(['dumpsys', 'battery', 'set', 'ac', '1'],
+ check_return=True)
def test_if_clear():
- self._device.RunShellCommand(
- ['dumpsys', 'batterystats', '--reset'], check_return=True)
+ self._device.RunShellCommand(['dumpsys', 'batterystats', '--reset'],
+ check_return=True)
battery_data = self._device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '-c'],
- check_return=True, large_output=True)
+ check_return=True,
+ large_output=True)
for line in battery_data:
l = line.split(',')
if (len(l) > _PWI_POWER_CONSUMPTION_INDEX
@@ -652,8 +659,8 @@ class BatteryUtils(object):
timeout_retry.WaitFor(test_if_clear, wait_period=1)
return True
finally:
- self._device.RunShellCommand(
- ['dumpsys', 'battery', 'reset'], check_return=True)
+ self._device.RunShellCommand(['dumpsys', 'battery', 'reset'],
+ check_return=True)
def _DiscoverDeviceProfile(self):
"""Checks and caches device information.
diff --git a/catapult/devil/devil/android/battery_utils_test.py b/catapult/devil/devil/android/battery_utils_test.py
index 07c74967..7bfb9551 100755
--- a/catapult/devil/devil/android/battery_utils_test.py
+++ b/catapult/devil/devil/android/battery_utils_test.py
@@ -2,7 +2,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for the contents of battery_utils.py
"""
@@ -34,46 +33,64 @@ _DUMPSYS_OUTPUT = [
class BatteryUtilsTest(mock_calls.TestCase):
_NEXUS_5 = {
- 'name': 'Nexus 5',
- 'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
- 'enable_command': (
- 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
- 'echo 1 > /sys/class/power_supply/usb/online'),
- 'disable_command': (
- 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
- 'chmod 644 /sys/class/power_supply/usb/online && '
- 'echo 0 > /sys/class/power_supply/usb/online'),
- 'charge_counter': None,
- 'voltage': None,
- 'current': None,
+ 'name':
+ 'Nexus 5',
+ 'witness_file':
+ '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
+ 'enable_command': (
+ 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
+ 'echo 1 > /sys/class/power_supply/usb/online'),
+ 'disable_command': (
+ 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
+ 'chmod 644 /sys/class/power_supply/usb/online && '
+ 'echo 0 > /sys/class/power_supply/usb/online'),
+ 'charge_counter':
+ None,
+ 'voltage':
+ None,
+ 'current':
+ None,
}
_NEXUS_6 = {
- 'name': 'Nexus 6',
- 'witness_file': None,
- 'enable_command': None,
- 'disable_command': None,
- 'charge_counter': (
- '/sys/class/power_supply/max170xx_battery/charge_counter_ext'),
- 'voltage': '/sys/class/power_supply/max170xx_battery/voltage_now',
- 'current': '/sys/class/power_supply/max170xx_battery/current_now',
+ 'name':
+ 'Nexus 6',
+ 'witness_file':
+ None,
+ 'enable_command':
+ None,
+ 'disable_command':
+ None,
+ 'charge_counter': (
+ '/sys/class/power_supply/max170xx_battery/charge_counter_ext'),
+ 'voltage':
+ '/sys/class/power_supply/max170xx_battery/voltage_now',
+ 'current':
+ '/sys/class/power_supply/max170xx_battery/current_now',
}
_NEXUS_10 = {
- 'name': 'Nexus 10',
- 'witness_file': None,
- 'enable_command': None,
- 'disable_command': None,
- 'charge_counter': (
- '/sys/class/power_supply/ds2784-fuelgauge/charge_counter_ext'),
- 'voltage': '/sys/class/power_supply/ds2784-fuelgauge/voltage_now',
- 'current': '/sys/class/power_supply/ds2784-fuelgauge/current_now',
+ 'name':
+ 'Nexus 10',
+ 'witness_file':
+ None,
+ 'enable_command':
+ None,
+ 'disable_command':
+ None,
+ 'charge_counter': (
+ '/sys/class/power_supply/ds2784-fuelgauge/charge_counter_ext'),
+ 'voltage':
+ '/sys/class/power_supply/ds2784-fuelgauge/voltage_now',
+ 'current':
+ '/sys/class/power_supply/ds2784-fuelgauge/current_now',
}
def ShellError(self, output=None, status=1):
def action(cmd, *args, **kwargs):
- raise device_errors.AdbShellCommandFailedError(
- cmd, output, status, str(self.device))
+ raise device_errors.AdbShellCommandFailedError(cmd, output, status,
+ str(self.device))
+
if output is None:
output = 'Permission denied\n'
return action
@@ -88,7 +105,6 @@ class BatteryUtilsTest(mock_calls.TestCase):
class BatteryUtilsInitTest(unittest.TestCase):
-
def testInitWithDeviceUtil(self):
serial = '0fedcba987654321'
d = device_utils.DeviceUtils(serial)
@@ -103,78 +119,75 @@ class BatteryUtilsInitTest(unittest.TestCase):
class BatteryUtilsSetChargingTest(BatteryUtilsTest):
-
@mock.patch('time.sleep', mock.Mock())
def testHardwareSetCharging_enabled(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.device.RunShellCommand(
- mock.ANY, shell=True, check_return=True, as_root=True,
- large_output=True), []),
- (self.call.battery.GetCharging(), False),
- (self.call.battery.GetCharging(), True)):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ mock.ANY,
+ shell=True,
+ check_return=True,
+ as_root=True,
+ large_output=True), []), (self.call.battery.GetCharging(), False),
+ (self.call.battery.GetCharging(), True)):
self.battery._HardwareSetCharging(True)
def testHardwareSetCharging_alreadyEnabled(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.device.RunShellCommand(
- mock.ANY, shell=True, check_return=True, as_root=True,
- large_output=True), []),
- (self.call.battery.GetCharging(), True)):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ mock.ANY,
+ shell=True,
+ check_return=True,
+ as_root=True,
+ large_output=True), []), (self.call.battery.GetCharging(), True)):
self.battery._HardwareSetCharging(True)
@mock.patch('time.sleep', mock.Mock())
def testHardwareSetCharging_disabled(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.device.RunShellCommand(
- mock.ANY, shell=True, check_return=True, as_root=True,
- large_output=True), []),
- (self.call.battery.GetCharging(), True),
- (self.call.battery.GetCharging(), False)):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ mock.ANY,
+ shell=True,
+ check_return=True,
+ as_root=True,
+ large_output=True), []), (self.call.battery.GetCharging(), True),
+ (self.call.battery.GetCharging(), False)):
self.battery._HardwareSetCharging(False)
class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest):
-
@mock.patch('time.sleep', mock.Mock())
def testBatteryMeasurementWifi(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=22):
+ with self.patch_call(self.call.device.build_version_sdk, return_value=22):
with self.assertCalls(
(self.call.battery._ClearPowerData(), True),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True),
- []),
- (self.call.battery.GetCharging(), False),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery', 'reset'], check_return=True), []),
+ []), (self.call.battery.GetCharging(), False),
+ (self.call.device.RunShellCommand(['dumpsys', 'battery', 'reset'],
+ check_return=True), []),
(self.call.battery.GetCharging(), False),
(self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
(self.call.battery.GetCharging(), False),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery'], check_return=True), [])):
+ (self.call.device.RunShellCommand(['dumpsys', 'battery'],
+ check_return=True), [])):
with self.battery.BatteryMeasurement():
pass
@mock.patch('time.sleep', mock.Mock())
def testBatteryMeasurementUsb(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=22):
+ with self.patch_call(self.call.device.build_version_sdk, return_value=22):
with self.assertCalls(
(self.call.battery._ClearPowerData(), True),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True),
- []),
- (self.call.battery.GetCharging(), False),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery', 'reset'], check_return=True), []),
+ []), (self.call.battery.GetCharging(), False),
+ (self.call.device.RunShellCommand(['dumpsys', 'battery', 'reset'],
+ check_return=True), []),
(self.call.battery.GetCharging(), False),
(self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
@@ -184,86 +197,102 @@ class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest):
class BatteryUtilsGetPowerData(BatteryUtilsTest):
-
def testGetPowerData(self):
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'batterystats', '-c'],
- check_return=True, large_output=True),
- _DUMPSYS_OUTPUT)):
+ (self.call.device.RunShellCommand(['dumpsys', 'batterystats', '-c'],
+ check_return=True,
+ large_output=True), _DUMPSYS_OUTPUT)):
data = self.battery.GetPowerData()
check = {
- 'system_total': 2000.0,
- 'per_package': {
- 'test_package1': {'uid': '1000', 'data': [1.0]},
- 'test_package2': {'uid': '1001', 'data': [2.0]}
- }
+ 'system_total': 2000.0,
+ 'per_package': {
+ 'test_package1': {
+ 'uid': '1000',
+ 'data': [1.0]
+ },
+ 'test_package2': {
+ 'uid': '1001',
+ 'data': [2.0]
+ }
+ }
}
self.assertEqual(data, check)
def testGetPowerData_packageCollisionSame(self):
self.battery._cache['uids'] = {'test_package1': '1000'}
with self.assertCall(
- self.call.device.RunShellCommand(
- ['dumpsys', 'batterystats', '-c'],
- check_return=True, large_output=True),
- _DUMPSYS_OUTPUT):
+ self.call.device.RunShellCommand(['dumpsys', 'batterystats', '-c'],
+ check_return=True,
+ large_output=True), _DUMPSYS_OUTPUT):
data = self.battery.GetPowerData()
check = {
- 'system_total': 2000.0,
- 'per_package': {
- 'test_package1': {'uid': '1000', 'data': [1.0]},
- 'test_package2': {'uid': '1001', 'data': [2.0]}
- }
+ 'system_total': 2000.0,
+ 'per_package': {
+ 'test_package1': {
+ 'uid': '1000',
+ 'data': [1.0]
+ },
+ 'test_package2': {
+ 'uid': '1001',
+ 'data': [2.0]
+ }
+ }
}
self.assertEqual(data, check)
def testGetPowerData_packageCollisionDifferent(self):
self.battery._cache['uids'] = {'test_package1': '1'}
with self.assertCall(
- self.call.device.RunShellCommand(
- ['dumpsys', 'batterystats', '-c'],
- check_return=True, large_output=True),
- _DUMPSYS_OUTPUT):
+ self.call.device.RunShellCommand(['dumpsys', 'batterystats', '-c'],
+ check_return=True,
+ large_output=True), _DUMPSYS_OUTPUT):
with self.assertRaises(device_errors.CommandFailedError):
self.battery.GetPowerData()
def testGetPowerData_cacheCleared(self):
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'batterystats', '-c'],
- check_return=True, large_output=True),
- _DUMPSYS_OUTPUT)):
+ (self.call.device.RunShellCommand(['dumpsys', 'batterystats', '-c'],
+ check_return=True,
+ large_output=True), _DUMPSYS_OUTPUT)):
self.battery._cache.clear()
data = self.battery.GetPowerData()
check = {
- 'system_total': 2000.0,
- 'per_package': {
- 'test_package1': {'uid': '1000', 'data': [1.0]},
- 'test_package2': {'uid': '1001', 'data': [2.0]}
- }
+ 'system_total': 2000.0,
+ 'per_package': {
+ 'test_package1': {
+ 'uid': '1000',
+ 'data': [1.0]
+ },
+ 'test_package2': {
+ 'uid': '1001',
+ 'data': [2.0]
+ }
+ }
}
self.assertEqual(data, check)
class BatteryUtilsChargeDevice(BatteryUtilsTest):
-
@mock.patch('time.sleep', mock.Mock())
def testChargeDeviceToLevel_pass(self):
- with self.assertCalls(
- (self.call.battery.SetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '50'}),
- (self.call.battery.GetBatteryInfo(), {'level': '100'})):
+ with self.assertCalls((self.call.battery.SetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '50'
+ }), (self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ })):
self.battery.ChargeDeviceToLevel(95)
@mock.patch('time.sleep', mock.Mock())
def testChargeDeviceToLevel_failureSame(self):
- with self.assertCalls(
- (self.call.battery.SetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '50'}),
- (self.call.battery.GetBatteryInfo(), {'level': '50'}),
-
- (self.call.battery.GetBatteryInfo(), {'level': '50'})):
+ with self.assertCalls((self.call.battery.SetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '50'
+ }), (self.call.battery.GetBatteryInfo(), {
+ 'level': '50'
+ }), (self.call.battery.GetBatteryInfo(), {
+ 'level': '50'
+ })):
with self.assertRaises(device_errors.DeviceChargingError):
old_max = battery_utils._MAX_CHARGE_ERROR
try:
@@ -274,11 +303,14 @@ class BatteryUtilsChargeDevice(BatteryUtilsTest):
@mock.patch('time.sleep', mock.Mock())
def testChargeDeviceToLevel_failureDischarge(self):
- with self.assertCalls(
- (self.call.battery.SetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '50'}),
- (self.call.battery.GetBatteryInfo(), {'level': '49'}),
- (self.call.battery.GetBatteryInfo(), {'level': '48'})):
+ with self.assertCalls((self.call.battery.SetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '50'
+ }), (self.call.battery.GetBatteryInfo(), {
+ 'level': '49'
+ }), (self.call.battery.GetBatteryInfo(), {
+ 'level': '48'
+ })):
with self.assertRaises(device_errors.DeviceChargingError):
old_max = battery_utils._MAX_CHARGE_ERROR
try:
@@ -289,158 +321,167 @@ class BatteryUtilsChargeDevice(BatteryUtilsTest):
class BatteryUtilsDischargeDevice(BatteryUtilsTest):
-
@mock.patch('time.sleep', mock.Mock())
def testDischargeDevice_exact(self):
- with self.assertCalls(
- (self.call.battery.GetBatteryInfo(), {'level': '100'}),
- (self.call.battery._HardwareSetCharging(False)),
- (self.call.battery._HardwareSetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '99'})):
+ with self.assertCalls((self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ }), (self.call.battery._HardwareSetCharging(False)),
+ (self.call.battery._HardwareSetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '99'
+ })):
self.battery._DischargeDevice(1)
@mock.patch('time.sleep', mock.Mock())
def testDischargeDevice_over(self):
- with self.assertCalls(
- (self.call.battery.GetBatteryInfo(), {'level': '100'}),
- (self.call.battery._HardwareSetCharging(False)),
- (self.call.battery._HardwareSetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '50'})):
+ with self.assertCalls((self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ }), (self.call.battery._HardwareSetCharging(False)),
+ (self.call.battery._HardwareSetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '50'
+ })):
self.battery._DischargeDevice(1)
@mock.patch('time.sleep', mock.Mock())
def testDischargeDevice_takeslong(self):
- with self.assertCalls(
- (self.call.battery.GetBatteryInfo(), {'level': '100'}),
- (self.call.battery._HardwareSetCharging(False)),
- (self.call.battery._HardwareSetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '100'}),
- (self.call.battery._HardwareSetCharging(False)),
- (self.call.battery._HardwareSetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '99'}),
- (self.call.battery._HardwareSetCharging(False)),
- (self.call.battery._HardwareSetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '98'}),
- (self.call.battery._HardwareSetCharging(False)),
- (self.call.battery._HardwareSetCharging(True)),
- (self.call.battery.GetBatteryInfo(), {'level': '97'})):
+ with self.assertCalls((self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ }), (self.call.battery._HardwareSetCharging(False)),
+ (self.call.battery._HardwareSetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ }), (self.call.battery._HardwareSetCharging(False)),
+ (self.call.battery._HardwareSetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '99'
+ }), (self.call.battery._HardwareSetCharging(False)),
+ (self.call.battery._HardwareSetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '98'
+ }), (self.call.battery._HardwareSetCharging(False)),
+ (self.call.battery._HardwareSetCharging(True)),
+ (self.call.battery.GetBatteryInfo(), {
+ 'level': '97'
+ })):
self.battery._DischargeDevice(3)
@mock.patch('time.sleep', mock.Mock())
def testDischargeDevice_dischargeTooClose(self):
- with self.assertCalls(
- (self.call.battery.GetBatteryInfo(), {'level': '100'})):
+ with self.assertCalls((self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ })):
self.battery._DischargeDevice(99)
@mock.patch('time.sleep', mock.Mock())
def testDischargeDevice_percentageOutOfBounds(self):
- with self.assertCalls(
- (self.call.battery.GetBatteryInfo(), {'level': '100'})):
+ with self.assertCalls((self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ })):
with self.assertRaises(ValueError):
self.battery._DischargeDevice(100)
- with self.assertCalls(
- (self.call.battery.GetBatteryInfo(), {'level': '100'})):
+ with self.assertCalls((self.call.battery.GetBatteryInfo(), {
+ 'level': '100'
+ })):
with self.assertRaises(ValueError):
self.battery._DischargeDevice(0)
class BatteryUtilsGetBatteryInfoTest(BatteryUtilsTest):
-
def testGetBatteryInfo_normal(self):
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery'], check_return=True),
- [
- 'Current Battery Service state:',
- ' AC powered: false',
- ' USB powered: true',
- ' level: 100',
- ' temperature: 321',
- ])):
- self.assertEquals(
- {
- 'AC powered': 'false',
- 'USB powered': 'true',
- 'level': '100',
- 'temperature': '321',
- },
- self.battery.GetBatteryInfo())
+ (self.call.device.RunShellCommand(['dumpsys', 'battery'],
+ check_return=True), [
+ 'Current Battery Service state:',
+ ' AC powered: false',
+ ' USB powered: true',
+ ' level: 100',
+ ' temperature: 321',
+ ])):
+ self.assertEquals({
+ 'AC powered': 'false',
+ 'USB powered': 'true',
+ 'level': '100',
+ 'temperature': '321',
+ }, self.battery.GetBatteryInfo())
def testGetBatteryInfo_nothing(self):
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery'], check_return=True), [])):
+ (self.call.device.RunShellCommand(['dumpsys', 'battery'],
+ check_return=True), [])):
self.assertEquals({}, self.battery.GetBatteryInfo())
class BatteryUtilsGetChargingTest(BatteryUtilsTest):
-
def testGetCharging_usb(self):
- with self.assertCall(
- self.call.battery.GetBatteryInfo(), {'USB powered': 'true'}):
+ with self.assertCall(self.call.battery.GetBatteryInfo(),
+ {'USB powered': 'true'}):
self.assertTrue(self.battery.GetCharging())
def testGetCharging_usbFalse(self):
- with self.assertCall(
- self.call.battery.GetBatteryInfo(), {'USB powered': 'false'}):
+ with self.assertCall(self.call.battery.GetBatteryInfo(),
+ {'USB powered': 'false'}):
self.assertFalse(self.battery.GetCharging())
def testGetCharging_ac(self):
- with self.assertCall(
- self.call.battery.GetBatteryInfo(), {'AC powered': 'true'}):
+ with self.assertCall(self.call.battery.GetBatteryInfo(),
+ {'AC powered': 'true'}):
self.assertTrue(self.battery.GetCharging())
def testGetCharging_wireless(self):
- with self.assertCall(
- self.call.battery.GetBatteryInfo(), {'Wireless powered': 'true'}):
+ with self.assertCall(self.call.battery.GetBatteryInfo(),
+ {'Wireless powered': 'true'}):
self.assertTrue(self.battery.GetCharging())
def testGetCharging_unknown(self):
- with self.assertCall(
- self.call.battery.GetBatteryInfo(), {'level': '42'}):
+ with self.assertCall(self.call.battery.GetBatteryInfo(), {'level': '42'}):
self.assertFalse(self.battery.GetCharging())
class BatteryUtilsLetBatteryCoolToTemperatureTest(BatteryUtilsTest):
-
@mock.patch('time.sleep', mock.Mock())
def testLetBatteryCoolToTemperature_startUnder(self):
self.battery._cache['profile'] = self._NEXUS_6
- with self.assertCalls(
- (self.call.battery.EnableBatteryUpdates(), []),
- (self.call.battery.GetBatteryInfo(), {'temperature': '500'})):
+ with self.assertCalls((self.call.battery.EnableBatteryUpdates(), []),
+ (self.call.battery.GetBatteryInfo(), {
+ 'temperature': '500'
+ })):
self.battery.LetBatteryCoolToTemperature(600)
@mock.patch('time.sleep', mock.Mock())
def testLetBatteryCoolToTemperature_startOver(self):
self.battery._cache['profile'] = self._NEXUS_6
- with self.assertCalls(
- (self.call.battery.EnableBatteryUpdates(), []),
- (self.call.battery.GetBatteryInfo(), {'temperature': '500'}),
- (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
+ with self.assertCalls((self.call.battery.EnableBatteryUpdates(), []),
+ (self.call.battery.GetBatteryInfo(), {
+ 'temperature': '500'
+ }), (self.call.battery.GetBatteryInfo(), {
+ 'temperature': '400'
+ })):
self.battery.LetBatteryCoolToTemperature(400)
@mock.patch('time.sleep', mock.Mock())
def testLetBatteryCoolToTemperature_nexus5Hot(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.battery.EnableBatteryUpdates(), []),
- (self.call.battery.GetBatteryInfo(), {'temperature': '500'}),
- (self.call.battery._DischargeDevice(1), []),
- (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
+ with self.assertCalls((self.call.battery.EnableBatteryUpdates(), []),
+ (self.call.battery.GetBatteryInfo(), {
+ 'temperature': '500'
+ }), (self.call.battery._DischargeDevice(1), []),
+ (self.call.battery.GetBatteryInfo(), {
+ 'temperature': '400'
+ })):
self.battery.LetBatteryCoolToTemperature(400)
@mock.patch('time.sleep', mock.Mock())
def testLetBatteryCoolToTemperature_nexus5Cool(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.battery.EnableBatteryUpdates(), []),
- (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
+ with self.assertCalls((self.call.battery.EnableBatteryUpdates(), []),
+ (self.call.battery.GetBatteryInfo(), {
+ 'temperature': '400'
+ })):
self.battery.LetBatteryCoolToTemperature(400)
class BatteryUtilsSupportsFuelGaugeTest(BatteryUtilsTest):
-
def testSupportsFuelGauge_false(self):
self.battery._cache['profile'] = self._NEXUS_5
self.assertFalse(self.battery.SupportsFuelGauge())
@@ -459,7 +500,6 @@ class BatteryUtilsSupportsFuelGaugeTest(BatteryUtilsTest):
class BatteryUtilsGetFuelGaugeChargeCounterTest(BatteryUtilsTest):
-
def testGetFuelGaugeChargeCounter_noFuelGauge(self):
self.battery._cache['profile'] = self._NEXUS_5
with self.assertRaises(device_errors.CommandFailedError):
@@ -467,21 +507,19 @@ class BatteryUtilsGetFuelGaugeChargeCounterTest(BatteryUtilsTest):
def testGetFuelGaugeChargeCounter_fuelGaugePresent(self):
self.battery._cache['profile'] = self._NEXUS_6
- with self.assertCalls(
- (self.call.battery.SupportsFuelGauge(), True),
- (self.call.device.ReadFile(mock.ANY), '123')):
+ with self.assertCalls((self.call.battery.SupportsFuelGauge(), True),
+ (self.call.device.ReadFile(mock.ANY), '123')):
self.assertEqual(self.battery.GetFuelGaugeChargeCounter(), 123)
class BatteryUtilsSetCharging(BatteryUtilsTest):
-
@mock.patch('time.sleep', mock.Mock())
def testSetCharging_softwareSetTrue(self):
self.battery._cache['profile'] = self._NEXUS_6
with self.assertCalls(
(self.call.battery.GetCharging(), False),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery', 'reset'], check_return=True), []),
+ (self.call.device.RunShellCommand(['dumpsys', 'battery', 'reset'],
+ check_return=True), []),
(self.call.battery.GetCharging(), False),
(self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
@@ -504,18 +542,16 @@ class BatteryUtilsSetCharging(BatteryUtilsTest):
@mock.patch('time.sleep', mock.Mock())
def testSetCharging_hardwareSetTrue(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.battery.GetCharging(), False),
- (self.call.battery._HardwareSetCharging(True))):
+ with self.assertCalls((self.call.battery.GetCharging(), False),
+ (self.call.battery._HardwareSetCharging(True))):
self.battery.SetCharging(True)
@mock.patch('time.sleep', mock.Mock())
def testSetCharging_hardwareSetFalse(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.battery.GetCharging(), True),
- (self.call.battery._ClearPowerData(), True),
- (self.call.battery._HardwareSetCharging(False))):
+ with self.assertCalls((self.call.battery.GetCharging(), True),
+ (self.call.battery._ClearPowerData(), True),
+ (self.call.battery._HardwareSetCharging(False))):
self.battery.SetCharging(False)
def testSetCharging_expectedStateAlreadyTrue(self):
@@ -528,15 +564,13 @@ class BatteryUtilsSetCharging(BatteryUtilsTest):
class BatteryUtilsPowerMeasurement(BatteryUtilsTest):
-
def testPowerMeasurement_hardware(self):
self.battery._cache['profile'] = self._NEXUS_5
- with self.assertCalls(
- (self.call.battery.GetCharging(), True),
- (self.call.battery._ClearPowerData(), True),
- (self.call.battery._HardwareSetCharging(False)),
- (self.call.battery.GetCharging(), False),
- (self.call.battery._HardwareSetCharging(True))):
+ with self.assertCalls((self.call.battery.GetCharging(), True),
+ (self.call.battery._ClearPowerData(), True),
+ (self.call.battery._HardwareSetCharging(False)),
+ (self.call.battery.GetCharging(), False),
+ (self.call.battery._HardwareSetCharging(True))):
with self.battery.PowerMeasurement():
pass
@@ -552,8 +586,8 @@ class BatteryUtilsPowerMeasurement(BatteryUtilsTest):
['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
(self.call.battery.GetCharging(), False),
(self.call.battery.GetCharging(), False),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery', 'reset'], check_return=True), []),
+ (self.call.device.RunShellCommand(['dumpsys', 'battery', 'reset'],
+ check_return=True), []),
(self.call.battery.GetCharging(), False),
(self.call.device.RunShellCommand(
['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
@@ -563,30 +597,25 @@ class BatteryUtilsPowerMeasurement(BatteryUtilsTest):
class BatteryUtilsDiscoverDeviceProfile(BatteryUtilsTest):
-
def testDiscoverDeviceProfile_known(self):
- with self.patch_call(self.call.device.product_model,
- return_value='Nexus 4'):
+ with self.patch_call(
+ self.call.device.product_model, return_value='Nexus 4'):
self.battery._DiscoverDeviceProfile()
self.assertListEqual(self.battery._cache['profile']['name'], ["Nexus 4"])
def testDiscoverDeviceProfile_unknown(self):
- with self.patch_call(self.call.device.product_model,
- return_value='Other'):
+ with self.patch_call(self.call.device.product_model, return_value='Other'):
self.battery._DiscoverDeviceProfile()
self.assertListEqual(self.battery._cache['profile']['name'], [])
class BatteryUtilsClearPowerData(BatteryUtilsTest):
-
def testClearPowerData_preL(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=20):
+ with self.patch_call(self.call.device.build_version_sdk, return_value=20):
self.assertFalse(self.battery._ClearPowerData())
def testClearPowerData_clearedL(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=22):
+ with self.patch_call(self.call.device.build_version_sdk, return_value=22):
with self.assertCalls(
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True),
@@ -597,15 +626,14 @@ class BatteryUtilsClearPowerData(BatteryUtilsTest):
['dumpsys', 'batterystats', '--reset'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '-c'],
- check_return=True, large_output=True), []),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery', 'reset'], check_return=True), [])):
+ check_return=True,
+ large_output=True), []), (self.call.device.RunShellCommand(
+ ['dumpsys', 'battery', 'reset'], check_return=True), [])):
self.assertTrue(self.battery._ClearPowerData())
@mock.patch('time.sleep', mock.Mock())
def testClearPowerData_notClearedL(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=22):
+ with self.patch_call(self.call.device.build_version_sdk, return_value=22):
with self.assertCalls(
(self.call.device.RunShellCommand(
['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True),
@@ -616,28 +644,28 @@ class BatteryUtilsClearPowerData(BatteryUtilsTest):
['dumpsys', 'batterystats', '--reset'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '-c'],
- check_return=True, large_output=True),
- ['9,1000,l,pwi,uid,0.0327']),
+ check_return=True,
+ large_output=True), ['9,1000,l,pwi,uid,0.0327']),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--reset'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '-c'],
- check_return=True, large_output=True),
- ['9,1000,l,pwi,uid,0.0327']),
+ check_return=True,
+ large_output=True), ['9,1000,l,pwi,uid,0.0327']),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--reset'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '-c'],
- check_return=True, large_output=True),
- ['9,1000,l,pwi,uid,0.0327']),
+ check_return=True,
+ large_output=True), ['9,1000,l,pwi,uid,0.0327']),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--reset'], check_return=True), []),
(self.call.device.RunShellCommand(
['dumpsys', 'batterystats', '--charged', '-c'],
- check_return=True, large_output=True),
- ['9,1000,l,pwi,uid,0.0']),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'battery', 'reset'], check_return=True), [])):
+ check_return=True,
+ large_output=True), ['9,1000,l,pwi,uid,0.0']),
+ (self.call.device.RunShellCommand(['dumpsys', 'battery', 'reset'],
+ check_return=True), [])):
self.battery._ClearPowerData()
diff --git a/catapult/devil/devil/android/constants/chrome.py b/catapult/devil/devil/android/constants/chrome.py
index 36bd972e..a02ce1d3 100644
--- a/catapult/devil/devil/android/constants/chrome.py
+++ b/catapult/devil/devil/android/constants/chrome.py
@@ -5,48 +5,36 @@
import collections
PackageInfo = collections.namedtuple(
- 'PackageInfo',
- ['package', 'activity', 'cmdline_file', 'devtools_socket'])
+ 'PackageInfo', ['package', 'activity', 'cmdline_file', 'devtools_socket'])
PACKAGE_INFO = {
- 'chrome_document': PackageInfo(
- 'com.google.android.apps.chrome.document',
- 'com.google.android.apps.chrome.document.ChromeLauncherActivity',
- 'chrome-command-line',
- 'chrome_devtools_remote'),
- 'chrome': PackageInfo(
- 'com.google.android.apps.chrome',
- 'com.google.android.apps.chrome.Main',
- 'chrome-command-line',
- 'chrome_devtools_remote'),
- 'chrome_beta': PackageInfo(
- 'com.chrome.beta',
- 'com.google.android.apps.chrome.Main',
- 'chrome-command-line',
- 'chrome_devtools_remote'),
- 'chrome_stable': PackageInfo(
- 'com.android.chrome',
- 'com.google.android.apps.chrome.Main',
- 'chrome-command-line',
- 'chrome_devtools_remote'),
- 'chrome_dev': PackageInfo(
- 'com.chrome.dev',
- 'com.google.android.apps.chrome.Main',
- 'chrome-command-line',
- 'chrome_devtools_remote'),
- 'chrome_canary': PackageInfo(
- 'com.chrome.canary',
- 'com.google.android.apps.chrome.Main',
- 'chrome-command-line',
- 'chrome_devtools_remote'),
- 'chromium': PackageInfo(
- 'org.chromium.chrome',
- 'com.google.android.apps.chrome.Main',
- 'chrome-command-line',
- 'chrome_devtools_remote'),
- 'content_shell': PackageInfo(
- 'org.chromium.content_shell_apk',
- '.ContentShellActivity',
- 'content-shell-command-line',
- 'content_shell_devtools_remote'),
+ 'chrome_document':
+ PackageInfo(
+ 'com.google.android.apps.chrome.document',
+ 'com.google.android.apps.chrome.document.ChromeLauncherActivity',
+ 'chrome-command-line', 'chrome_devtools_remote'),
+ 'chrome':
+ PackageInfo('com.google.android.apps.chrome',
+ 'com.google.android.apps.chrome.Main',
+ 'chrome-command-line', 'chrome_devtools_remote'),
+ 'chrome_beta':
+ PackageInfo('com.chrome.beta', 'com.google.android.apps.chrome.Main',
+ 'chrome-command-line', 'chrome_devtools_remote'),
+ 'chrome_stable':
+ PackageInfo('com.android.chrome', 'com.google.android.apps.chrome.Main',
+ 'chrome-command-line', 'chrome_devtools_remote'),
+ 'chrome_dev':
+ PackageInfo('com.chrome.dev', 'com.google.android.apps.chrome.Main',
+ 'chrome-command-line', 'chrome_devtools_remote'),
+ 'chrome_canary':
+ PackageInfo('com.chrome.canary', 'com.google.android.apps.chrome.Main',
+ 'chrome-command-line', 'chrome_devtools_remote'),
+ 'chromium':
+ PackageInfo('org.chromium.chrome',
+ 'com.google.android.apps.chrome.Main',
+ 'chrome-command-line', 'chrome_devtools_remote'),
+ 'content_shell':
+ PackageInfo('org.chromium.content_shell_apk', '.ContentShellActivity',
+ 'content-shell-command-line',
+ 'content_shell_devtools_remote'),
}
diff --git a/catapult/devil/devil/android/constants/webapk.py b/catapult/devil/devil/android/constants/webapk.py
index 5a17e724..64241681 100644
--- a/catapult/devil/devil/android/constants/webapk.py
+++ b/catapult/devil/devil/android/constants/webapk.py
@@ -3,4 +3,3 @@
# found in the LICENSE file.
WEBAPK_MAIN_ACTIVITY = 'org.chromium.webapk.shell_apk.MainActivity'
-
diff --git a/catapult/devil/devil/android/cpu_temperature.py b/catapult/devil/devil/android/cpu_temperature.py
index 58ce87a0..7fa724c6 100644
--- a/catapult/devil/devil/android/cpu_temperature.py
+++ b/catapult/devil/devil/android/cpu_temperature.py
@@ -64,7 +64,6 @@ _DEVICE_THERMAL_INFORMATION = {
class CpuTemperature(object):
-
def __init__(self, device):
"""CpuTemperature constructor.
@@ -83,7 +82,7 @@ class CpuTemperature(object):
"""Init the current devices thermal information.
"""
self._device_info = _DEVICE_THERMAL_INFORMATION.get(
- self._device.build_product)
+ self._device.build_product)
def IsSupported(self):
"""Check if the current device is supported.
diff --git a/catapult/devil/devil/android/cpu_temperature_test.py b/catapult/devil/devil/android/cpu_temperature_test.py
index f0f99de0..8d082bb9 100644
--- a/catapult/devil/devil/android/cpu_temperature_test.py
+++ b/catapult/devil/devil/android/cpu_temperature_test.py
@@ -22,7 +22,6 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
class CpuTemperatureTest(mock_calls.TestCase):
-
@mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
def setUp(self):
# Mock the device
@@ -36,7 +35,6 @@ class CpuTemperatureTest(mock_calls.TestCase):
class CpuTemperatureInitTest(unittest.TestCase):
-
@mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
def testInitWithDeviceUtil(self):
d = mock.Mock(spec=device_utils.DeviceUtils)
@@ -52,7 +50,6 @@ class CpuTemperatureInitTest(unittest.TestCase):
class CpuTemperatureGetThermalDeviceInformationTest(CpuTemperatureTest):
-
@mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
def testGetThermalDeviceInformation_noneWhenIncorrectLabel(self):
invalid_device = mock.Mock(spec=device_utils.DeviceUtils)
@@ -78,7 +75,6 @@ class CpuTemperatureGetThermalDeviceInformationTest(CpuTemperatureTest):
class CpuTemperatureIsSupportedTest(CpuTemperatureTest):
-
@mock.patch('devil.android.perf.perf_control.PerfControl', mock.Mock())
def testIsSupported_returnsTrue(self):
d = mock.Mock(spec=device_utils.DeviceUtils)
@@ -98,8 +94,10 @@ class CpuTemperatureIsSupportedTest(CpuTemperatureTest):
class CpuTemperatureLetCpuCoolToTemperatureTest(CpuTemperatureTest):
# Return values for the mock side effect
- cooling_down0 = ([45000 for _ in range(8)] + [43000 for _ in range(8)] +
- [41000 for _ in range(8)])
+ cooling_down0 = (
+ [45000
+ for _ in range(8)] + [43000
+ for _ in range(8)] + [41000 for _ in range(8)])
@mock.patch('time.sleep', mock.Mock())
def testLetBatteryCoolToTemperature_coolWithin24Calls(self):
diff --git a/catapult/devil/devil/android/crash_handler.py b/catapult/devil/devil/android/crash_handler.py
index 028e787d..9db5444b 100644
--- a/catapult/devil/devil/android/crash_handler.py
+++ b/catapult/devil/devil/android/crash_handler.py
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
import logging
from devil import base_error
@@ -33,7 +32,7 @@ def RetryOnSystemCrash(f, device, retries=3):
except device_errors.DeviceUnreachableError:
if num_try > retries:
logger.error('%d consecutive device crashes. No longer retrying.',
- num_try)
+ num_try)
raise
try:
logger.warning('Device is unreachable. Waiting for recovery...')
diff --git a/catapult/devil/devil/android/crash_handler_devicetest.py b/catapult/devil/devil/android/crash_handler_devicetest.py
index 6365104d..68c0d3ad 100755
--- a/catapult/devil/devil/android/crash_handler_devicetest.py
+++ b/catapult/devil/devil/android/crash_handler_devicetest.py
@@ -9,7 +9,11 @@ import unittest
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', )))
+ os.path.abspath(os.path.join(
+ os.path.dirname(__file__),
+ '..',
+ '..',
+ )))
from devil.android import crash_handler
from devil.android import device_errors
@@ -22,7 +26,6 @@ from devil.utils import timeout_retry
class DeviceCrashTest(device_test_case.DeviceTestCase):
-
def setUp(self):
super(DeviceCrashTest, self).setUp()
self.device = device_utils.DeviceUtils(self.serial)
@@ -34,13 +37,16 @@ class DeviceCrashTest(device_test_case.DeviceTestCase):
trigger_text = 'hello world'
def victim():
- trigger_cmd = 'echo -n %s > %s; sleep 20' % (
- cmd_helper.SingleQuote(trigger_text),
- cmd_helper.SingleQuote(trigger_file.name))
+ trigger_cmd = 'echo -n %s > %s; sleep 20' % (cmd_helper.SingleQuote(
+ trigger_text), cmd_helper.SingleQuote(trigger_file.name))
crash_handler.RetryOnSystemCrash(
lambda d: d.RunShellCommand(
- trigger_cmd, shell=True, check_return=True, retries=1,
- as_root=True, timeout=180),
+ trigger_cmd,
+ shell=True,
+ check_return=True,
+ retries=1,
+ as_root=True,
+ timeout=180),
device=self.device)
self.assertEquals(
trigger_text,
@@ -60,11 +66,12 @@ class DeviceCrashTest(device_test_case.DeviceTestCase):
return False
self.device.adb.Shell(
'echo c > /proc/sysrq-trigger',
- expect_status=None, timeout=60, retries=0)
+ expect_status=None,
+ timeout=60,
+ retries=0)
return True
- self.assertEquals([True, True],
- reraiser_thread.RunAsync([crasher, victim]))
+ self.assertEquals([True, True], reraiser_thread.RunAsync([crasher, victim]))
if __name__ == '__main__':
diff --git a/catapult/devil/devil/android/decorators.py b/catapult/devil/devil/android/decorators.py
index 93e10544..0b3778aa 100644
--- a/catapult/devil/devil/android/decorators.py
+++ b/catapult/devil/devil/android/decorators.py
@@ -1,7 +1,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Function/method decorators that provide timeout and retry logic.
"""
@@ -19,9 +18,11 @@ DEFAULT_TIMEOUT_ATTR = '_default_timeout'
DEFAULT_RETRIES_ATTR = '_default_retries'
-def _TimeoutRetryWrapper(
- f, timeout_func, retries_func, retry_if_func=timeout_retry.AlwaysRetry,
- pass_values=False):
+def _TimeoutRetryWrapper(f,
+ timeout_func,
+ retries_func,
+ retry_if_func=timeout_retry.AlwaysRetry,
+ pass_values=False):
""" Wraps a funcion with timeout and retry handling logic.
Args:
@@ -34,6 +35,7 @@ def _TimeoutRetryWrapper(
Returns:
The wrapped function.
"""
+
@functools.wraps(f)
def timeout_retry_wrapper(*args, **kwargs):
timeout = timeout_func(*args, **kwargs)
@@ -45,22 +47,24 @@ def _TimeoutRetryWrapper(
@functools.wraps(f)
def impl():
return f(*args, **kwargs)
+
try:
if timeout_retry.CurrentTimeoutThreadGroup():
# Don't wrap if there's already an outer timeout thread.
return impl()
else:
- desc = '%s(%s)' % (f.__name__, ', '.join(itertools.chain(
- (str(a) for a in args),
- ('%s=%s' % (k, str(v)) for k, v in kwargs.iteritems()))))
- return timeout_retry.Run(impl, timeout, retries, desc=desc,
- retry_if_func=retry_if_func)
+ desc = '%s(%s)' % (f.__name__, ', '.join(
+ itertools.chain(
+ (str(a) for a in args),
+ ('%s=%s' % (k, str(v)) for k, v in kwargs.iteritems()))))
+ return timeout_retry.Run(
+ impl, timeout, retries, desc=desc, retry_if_func=retry_if_func)
except reraiser_thread.TimeoutError as e:
- raise device_errors.CommandTimeoutError(str(e)), None, (
- sys.exc_info()[2])
+ raise device_errors.CommandTimeoutError(str(e)), None, (sys.exc_info()[2])
except cmd_helper.TimeoutError as e:
- raise device_errors.CommandTimeoutError(str(e), output=e.output), None, (
- sys.exc_info()[2])
+ raise device_errors.CommandTimeoutError(
+ str(e), output=e.output), None, (sys.exc_info()[2])
+
return timeout_retry_wrapper
@@ -90,11 +94,13 @@ def WithTimeoutAndConditionalRetries(retry_if_func):
Returns:
The actual decorator.
"""
+
def decorator(f):
get_timeout = lambda *a, **kw: kw['timeout']
get_retries = lambda *a, **kw: kw['retries']
return _TimeoutRetryWrapper(
f, get_timeout, get_retries, retry_if_func=retry_if_func)
+
return decorator
@@ -111,10 +117,12 @@ def WithExplicitTimeoutAndRetries(timeout, retries):
Returns:
The actual decorator.
"""
+
def decorator(f):
get_timeout = lambda *a, **kw: timeout
get_retries = lambda *a, **kw: retries
return _TimeoutRetryWrapper(f, get_timeout, get_retries)
+
return decorator
@@ -134,17 +142,18 @@ def WithTimeoutAndRetriesDefaults(default_timeout, default_retries):
Returns:
The actual decorator.
"""
+
def decorator(f):
get_timeout = lambda *a, **kw: kw.get('timeout', default_timeout)
get_retries = lambda *a, **kw: kw.get('retries', default_retries)
return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True)
+
return decorator
-def WithTimeoutAndRetriesFromInstance(
- default_timeout_name=DEFAULT_TIMEOUT_ATTR,
- default_retries_name=DEFAULT_RETRIES_ATTR,
- min_default_timeout=None):
+def WithTimeoutAndRetriesFromInstance(default_timeout_name=DEFAULT_TIMEOUT_ATTR,
+ default_retries_name=DEFAULT_RETRIES_ATTR,
+ min_default_timeout=None):
"""Returns a decorator that handles timeouts and retries.
The provided |default_timeout_name| and |default_retries_name| are used to
@@ -162,6 +171,7 @@ def WithTimeoutAndRetriesFromInstance(
Returns:
The actual decorator.
"""
+
def decorator(f):
def get_timeout(inst, *_args, **kwargs):
ret = getattr(inst, default_timeout_name)
@@ -171,6 +181,7 @@ def WithTimeoutAndRetriesFromInstance(
def get_retries(inst, *_args, **kwargs):
return kwargs.get('retries', getattr(inst, default_retries_name))
+
return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True)
- return decorator
+ return decorator
diff --git a/catapult/devil/devil/android/decorators_test.py b/catapult/devil/devil/android/decorators_test.py
index f60953e1..994e35e8 100644
--- a/catapult/devil/devil/android/decorators_test.py
+++ b/catapult/devil/devil/android/decorators_test.py
@@ -1,7 +1,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for decorators.py.
"""
@@ -54,6 +53,7 @@ class DecoratorsTest(unittest.TestCase):
def testFunctionDecoratorRequiresParams(self):
"""Tests that the base decorator requires timeout and retries params."""
+
@decorators.WithTimeoutAndRetries
def requiresExplicitTimeoutAndRetries(timeout=None, retries=None):
return (timeout, retries)
@@ -66,14 +66,14 @@ class DecoratorsTest(unittest.TestCase):
requiresExplicitTimeoutAndRetries(retries=0)
expected_timeout = 10
expected_retries = 1
- (actual_timeout, actual_retries) = (
- requiresExplicitTimeoutAndRetries(timeout=expected_timeout,
- retries=expected_retries))
+ (actual_timeout, actual_retries) = (requiresExplicitTimeoutAndRetries(
+ timeout=expected_timeout, retries=expected_retries))
self.assertEquals(expected_timeout, actual_timeout)
self.assertEquals(expected_retries, actual_retries)
def testFunctionDecoratorTranslatesReraiserExceptions(self):
"""Tests that the explicit decorator translates reraiser exceptions."""
+
@decorators.WithTimeoutAndRetries
def alwaysRaisesProvidedException(exception, timeout=None, retries=None):
raise exception
@@ -81,8 +81,7 @@ class DecoratorsTest(unittest.TestCase):
exception_desc = 'Reraiser thread timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
alwaysRaisesProvidedException(
- reraiser_thread.TimeoutError(exception_desc),
- timeout=10, retries=1)
+ reraiser_thread.TimeoutError(exception_desc), timeout=10, retries=1)
self.assertEquals(exception_desc, str(e.exception))
def testConditionalRetriesDecoratorRetries(self):
@@ -158,6 +157,7 @@ class DecoratorsTest(unittest.TestCase):
def testDefaultsFunctionDecoratorPassesValues(self):
"""Tests that the defaults decorator passes timeout and retries kwargs."""
+
@decorators.WithTimeoutAndRetriesDefaults(30, 10)
def alwaysReturnsTimeouts(timeout=None, retries=None):
return timeout
@@ -174,6 +174,7 @@ class DecoratorsTest(unittest.TestCase):
def testDefaultsFunctionDecoratorTranslatesReraiserExceptions(self):
"""Tests that the explicit decorator translates reraiser exceptions."""
+
@decorators.WithTimeoutAndRetriesDefaults(30, 10)
def alwaysRaisesProvidedException(exception, timeout=None, retries=None):
raise exception
@@ -215,6 +216,7 @@ class DecoratorsTest(unittest.TestCase):
def testExplicitDecoratorTranslatesReraiserExceptions(self):
"""Tests that the explicit decorator translates reraiser exceptions."""
+
@decorators.WithExplicitTimeoutAndRetries(30, 10)
def alwaysRaisesProvidedException(exception):
raise exception
@@ -228,7 +230,9 @@ class DecoratorsTest(unittest.TestCase):
class _MethodDecoratorTestObject(object):
"""An object suitable for testing the method decorator."""
- def __init__(self, test_case, default_timeout=_DEFAULT_TIMEOUT,
+ def __init__(self,
+ test_case,
+ default_timeout=_DEFAULT_TIMEOUT,
default_retries=_DEFAULT_RETRIES):
self._test_case = test_case
self.default_timeout = default_timeout
@@ -239,23 +243,23 @@ class DecoratorsTest(unittest.TestCase):
'requiresExplicitTimeoutAndRetries': 0,
}
- @decorators.WithTimeoutAndRetriesFromInstance(
- 'default_timeout', 'default_retries')
+ @decorators.WithTimeoutAndRetriesFromInstance('default_timeout',
+ 'default_retries')
def alwaysTimesOut(self, timeout=None, retries=None):
self.function_call_counters['alwaysTimesOut'] += 1
time.sleep(100)
self._test_case.assertFalse(True, msg='Failed to time out?')
- @decorators.WithTimeoutAndRetriesFromInstance(
- 'default_timeout', 'default_retries')
+ @decorators.WithTimeoutAndRetriesFromInstance('default_timeout',
+ 'default_retries')
def alwaysRaisesCommandFailedError(self, timeout=None, retries=None):
self.function_call_counters['alwaysRaisesCommandFailedError'] += 1
raise device_errors.CommandFailedError('testCommand failed')
# pylint: disable=no-self-use
- @decorators.WithTimeoutAndRetriesFromInstance(
- 'default_timeout', 'default_retries')
+ @decorators.WithTimeoutAndRetriesFromInstance('default_timeout',
+ 'default_retries')
def alwaysReturnsTimeout(self, timeout=None, retries=None):
return timeout
@@ -264,14 +268,16 @@ class DecoratorsTest(unittest.TestCase):
def alwaysReturnsTimeoutWithMin(self, timeout=None, retries=None):
return timeout
- @decorators.WithTimeoutAndRetriesFromInstance(
- 'default_timeout', 'default_retries')
+ @decorators.WithTimeoutAndRetriesFromInstance('default_timeout',
+ 'default_retries')
def alwaysReturnsRetries(self, timeout=None, retries=None):
return retries
- @decorators.WithTimeoutAndRetriesFromInstance(
- 'default_timeout', 'default_retries')
- def alwaysRaisesProvidedException(self, exception, timeout=None,
+ @decorators.WithTimeoutAndRetriesFromInstance('default_timeout',
+ 'default_retries')
+ def alwaysRaisesProvidedException(self,
+ exception,
+ timeout=None,
retries=None):
raise exception
@@ -327,6 +333,6 @@ class DecoratorsTest(unittest.TestCase):
reraiser_thread.TimeoutError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/catapult/devil/devil/android/device_blacklist.py b/catapult/devil/devil/android/device_blacklist.py
index 010e9965..7112204e 100644
--- a/catapult/devil/devil/android/device_blacklist.py
+++ b/catapult/devil/devil/android/device_blacklist.py
@@ -2,79 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import json
-import logging
-import os
-import threading
-import time
+from devil.android import device_denylist
-logger = logging.getLogger(__name__)
-
-
-class Blacklist(object):
-
- def __init__(self, path):
- self._blacklist_lock = threading.RLock()
- self._path = path
-
- def Read(self):
- """Reads the blacklist from the blacklist file.
-
- Returns:
- A dict containing bad devices.
- """
- with self._blacklist_lock:
- blacklist = dict()
- if not os.path.exists(self._path):
- return blacklist
-
- try:
- with open(self._path, 'r') as f:
- blacklist = json.load(f)
- except (IOError, ValueError) as e:
- logger.warning('Unable to read blacklist: %s', str(e))
- os.remove(self._path)
-
- if not isinstance(blacklist, dict):
- logger.warning('Ignoring %s: %s (a dict was expected instead)',
- self._path, blacklist)
- blacklist = dict()
-
- return blacklist
-
- def Write(self, blacklist):
- """Writes the provided blacklist to the blacklist file.
-
- Args:
- blacklist: list of bad devices to write to the blacklist file.
- """
- with self._blacklist_lock:
- with open(self._path, 'w') as f:
- json.dump(blacklist, f)
-
- def Extend(self, devices, reason='unknown'):
- """Adds devices to blacklist file.
-
- Args:
- devices: list of bad devices to be added to the blacklist file.
- reason: string specifying the reason for blacklist (eg: 'unauthorized')
- """
- timestamp = time.time()
- event_info = {
- 'timestamp': timestamp,
- 'reason': reason,
- }
- device_dicts = {device: event_info for device in devices}
- logger.info('Adding %s to blacklist %s for reason: %s',
- ','.join(devices), self._path, reason)
- with self._blacklist_lock:
- blacklist = self.Read()
- blacklist.update(device_dicts)
- self.Write(blacklist)
-
- def Reset(self):
- """Erases the blacklist file if it exists."""
- logger.info('Resetting blacklist %s', self._path)
- with self._blacklist_lock:
- if os.path.exists(self._path):
- os.remove(self._path)
+# TODO(crbug.com/1097306): Remove this (and this file) once existing uses
+# have switched to using device_denylist directly.
+Blacklist = device_denylist.Denylist
diff --git a/catapult/devil/devil/android/device_blacklist_test.py b/catapult/devil/devil/android/device_blacklist_test.py
deleted file mode 100644
index bc44da55..00000000
--- a/catapult/devil/devil/android/device_blacklist_test.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#! /usr/bin/env python
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import tempfile
-import unittest
-
-from devil.android import device_blacklist
-
-
-class DeviceBlacklistTest(unittest.TestCase):
-
- def testBlacklistFileDoesNotExist(self):
- with tempfile.NamedTemporaryFile() as blacklist_file:
- # Allow the temporary file to be deleted.
- pass
-
- test_blacklist = device_blacklist.Blacklist(blacklist_file.name)
- self.assertEquals({}, test_blacklist.Read())
-
- def testBlacklistFileIsEmpty(self):
- try:
- with tempfile.NamedTemporaryFile(delete=False) as blacklist_file:
- # Allow the temporary file to be closed.
- pass
-
- test_blacklist = device_blacklist.Blacklist(blacklist_file.name)
- self.assertEquals({}, test_blacklist.Read())
-
- finally:
- if os.path.exists(blacklist_file.name):
- os.remove(blacklist_file.name)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/catapult/devil/devil/android/device_denylist.py b/catapult/devil/devil/android/device_denylist.py
new file mode 100644
index 00000000..88b5969c
--- /dev/null
+++ b/catapult/devil/devil/android/device_denylist.py
@@ -0,0 +1,79 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import os
+import threading
+import time
+
+logger = logging.getLogger(__name__)
+
+
+class Denylist(object):
+ def __init__(self, path):
+ self._denylist_lock = threading.RLock()
+ self._path = path
+
+ def Read(self):
+ """Reads the denylist from the denylist file.
+
+ Returns:
+ A dict containing bad devices.
+ """
+ with self._denylist_lock:
+ denylist = dict()
+ if not os.path.exists(self._path):
+ return denylist
+
+ try:
+ with open(self._path, 'r') as f:
+ denylist = json.load(f)
+ except (IOError, ValueError) as e:
+ logger.warning('Unable to read denylist: %s', str(e))
+ os.remove(self._path)
+
+ if not isinstance(denylist, dict):
+ logger.warning('Ignoring %s: %s (a dict was expected instead)',
+ self._path, denylist)
+ denylist = dict()
+
+ return denylist
+
+ def Write(self, denylist):
+ """Writes the provided denylist to the denylist file.
+
+ Args:
+ denylist: list of bad devices to write to the denylist file.
+ """
+ with self._denylist_lock:
+ with open(self._path, 'w') as f:
+ json.dump(denylist, f)
+
+ def Extend(self, devices, reason='unknown'):
+ """Adds devices to denylist file.
+
+ Args:
+ devices: list of bad devices to be added to the denylist file.
+ reason: string specifying the reason for denylist (eg: 'unauthorized')
+ """
+ timestamp = time.time()
+ event_info = {
+ 'timestamp': timestamp,
+ 'reason': reason,
+ }
+ device_dicts = {device: event_info for device in devices}
+ logger.info('Adding %s to denylist %s for reason: %s', ','.join(devices),
+ self._path, reason)
+ with self._denylist_lock:
+ denylist = self.Read()
+ denylist.update(device_dicts)
+ self.Write(denylist)
+
+ def Reset(self):
+ """Erases the denylist file if it exists."""
+ logger.info('Resetting denylist %s', self._path)
+ with self._denylist_lock:
+ if os.path.exists(self._path):
+ os.remove(self._path)
diff --git a/catapult/devil/devil/android/device_denylist_test.py b/catapult/devil/devil/android/device_denylist_test.py
new file mode 100644
index 00000000..b5d79e83
--- /dev/null
+++ b/catapult/devil/devil/android/device_denylist_test.py
@@ -0,0 +1,37 @@
+#! /usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import tempfile
+import unittest
+
+from devil.android import device_denylist
+
+
+class DeviceDenylistTest(unittest.TestCase):
+ def testDenylistFileDoesNotExist(self):
+ with tempfile.NamedTemporaryFile() as denylist_file:
+ # Allow the temporary file to be deleted.
+ pass
+
+ test_denylist = device_denylist.Denylist(denylist_file.name)
+ self.assertEquals({}, test_denylist.Read())
+
+ def testDenylistFileIsEmpty(self):
+ try:
+ with tempfile.NamedTemporaryFile(delete=False) as denylist_file:
+ # Allow the temporary file to be closed.
+ pass
+
+ test_denylist = device_denylist.Denylist(denylist_file.name)
+ self.assertEquals({}, test_denylist.Read())
+
+ finally:
+ if os.path.exists(denylist_file.name):
+ os.remove(denylist_file.name)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/catapult/devil/devil/android/device_errors.py b/catapult/devil/devil/android/device_errors.py
index e6893a4f..d1454683 100644
--- a/catapult/devil/devil/android/device_errors.py
+++ b/catapult/devil/devil/android/device_errors.py
@@ -1,7 +1,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Exception classes raised by AdbWrapper and DeviceUtils.
@@ -11,6 +10,7 @@ The class hierarchy for device exceptions is:
+-- CommandFailedError
| +-- AdbCommandFailedError
| | +-- AdbShellCommandFailedError
+ | +-- AdbVersionError
| +-- FastbootCommandFailedError
| +-- DeviceVersionError
| +-- DeviceChargingError
@@ -48,7 +48,11 @@ class CommandFailedError(base_error.BaseError):
class _BaseCommandFailedError(CommandFailedError):
"""Base Exception for adb and fastboot command failures."""
- def __init__(self, args, output, status=None, device_serial=None,
+ def __init__(self,
+ args,
+ output,
+ status=None,
+ device_serial=None,
message=None):
self.args = args
self.output = output
@@ -68,8 +72,7 @@ class _BaseCommandFailedError(CommandFailedError):
def __eq__(self, other):
return (super(_BaseCommandFailedError, self).__eq__(other)
- and self.args == other.args
- and self.output == other.output
+ and self.args == other.args and self.output == other.output
and self.status == other.status)
def __ne__(self, other):
@@ -82,28 +85,42 @@ class _BaseCommandFailedError(CommandFailedError):
result[:len(super_result)] = super_result
# Update the args used to reconstruct this exception.
- result[1] = (
- self.args, self.output, self.status, self.device_serial, self.message)
+ result[1] = (self.args, self.output, self.status, self.device_serial,
+ self.message)
return tuple(result)
class AdbCommandFailedError(_BaseCommandFailedError):
"""Exception for adb command failures."""
- def __init__(self, args, output, status=None, device_serial=None,
+ def __init__(self,
+ args,
+ output,
+ status=None,
+ device_serial=None,
message=None):
super(AdbCommandFailedError, self).__init__(
- args, output, status=status, message=message,
+ args,
+ output,
+ status=status,
+ message=message,
device_serial=device_serial)
class FastbootCommandFailedError(_BaseCommandFailedError):
"""Exception for fastboot command failures."""
- def __init__(self, args, output, status=None, device_serial=None,
+ def __init__(self,
+ args,
+ output,
+ status=None,
+ device_serial=None,
message=None):
super(FastbootCommandFailedError, self).__init__(
- args, output, status=status, message=message,
+ args,
+ output,
+ status=status,
+ message=message,
device_serial=device_serial)
@@ -114,13 +131,29 @@ class DeviceVersionError(CommandFailedError):
super(DeviceVersionError, self).__init__(message, device_serial)
+class AdbVersionError(CommandFailedError):
+ """Exception for running a command on an incompatible version of adb."""
+
+ def __init__(self, args, desc=None, actual_version=None, min_version=None):
+ adb_cmd = ' '.join(cmd_helper.SingleQuote(arg) for arg in args)
+ desc = desc or 'not supported'
+ if min_version:
+ desc += ' prior to %s' % min_version
+ if actual_version:
+ desc += ' (actual: %s)' % actual_version
+ super(AdbVersionError,
+ self).__init__(message='adb %s: %s' % (adb_cmd, desc))
+
+
class AdbShellCommandFailedError(AdbCommandFailedError):
"""Exception for shell command failures run via adb."""
def __init__(self, command, output, status, device_serial=None):
self.command = command
- segments = ['shell command run via adb failed on the device:\n',
- ' command: %s\n' % command]
+ segments = [
+ 'shell command run via adb failed on the device:\n',
+ ' command: %s\n' % command
+ ]
segments.append(' exit status: %s\n' % status)
if output:
segments.append(' output:\n')
@@ -133,7 +166,7 @@ class AdbShellCommandFailedError(AdbCommandFailedError):
segments.append(" output: ''\n")
message = ''.join(segments)
super(AdbShellCommandFailedError, self).__init__(
- ['shell', command], output, status, device_serial, message)
+ ['shell', command], output, status, device_serial, message)
def __reduce__(self):
"""Support pickling."""
@@ -148,6 +181,7 @@ class AdbShellCommandFailedError(AdbCommandFailedError):
class CommandTimeoutError(base_error.BaseError):
"""Exception for command timeouts."""
+
def __init__(self, message, is_infra_error=False, output=None):
super(CommandTimeoutError, self).__init__(message, is_infra_error)
self.output = output
@@ -171,8 +205,8 @@ class MultipleDevicesError(base_error.BaseError):
def __init__(self, devices):
parallel_devices = parallelizer.Parallelizer(devices)
- descriptions = parallel_devices.pMap(
- lambda d: d.build_description).pGet(None)
+ descriptions = parallel_devices.pMap(lambda d: d.build_description).pGet(
+ None)
msg = ('More than one device available. Use -d/--device to select a device '
'by serial.\n\nAvailable devices:\n')
for d, desc in zip(devices, descriptions):
diff --git a/catapult/devil/devil/android/device_errors_test.py b/catapult/devil/devil/android/device_errors_test.py
index 68a4f167..5fc9e254 100755
--- a/catapult/devil/devil/android/device_errors_test.py
+++ b/catapult/devil/devil/android/device_errors_test.py
@@ -11,7 +11,6 @@ from devil.android import device_errors
class DeviceErrorsTest(unittest.TestCase):
-
def assertIsPicklable(self, original):
pickled = pickle.dumps(original)
reconstructed = pickle.loads(pickled)
@@ -19,7 +18,9 @@ class DeviceErrorsTest(unittest.TestCase):
def testPicklable_AdbCommandFailedError(self):
original = device_errors.AdbCommandFailedError(
- ['these', 'are', 'adb', 'args'], 'adb failure output', status=':(',
+ ['these', 'are', 'adb', 'args'],
+ 'adb failure output',
+ status=':(',
device_serial='0123456789abcdef')
self.assertIsPicklable(original)
@@ -29,18 +30,15 @@ class DeviceErrorsTest(unittest.TestCase):
self.assertIsPicklable(original)
def testPicklable_CommandFailedError(self):
- original = device_errors.CommandFailedError(
- 'sample command failed')
+ original = device_errors.CommandFailedError('sample command failed')
self.assertIsPicklable(original)
def testPicklable_CommandTimeoutError(self):
- original = device_errors.CommandTimeoutError(
- 'My fake command timed out :(')
+ original = device_errors.CommandTimeoutError('My fake command timed out :(')
self.assertIsPicklable(original)
def testPicklable_DeviceChargingError(self):
- original = device_errors.DeviceChargingError(
- 'Fake device failed to charge')
+ original = device_errors.DeviceChargingError('Fake device failed to charge')
self.assertIsPicklable(original)
def testPicklable_DeviceUnreachableError(self):
@@ -49,8 +47,10 @@ class DeviceErrorsTest(unittest.TestCase):
def testPicklable_FastbootCommandFailedError(self):
original = device_errors.FastbootCommandFailedError(
- ['these', 'are', 'fastboot', 'args'], 'fastboot failure output',
- status=':(', device_serial='0123456789abcdef')
+ ['these', 'are', 'fastboot', 'args'],
+ 'fastboot failure output',
+ status=':(',
+ device_serial='0123456789abcdef')
self.assertIsPicklable(original)
def testPicklable_MultipleDevicesError(self):
@@ -67,6 +67,5 @@ class DeviceErrorsTest(unittest.TestCase):
self.assertIsPicklable(original)
-
if __name__ == '__main__':
sys.exit(unittest.main())
diff --git a/catapult/devil/devil/android/device_list.py b/catapult/devil/devil/android/device_list.py
index 0fbb0f15..cd631dbd 100644
--- a/catapult/devil/devil/android/device_list.py
+++ b/catapult/devil/devil/android/device_list.py
@@ -1,7 +1,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A module to keep track of devices across builds."""
import json
@@ -26,8 +25,8 @@ def GetPersistentDeviceList(file_name):
try:
with open(file_name) as f:
devices = json.load(f)
- if not isinstance(devices, list) or not all(isinstance(d, basestring)
- for d in devices):
+ if not isinstance(devices, list) or not all(
+ isinstance(d, basestring) for d in devices):
logger.warning('Unrecognized device file format: %s', devices)
return []
return [d for d in devices if d != '(error)']
diff --git a/catapult/devil/devil/android/device_signal.py b/catapult/devil/devil/android/device_signal.py
index 2cec46d7..a7149b37 100644
--- a/catapult/devil/devil/android/device_signal.py
+++ b/catapult/devil/devil/android/device_signal.py
@@ -1,13 +1,11 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Defines constants for signals that should be supported on devices.
Note: Obtained by running `kill -l` on a user device.
"""
-
SIGHUP = 1 # Hangup
SIGINT = 2 # Interrupt
SIGQUIT = 3 # Quit
diff --git a/catapult/devil/devil/android/device_temp_file.py b/catapult/devil/devil/android/device_temp_file.py
index 74cc5099..84e51cb2 100644
--- a/catapult/devil/devil/android/device_temp_file.py
+++ b/catapult/devil/devil/android/device_temp_file.py
@@ -1,7 +1,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A temp file that automatically gets pushed and deleted from a device."""
# pylint: disable=W0622
@@ -19,7 +18,7 @@ logger = logging.getLogger(__name__)
def _GenerateName(prefix, suffix, dir):
- random_hex = hex(random.randint(0, 2 ** 52))[2:]
+ random_hex = hex(random.randint(0, 2**52))[2:]
return posixpath.join(dir, '%s-%s%s' % (prefix, random_hex, suffix))
@@ -55,15 +54,17 @@ class DeviceTempFile(object):
def close(self):
"""Deletes the temporary file from the device."""
+
# ignore exception if the file is already gone.
def delete_temporary_file():
try:
- self._adb.Shell('rm -f %s' % self.name_quoted, expect_status=None)
+ self._adb.Shell(
+ 'rm -f %s' % self.name_quoted, expect_status=None, retries=0)
except base_error.BaseError as e:
# We don't really care, and stack traces clog up the log.
# Log a warning and move on.
- logger.warning('Failed to delete temporary file %s: %s',
- self.name, str(e))
+ logger.warning('Failed to delete temporary file %s: %s', self.name,
+ str(e))
# It shouldn't matter when the temp file gets deleted, so do so
# asynchronously.
@@ -101,6 +102,7 @@ class NamedDeviceTemporaryDirectory(object):
def close(self):
"""Deletes the temporary directory from the device."""
+
def delete_temporary_dir():
try:
self._adb.Shell('rm -rf %s' % self.name, expect_status=None)
diff --git a/catapult/devil/devil/android/device_test_case.py b/catapult/devil/devil/android/device_test_case.py
index 1148b544..327d67a6 100644
--- a/catapult/devil/devil/android/device_test_case.py
+++ b/catapult/devil/devil/android/device_test_case.py
@@ -21,8 +21,7 @@ def PrepareDevices(*_args):
try:
d.WaitUntilFullyBooted(timeout=5, retries=0)
live_devices.append(str(d))
- except (device_errors.CommandFailedError,
- device_errors.CommandTimeoutError,
+ except (device_errors.CommandFailedError, device_errors.CommandTimeoutError,
device_errors.DeviceUnreachableError):
pass
with _devices_lock:
@@ -33,7 +32,6 @@ def PrepareDevices(*_args):
class DeviceTestCase(unittest.TestCase):
-
def __init__(self, *args, **kwargs):
super(DeviceTestCase, self).__init__(*args, **kwargs)
self.serial = None
diff --git a/catapult/devil/devil/android/device_utils.py b/catapult/devil/devil/android/device_utils.py
index 6182a527..0a041edb 100644
--- a/catapult/devil/devil/android/device_utils.py
+++ b/catapult/devil/devil/android/device_utils.py
@@ -1,11 +1,7 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
-"""Provides a variety of device interactions based on adb.
-
-Eventually, this will be based on adb_wrapper.
-"""
+"""Provides a variety of device interactions based on adb."""
# pylint: disable=unused-argument
import calendar
@@ -42,7 +38,6 @@ from devil.android import md5sum
from devil.android.sdk import adb_wrapper
from devil.android.sdk import intent
from devil.android.sdk import keyevent
-from devil.android.sdk import split_select
from devil.android.sdk import version_codes
from devil.utils import host_utils
from devil.utils import parallelizer
@@ -64,7 +59,7 @@ _DEFAULT_TIMEOUT = 30
_DEFAULT_RETRIES = 3
# A sentinel object for default values
-# TODO(jbudorick,perezju): revisit how default values are handled by
+# TODO(jbudorick): revisit how default values are handled by
# the timeout_retry decorators.
DEFAULT = object()
@@ -73,22 +68,29 @@ DEFAULT = object()
# as_root param.
_FORCE_SU = object()
-_RECURSIVE_DIRECTORY_LIST_SCRIPT = """
- function list_subdirs() {
- for f in "$1"/* ;
+# Lists all files for the specified directories.
+# In order to minimize data transfer, prints directories as absolute paths
+# followed by files within that directory without their path.
+_FILE_LIST_SCRIPT = """
+ function list_files() {
+ for f in "$1"/{.,}*
do
- if [ -d "$f" ] ;
+ if [ "$f" == "." ] || [ "$f" == ".." ] || [ "$f" == "${1}/.*" ] \
+ || [ "$f" == "${1}/*" ]
then
- if [ "$f" == "." ] || [ "$f" == ".." ] ;
- then
- continue ;
- fi ;
- echo "$f" ;
- list_subdirs "$f" ;
- fi ;
- done ;
- } ;
- list_subdirs %s
+ continue
+ fi
+ base=${f##*/} # Get the basename for the file, dropping the path.
+ echo "$base"
+ done
+ }
+ for dir in %s
+ do
+ if [ -d "$dir" ]; then
+ echo "$dir"
+ list_files "$dir"
+ fi
+ done
"""
_RESTART_ADBD_SCRIPT = """
@@ -103,74 +105,78 @@ _RESTART_ADBD_SCRIPT = """
"""
# Not all permissions can be set.
-_PERMISSIONS_BLACKLIST_RE = re.compile('|'.join(fnmatch.translate(p) for p in [
- 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS',
- 'android.permission.ACCESS_MOCK_LOCATION',
- 'android.permission.ACCESS_NETWORK_STATE',
- 'android.permission.ACCESS_NOTIFICATION_POLICY',
- 'android.permission.ACCESS_VR_STATE',
- 'android.permission.ACCESS_WIFI_STATE',
- 'android.permission.AUTHENTICATE_ACCOUNTS',
- 'android.permission.BLUETOOTH',
- 'android.permission.BLUETOOTH_ADMIN',
- 'android.permission.BROADCAST_STICKY',
- 'android.permission.CHANGE_NETWORK_STATE',
- 'android.permission.CHANGE_WIFI_MULTICAST_STATE',
- 'android.permission.CHANGE_WIFI_STATE',
- 'android.permission.DISABLE_KEYGUARD',
- 'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION',
- 'android.permission.EXPAND_STATUS_BAR',
- 'android.permission.FOREGROUND_SERVICE',
- 'android.permission.GET_PACKAGE_SIZE',
- 'android.permission.INSTALL_SHORTCUT',
- 'android.permission.INJECT_EVENTS',
- 'android.permission.INTERNET',
- 'android.permission.KILL_BACKGROUND_PROCESSES',
- 'android.permission.MANAGE_ACCOUNTS',
- 'android.permission.MODIFY_AUDIO_SETTINGS',
- 'android.permission.NFC',
- 'android.permission.READ_SYNC_SETTINGS',
- 'android.permission.READ_SYNC_STATS',
- 'android.permission.RECEIVE_BOOT_COMPLETED',
- 'android.permission.RECORD_VIDEO',
- 'android.permission.REORDER_TASKS',
- 'android.permission.REQUEST_INSTALL_PACKAGES',
- 'android.permission.RESTRICTED_VR_ACCESS',
- 'android.permission.RUN_INSTRUMENTATION',
- 'android.permission.SET_ALARM',
- 'android.permission.SET_TIME_ZONE',
- 'android.permission.SET_WALLPAPER',
- 'android.permission.SET_WALLPAPER_HINTS',
- 'android.permission.TRANSMIT_IR',
- 'android.permission.USE_CREDENTIALS',
- 'android.permission.USE_FINGERPRINT',
- 'android.permission.VIBRATE',
- 'android.permission.WAKE_LOCK',
- 'android.permission.WRITE_SYNC_SETTINGS',
- 'com.android.browser.permission.READ_HISTORY_BOOKMARKS',
- 'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS',
- 'com.android.launcher.permission.INSTALL_SHORTCUT',
- 'com.chrome.permission.DEVICE_EXTRAS',
- 'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS',
- 'com.google.android.c2dm.permission.RECEIVE',
- 'com.google.android.providers.gsf.permission.READ_GSERVICES',
- 'com.google.vr.vrcore.permission.VRCORE_INTERNAL',
- 'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER',
- '*.permission.C2D_MESSAGE',
- '*.permission.READ_WRITE_BOOKMARK_FOLDERS',
- '*.TOS_ACKED',
-]))
+_PERMISSIONS_DENYLIST_RE = re.compile('|'.join(
+ fnmatch.translate(p) for p in [
+ 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS',
+ 'android.permission.ACCESS_MOCK_LOCATION',
+ 'android.permission.ACCESS_NETWORK_STATE',
+ 'android.permission.ACCESS_NOTIFICATION_POLICY',
+ 'android.permission.ACCESS_VR_STATE',
+ 'android.permission.ACCESS_WIFI_STATE',
+ 'android.permission.AUTHENTICATE_ACCOUNTS',
+ 'android.permission.BLUETOOTH',
+ 'android.permission.BLUETOOTH_ADMIN',
+ 'android.permission.BROADCAST_STICKY',
+ 'android.permission.CHANGE_NETWORK_STATE',
+ 'android.permission.CHANGE_WIFI_MULTICAST_STATE',
+ 'android.permission.CHANGE_WIFI_STATE',
+ 'android.permission.DISABLE_KEYGUARD',
+ 'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION',
+ 'android.permission.EXPAND_STATUS_BAR',
+ 'android.permission.FOREGROUND_SERVICE',
+ 'android.permission.GET_PACKAGE_SIZE',
+ 'android.permission.INSTALL_SHORTCUT',
+ 'android.permission.INJECT_EVENTS',
+ 'android.permission.INTERNET',
+ 'android.permission.KILL_BACKGROUND_PROCESSES',
+ 'android.permission.MANAGE_ACCOUNTS',
+ 'android.permission.MODIFY_AUDIO_SETTINGS',
+ 'android.permission.NFC',
+ 'android.permission.READ_SYNC_SETTINGS',
+ 'android.permission.READ_SYNC_STATS',
+ 'android.permission.RECEIVE_BOOT_COMPLETED',
+ 'android.permission.RECORD_VIDEO',
+ 'android.permission.REORDER_TASKS',
+ 'android.permission.REQUEST_INSTALL_PACKAGES',
+ 'android.permission.RESTRICTED_VR_ACCESS',
+ 'android.permission.RUN_INSTRUMENTATION',
+ 'android.permission.SET_ALARM',
+ 'android.permission.SET_TIME_ZONE',
+ 'android.permission.SET_WALLPAPER',
+ 'android.permission.SET_WALLPAPER_HINTS',
+ 'android.permission.TRANSMIT_IR',
+ 'android.permission.USE_CREDENTIALS',
+ 'android.permission.USE_FINGERPRINT',
+ 'android.permission.VIBRATE',
+ 'android.permission.WAKE_LOCK',
+ 'android.permission.WRITE_SYNC_SETTINGS',
+ 'com.android.browser.permission.READ_HISTORY_BOOKMARKS',
+ 'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS',
+ 'com.android.launcher.permission.INSTALL_SHORTCUT',
+ 'com.chrome.permission.DEVICE_EXTRAS',
+ 'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS',
+ 'com.google.android.c2dm.permission.RECEIVE',
+ 'com.google.android.providers.gsf.permission.READ_GSERVICES',
+ 'com.google.vr.vrcore.permission.VRCORE_INTERNAL',
+ 'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER',
+ '*.permission.C2D_MESSAGE',
+ '*.permission.READ_WRITE_BOOKMARK_FOLDERS',
+ '*.TOS_ACKED',
+ ]))
_SHELL_OUTPUT_SEPARATOR = '~X~'
-_PERMISSIONS_EXCEPTION_RE = re.compile(
- r'java\.lang\.\w+Exception: .*$', re.MULTILINE)
+_PERMISSIONS_EXCEPTION_RE = re.compile(r'java\.lang\.\w+Exception: .*$',
+ re.MULTILINE)
_CURRENT_FOCUS_CRASH_RE = re.compile(
r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
_GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]')
+_VERSION_CODE_SDK_RE = re.compile(
+ r'\s*versionCode=(\d+).*minSdk=(\d+).*targetSdk=(.*)\s*')
# Regex to parse the long (-l) output of 'ls' command, c.f.
# https://github.com/landley/toybox/blob/master/toys/posix/ls.c#L446
+# yapf: disable
_LONG_LS_OUTPUT_RE = re.compile(
r'(?P<st_mode>[\w-]{10})\s+' # File permissions
r'(?:(?P<st_nlink>\d+)\s+)?' # Number of links (optional)
@@ -187,44 +193,60 @@ _LONG_LS_OUTPUT_RE = re.compile(
r'(?: -> (?P<symbolic_link_to>.+))?' # Symbolic link (optional)
r'$' # End of string
)
+# yapf: enable
+
_LS_DATE_FORMAT = '%Y-%m-%d %H:%M'
_FILE_MODE_RE = re.compile(r'[dbclps-](?:[r-][w-][xSs-]){2}[r-][w-][xTt-]$')
_FILE_MODE_KIND = {
- 'd': stat.S_IFDIR, 'b': stat.S_IFBLK, 'c': stat.S_IFCHR,
- 'l': stat.S_IFLNK, 'p': stat.S_IFIFO, 's': stat.S_IFSOCK,
- '-': stat.S_IFREG}
+ 'd': stat.S_IFDIR,
+ 'b': stat.S_IFBLK,
+ 'c': stat.S_IFCHR,
+ 'l': stat.S_IFLNK,
+ 'p': stat.S_IFIFO,
+ 's': stat.S_IFSOCK,
+ '-': stat.S_IFREG
+}
_FILE_MODE_PERMS = [
- stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR,
- stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP,
- stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH,
+ stat.S_IRUSR,
+ stat.S_IWUSR,
+ stat.S_IXUSR,
+ stat.S_IRGRP,
+ stat.S_IWGRP,
+ stat.S_IXGRP,
+ stat.S_IROTH,
+ stat.S_IWOTH,
+ stat.S_IXOTH,
]
_FILE_MODE_SPECIAL = [
('s', stat.S_ISUID),
('s', stat.S_ISGID),
('t', stat.S_ISVTX),
]
-_PS_COLUMNS = {
- 'pid': 1,
- 'ppid': 2,
- 'name': -1
-}
-_SELINUX_MODE = {
- 'enforcing': True,
- 'permissive': False,
- 'disabled': None
-}
+_PS_COLUMNS = {'pid': 1, 'ppid': 2, 'name': -1}
+_SELINUX_MODE = {'enforcing': True, 'permissive': False, 'disabled': None}
# Some devices require different logic for checking if root is necessary
_SPECIAL_ROOT_DEVICE_LIST = [
- 'marlin', # Pixel XL
- 'sailfish', # Pixel
- 'taimen', # Pixel 2 XL
- 'vega', # Lenovo Mirage Solo
- 'walleye', # Pixel 2
- 'crosshatch', # Pixel 3 XL
- 'blueline', # Pixel 3
+ 'marlin', # Pixel XL
+ 'sailfish', # Pixel
+ 'taimen', # Pixel 2 XL
+ 'vega', # Lenovo Mirage Solo
+ 'walleye', # Pixel 2
+ 'crosshatch', # Pixel 3 XL
+ 'blueline', # Pixel 3
+ 'sargo', # Pixel 3a
+ 'bonito', # Pixel 3a XL
+ 'sdk_goog3_x86', # Crow emulator
+]
+_SPECIAL_ROOT_DEVICE_LIST += [
+ 'aosp_%s' % _d for _d in _SPECIAL_ROOT_DEVICE_LIST
+]
+
+# Somce devices are slow/timeout when using default install.
+# Devices listed here will perform no_streaming app installation.
+_NO_STREAMING_DEVICE_LIST = [
+ 'flounder', # Nexus 9
+ 'volantis', # Another product name for Nexus 9
]
-_SPECIAL_ROOT_DEVICE_LIST += ['aosp_%s' % _d for _d in
- _SPECIAL_ROOT_DEVICE_LIST]
_IMEI_RE = re.compile(r' Device ID = (.+)$')
# The following regex is used to match result parcels like:
@@ -236,8 +258,6 @@ Result: Parcel(
"""
_PARCEL_RESULT_RE = re.compile(
r'0x[0-9a-f]{8}\: (?:[0-9a-f]{8}\s+){1,4}\'(.{16})\'')
-_EBUSY_RE = re.compile(
- r'mkdir failed for ([^,]*), Device or resource busy')
# http://bit.ly/2WLZhUF added a timeout to adb wait-for-device. We sometimes
# want to wait longer than the implicit call within adb root allows.
@@ -245,8 +265,7 @@ _WAIT_FOR_DEVICE_TIMEOUT_STR = 'timeout expired while waiting for device'
_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE = re.compile(
r'Current WebView package.*:.*\(([a-z.]*),')
-_WEBVIEW_SYSUPDATE_NULL_PKG_RE = re.compile(
- r'Current WebView package is null')
+_WEBVIEW_SYSUPDATE_NULL_PKG_RE = re.compile(r'Current WebView package is null')
_WEBVIEW_SYSUPDATE_FALLBACK_LOGIC_RE = re.compile(
r'Fallback logic enabled: (true|false)')
_WEBVIEW_SYSUPDATE_PACKAGE_INSTALLED_RE = re.compile(
@@ -262,8 +281,7 @@ PS_COLUMNS = ('name', 'pid', 'ppid')
ProcessInfo = collections.namedtuple('ProcessInfo', PS_COLUMNS)
-@decorators.WithExplicitTimeoutAndRetries(
- _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
+@decorators.WithExplicitTimeoutAndRetries(_DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
def GetAVDs():
"""Returns a list of Android Virtual Devices.
@@ -271,9 +289,10 @@ def GetAVDs():
A list containing the configured AVDs.
"""
lines = cmd_helper.GetCmdOutput([
- os.path.join(devil_env.config.LocalPath('android_sdk'),
- 'tools', 'android'),
- 'list', 'avd']).splitlines()
+ os.path.join(
+ devil_env.config.LocalPath('android_sdk'), 'tools', 'android'),
+ 'list', 'avd'
+ ]).splitlines()
avds = []
for line in lines:
if 'Name:' not in line:
@@ -284,14 +303,14 @@ def GetAVDs():
return avds
-@decorators.WithExplicitTimeoutAndRetries(
- _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
+@decorators.WithExplicitTimeoutAndRetries(_DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
def RestartServer():
"""Restarts the adb server.
Raises:
CommandFailedError if we fail to kill or restart the server.
"""
+
def adb_killed():
return not adb_wrapper.AdbWrapper.IsServerOnline()
@@ -300,7 +319,8 @@ def RestartServer():
adb_wrapper.AdbWrapper.KillServer()
if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
- # TODO(perezju): raise an exception after fixng http://crbug.com/442319
+ # TODO(crbug.com/442319): Switch this to raise an exception if we
+ # figure out why sometimes not all adb servers on bots get killed.
logger.warning('Failed to kill adb server')
adb_wrapper.AdbWrapper.StartServer()
if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
@@ -356,12 +376,62 @@ def _FormatPartialOutputError(output):
return '\n'.join(message)
+_PushableComponents = collections.namedtuple('_PushableComponents',
+ ('host', 'device', 'collapse'))
+
+
+def _IterPushableComponents(host_path, device_path):
+ """Yields a sequence of paths that can be pushed directly via adb push.
+
+ `adb push` doesn't currently handle pushing directories that contain
+ symlinks: https://bit.ly/2pMBlW5
+
+ To circumvent this issue, we get the smallest set of files and/or
+ directories that can be pushed without attempting to push a directory
+ that contains a symlink.
+
+ This function does so by recursing through |host_path|. Each call
+ yields 3-tuples that include the smallest set of (host, device) path pairs
+ that can be passed to adb push and a bool indicating whether the parent
+ directory can be pushed -- i.e., if True, the host path is neither a
+ symlink nor a directory that contains a symlink.
+
+ Args:
+ host_path: an absolute path of a file or directory on the host
+ device_path: an absolute path of a file or directory on the device
+ Yields:
+ 3-tuples containing
+ host (str): the host path, with symlinks dereferenced
+ device (str): the device path
+ collapse (bool): whether this entity permits its parent to be pushed
+ in its entirety. (Parents need permission from all child entities
+ in order to be pushed in their entirety.)
+ """
+ if os.path.isfile(host_path):
+ yield _PushableComponents(
+ os.path.realpath(host_path), device_path, not os.path.islink(host_path))
+ else:
+ components = []
+ for child in os.listdir(host_path):
+ components.extend(
+ _IterPushableComponents(
+ os.path.join(host_path, child), posixpath.join(
+ device_path, child)))
+
+ if all(c.collapse for c in components):
+ yield _PushableComponents(
+ os.path.realpath(host_path), device_path,
+ not os.path.islink(host_path))
+ else:
+ for c in components:
+ yield c
+
+
class DeviceUtils(object):
_MAX_ADB_COMMAND_LENGTH = 512
_MAX_ADB_OUTPUT_LENGTH = 32768
- _LAUNCHER_FOCUSED_RE = re.compile(
- r'\s*mCurrentFocus.*(Launcher|launcher).*')
+ _LAUNCHER_FOCUSED_RE = re.compile(r'\s*mCurrentFocus.*(Launcher|launcher).*')
_VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop')
@@ -369,7 +439,9 @@ class DeviceUtils(object):
# Property in /data/local.prop that controls Java assertions.
JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
- def __init__(self, device, enable_device_files_cache=False,
+ def __init__(self,
+ device,
+ enable_device_files_cache=False,
default_timeout=_DEFAULT_TIMEOUT,
default_retries=_DEFAULT_RETRIES):
"""DeviceUtils constructor.
@@ -489,7 +561,12 @@ class DeviceUtils(object):
# 'eng' builds have root enabled by default and the adb session cannot
# be unrooted.
return True
- if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
+ # Devices using the system-as-root partition layout appear to not have
+ # a /root directory. See http://bit.ly/37F34sx for more context.
+ if (self.build_system_root_image == 'true'
+ or self.build_version_sdk >= version_codes.Q
+ # This may be redundant with the checks above.
+ or self.product_name in _SPECIAL_ROOT_DEVICE_LIST):
return self.GetProp('service.adb.root') == '1'
self.RunShellCommand(['ls', '/root'], check_return=True)
return True
@@ -515,13 +592,21 @@ class DeviceUtils(object):
"""
if 'needs_su' not in self._cache:
cmd = '%s && ! ls /root' % self._Su('ls /root')
- if self.product_name in _SPECIAL_ROOT_DEVICE_LIST:
+ # Devices using the system-as-root partition layout appear to not have
+ # a /root directory. See http://bit.ly/37F34sx for more context.
+ if (self.build_system_root_image == 'true'
+ or self.build_version_sdk >= version_codes.Q
+ # This may be redundant with the checks above.
+ or self.product_name in _SPECIAL_ROOT_DEVICE_LIST):
if self.HasRoot():
self._cache['needs_su'] = False
return False
cmd = 'which which && which su'
try:
- self.RunShellCommand(cmd, shell=True, check_return=True,
+ self.RunShellCommand(
+ cmd,
+ shell=True,
+ check_return=True,
timeout=self._default_timeout if timeout is DEFAULT else timeout,
retries=self._default_retries if retries is DEFAULT else retries)
self._cache['needs_su'] = True
@@ -529,7 +614,6 @@ class DeviceUtils(object):
self._cache['needs_su'] = False
return self._cache['needs_su']
-
def _Su(self, command):
if self.build_version_sdk >= version_codes.MARSHMALLOW:
return 'su 0 %s' % command
@@ -596,6 +680,9 @@ class DeviceUtils(object):
def GetExternalStoragePath(self, timeout=None, retries=None):
"""Get the device's path to its SD card.
+ Note: this path is read-only by apps in R+. Use GetAppWritablePath() to
+ obtain a path writable by apps.
+
Args:
timeout: timeout in seconds
retries: number of retries
@@ -614,6 +701,30 @@ class DeviceUtils(object):
str(self))
return self._cache['external_storage']
+ def GetAppWritablePath(self, timeout=None, retries=None):
+ """Get a path that on the device's SD card that apps can write.
+
+ Args:
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ A app-writeable path on the device's SD card.
+
+ Raises:
+ CommandFailedError if the external storage path could not be determined.
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
+ """
+ if self.build_version_sdk >= version_codes.Q:
+ # On Q+ apps don't require permissions to access well-defined media
+ # locations like /sdcard/Download. On R+ the WRITE_EXTERNAL_STORAGE
+ # permission no longer provides access to the external storage root. See
+ # https://developer.android.com/preview/privacy/storage#permissions-target-11
+ # So use /sdcard/Download for the app-writable path on those versions.
+ return posixpath.join(self.GetExternalStoragePath(), 'Download')
+ return self.GetExternalStoragePath()
+
@decorators.WithTimeoutAndRetriesFromInstance()
def GetIMEI(self, timeout=None, retries=None):
"""Get the device's IMEI.
@@ -633,7 +744,8 @@ class DeviceUtils(object):
if self.build_version_sdk < 21:
out = self.RunShellCommand(['dumpsys', 'iphonesubinfo'],
- raw_output=True, check_return=True)
+ raw_output=True,
+ check_return=True)
if out:
match = re.search(_IMEI_RE, out)
if match:
@@ -656,6 +768,23 @@ class DeviceUtils(object):
raise device_errors.CommandFailedError('Unable to fetch IMEI.')
@decorators.WithTimeoutAndRetriesFromInstance()
+ def IsApplicationInstalled(self, package, timeout=None, retries=None):
+ """Determines whether a particular package is installed on the device.
+
+ Args:
+ package: Name of the package.
+
+ Returns:
+ True if the application is installed, False otherwise.
+ """
+ # `pm list packages` allows matching substrings, but we want exact matches
+ # only.
+ matching_packages = self.RunShellCommand(
+ ['pm', 'list', 'packages', package], check_return=True)
+ desired_line = 'package:' + package
+ return desired_line in matching_packages
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def GetApplicationPaths(self, package, timeout=None, retries=None):
"""Get the paths of the installed apks on the device for the given package.
@@ -685,8 +814,8 @@ class DeviceUtils(object):
# TODO(jbudorick): Check if this is fixed as new Android versions are
# released to put an upper bound on this.
should_check_return = (self.build_version_sdk < version_codes.LOLLIPOP)
- output = self.RunShellCommand(
- ['pm', 'path', package], check_return=should_check_return)
+ output = self.RunShellCommand(['pm', 'path', package],
+ check_return=should_check_return)
apks = []
bad_output = False
for line in output:
@@ -718,8 +847,8 @@ class DeviceUtils(object):
A string with the version name or None if the package is not found
on the device.
"""
- output = self.RunShellCommand(
- ['dumpsys', 'package', package], check_return=True)
+ output = self.RunShellCommand(['dumpsys', 'package', package],
+ check_return=True)
if not output:
return None
for line in output:
@@ -730,6 +859,35 @@ class DeviceUtils(object):
'Version name for %s not found on dumpsys output' % package, str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
+ def GetApplicationTargetSdk(self, package, timeout=None, retries=None):
+ """Get the targetSdkVersion of a package installed on the device.
+
+ Args:
+ package: Name of the package.
+
+ Returns:
+ A string with the targetSdkVersion or None if the package is not found on
+ the device. Note: this cannot always be cast to an integer. If this
+ application targets a pre-release SDK, this returns the version codename
+ instead (ex. "R").
+ """
+ if not self.IsApplicationInstalled(package):
+ return None
+ lines = self._GetDumpsysOutput(['package', package], 'targetSdk=')
+ for line in lines:
+ m = _VERSION_CODE_SDK_RE.match(line)
+ if m:
+ value = m.group(3)
+ # 10000 is the code used by Android for a pre-finalized SDK.
+ if value == '10000':
+ return self.GetProp('ro.build.version.codename', cache=True)
+ else:
+ return value
+ raise device_errors.CommandFailedError(
+ 'targetSdkVersion for %s not found on dumpsys output' % package,
+ str(self))
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def GetPackageArchitecture(self, package, timeout=None, retries=None):
"""Get the architecture of a package installed on the device.
@@ -758,6 +916,9 @@ class DeviceUtils(object):
CommandFailedError if the package's data directory can't be found,
whether because it's not installed or otherwise.
"""
+ if not self.IsApplicationInstalled(package):
+ raise device_errors.CommandFailedError('%s is not installed' % package,
+ str(self))
output = self._RunPipedShellCommand(
'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package))
for line in output:
@@ -765,11 +926,14 @@ class DeviceUtils(object):
if dataDir:
return dataDir
raise device_errors.CommandFailedError(
- 'Could not find data directory for %s', package)
+ 'Could not find data directory for %s' % package, str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
- def GetSecurityContextForPackage(self, package, encrypted=False, timeout=None,
- retries=None):
+ def GetSecurityContextForPackage(self,
+ package,
+ encrypted=False,
+ timeout=None,
+ retries=None):
"""Gets the SELinux security context for the given package.
Args:
@@ -782,7 +946,8 @@ class DeviceUtils(object):
"""
directory = '/data/user_de/0/' if encrypted else '/data/data/'
for line in self.RunShellCommand(['ls', '-Z', directory],
- as_root=True, check_return=True):
+ as_root=True,
+ check_return=True):
split_line = line.split()
# ls -Z output differs between Android versions, but the package is
# always last and the context always starts with "u:object"
@@ -792,7 +957,7 @@ class DeviceUtils(object):
return column
return None
- def TakeBugReport(self, path, timeout=60*5, retries=None):
+ def TakeBugReport(self, path, timeout=60 * 5, retries=None):
"""Takes a bug report and dumps it to the specified path.
This doesn't use adb's bugreport option since its behavior is dependent on
@@ -812,15 +977,23 @@ class DeviceUtils(object):
self.PullFile(device_tmp_file.name, path)
@decorators.WithTimeoutAndRetriesFromInstance()
- def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
+ def WaitUntilFullyBooted(self,
+ wifi=False,
+ decrypt=False,
+ timeout=None,
+ retries=None):
"""Wait for the device to fully boot.
This means waiting for the device to boot, the package manager to be
- available, and the SD card to be ready. It can optionally mean waiting
- for wifi to come up, too.
+ available, and the SD card to be ready.
+ It can optionally wait the following:
+ - Wait for wifi to come up.
+ - Wait for full-disk decryption to complete.
Args:
wifi: A boolean indicating if we should wait for wifi to come up or not.
+ decrypt: A boolean indicating if we should wait for full-disk decryption
+ to complete.
timeout: timeout in seconds
retries: number of retries
@@ -829,10 +1002,11 @@ class DeviceUtils(object):
CommandTimeoutError if one of the component waits times out.
DeviceUnreachableError if the device becomes unresponsive.
"""
+
def sd_card_ready():
try:
- self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
- check_return=True)
+ self.RunShellCommand(
+ ['test', '-d', self.GetExternalStoragePath()], check_return=True)
return True
except device_errors.AdbCommandFailedError:
return False
@@ -853,24 +1027,49 @@ class DeviceUtils(object):
return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
check_return=False)
+ def decryption_completed():
+ try:
+ decrypt = self.GetProp('vold.decrypt', cache=False)
+ # The prop "void.decrypt" will only be set when the device uses
+ # full-disk encryption (FDE).
+ # Return true when:
+ # - The prop is empty, which means the device is unencrypted or uses
+ # file-based encryption (FBE).
+ # - or the prop has value "trigger_restart_framework", which means
+ # the decription is finished.
+ return decrypt == '' or decrypt == 'trigger_restart_framework'
+ except device_errors.CommandFailedError:
+ return False
+
self.adb.WaitForDevice()
timeout_retry.WaitFor(sd_card_ready)
timeout_retry.WaitFor(pm_ready)
timeout_retry.WaitFor(boot_completed)
if wifi:
timeout_retry.WaitFor(wifi_enabled)
+ if decrypt:
+ timeout_retry.WaitFor(decryption_completed)
REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=REBOOT_DEFAULT_TIMEOUT)
- def Reboot(self, block=True, wifi=False, timeout=None, retries=None):
+ def Reboot(self,
+ block=True,
+ wifi=False,
+ decrypt=False,
+ timeout=None,
+ retries=None):
"""Reboot the device.
Args:
block: A boolean indicating if we should wait for the reboot to complete.
wifi: A boolean indicating if we should wait for wifi to be enabled after
- the reboot. The option has no effect unless |block| is also True.
+ the reboot.
+ The option has no effect unless |block| is also True.
+ decrypt: A boolean indicating if we should wait for full-disk decryption
+ to complete after the reboot.
+ The option has no effect unless |block| is also True.
timeout: timeout in seconds
retries: number of retries
@@ -878,6 +1077,7 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
+
def device_offline():
return not self.IsOnline()
@@ -885,14 +1085,23 @@ class DeviceUtils(object):
self.ClearCache()
timeout_retry.WaitFor(device_offline, wait_period=1)
if block:
- self.WaitUntilFullyBooted(wifi=wifi)
+ self.WaitUntilFullyBooted(wifi=wifi, decrypt=decrypt)
INSTALL_DEFAULT_TIMEOUT = 8 * _DEFAULT_TIMEOUT
+ MODULES_SRC_DIRECTORY_PATH = '/data/local/tmp/modules'
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
- def Install(self, apk, allow_downgrade=False, reinstall=False,
- permissions=None, timeout=None, retries=None, modules=None):
+ def Install(self,
+ apk,
+ allow_downgrade=False,
+ reinstall=False,
+ permissions=None,
+ timeout=None,
+ retries=None,
+ modules=None,
+ fake_modules=None,
+ additional_locales=None):
"""Install an APK or app bundle.
Noop if an identical APK is already installed. If installing a bundle, the
@@ -911,21 +1120,93 @@ class DeviceUtils(object):
retries: number of retries
modules: An iterable containing specific bundle modules to install.
Error if set and |apk| points to an APK instead of a bundle.
+ fake_modules: An iterable containing specific bundle modules that should
+ have their apks copied to |MODULES_SRC_DIRECTORY_PATH| subdirectory
+ rather than installed. Thus the app can emulate SplitCompat while
+ running. This should not have any overlap with |modules|.
+ additional_locales: An iterable with additional locales to install for a
+ bundle.
Raises:
CommandFailedError if the installation fails.
CommandTimeoutError if the installation times out.
DeviceUnreachableError on missing device.
"""
- self._InstallInternal(apk, None, allow_downgrade=allow_downgrade,
- reinstall=reinstall, permissions=permissions,
- modules=modules)
+ apk = apk_helper.ToHelper(apk)
+ modules_set = set(modules or [])
+ fake_modules_set = set(fake_modules or [])
+ assert modules_set.isdisjoint(fake_modules_set), (
+ 'These modules overlap: %s' % (modules_set & fake_modules_set))
+ all_modules = modules_set | fake_modules_set
+ package_name = apk.GetPackageName()
+
+ with apk.GetApkPaths(self,
+ modules=all_modules,
+ additional_locales=additional_locales) as apk_paths:
+ if apk.SupportsSplits():
+ fake_apk_paths = self._GetFakeInstallPaths(apk_paths, fake_modules)
+ self._FakeInstall(fake_apk_paths, fake_modules, package_name)
+ apk_paths_to_install = [p for p in apk_paths if p not in fake_apk_paths]
+ else:
+ apk_paths_to_install = apk_paths
+ self._InstallInternal(
+ apk,
+ apk_paths_to_install,
+ allow_downgrade=allow_downgrade,
+ reinstall=reinstall,
+ permissions=permissions)
+
+ @staticmethod
+ def _GetFakeInstallPaths(apk_paths, fake_modules):
+ def IsFakeModulePath(path):
+ filename = os.path.basename(path)
+ return any(filename.startswith(f + '-') for f in fake_modules)
+
+ if not fake_modules:
+ return set()
+ return set(p for p in apk_paths if IsFakeModulePath(p))
+
+ def _FakeInstall(self, fake_apk_paths, fake_modules, package_name):
+ with tempfile_ext.NamedTemporaryDirectory() as modules_dir:
+ device_dir = posixpath.join(self.MODULES_SRC_DIRECTORY_PATH, package_name)
+ if not fake_modules:
+ # Push empty module dir to clear device dir and update the cache.
+ self.PushChangedFiles([(modules_dir, device_dir)],
+ delete_device_stale=True)
+ return
+
+ still_need_master = set(fake_modules)
+ for path in fake_apk_paths:
+ filename = os.path.basename(path)
+ # Example names: base-en.apk, test_dummy-master.apk.
+ module_name, suffix = filename.split('-', 1)
+ if 'master' in suffix:
+ assert module_name in still_need_master, (
+ 'Duplicate master apk file for %s' % module_name)
+ still_need_master.remove(module_name)
+ new_filename = '%s.apk' % module_name
+ else:
+ # |suffix| includes .apk extension.
+ new_filename = '%s.config.%s' % (module_name, suffix)
+ new_path = os.path.join(modules_dir, new_filename)
+ os.rename(path, new_path)
+
+ assert not still_need_master, (
+ 'Missing master apk file for %s' % still_need_master)
+ self.PushChangedFiles([(modules_dir, device_dir)],
+ delete_device_stale=True)
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=INSTALL_DEFAULT_TIMEOUT)
- def InstallSplitApk(self, base_apk, split_apks, allow_downgrade=False,
- reinstall=False, allow_cached_props=False,
- permissions=None, timeout=None, retries=None):
+ def InstallSplitApk(self,
+ base_apk,
+ split_apks,
+ allow_downgrade=False,
+ reinstall=False,
+ allow_cached_props=False,
+ permissions=None,
+ timeout=None,
+ retries=None):
"""Install a split APK.
Noop if all of the APK splits are already installed.
@@ -948,73 +1229,57 @@ class DeviceUtils(object):
DeviceUnreachableError on missing device.
DeviceVersionError if device SDK is less than Android L.
"""
- self._InstallInternal(base_apk, split_apks, reinstall=reinstall,
- allow_cached_props=allow_cached_props,
- permissions=permissions,
- allow_downgrade=allow_downgrade)
-
- def _InstallInternal(self, base_apk, split_apks, allow_downgrade=False,
- reinstall=False, allow_cached_props=False,
- permissions=None, modules=None):
- base_apk = apk_helper.ToHelper(base_apk)
- if base_apk.is_bundle:
- if split_apks:
- raise device_errors.CommandFailedError(
- 'Attempted to install a bundle {} while specifying split apks'
- .format(base_apk))
- if allow_downgrade:
- logging.warning('Installation of a bundle requested with '
- 'allow_downgrade=False. This is not possible with '
- 'bundletools, no downgrading is possible. This '
- 'flag will be ignored and installation will proceed.')
- # |allow_cached_props| is unused and ignored for bundles.
- self._InstallBundleInternal(base_apk, permissions, modules)
- return
-
- if modules:
- raise device_errors.CommandFailedError(
- 'Attempted to specify modules to install when providing an APK')
-
- if split_apks:
+ apk = apk_helper.ToSplitHelper(base_apk, split_apks)
+ with apk.GetApkPaths(
+ self, allow_cached_props=allow_cached_props) as apk_paths:
+ self._InstallInternal(
+ apk,
+ apk_paths,
+ reinstall=reinstall,
+ permissions=permissions,
+ allow_downgrade=allow_downgrade)
+
+ def _InstallInternal(self,
+ apk,
+ apk_paths,
+ allow_downgrade=False,
+ reinstall=False,
+ permissions=None):
+ if not apk_paths:
+ raise device_errors.CommandFailedError('Did not get any APKs to install')
+
+ if len(apk_paths) > 1:
self._CheckSdkLevel(version_codes.LOLLIPOP)
- all_apks = [base_apk.path]
- if split_apks:
- all_apks += split_select.SelectSplits(
- self, base_apk.path, split_apks, allow_cached_props=allow_cached_props)
- if len(all_apks) == 1:
- logger.warning('split-select did not select any from %s', split_apks)
-
- missing_apks = [apk for apk in all_apks if not os.path.exists(apk)]
+ missing_apks = [a for a in apk_paths if not os.path.exists(a)]
if missing_apks:
raise device_errors.CommandFailedError(
- 'Attempted to install non-existent apks: %s'
- % pprint.pformat(missing_apks))
+ 'Attempted to install non-existent apks: %s' %
+ pprint.pformat(missing_apks))
- package_name = base_apk.GetPackageName()
+ package_name = apk.GetPackageName()
device_apk_paths = self._GetApplicationPathsInternal(package_name)
- apks_to_install = None
host_checksums = None
if not device_apk_paths:
- apks_to_install = all_apks
- elif len(device_apk_paths) > 1 and not split_apks:
+ apks_to_install = apk_paths
+ elif len(device_apk_paths) > 1 and len(apk_paths) == 1:
logger.warning(
'Installing non-split APK when split APK was previously installed')
- apks_to_install = all_apks
- elif len(device_apk_paths) == 1 and split_apks:
+ apks_to_install = apk_paths
+ elif len(device_apk_paths) == 1 and len(apk_paths) > 1:
logger.warning(
'Installing split APK when non-split APK was previously installed')
- apks_to_install = all_apks
+ apks_to_install = apk_paths
else:
try:
- apks_to_install, host_checksums = (
- self._ComputeStaleApks(package_name, all_apks))
- except EnvironmentError as e:
+ apks_to_install, host_checksums = (self._ComputeStaleApks(
+ package_name, apk_paths))
+ except device_errors.CommandFailedError as e:
logger.warning('Error calculating md5: %s', e)
- apks_to_install, host_checksums = all_apks, None
+ apks_to_install, host_checksums = apk_paths, None
if apks_to_install and not reinstall:
- apks_to_install = all_apks
+ apks_to_install = apk_paths
if device_apk_paths and apks_to_install and not reinstall:
self.Uninstall(package_name)
@@ -1023,14 +1288,23 @@ class DeviceUtils(object):
# Assume that we won't know the resulting device state.
self._cache['package_apk_paths'].pop(package_name, 0)
self._cache['package_apk_checksums'].pop(package_name, 0)
- if split_apks:
- partial = package_name if len(apks_to_install) < len(all_apks) else None
+ partial = package_name if len(apks_to_install) < len(apk_paths) else None
+ streaming = None
+ if self.product_name in _NO_STREAMING_DEVICE_LIST:
+ streaming = False
+ if len(apks_to_install) > 1 or partial:
self.adb.InstallMultiple(
- apks_to_install, partial=partial, reinstall=reinstall,
+ apks_to_install,
+ partial=partial,
+ reinstall=reinstall,
+ streaming=streaming,
allow_downgrade=allow_downgrade)
else:
self.adb.Install(
- base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade)
+ apks_to_install[0],
+ reinstall=reinstall,
+ streaming=streaming,
+ allow_downgrade=allow_downgrade)
else:
# Running adb install terminates running instances of the app, so to be
# consistent, we explicitly terminate it when skipping the install.
@@ -1038,26 +1312,12 @@ class DeviceUtils(object):
if (permissions is None
and self.build_version_sdk >= version_codes.MARSHMALLOW):
- permissions = base_apk.GetPermissions()
+ permissions = apk.GetPermissions()
self.GrantPermissions(package_name, permissions)
# Upon success, we know the device checksums, but not their paths.
if host_checksums is not None:
self._cache['package_apk_checksums'][package_name] = host_checksums
- def _InstallBundleInternal(self, bundle, permissions, modules):
- cmd = [bundle.path, 'install', '--device', self.serial]
- if modules:
- for m in modules:
- cmd.extend(['-m', m])
- status = cmd_helper.RunCmd(cmd)
- if status != 0:
- raise device_errors.CommandFailedError('Cound not install {}'.format(
- bundle.path))
- if (permissions is None
- and self.build_version_sdk >= version_codes.MARSHMALLOW):
- permissions = bundle.GetPermissions()
- self.GrantPermissions(bundle.GetPackageName(), permissions)
-
@decorators.WithTimeoutAndRetriesFromInstance()
def Uninstall(self, package_name, keep_data=False, timeout=None,
retries=None):
@@ -1092,12 +1352,21 @@ class DeviceUtils(object):
raise device_errors.DeviceVersionError(
('Requires SDK level %s, device is SDK level %s' %
(required_sdk_level, self.build_version_sdk)),
- device_serial=self.serial)
+ device_serial=self.serial)
@decorators.WithTimeoutAndRetriesFromInstance()
- def RunShellCommand(self, cmd, shell=False, check_return=False, cwd=None,
- env=None, run_as=None, as_root=False, single_line=False,
- large_output=False, raw_output=False, timeout=None,
+ def RunShellCommand(self,
+ cmd,
+ shell=False,
+ check_return=False,
+ cwd=None,
+ env=None,
+ run_as=None,
+ as_root=False,
+ single_line=False,
+ large_output=False,
+ raw_output=False,
+ timeout=None,
retries=None):
"""Run an ADB shell command.
@@ -1120,8 +1389,8 @@ class DeviceUtils(object):
This behaviour is consistent with that of command runners in cmd_helper as
well as Python's own subprocess.Popen.
- TODO(perezju) Change the default of |check_return| to True when callers
- have switched to the new behaviour.
+ TODO(crbug.com/1029769) Change the default of |check_return| to True when
+ callers have switched to the new behaviour.
Args:
cmd: A sequence containing the command to run and its arguments, or a
@@ -1157,6 +1426,7 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
+
def env_quote(key, value):
if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
raise KeyError('Invalid shell variable name %r' % key)
@@ -1181,8 +1451,8 @@ class DeviceUtils(object):
else:
with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
self._WriteFileWithPush(script.name, cmd)
- logger.info('Large shell command will be run from file: %s ...',
- cmd[:self._MAX_ADB_COMMAND_LENGTH])
+ logger.debug('Large shell command will be run from file: %s ...',
+ cmd[:self._MAX_ADB_COMMAND_LENGTH])
return handle_check_return('sh %s' % script.name_quoted)
def handle_large_output(cmd, large_output_mode):
@@ -1213,6 +1483,7 @@ class DeviceUtils(object):
if isinstance(cmd, basestring):
if not shell:
+ # TODO(crbug.com/1029769): Make this an error instead.
logger.warning(
'The command to run should preferably be passed as a sequence of'
' args. If shell features are needed (pipes, wildcards, variables)'
@@ -1259,22 +1530,27 @@ class DeviceUtils(object):
if not pipestatus_line.startswith(PIPESTATUS_LEADER):
logger.error('Pipe exit statuses of shell script missing.')
raise device_errors.AdbShellCommandFailedError(
- script, output, status=None,
- device_serial=self.serial)
+ script, output, status=None, device_serial=self.serial)
output = output[:-1]
statuses = [
- int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()]
+ int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()
+ ]
if any(statuses):
raise device_errors.AdbShellCommandFailedError(
- script, output, status=statuses,
- device_serial=self.serial)
+ script, output, status=statuses, device_serial=self.serial)
return output
@decorators.WithTimeoutAndRetriesFromInstance()
- def KillAll(self, process_name, exact=False, signum=device_signal.SIGKILL,
- as_root=False, blocking=False, quiet=False,
- timeout=None, retries=None):
+ def KillAll(self,
+ process_name,
+ exact=False,
+ signum=device_signal.SIGKILL,
+ as_root=False,
+ blocking=False,
+ quiet=False,
+ timeout=None,
+ retries=None):
"""Kill all processes with the given name on the device.
Args:
@@ -1312,8 +1588,8 @@ class DeviceUtils(object):
'No processes matching %r (exact=%r)' % (process_name, exact),
str(self))
- logger.info(
- 'KillAll(%r, ...) attempting to kill the following:', process_name)
+ logger.info('KillAll(%r, ...) attempting to kill the following:',
+ process_name)
for p in processes:
logger.info(' %05d %s', p.pid, p.name)
@@ -1331,8 +1607,13 @@ class DeviceUtils(object):
return len(pids)
@decorators.WithTimeoutAndRetriesFromInstance()
- def StartActivity(self, intent_obj, blocking=False, trace_file_name=None,
- force_stop=False, timeout=None, retries=None):
+ def StartActivity(self,
+ intent_obj,
+ blocking=False,
+ trace_file_name=None,
+ force_stop=False,
+ timeout=None,
+ retries=None):
"""Start package's activity on the device.
Args:
@@ -1392,8 +1673,13 @@ class DeviceUtils(object):
raise device_errors.CommandFailedError(line, str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
- def StartInstrumentation(self, component, finish=True, raw=False,
- extras=None, timeout=None, retries=None):
+ def StartInstrumentation(self,
+ component,
+ finish=True,
+ raw=False,
+ extras=None,
+ timeout=None,
+ retries=None):
if extras is None:
extras = {}
@@ -1411,8 +1697,8 @@ class DeviceUtils(object):
package = component.split('/')[0]
shell_snippet = 'p=%s;%s' % (package,
cmd_helper.ShrinkToSnippet(cmd, 'p', package))
- return self.RunShellCommand(shell_snippet, shell=True, check_return=True,
- large_output=True)
+ return self.RunShellCommand(
+ shell_snippet, shell=True, check_return=True, large_output=True)
@decorators.WithTimeoutAndRetriesFromInstance()
def BroadcastIntent(self, intent_obj, timeout=None, retries=None):
@@ -1445,9 +1731,11 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
+
def is_launcher_focused():
output = self.RunShellCommand(['dumpsys', 'window', 'windows'],
- check_return=True, large_output=True)
+ check_return=True,
+ large_output=True)
return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output)
def dismiss_popups():
@@ -1462,8 +1750,9 @@ class DeviceUtils(object):
return
self.StartActivity(
- intent.Intent(action='android.intent.action.MAIN',
- category='android.intent.category.HOME'),
+ intent.Intent(
+ action='android.intent.action.MAIN',
+ category='android.intent.category.HOME'),
blocking=True)
if not is_launcher_focused():
@@ -1486,8 +1775,11 @@ class DeviceUtils(object):
self.RunShellCommand(['am', 'force-stop', package], check_return=True)
@decorators.WithTimeoutAndRetriesFromInstance()
- def ClearApplicationState(
- self, package, permissions=None, timeout=None, retries=None):
+ def ClearApplicationState(self,
+ package,
+ permissions=None,
+ timeout=None,
+ retries=None):
"""Clear all state for the given package.
Args:
@@ -1523,15 +1815,18 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')],
- check_return=True)
+ self.RunShellCommand(
+ ['input', 'keyevent', format(keycode, 'd')], check_return=True)
PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=PUSH_CHANGED_FILES_DEFAULT_TIMEOUT)
- def PushChangedFiles(self, host_device_tuples, timeout=None,
- retries=None, delete_device_stale=False):
+ def PushChangedFiles(self,
+ host_device_tuples,
+ delete_device_stale=False,
+ timeout=None,
+ retries=None):
"""Push files to the device, skipping files that don't need updating.
When a directory is pushed, it is traversed recursively on the host and
@@ -1544,194 +1839,208 @@ class DeviceUtils(object):
|host_path| is an absolute path of a file or directory on the host
that should be minimially pushed to the device, and |device_path| is
an absolute path of the destination on the device.
+ delete_device_stale: option to delete stale files on device
timeout: timeout in seconds
retries: number of retries
- delete_device_stale: option to delete stale files on device
Raises:
CommandFailedError on failure.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
+ # TODO(crbug.com/1005504): Experiment with this on physical devices after
+ # upgrading devil's default adb beyond 1.0.39.
+ # TODO(crbug.com/1020716): disabled as can result in extra directory.
+ enable_push_sync = False
- all_changed_files = []
- all_stale_files = []
- missing_dirs = set()
- cache_commit_funcs = []
- for h, d in host_device_tuples:
- assert os.path.isabs(h) and posixpath.isabs(d)
- h = os.path.realpath(h)
- changed_files, up_to_date_files, stale_files, cache_commit_func = (
- self._GetChangedAndStaleFiles(h, d, delete_device_stale))
- all_changed_files += changed_files
- all_stale_files += stale_files
- cache_commit_funcs.append(cache_commit_func)
- if changed_files and not up_to_date_files and not stale_files:
- if os.path.isdir(h):
- missing_dirs.add(d)
- else:
- missing_dirs.add(posixpath.dirname(d))
+ if enable_push_sync:
+ try:
+ self._PushChangedFilesSync(host_device_tuples)
+ return
+ except device_errors.AdbVersionError as e:
+ # If we don't meet the adb requirements, fall back to the previous
+ # sync-unaware implementation.
+ logging.warning(str(e))
- if delete_device_stale and all_stale_files:
- self.RemovePath(all_stale_files, force=True, recursive=True)
+ changed_files, missing_dirs, cache_commit_func = (self._GetChangedFiles(
+ host_device_tuples, delete_device_stale))
- if all_changed_files:
+ if changed_files:
if missing_dirs:
- try:
- self.RunShellCommand(['mkdir', '-p'] + list(missing_dirs),
- check_return=True)
- except device_errors.AdbShellCommandFailedError as e:
- # TODO(crbug.com/739899): This is attempting to diagnose flaky EBUSY
- # errors that have been popping up in single-device scenarios.
- # Remove it once we've figured out what's causing them and how best
- # to handle them.
- m = _EBUSY_RE.search(e.output)
- if m:
- logging.error(
- 'Hit EBUSY while attempting to make missing directories.')
- logging.error('lsof output:')
- # Don't check for return below since grep exits with a non-zero when
- # no match is found.
- for l in self.RunShellCommand(
- 'lsof | grep %s' % cmd_helper.SingleQuote(m.group(1)),
- check_return=False):
- logging.error(' %s', l)
- raise
- self._PushFilesImpl(host_device_tuples, all_changed_files)
- for func in cache_commit_funcs:
- func()
+ self.RunShellCommand(['mkdir', '-p'] + list(missing_dirs),
+ check_return=True)
+ self._PushFilesImpl(host_device_tuples, changed_files)
+ cache_commit_func()
+
+ def _PushChangedFilesSync(self, host_device_tuples):
+ """Push changed files via `adb sync`.
+
+ Args:
+ host_device_tuples: Same as PushChangedFiles.
+ """
+ for h, d in host_device_tuples:
+ for ph, pd, _ in _IterPushableComponents(h, d):
+ self.adb.Push(ph, pd, sync=True)
+
- def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False):
- """Get files to push and delete
+ def _GetDeviceNodes(self, paths):
+ """Get the set of all files and directories on the device contained within
+ the provided list of paths, without recursively expanding directories.
Args:
- host_path: an absolute path of a file or directory on the host
- device_path: an absolute path of a file or directory on the device
- track_stale: whether to bother looking for stale files (slower)
+ paths: The list of paths for which to list files and directories.
Returns:
- a four-element tuple
+ a set containing all files and directories contained within |paths| on the
+ device.
+ """
+ nodes = set()
+ paths = [p.replace(' ', r'\ ') for p in paths]
+ command = _FILE_LIST_SCRIPT % ' '.join(paths)
+ current_path = ""
+ # We use shell=True to evaluate the command as a script through the shell,
+ # otherwise RunShellCommand tries to interpret it as the name of a (non
+ # existent) command to run.
+ for line in self.RunShellCommand(command, shell=True, check_return=True):
+ # If the line is an absolute path it's a directory, otherwise it's a file
+ # within the most recent directory.
+ if posixpath.isabs(line):
+ current_path = line + '/'
+ else:
+ line = current_path + line
+ nodes.add(line)
+
+ return nodes
+
+ def _GetChangedFiles(self, host_device_tuples, delete_stale=False):
+ """Get files to push and delete.
+
+ Args:
+ host_device_tuples: a list of (host_files_path, device_files_path) tuples
+ to find changed files from
+ delete_stale: Whether to delete stale files
+
+ Returns:
+ a three-element tuple
1st element: a list of (host_files_path, device_files_path) tuples to push
- 2nd element: a list of host_files_path that are up-to-date
- 3rd element: a list of stale files under device_path, or [] when
- track_stale == False
- 4th element: a cache commit function.
+ 2nd element: a list of missing device directories to mkdir
+ 3rd element: a cache commit function
"""
- try:
- # Length calculations below assume no trailing /.
- host_path = host_path.rstrip('/')
- device_path = device_path.rstrip('/')
-
- specific_device_paths = [device_path]
- ignore_other_files = not track_stale and os.path.isdir(host_path)
- if ignore_other_files:
- specific_device_paths = []
+ # The fully expanded list of host/device tuples of files to push.
+ file_tuples = []
+ # All directories we're pushing files to.
+ device_dirs_to_push_to = set()
+ # All files and directories we expect to have on the device after pushing
+ # files.
+ expected_device_nodes = set()
+
+ for h, d in host_device_tuples:
+ assert os.path.isabs(h) and posixpath.isabs(d)
+ h = os.path.realpath(h)
+ host_path = h.rstrip('/')
+ device_dir = d.rstrip('/')
+
+ expected_device_nodes.add(device_dir)
+
+ # Add all parent directories to the directories we expect to have so we
+ # don't delete empty nested directories.
+ parent = posixpath.dirname(device_dir)
+ while parent and parent != '/':
+ expected_device_nodes.add(parent)
+ parent = posixpath.dirname(parent)
+
+ if os.path.isdir(host_path):
+ device_dirs_to_push_to.add(device_dir)
for root, _, filenames in os.walk(host_path):
- relative_dir = root[len(host_path) + 1:]
- specific_device_paths.extend(
- posixpath.join(device_path, relative_dir, f) for f in filenames)
+ # ignore hidden directories
+ if os.path.sep + '.' in root:
+ continue
+ relative_dir = os.path.relpath(root, host_path).rstrip('.')
+ device_path = posixpath.join(device_dir, relative_dir).rstrip('/')
+ expected_device_nodes.add(device_path)
+ device_dirs_to_push_to.add(device_path)
+ files = (
+ [posixpath.join(device_dir, relative_dir, f) for f in filenames])
+ expected_device_nodes.update(files)
+ file_tuples.extend(zip(
+ (os.path.join(root, f) for f in filenames), files))
+ else:
+ device_dirs_to_push_to.add(posixpath.dirname(device_dir))
+ file_tuples.append((host_path, device_dir))
- def calculate_host_checksums():
- return md5sum.CalculateHostMd5Sums([host_path])
+ if file_tuples or delete_stale:
+ current_device_nodes = self._GetDeviceNodes(device_dirs_to_push_to)
+ nodes_to_delete = current_device_nodes - expected_device_nodes
- def calculate_device_checksums():
- if self._enable_device_files_cache:
- cache_entry = self._cache['device_path_checksums'].get(device_path)
- if cache_entry and cache_entry[0] == ignore_other_files:
- return dict(cache_entry[1])
+ missing_dirs = device_dirs_to_push_to - current_device_nodes
- sums = md5sum.CalculateDeviceMd5Sums(specific_device_paths, self)
+ if not file_tuples:
+ if delete_stale and nodes_to_delete:
+ self.RemovePath(nodes_to_delete, force=True, recursive=True)
+ return (host_device_tuples, missing_dirs, lambda: 0)
- cache_entry = [ignore_other_files, sums]
- self._cache['device_path_checksums'][device_path] = cache_entry
- return dict(sums)
+ possibly_stale_device_nodes = current_device_nodes - nodes_to_delete
+ possibly_stale_tuples = (
+ [t for t in file_tuples if t[1] in possibly_stale_device_nodes])
- host_checksums, device_checksums = reraiser_thread.RunAsync((
- calculate_host_checksums,
- calculate_device_checksums))
- except EnvironmentError as e:
- logger.warning('Error calculating md5: %s', e)
- return ([(host_path, device_path)], [], [], lambda: 0)
-
- to_push = []
- up_to_date = []
- to_delete = []
- if os.path.isfile(host_path):
- host_checksum = host_checksums.get(host_path)
- device_checksum = device_checksums.get(device_path)
- if host_checksum == device_checksum:
- up_to_date.append(host_path)
+ def calculate_host_checksums():
+ # Need to compute all checksums when caching.
+ if self._enable_device_files_cache:
+ return md5sum.CalculateHostMd5Sums([t[0] for t in file_tuples])
else:
- to_push.append((host_path, device_path))
- else:
- for host_abs_path, host_checksum in host_checksums.iteritems():
- device_abs_path = posixpath.join(
- device_path, os.path.relpath(host_abs_path, host_path))
- device_checksum = device_checksums.pop(device_abs_path, None)
- if device_checksum == host_checksum:
- up_to_date.append(host_abs_path)
- else:
- to_push.append((host_abs_path, device_abs_path))
- to_delete = device_checksums.keys()
- # We can't rely solely on the checksum approach since it does not catch
- # stale directories, which can result in empty directories that cause issues
- # during copying in efficient_android_directory_copy.sh. So, find any stale
- # directories here so they can be removed in addition to stale files.
- if track_stale:
- to_delete.extend(self._GetStaleDirectories(host_path, device_path))
+ return md5sum.CalculateHostMd5Sums(
+ [t[0] for t in possibly_stale_tuples])
- def cache_commit_func():
- # When host_path is a not a directory, the path.join() call below would
- # have an '' as the second argument, causing an unwanted / to be appended.
- if os.path.isfile(host_path):
- assert len(host_checksums) == 1
- new_sums = {device_path: host_checksums[host_path]}
+ def calculate_device_checksums():
+ paths = set([t[1] for t in possibly_stale_tuples])
+ if not paths:
+ return dict()
+ sums = dict()
+ if self._enable_device_files_cache:
+ paths_not_in_cache = set()
+ for path in paths:
+ cache_entry = self._cache['device_path_checksums'].get(path)
+ if cache_entry:
+ sums[path] = cache_entry
+ else:
+ paths_not_in_cache.add(path)
+ paths = paths_not_in_cache
+ sums.update(dict(md5sum.CalculateDeviceMd5Sums(paths, self)))
+ if self._enable_device_files_cache:
+ for path, checksum in sums.iteritems():
+ self._cache['device_path_checksums'][path] = checksum
+ return sums
+ try:
+ host_checksums, device_checksums = reraiser_thread.RunAsync(
+ (calculate_host_checksums, calculate_device_checksums))
+ except device_errors.CommandFailedError as e:
+ logger.warning('Error calculating md5: %s', e)
+ return (host_device_tuples, set(), lambda: 0)
+
+ up_to_date = set()
+
+ for host_path, device_path in possibly_stale_tuples:
+ device_checksum = device_checksums.get(device_path, None)
+ host_checksum = host_checksums.get(host_path, None)
+ if device_checksum == host_checksum and device_checksum is not None:
+ up_to_date.add(device_path)
else:
- new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val
- for path, val in host_checksums.iteritems()}
- cache_entry = [ignore_other_files, new_sums]
- self._cache['device_path_checksums'][device_path] = cache_entry
+ nodes_to_delete.add(device_path)
- return (to_push, up_to_date, to_delete, cache_commit_func)
+ if delete_stale and nodes_to_delete:
+ self.RemovePath(nodes_to_delete, force=True, recursive=True)
- def _GetStaleDirectories(self, host_path, device_path):
- """Gets a list of stale directories on the device.
+ to_push = (
+ [t for t in file_tuples if t[1] not in up_to_date])
- Args:
- host_path: an absolute path of a directory on the host
- device_path: an absolute path of a directory on the device
+ def cache_commit_func():
+ if not self._enable_device_files_cache:
+ return
+ for host_path, device_path in file_tuples:
+ host_checksum = host_checksums.get(host_path, None)
+ self._cache['device_path_checksums'][device_path] = host_checksum
- Returns:
- A list containing absolute paths to directories on the device that are
- considered stale.
- """
- def get_device_dirs(path):
- directories = set()
- command = _RECURSIVE_DIRECTORY_LIST_SCRIPT % cmd_helper.SingleQuote(path)
- # We use shell=True to evaluate the command as a script through the shell,
- # otherwise RunShellCommand tries to interpret it as the name of a (non
- # existent) command to run.
- for line in self.RunShellCommand(
- command, shell=True, check_return=True):
- directories.add(posixpath.relpath(posixpath.normpath(line), path))
- return directories
-
- def get_host_dirs(path):
- directories = set()
- if not os.path.isdir(path):
- return directories
- for root, _, _ in os.walk(path):
- if root != path:
- # Strip off the top level directory so we can compare the device and
- # host.
- directories.add(
- os.path.relpath(root, path).replace(os.sep, posixpath.sep))
- return directories
-
- host_dirs = get_host_dirs(host_path)
- device_dirs = get_device_dirs(device_path)
- stale_dirs = device_dirs - host_dirs
- return [posixpath.join(device_path, d) for d in stale_dirs]
+ return (to_push, missing_dirs, cache_commit_func)
def _ComputeDeviceChecksumsForApks(self, package_name):
ret = self._cache['package_apk_checksums'].get(package_name)
@@ -1749,10 +2058,11 @@ class DeviceUtils(object):
def calculate_device_checksums():
return self._ComputeDeviceChecksumsForApks(package_name)
- host_checksums, device_checksums = reraiser_thread.RunAsync((
- calculate_host_checksums, calculate_device_checksums))
- stale_apks = [k for (k, v) in host_checksums.iteritems()
- if v not in device_checksums]
+ host_checksums, device_checksums = reraiser_thread.RunAsync(
+ (calculate_host_checksums, calculate_device_checksums))
+ stale_apks = [
+ k for (k, v) in host_checksums.iteritems() if v not in device_checksums
+ ]
return stale_apks, set(host_checksums.values())
def _PushFilesImpl(self, host_device_tuples, files):
@@ -1761,8 +2071,8 @@ class DeviceUtils(object):
size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
file_count = len(files)
- dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
- for h, _ in host_device_tuples)
+ dir_size = sum(
+ host_utils.GetRecursiveDiskUsage(h) for h, _ in host_device_tuples)
dir_file_count = 0
for h, _ in host_device_tuples:
if os.path.isdir(h):
@@ -1770,8 +2080,8 @@ class DeviceUtils(object):
else:
dir_file_count += 1
- push_duration = self._ApproximateDuration(
- file_count, file_count, size, False)
+ push_duration = self._ApproximateDuration(file_count, file_count, size,
+ False)
dir_push_duration = self._ApproximateDuration(
len(host_device_tuples), dir_file_count, dir_size, False)
zip_duration = self._ApproximateDuration(1, 1, size, True)
@@ -1786,8 +2096,8 @@ class DeviceUtils(object):
elif self._commands_installed is False:
# Already tried and failed to install unzip command.
self._PushChangedFilesIndividually(files)
- elif not self._PushChangedFilesZipped(
- files, [d for _, d in host_device_tuples]):
+ elif not self._PushChangedFilesZipped(files,
+ [d for _, d in host_device_tuples]):
self._PushChangedFilesIndividually(files)
def _MaybeInstallCommands(self):
@@ -1859,13 +2169,15 @@ class DeviceUtils(object):
quoted_dirs = ' '.join(cmd_helper.SingleQuote(d) for d in dirs)
self.RunShellCommand(
'unzip %s&&chmod -R 777 %s' % (device_temp.name, quoted_dirs),
- shell=True, as_root=True,
+ shell=True,
+ as_root=True,
env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
check_return=True)
return True
- # TODO(nednguyen): remove this and migrate the callsite to PathExists().
+ # TODO(crbug.com/1111556): remove this and migrate the callsite to
+ # PathExists().
@decorators.WithTimeoutAndRetriesFromInstance()
def FileExists(self, device_path, timeout=None, retries=None):
"""Checks whether the given file exists on the device.
@@ -1895,22 +2207,32 @@ class DeviceUtils(object):
"""
paths = device_paths
if isinstance(paths, basestring):
- paths = (paths,)
+ paths = (paths, )
if not paths:
return True
cmd = ['test', '-e', paths[0]]
for p in paths[1:]:
cmd.extend(['-a', '-e', p])
try:
- self.RunShellCommand(cmd, as_root=as_root, check_return=True,
- timeout=timeout, retries=retries)
+ self.RunShellCommand(
+ cmd,
+ as_root=as_root,
+ check_return=True,
+ timeout=timeout,
+ retries=retries)
return True
except device_errors.CommandFailedError:
return False
@decorators.WithTimeoutAndRetriesFromInstance()
- def RemovePath(self, device_path, force=False, recursive=False,
- as_root=False, rename=False, timeout=None, retries=None):
+ def RemovePath(self,
+ device_path,
+ force=False,
+ recursive=False,
+ as_root=False,
+ rename=False,
+ timeout=None,
+ retries=None):
"""Removes the given path(s) from the device.
Args:
@@ -1925,16 +2247,19 @@ class DeviceUtils(object):
timeout: timeout in seconds
retries: number of retries
"""
+
def _RenamePath(path):
- random_suffix = hex(random.randint(2 ** 12, 2 ** 16 - 1))[2:]
+ random_suffix = hex(random.randint(2**12, 2**16 - 1))[2:]
dest = '%s-%s' % (path, random_suffix)
try:
- self.RunShellCommand(
- ['mv', path, dest], as_root=as_root, check_return=True)
+ self.RunShellCommand(['mv', path, dest],
+ as_root=as_root,
+ check_return=True)
return dest
except device_errors.AdbShellCommandFailedError:
# If it couldn't be moved, just try rm'ing the original path instead.
return path
+
args = ['rm']
if force:
args.append('-f')
@@ -1968,7 +2293,11 @@ class DeviceUtils(object):
yield device_temp
@decorators.WithTimeoutAndRetriesFromInstance()
- def PullFile(self, device_path, host_path, as_root=False, timeout=None,
+ def PullFile(self,
+ device_path,
+ host_path,
+ as_root=False,
+ timeout=None,
retries=None):
"""Pull a file from the device.
@@ -2010,8 +2339,12 @@ class DeviceUtils(object):
shutil.rmtree(d)
@decorators.WithTimeoutAndRetriesFromInstance()
- def ReadFile(self, device_path, as_root=False, force_pull=False,
- timeout=None, retries=None):
+ def ReadFile(self,
+ device_path,
+ as_root=False,
+ force_pull=False,
+ timeout=None,
+ retries=None):
"""Reads the contents of a file from the device.
Args:
@@ -2035,16 +2368,22 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
+
def get_size(path):
return self.FileSize(path, as_root=as_root)
- if (not force_pull
- and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH):
- return _JoinLines(self.RunShellCommand(
- ['cat', device_path], as_root=as_root, check_return=True))
- elif as_root and self.NeedsSU():
- with self._CopyToReadableLocation(device_path) as readable_temp_file:
- return self._ReadFileWithPull(readable_temp_file.name)
+ # Reading by pulling is faster than first getting the file size and cat-ing,
+ # so only read by cat when we need root.
+ if as_root and self.NeedsSU():
+ if (not force_pull
+ and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH):
+ return _JoinLines(
+ self.RunShellCommand(['cat', device_path],
+ as_root=as_root,
+ check_return=True))
+ else:
+ with self._CopyToReadableLocation(device_path) as readable_temp_file:
+ return self._ReadFileWithPull(readable_temp_file.name)
else:
return self._ReadFileWithPull(device_path)
@@ -2055,8 +2394,13 @@ class DeviceUtils(object):
self.adb.Push(host_temp.name, device_path)
@decorators.WithTimeoutAndRetriesFromInstance()
- def WriteFile(self, device_path, contents, as_root=False, force_push=False,
- timeout=None, retries=None):
+ def WriteFile(self,
+ device_path,
+ contents,
+ as_root=False,
+ force_push=False,
+ timeout=None,
+ retries=None):
"""Writes |contents| to a file on the device.
Args:
@@ -2091,7 +2435,8 @@ class DeviceUtils(object):
# destination files might be on different file systems (e.g.
# on internal storage and an external sd card).
self.RunShellCommand(['cp', device_temp.name, device_path],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
else:
# If root is not needed, we can push directly to the desired location.
self._WriteFileWithPush(device_path, contents)
@@ -2099,11 +2444,13 @@ class DeviceUtils(object):
def _ParseLongLsOutput(self, device_path, as_root=False, **kwargs):
"""Run and scrape the output of 'ls -a -l' on a device directory."""
device_path = posixpath.join(device_path, '') # Force trailing '/'.
- output = self.RunShellCommand(
- ['ls', '-a', '-l', device_path], as_root=as_root,
- check_return=True, env={'TZ': 'utc'}, **kwargs)
+ output = self.RunShellCommand(['ls', '-a', '-l', device_path],
+ as_root=as_root,
+ check_return=True,
+ env={'TZ': 'utc'},
+ **kwargs)
if output and output[0].startswith('total '):
- output.pop(0) # pylint: disable=maybe-no-member
+ output.pop(0) # pylint: disable=maybe-no-member
entries = []
for line in output:
@@ -2278,6 +2625,7 @@ class DeviceUtils(object):
Raises:
CommandTimeoutError on timeout.
"""
+
def find_property(lines, property_name):
for index, line in enumerate(lines):
if line.strip() == '':
@@ -2360,18 +2708,21 @@ class DeviceUtils(object):
def screen_density(self):
"""Returns the screen density of the device."""
DPI_TO_DENSITY = {
- 120: 'ldpi',
- 160: 'mdpi',
- 240: 'hdpi',
- 320: 'xhdpi',
- 480: 'xxhdpi',
- 640: 'xxxhdpi',
+ 120: 'ldpi',
+ 160: 'mdpi',
+ 240: 'hdpi',
+ 320: 'xhdpi',
+ 480: 'xxhdpi',
+ 640: 'xxxhdpi',
}
return DPI_TO_DENSITY.get(self.pixel_density, 'tvdpi')
@property
def pixel_density(self):
- return int(self.GetProp('ro.sf.lcd_density', cache=True))
+ density = self.GetProp('ro.sf.lcd_density', cache=True)
+ if not density and self.adb.is_emulator:
+ density = self.GetProp('qemu.sf.lcd_density', cache=True)
+ return int(density)
@property
def build_description(self):
@@ -2402,6 +2753,15 @@ class DeviceUtils(object):
return self.GetProp('ro.build.product', cache=True)
@property
+ def build_system_root_image(self):
+ """Returns the system_root_image property.
+
+ This seems to indicate whether the device is using a system-as-root
+ partition layout. See http://bit.ly/37F34sx for more info.
+ """
+ return self.GetProp('ro.build.system_root_image', cache=True)
+
+ @property
def build_type(self):
"""Returns the build type of the system (e.g. 'user')."""
return self.GetProp('ro.build.type', cache=True)
@@ -2426,6 +2786,11 @@ class DeviceUtils(object):
'Invalid build version sdk: %r' % value)
@property
+ def tracing_path(self):
+ """Returns the tracing path of the device for atrace."""
+ return self.GetTracingPath()
+
+ @property
def product_cpu_abi(self):
"""Returns the product cpu abi of the device (e.g. 'armeabi-v7a').
@@ -2435,6 +2800,11 @@ class DeviceUtils(object):
return self.GetProp('ro.product.cpu.abi', cache=True)
@property
+ def product_cpu_abis(self):
+ """Returns all product cpu abi of the device."""
+ return self.GetProp('ro.product.cpu.abilist', cache=True).split(',')
+
+ @property
def product_model(self):
"""Returns the name of the product model (e.g. 'Nexus 7')."""
return self.GetProp('ro.product.model', cache=True)
@@ -2459,13 +2829,10 @@ class DeviceUtils(object):
# Change the token every time to ensure that it will match only the
# previously dumped cache.
token = str(uuid.uuid1())
- cmd = (
- 'c=/data/local/tmp/cache_token;'
- 'echo $EXTERNAL_STORAGE;'
- 'cat $c 2>/dev/null||echo;'
- 'echo "%s">$c &&' % token +
- 'getprop'
- )
+ cmd = ('c=/data/local/tmp/cache_token;'
+ 'echo $EXTERNAL_STORAGE;'
+ 'cat $c 2>/dev/null||echo;'
+ 'echo "%s">$c &&' % token + 'getprop')
output = self.RunShellCommand(
cmd, shell=True, check_return=True, large_output=True)
# Error-checking for this existing is done in GetExternalStoragePath().
@@ -2480,6 +2847,39 @@ class DeviceUtils(object):
self._cache['token'] = token
@decorators.WithTimeoutAndRetriesFromInstance()
+ def GetTracingPath(self, timeout=None, retries=None):
+ """Gets tracing path from the device.
+
+ Args:
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ /sys/kernel/debug/tracing for device with debugfs mount support;
+ /sys/kernel/tracing for device with tracefs support;
+ /sys/kernel/debug/tracing if support can't be determined.
+
+ Raises:
+ CommandTimeoutError on timeout.
+ """
+ tracing_path = self._cache['tracing_path']
+ if tracing_path:
+ return tracing_path
+ with self._cache_lock:
+ tracing_path = '/sys/kernel/debug/tracing'
+ try:
+ lines = self.RunShellCommand(['mount'],
+ check_return=True,
+ timeout=timeout,
+ retries=retries)
+ if not any('debugfs' in line for line in lines):
+ tracing_path = '/sys/kernel/tracing'
+ except device_errors.AdbCommandFailedError:
+ pass
+ self._cache['tracing_path'] = tracing_path
+ return tracing_path
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def GetProp(self, property_name, cache=False, timeout=None, retries=None):
"""Gets a property from the device.
@@ -2496,8 +2896,9 @@ class DeviceUtils(object):
Raises:
CommandTimeoutError on timeout.
"""
- assert isinstance(property_name, basestring), (
- "property_name is not a string: %r" % property_name)
+ assert isinstance(
+ property_name,
+ basestring), ("property_name is not a string: %r" % property_name)
if cache:
# It takes ~120ms to query a single property, and ~130ms to query all
@@ -2506,15 +2907,21 @@ class DeviceUtils(object):
else:
# timeout and retries are handled down at run shell, because we don't
# want to apply them in the other branch when reading from the cache
- value = self.RunShellCommand(
- ['getprop', property_name], single_line=True, check_return=True,
- timeout=timeout, retries=retries)
+ value = self.RunShellCommand(['getprop', property_name],
+ single_line=True,
+ check_return=True,
+ timeout=timeout,
+ retries=retries)
self._cache['getprop'][property_name] = value
# Non-existent properties are treated as empty strings by getprop.
return self._cache['getprop'].get(property_name, '')
@decorators.WithTimeoutAndRetriesFromInstance()
- def SetProp(self, property_name, value, check=False, timeout=None,
+ def SetProp(self,
+ property_name,
+ value,
+ check=False,
+ timeout=None,
retries=None):
"""Sets a property on the device.
@@ -2533,20 +2940,21 @@ class DeviceUtils(object):
set on the device (e.g. because it is not rooted).
CommandTimeoutError on timeout.
"""
- assert isinstance(property_name, basestring), (
- "property_name is not a string: %r" % property_name)
+ assert isinstance(
+ property_name,
+ basestring), ("property_name is not a string: %r" % property_name)
assert isinstance(value, basestring), "value is not a string: %r" % value
self.RunShellCommand(['setprop', property_name, value], check_return=True)
prop_cache = self._cache['getprop']
if property_name in prop_cache:
del prop_cache[property_name]
- # TODO(perezju) remove the option and make the check mandatory, but using a
- # single shell script to both set- and getprop.
+ # TODO(crbug.com/1029772) remove the option and make the check mandatory,
+ # but using a single shell script to both set- and getprop.
if check and value != self.GetProp(property_name, cache=False):
raise device_errors.CommandFailedError(
- 'Unable to set property %r on the device to %r'
- % (property_name, value), str(self))
+ 'Unable to set property %r on the device to %r' % (property_name,
+ value), str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
def GetABI(self, timeout=None, retries=None):
@@ -2565,6 +2973,12 @@ class DeviceUtils(object):
"""
return self.GetProp('ro.product.cpu.abi', cache=True)
+ @decorators.WithTimeoutAndRetriesFromInstance()
+ def GetFeatures(self, timeout=None, retries=None):
+ """Returns the features supported on the device."""
+ lines = self.RunShellCommand(['pm', 'list', 'features'], check_return=True)
+ return [f[8:] for f in lines if f.startswith('feature:')]
+
def _GetPsOutput(self, pattern):
"""Runs |ps| command on the device and returns its output,
@@ -2677,8 +3091,11 @@ class DeviceUtils(object):
return procs_pids
@decorators.WithTimeoutAndRetriesFromInstance()
- def GetApplicationPids(self, process_name, at_most_one=False,
- timeout=None, retries=None):
+ def GetApplicationPids(self,
+ process_name,
+ at_most_one=False,
+ timeout=None,
+ retries=None):
"""Returns the PID or PIDs of a given process name.
Note that the |process_name|, often the package name, must match exactly.
@@ -2700,13 +3117,15 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- pids = [p.pid for p in self.ListProcesses(process_name)
- if p.name == process_name]
+ pids = [
+ p.pid for p in self.ListProcesses(process_name)
+ if p.name == process_name
+ ]
if at_most_one:
if len(pids) > 1:
raise device_errors.CommandFailedError(
- 'Expected a single PID for %r but found: %r.' % (
- process_name, pids),
+ 'Expected a single PID for %r but found: %r.' % (process_name,
+ pids),
device_serial=str(self))
return pids[0] if pids else None
else:
@@ -2728,8 +3147,9 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- output = self.RunShellCommand(
- ['getenforce'], check_return=True, single_line=True).lower()
+ output = self.RunShellCommand(['getenforce'],
+ check_return=True,
+ single_line=True).lower()
if output not in _SELINUX_MODE:
raise device_errors.CommandFailedError(
'Unexpected getenforce output: %s' % output)
@@ -2750,9 +3170,9 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- self.RunShellCommand(
- ['setenforce', '1' if int(enabled) else '0'], as_root=True,
- check_return=True)
+ self.RunShellCommand(['setenforce', '1' if int(enabled) else '0'],
+ as_root=True,
+ check_return=True)
@decorators.WithTimeoutAndRetriesFromInstance()
def GetWebViewUpdateServiceDump(self, timeout=None, retries=None):
@@ -2780,8 +3200,8 @@ class DeviceUtils(object):
if self.build_version_sdk < version_codes.OREO:
return result
- output = self.RunShellCommand(
- ['dumpsys', 'webviewupdate'], check_return=True)
+ output = self.RunShellCommand(['dumpsys', 'webviewupdate'],
+ check_return=True)
webview_packages = {}
for line in output:
match = re.search(_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE, line)
@@ -2832,8 +3252,7 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- installed = self.GetApplicationPaths(package_name)
- if not installed:
+ if not self.IsApplicationInstalled(package_name):
raise device_errors.CommandFailedError(
'%s is not installed' % package_name, str(self))
output = self.RunShellCommand(
@@ -2861,9 +3280,20 @@ class DeviceUtils(object):
'%s does not declare a WebView native library, so it cannot '
'be a WebView provider' % package_name, str(self))
if re.search(r'SDK version too low', reason):
- raise device_errors.CommandFailedError(
- '%s needs a higher targetSdkVersion (must be >= %d)' %
- (package_name, self.build_version_sdk), str(self))
+ app_target_sdk_version = self.GetApplicationTargetSdk(package_name)
+ is_preview_sdk = self.GetProp('ro.build.version.preview_sdk') == '1'
+ if is_preview_sdk:
+ codename = self.GetProp('ro.build.version.codename')
+ raise device_errors.CommandFailedError(
+ '%s targets a finalized SDK (%r), but valid WebView providers '
+ 'must target a pre-finalized SDK (%r) on this device' %
+ (package_name, app_target_sdk_version, codename), str(self))
+ else:
+ raise device_errors.CommandFailedError(
+ '%s has targetSdkVersion %r, but valid WebView providers must '
+ 'target >= %r on this device' %
+ (package_name, app_target_sdk_version, self.build_version_sdk),
+ str(self))
if re.search(r'Version code too low', reason):
raise device_errors.CommandFailedError(
'%s needs a higher versionCode (must be >= %d)' %
@@ -2920,8 +3350,10 @@ class DeviceUtils(object):
# redundant-packages is the opposite of fallback logic
enable_string = 'disable' if enabled else 'enable'
output = self.RunShellCommand(
- ['cmd', 'webviewupdate', '%s-redundant-packages' % enable_string],
- single_line=True, check_return=True)
+ ['cmd', 'webviewupdate',
+ '%s-redundant-packages' % enable_string],
+ single_line=True,
+ check_return=True)
if output == 'Success':
logging.info('WebView Fallback Logic is %s',
'enabled' if enabled else 'disabled')
@@ -2949,8 +3381,8 @@ class DeviceUtils(object):
DeviceUnreachableError on missing device.
"""
if not host_path:
- host_path = os.path.abspath('screenshot-%s-%s.png' % (
- self.serial, _GetTimeStamp()))
+ host_path = os.path.abspath(
+ 'screenshot-%s-%s.png' % (self.serial, _GetTimeStamp()))
with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
check_return=True)
@@ -2964,13 +3396,15 @@ class DeviceUtils(object):
Returns: Name of the crashed package if a dialog is focused,
None otherwise.
"""
+
def _FindFocusedWindow():
match = None
# TODO(jbudorick): Try to grep the output on the device instead of using
# large_output if/when DeviceUtils exposes a public interface for piped
# shell command handling.
for line in self.RunShellCommand(['dumpsys', 'window', 'windows'],
- check_return=True, large_output=True):
+ check_return=True,
+ large_output=True):
match = re.match(_CURRENT_FOCUS_CRASH_RE, line)
if match:
break
@@ -3017,13 +3451,15 @@ class DeviceUtils(object):
'package_apk_checksums': {},
# Map of property_name -> value
'getprop': {},
- # Map of device_path -> [ignore_other_files, map of path->checksum]
+ # Map of device path -> checksum]
'device_path_checksums': {},
# Location of sdcard ($EXTERNAL_STORAGE).
'external_storage': None,
# Token used to detect when LoadCacheData is stale.
'token': None,
'prev_token': None,
+ # Path for tracing.
+ 'tracing_path': None,
}
@decorators.WithTimeoutAndRetriesFromInstance()
@@ -3042,7 +3478,11 @@ class DeviceUtils(object):
Returns:
Whether the cache was loaded.
"""
- obj = json.loads(data)
+ try:
+ obj = json.loads(data)
+ except ValueError:
+ logger.error('Unable to parse cache file. Not using it.')
+ return False
self._EnsureCacheInitialized()
given_token = obj.get('token')
if not given_token or self._cache['prev_token'] != given_token:
@@ -3106,17 +3546,25 @@ class DeviceUtils(object):
return parallelizer.SyncParallelizer(devices)
@classmethod
- def HealthyDevices(cls, blacklist=None, device_arg='default', retries=1,
- enable_usb_resets=False, abis=None, **kwargs):
+ def HealthyDevices(cls,
+ denylist=None,
+ device_arg='default',
+ retries=1,
+ enable_usb_resets=False,
+ abis=None,
+ # TODO(crbug.com/1097306): Remove this once clients have
+ # stopped passing it.
+ blacklist=None,
+ **kwargs):
"""Returns a list of DeviceUtils instances.
- Returns a list of DeviceUtils instances that are attached, not blacklisted,
+ Returns a list of DeviceUtils instances that are attached, not denylisted,
and optionally filtered by --device flags or ANDROID_SERIAL environment
variable.
Args:
- blacklist: A DeviceBlacklist instance (optional). Device serials in this
- blacklist will never be returned, but a warning will be logged if they
+ denylist: A DeviceDenylist instance (optional). Device serials in this
+ denylist will never be returned, but a warning will be logged if they
otherwise would have been.
device_arg: The value of the --device flag. This can be:
'default' -> Same as [], but returns an empty list rather than raise a
@@ -3126,9 +3574,9 @@ class DeviceUtils(object):
attached device. Raises an exception if multiple devices are
attached.
'serial' -> Returns an instance for the given serial, if not
- blacklisted.
+ denylisted.
['A', 'B', ...] -> Returns instances for the subset that is not
- blacklisted.
+ denylisted.
retries: Number of times to restart adb server and query it again if no
devices are found on the previous attempts, with exponential backoffs
up to 60s between each retry.
@@ -3143,7 +3591,7 @@ class DeviceUtils(object):
A list of DeviceUtils instances.
Raises:
- NoDevicesError: Raised when no non-blacklisted devices exist and
+ NoDevicesError: Raised when no non-denylisted devices exist and
device_arg is passed.
MultipleDevicesError: Raise when multiple devices exist, but |device_arg|
is None.
@@ -3157,18 +3605,22 @@ class DeviceUtils(object):
if not (isinstance(device_arg, tuple) or isinstance(device_arg, list)):
select_multiple = False
if device_arg:
- device_arg = (device_arg,)
+ device_arg = (device_arg, )
+
+ # TODO(crbug.com/1097306): Remove this once clients have switched.
+ if blacklist and not denylist:
+ denylist = blacklist
- blacklisted_devices = blacklist.Read() if blacklist else []
+ denylisted_devices = denylist.Read() if denylist else []
# adb looks for ANDROID_SERIAL, so support it as well.
android_serial = os.environ.get('ANDROID_SERIAL')
if not device_arg and android_serial:
- device_arg = (android_serial,)
+ device_arg = (android_serial, )
- def blacklisted(serial):
- if serial in blacklisted_devices:
- logger.warning('Device %s is blacklisted.', serial)
+ def denylisted(serial):
+ if serial in denylisted_devices:
+ logger.warning('Device %s is denylisted.', serial)
return True
return False
@@ -3180,12 +3632,12 @@ class DeviceUtils(object):
def _get_devices():
if device_arg:
- devices = [cls(x, **kwargs) for x in device_arg if not blacklisted(x)]
+ devices = [cls(x, **kwargs) for x in device_arg if not denylisted(x)]
else:
devices = []
for adb in adb_wrapper.AdbWrapper.Devices():
serial = adb.GetDeviceSerial()
- if not blacklisted(serial):
+ if not denylisted(serial):
device = cls(_CreateAdbWrapper(adb), **kwargs)
if supports_abi(device.GetABI(), serial):
devices.append(device)
@@ -3208,7 +3660,7 @@ class DeviceUtils(object):
else:
reset_usb.reset_all_android_devices()
- for attempt in xrange(retries+1):
+ for attempt in xrange(retries + 1):
try:
return _get_devices()
except device_errors.NoDevicesError:
@@ -3233,30 +3685,26 @@ class DeviceUtils(object):
logger.info('Restarting adbd on device.')
with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
self.WriteFile(script.name, _RESTART_ADBD_SCRIPT)
- self.RunShellCommand(
- ['source', script.name], check_return=True, as_root=True)
+ self.RunShellCommand(['source', script.name],
+ check_return=True,
+ as_root=True)
self.adb.WaitForDevice()
@decorators.WithTimeoutAndRetriesFromInstance()
def GrantPermissions(self, package, permissions, timeout=None, retries=None):
- # Permissions only need to be set on M and above because of the changes to
- # the permission model.
- if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW:
+ if not permissions:
return
- permissions = set(
- p for p in permissions if not _PERMISSIONS_BLACKLIST_RE.match(p))
+ permissions = set(p for p in permissions
+ if not _PERMISSIONS_DENYLIST_RE.match(p))
if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions
and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions):
permissions.add('android.permission.READ_EXTERNAL_STORAGE')
script = ';'.join([
- 'p={package}',
- 'for q in {permissions}',
- 'do pm grant "$p" "$q"',
- 'echo "{sep}$q{sep}$?{sep}"',
- 'done'
+ 'p={package}', 'for q in {permissions}', 'do pm grant "$p" "$q"',
+ 'echo "{sep}$q{sep}$?{sep}"', 'done'
]).format(
package=cmd_helper.SingleQuote(package),
permissions=' '.join(
@@ -3265,17 +3713,21 @@ class DeviceUtils(object):
logger.info('Setting permissions for %s.', package)
res = self.RunShellCommand(
- script, shell=True, raw_output=True, large_output=True,
+ script,
+ shell=True,
+ raw_output=True,
+ large_output=True,
check_return=True)
res = res.split(_SHELL_OUTPUT_SEPARATOR)
failures = [
- (permission, output.strip())
- for permission, status, output in zip(res[1::3], res[2::3], res[0::3])
- if int(status)]
+ (permission, output.strip())
+ for permission, status, output in zip(res[1::3], res[2::3], res[0::3])
+ if int(status)
+ ]
if failures:
logger.warning(
- 'Failed to grant some permissions. Blacklist may need to be updated?')
+ 'Failed to grant some permissions. Denylist may need to be updated?')
for permission, output in failures:
# Try to grab the relevant error message from the output.
m = _PERMISSIONS_EXCEPTION_RE.search(output)
@@ -3316,8 +3768,8 @@ class DeviceUtils(object):
dumpsys_out = self._RunPipedShellCommand(
'dumpsys input_method | grep %s' % input_check)
if not dumpsys_out:
- raise device_errors.CommandFailedError(
- 'Unable to detect screen state', str(self))
+ raise device_errors.CommandFailedError('Unable to detect screen state',
+ str(self))
return check_value in dumpsys_out[0]
@decorators.WithTimeoutAndRetriesFromInstance()
@@ -3327,6 +3779,7 @@ class DeviceUtils(object):
Args:
on: bool to decide state to switch to. True = on False = off.
"""
+
def screen_test():
return self.IsScreenOn() == on
@@ -3353,7 +3806,10 @@ class DeviceUtils(object):
self.RunShellCommand(['chown', owner_group] + paths, check_return=True)
@decorators.WithTimeoutAndRetriesFromInstance()
- def ChangeSecurityContext(self, security_context, paths, timeout=None,
+ def ChangeSecurityContext(self,
+ security_context,
+ paths,
+ timeout=None,
retries=None):
"""Changes the SELinux security context for files.
diff --git a/catapult/devil/devil/android/device_utils_devicetest.py b/catapult/devil/devil/android/device_utils_devicetest.py
index 0836f3ea..a0734c2a 100755
--- a/catapult/devil/devil/android/device_utils_devicetest.py
+++ b/catapult/devil/devil/android/device_utils_devicetest.py
@@ -2,7 +2,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for the contents of device_utils.py (mostly DeviceUtils).
The test will invoke real devices
@@ -16,7 +15,11 @@ import unittest
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', )))
+ os.path.abspath(os.path.join(
+ os.path.dirname(__file__),
+ '..',
+ '..',
+ )))
from devil.android import device_test_case
from devil.android import device_utils
@@ -32,7 +35,6 @@ _SUB_DIR2 = "sub2"
class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
-
def setUp(self):
super(DeviceUtilsPushDeleteFilesTest, self).setUp()
self.adb = adb_wrapper.AdbWrapper(self.serial)
@@ -88,8 +90,9 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
device_file_path = "%s/%s" % (_DEVICE_DIR, file_name)
self.adb.Push(host_file_path, device_file_path)
self.device.PushChangedFiles([(host_file_path, device_file_path)])
- result = self.device.RunShellCommand(
- ['cat', device_file_path], check_return=True, single_line=True)
+ result = self.device.RunShellCommand(['cat', device_file_path],
+ check_return=True,
+ single_line=True)
self.assertEqual(_OLD_CONTENTS, result)
cmd_helper.RunCmd(['rm', host_file_path])
@@ -103,8 +106,9 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
with open(host_file_path, 'w') as f:
f.write(_NEW_CONTENTS)
self.device.PushChangedFiles([(host_file_path, device_file_path)])
- result = self.device.RunShellCommand(
- ['cat', device_file_path], check_return=True, single_line=True)
+ result = self.device.RunShellCommand(['cat', device_file_path],
+ check_return=True,
+ single_line=True)
self.assertEqual(_NEW_CONTENTS, result)
cmd_helper.RunCmd(['rm', host_file_path])
@@ -144,9 +148,10 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
cmd_helper.RunCmd(['rm', host_file_path2])
self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
- delete_device_stale=True)
- result = self.device.RunShellCommand(
- ['cat', device_file_path1], check_return=True, single_line=True)
+ delete_device_stale=True)
+ result = self.device.RunShellCommand(['cat', device_file_path1],
+ check_return=True,
+ single_line=True)
self.assertEqual(_NEW_CONTENTS, result)
filenames = self.device.ListDirectory(_DEVICE_DIR)
@@ -174,8 +179,8 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
device_file_path1 = "%s/%s" % (_DEVICE_DIR, file_name1)
device_file_path2 = "%s/%s" % (_DEVICE_DIR, file_name2)
device_file_path3 = "%s/%s/%s" % (_DEVICE_DIR, _SUB_DIR1, file_name3)
- device_file_path4 = "%s/%s/%s/%s" % (_DEVICE_DIR, _SUB_DIR,
- _SUB_DIR2, file_name4)
+ device_file_path4 = "%s/%s/%s/%s" % (_DEVICE_DIR, _SUB_DIR, _SUB_DIR2,
+ file_name4)
self.adb.Push(host_file_path1, device_file_path1)
self.adb.Push(host_file_path2, device_file_path2)
@@ -188,9 +193,10 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
cmd_helper.RunCmd(['rm', host_file_path4])
self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
- delete_device_stale=True)
- result = self.device.RunShellCommand(
- ['cat', device_file_path1], check_return=True, single_line=True)
+ delete_device_stale=True)
+ result = self.device.RunShellCommand(['cat', device_file_path1],
+ check_return=True,
+ single_line=True)
self.assertEqual(_NEW_CONTENTS, result)
filenames = self.device.ListDirectory(_DEVICE_DIR)
@@ -199,8 +205,9 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
self.assertIn(_SUB_DIR, filenames)
self.assertEqual(3, len(filenames))
- result = self.device.RunShellCommand(
- ['cat', device_file_path3], check_return=True, single_line=True)
+ result = self.device.RunShellCommand(['cat', device_file_path3],
+ check_return=True,
+ single_line=True)
self.assertEqual(_OLD_CONTENTS, result)
filenames = self.device.ListDirectory(
@@ -223,7 +230,7 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
# Push all our created files/directories and verify they're on the device.
self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
- delete_device_stale=True)
+ delete_device_stale=True)
top_level_dirs = self.device.ListDirectory(_DEVICE_DIR)
self.assertIn(_SUB_DIR1, top_level_dirs)
self.assertIn(_SUB_DIR, top_level_dirs)
@@ -233,7 +240,7 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
# Remove one of the directories on the host and push again.
cmd_helper.RunCmd(['rm', '-rf', host_sub_dir2])
self.device.PushChangedFiles([(host_tmp_dir, _DEVICE_DIR)],
- delete_device_stale=True)
+ delete_device_stale=True)
# Verify that the directory we removed is no longer on the device, but the
# other directories still are.
@@ -266,7 +273,6 @@ class DeviceUtilsPushDeleteFilesTest(device_test_case.DeviceTestCase):
class PsOutputCompatibilityTests(device_test_case.DeviceTestCase):
-
def setUp(self):
super(PsOutputCompatibilityTests, self).setUp()
self.adb = adb_wrapper.AdbWrapper(self.serial)
@@ -283,8 +289,8 @@ class PsOutputCompatibilityTests(device_test_case.DeviceTestCase):
column = column.upper()
self.assertEqual(
header[idx], column,
- 'Expected column %s at index %d but found %s\nsource: %r' % (
- column, idx, header[idx], lines[0]))
+ 'Expected column %s at index %d but found %s\nsource: %r' %
+ (column, idx, header[idx], lines[0]))
# Check pid and ppid are numeric values.
for line in lines[1:]:
@@ -293,8 +299,8 @@ class PsOutputCompatibilityTests(device_test_case.DeviceTestCase):
for key in ('pid', 'ppid'):
self.assertTrue(
row[key].isdigit(),
- 'Expected numeric %s value but found %r\nsource: %r' % (
- key, row[key], line))
+ 'Expected numeric %s value but found %r\nsource: %r' %
+ (key, row[key], line))
if __name__ == '__main__':
diff --git a/catapult/devil/devil/android/device_utils_test.py b/catapult/devil/devil/android/device_utils_test.py
index 5799c7b8..38e64a97 100755
--- a/catapult/devil/devil/android/device_utils_test.py
+++ b/catapult/devil/devil/android/device_utils_test.py
@@ -2,7 +2,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for the contents of device_utils.py (mostly DeviceUtils).
"""
@@ -10,10 +9,12 @@ Unit tests for the contents of device_utils.py (mostly DeviceUtils).
# pylint: disable=protected-access
# pylint: disable=unused-argument
+import collections
import contextlib
import json
import logging
import os
+import posixpath
import stat
import sys
import unittest
@@ -30,9 +31,17 @@ from devil.android.sdk import version_codes
from devil.utils import cmd_helper
from devil.utils import mock_calls
+with devil_env.SysPath(
+ os.path.join(devil_env.CATAPULT_ROOT_PATH, 'common', 'py_utils')):
+ from py_utils import tempfile_ext
+
with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
+TEST_APK_PATH = '/fake/test/app.apk'
+TEST_PACKAGE = 'test.package'
+
+
def Process(name, pid, ppid='1'):
return device_utils.ProcessInfo(name=name, pid=pid, ppid=ppid)
@@ -52,13 +61,24 @@ class AnyStringWith(object):
return '<AnyStringWith: %s>' % self._value
-class _MockApkHelper(object):
+class _FakeContextManager(object):
+ def __init__(self, obj):
+ self._obj = obj
+
+ def __enter__(self):
+ return self._obj
+
+ def __exit__(self, type_, value, traceback):
+ pass
- def __init__(self, path, package_name, perms=None):
+
+class _MockApkHelper(object):
+ def __init__(self, path, package_name, perms=None, splits=None):
self.path = path
self.is_bundle = path.endswith('_bundle')
self.package_name = package_name
self.perms = perms
+ self.splits = splits if splits else []
self.abis = [abis.ARM]
def GetPackageName(self):
@@ -70,13 +90,24 @@ class _MockApkHelper(object):
def GetAbis(self):
return self.abis
+ def GetApkPaths(self,
+ device,
+ modules=None,
+ allow_cached_props=False,
+ additional_locales=None):
+ return _FakeContextManager([self.path] + self.splits)
+
+ #override
+ @staticmethod
+ def SupportsSplits():
+ return True
+
class _MockMultipleDevicesError(Exception):
pass
class DeviceUtilsInitTest(unittest.TestCase):
-
def testInitWithStr(self):
serial_as_str = str('0123456789abcdef')
d = device_utils.DeviceUtils('0123456789abcdef')
@@ -101,17 +132,13 @@ class DeviceUtilsInitTest(unittest.TestCase):
class DeviceUtilsGetAVDsTest(mock_calls.TestCase):
-
def testGetAVDs(self):
- mocked_attrs = {
- 'android_sdk': '/my/sdk/path'
- }
+ mocked_attrs = {'android_sdk': '/my/sdk/path'}
with mock.patch('devil.devil_env._Environment.LocalPath',
mock.Mock(side_effect=lambda a: mocked_attrs[a])):
with self.assertCall(
mock.call.devil.utils.cmd_helper.GetCmdOutput(
- [mock.ANY, 'list', 'avd']),
- 'Available Android Virtual Devices:\n'
+ [mock.ANY, 'list', 'avd']), 'Available Android Virtual Devices:\n'
' Name: my_android5.0\n'
' Path: /some/path/to/.android/avd/my_android5.0.avd\n'
' Target: Android 5.0 (API level 21)\n'
@@ -121,26 +148,21 @@ class DeviceUtilsGetAVDsTest(mock_calls.TestCase):
class DeviceUtilsRestartServerTest(mock_calls.TestCase):
-
@mock.patch('time.sleep', mock.Mock())
def testRestartServer_succeeds(self):
with self.assertCalls(
mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.KillServer(),
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutput(
- ['pgrep', 'adb']),
- (1, '')),
+ ['pgrep', 'adb']), (1, '')),
mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.StartServer(),
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutput(
['pgrep', 'adb']),
- (1, '')),
- (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutput(
- ['pgrep', 'adb']),
- (0, '123\n'))):
+ (1, '')), (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutput(
+ ['pgrep', 'adb']), (0, '123\n'))):
device_utils.RestartServer()
class MockTempFile(object):
-
def __init__(self, name='/tmp/some/file'):
self.file = mock.MagicMock(spec=file)
self.file.name = name
@@ -160,7 +182,6 @@ class MockTempFile(object):
class MockLogger(mock.Mock):
def __init__(self, *args, **kwargs):
super(MockLogger, self).__init__(*args, **kwargs)
- # TODO(perezju): Consider adding traps for error, info, etc.
self.warnings = []
def warning(self, message, *args):
@@ -173,7 +194,6 @@ def PatchLogger():
class _PatchedFunction(object):
-
def __init__(self, patched=None, mocked=None):
self.patched = patched
self.mocked = mocked
@@ -188,7 +208,6 @@ def _AdbWrapperMock(test_serial, is_ready=True):
class DeviceUtilsTest(mock_calls.TestCase):
-
def setUp(self):
self.adb = _AdbWrapperMock('0123456789abcdef')
self.device = device_utils.DeviceUtils(
@@ -198,19 +217,21 @@ class DeviceUtilsTest(mock_calls.TestCase):
def AdbCommandError(self, args=None, output=None, status=None, msg=None):
if args is None:
args = ['[unspecified]']
- return mock.Mock(side_effect=device_errors.AdbCommandFailedError(
- args, output, status, msg, str(self.device)))
+ return mock.Mock(
+ side_effect=device_errors.AdbCommandFailedError(args, output, status,
+ msg, str(self.device)))
def CommandError(self, msg=None):
if msg is None:
msg = 'Command failed'
- return mock.Mock(side_effect=device_errors.CommandFailedError(
- msg, str(self.device)))
+ return mock.Mock(
+ side_effect=device_errors.CommandFailedError(msg, str(self.device)))
def ShellError(self, output=None, status=1):
def action(cmd, *args, **kwargs):
- raise device_errors.AdbShellCommandFailedError(
- cmd, output, status, str(self.device))
+ raise device_errors.AdbShellCommandFailedError(cmd, output, status,
+ str(self.device))
+
if output is None:
output = 'Permission denied\n'
return action
@@ -218,19 +239,20 @@ class DeviceUtilsTest(mock_calls.TestCase):
def TimeoutError(self, msg=None):
if msg is None:
msg = 'Operation timed out'
- return mock.Mock(side_effect=device_errors.CommandTimeoutError(
- msg, str(self.device)))
+ return mock.Mock(
+ side_effect=device_errors.CommandTimeoutError(msg, str(self.device)))
def EnsureCacheInitialized(self, props=None, sdcard='/sdcard'):
props = props or []
ret = [sdcard, 'TOKEN'] + props
return (self.call.device.RunShellCommand(
AnyStringWith('getprop'),
- shell=True, check_return=True, large_output=True), ret)
+ shell=True,
+ check_return=True,
+ large_output=True), ret)
class DeviceUtilsEqTest(DeviceUtilsTest):
-
def testEq_equal_deviceUtils(self):
other = device_utils.DeviceUtils(_AdbWrapperMock('0123456789abcdef'))
self.assertTrue(self.device == other)
@@ -260,7 +282,6 @@ class DeviceUtilsEqTest(DeviceUtilsTest):
class DeviceUtilsLtTest(DeviceUtilsTest):
-
def testLt_lessThan(self):
other = device_utils.DeviceUtils(_AdbWrapperMock('ffffffffffffffff'))
self.assertTrue(self.device < other)
@@ -289,15 +310,13 @@ class DeviceUtilsLtTest(DeviceUtilsTest):
class DeviceUtilsStrTest(DeviceUtilsTest):
-
def testStr_returnsSerial(self):
- with self.assertCalls(
- (self.call.adb.GetDeviceSerial(), '0123456789abcdef')):
+ with self.assertCalls((self.call.adb.GetDeviceSerial(),
+ '0123456789abcdef')):
self.assertEqual('0123456789abcdef', str(self.device))
class DeviceUtilsIsOnlineTest(DeviceUtilsTest):
-
def testIsOnline_true(self):
with self.assertCall(self.call.adb.GetState(), 'device'):
self.assertTrue(self.device.IsOnline())
@@ -312,85 +331,136 @@ class DeviceUtilsIsOnlineTest(DeviceUtilsTest):
class DeviceUtilsHasRootTest(DeviceUtilsTest):
-
def testHasRoot_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='notasailfish')), (
- self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n')):
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.PIE)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='notasailfish')), (self.assertCall(
+ self.call.adb.Shell('ls /root'), 'foo\n')):
self.assertTrue(self.device.HasRoot())
def testhasRootSpecial_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='sailfish')), (
- self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.PIE)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='sailfish')), (self.assertCall(
+ self.call.adb.Shell('getprop service.adb.root'),
'1\n')):
self.assertTrue(self.device.HasRoot())
def testhasRootSpecialAosp_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='aosp_sailfish')), (
- self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.PIE)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='aosp_sailfish')), (self.assertCall(
+ self.call.adb.Shell('getprop service.adb.root'),
'1\n')):
self.assertTrue(self.device.HasRoot())
def testhasRootEngBuild_true(self):
- with self.patch_call(self.call.device.build_type,
- return_value='eng'):
+ with self.patch_call(self.call.device.build_type, return_value='eng'):
self.assertTrue(self.device.HasRoot())
def testHasRoot_false(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='notasailfish')), (
- self.assertCall(self.call.adb.Shell('ls /root'),
- self.ShellError())):
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.PIE)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='notasailfish')), (self.assertCall(
+ self.call.adb.Shell('ls /root'), self.ShellError())):
self.assertFalse(self.device.HasRoot())
def testHasRootSpecial_false(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='sailfish')), (
- self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
- '\n')):
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.PIE)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='sailfish')), (self.assertCall(
+ self.call.adb.Shell('getprop service.adb.root'), '\n')):
self.assertFalse(self.device.HasRoot())
def testHasRootSpecialAosp_false(self):
- with self.patch_call(self.call.device.build_type,
- return_value='userdebug'), (
- self.patch_call(self.call.device.product_name,
- return_value='aosp_sailfish')), (
- self.assertCall(self.call.adb.Shell('getprop service.adb.root'),
- '\n')):
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.PIE)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='aosp_sailfish')), (self.assertCall(
+ self.call.adb.Shell('getprop service.adb.root'), '\n')):
self.assertFalse(self.device.HasRoot())
-class DeviceUtilsEnableRootTest(DeviceUtilsTest):
+ def testHasRootSystemRootImage(self):
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='true')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.PIE)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='notasailfish')), (self.assertCall(
+ self.call.adb.Shell('getprop service.adb.root'),
+ '1\n')):
+ self.assertTrue(self.device.HasRoot())
+ def testHasRoot10(self):
+ # All devices on Android 10 / Q and above should use the system-as-root
+ # partition layout, though they may not have the property set.
+ with self.patch_call(
+ self.call.device.build_type,
+ return_value='userdebug'), (self.patch_call(
+ self.call.device.build_system_root_image,
+ return_value='')), (self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.Q)), (self.patch_call(
+ self.call.device.product_name,
+ return_value='notasailfish')), (self.assertCall(
+ self.call.adb.Shell('getprop service.adb.root'),
+ '1\n')):
+ self.assertTrue(self.device.HasRoot())
+
+
+class DeviceUtilsEnableRootTest(DeviceUtilsTest):
def testEnableRoot_succeeds(self):
- with self.assertCalls(
- self.call.adb.Root(),
- self.call.adb.WaitForDevice(),
- (self.call.device.HasRoot(), True)):
+ with self.assertCalls(self.call.adb.Root(), self.call.adb.WaitForDevice(),
+ (self.call.device.HasRoot(), True)):
self.device.EnableRoot()
def testEnableRoot_userBuild(self):
- with self.assertCalls(
- (self.call.adb.Root(), self.AdbCommandError()),
- (self.call.device.IsUserBuild(), True)):
+ with self.assertCalls((self.call.adb.Root(), self.AdbCommandError()),
+ (self.call.device.IsUserBuild(), True)):
with self.assertRaises(device_errors.CommandFailedError):
self.device.EnableRoot()
def testEnableRoot_rootFails(self):
- with self.assertCalls(
- (self.call.adb.Root(), self.AdbCommandError()),
- (self.call.device.IsUserBuild(), False)):
+ with self.assertCalls((self.call.adb.Root(), self.AdbCommandError()),
+ (self.call.device.IsUserBuild(), False)):
with self.assertRaises(device_errors.AdbCommandFailedError):
self.device.EnableRoot()
@@ -399,14 +469,12 @@ class DeviceUtilsEnableRootTest(DeviceUtilsTest):
(self.call.adb.Root(),
self.AdbCommandError(
output='timeout expired while waiting for device')),
- (self.call.device.IsUserBuild(), False),
- self.call.adb.WaitForDevice(),
+ (self.call.device.IsUserBuild(), False), self.call.adb.WaitForDevice(),
(self.call.device.HasRoot(), True)):
self.device.EnableRoot()
class DeviceUtilsIsUserBuildTest(DeviceUtilsTest):
-
def testIsUserBuild_yes(self):
with self.assertCall(
self.call.device.GetProp('ro.build.type', cache=True), 'user'):
@@ -419,7 +487,6 @@ class DeviceUtilsIsUserBuildTest(DeviceUtilsTest):
class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsTest):
-
def testGetExternalStoragePath_succeeds(self):
with self.assertCalls(
self.EnsureCacheInitialized(sdcard='/fake/storage/path')):
@@ -427,19 +494,64 @@ class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsTest):
self.device.GetExternalStoragePath())
def testGetExternalStoragePath_fails(self):
+ with self.assertCalls(self.EnsureCacheInitialized(sdcard='')):
+ with self.assertRaises(device_errors.CommandFailedError):
+ self.device.GetExternalStoragePath()
+
+
+class DeviceUtilsGetAppWritablePathTest(DeviceUtilsTest):
+ def testGetAppWritablePath_succeeds_sdk_pre_q(self):
with self.assertCalls(
+ (self.call.device.GetProp('ro.build.version.sdk', cache=True), '28'),
+ self.EnsureCacheInitialized(sdcard='/fake/storage/path')):
+ self.assertEquals('/fake/storage/path',
+ self.device.GetAppWritablePath())
+
+ def testGetAppWritablePath_succeeds_sdk_q(self):
+ with self.assertCalls(
+ (self.call.device.GetProp('ro.build.version.sdk', cache=True), '29'),
+ self.EnsureCacheInitialized(sdcard='/fake/storage/path')):
+ self.assertEquals('/fake/storage/path/Download',
+ self.device.GetAppWritablePath())
+
+ def testGetAppWritablePath_fails(self):
+ with self.assertCalls(
+ (self.call.device.GetProp('ro.build.version.sdk', cache=True), '29'),
self.EnsureCacheInitialized(sdcard='')):
with self.assertRaises(device_errors.CommandFailedError):
- self.device.GetExternalStoragePath()
+ self.device.GetAppWritablePath()
+
+
+class DeviceUtilsIsApplicationInstalledTest(DeviceUtilsTest):
+ def testIsApplicationInstalled_installed(self):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ ['pm', 'list', 'packages', 'some.installed.app'], check_return=True),
+ ['package:some.installed.app'])):
+ self.assertTrue(self.device.IsApplicationInstalled('some.installed.app'))
+
+ def testIsApplicationInstalled_notInstalled(self):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ ['pm', 'list', 'packages', 'not.installed.app'], check_return=True),
+ '')):
+ self.assertFalse(self.device.IsApplicationInstalled('not.installed.app'))
+
+ def testIsApplicationInstalled_substringMatch(self):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ ['pm', 'list', 'packages', 'substring.of.package'], check_return=True),
+ [
+ 'package:first.substring.of.package',
+ 'package:second.substring.of.package',
+ ])):
+ self.assertFalse(
+ self.device.IsApplicationInstalled('substring.of.package'))
class DeviceUtilsGetApplicationPathsInternalTest(DeviceUtilsTest):
-
def testGetApplicationPathsInternal_exists(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
- (self.call.device.RunShellCommand(
- ['pm', 'path', 'android'], check_return=True),
+ (self.call.device.RunShellCommand(['pm', 'path', 'android'],
+ check_return=True),
['package:/path/to/android.apk'])):
self.assertEquals(['/path/to/android.apk'],
self.device._GetApplicationPathsInternal('android'))
@@ -447,17 +559,16 @@ class DeviceUtilsGetApplicationPathsInternalTest(DeviceUtilsTest):
def testGetApplicationPathsInternal_notExists(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
- (self.call.device.RunShellCommand(
- ['pm', 'path', 'not.installed.app'], check_return=True),
- '')):
- self.assertEquals([],
- self.device._GetApplicationPathsInternal('not.installed.app'))
+ (self.call.device.RunShellCommand(['pm', 'path', 'not.installed.app'],
+ check_return=True), '')):
+ self.assertEquals(
+ [], self.device._GetApplicationPathsInternal('not.installed.app'))
def testGetApplicationPathsInternal_garbageOutputRaises(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
- (self.call.device.RunShellCommand(
- ['pm', 'path', 'android'], check_return=True),
+ (self.call.device.RunShellCommand(['pm', 'path', 'android'],
+ check_return=True),
['garbage first line'])):
with self.assertRaises(device_errors.CommandFailedError):
self.device._GetApplicationPathsInternal('android')
@@ -465,24 +576,23 @@ class DeviceUtilsGetApplicationPathsInternalTest(DeviceUtilsTest):
def testGetApplicationPathsInternal_outputWarningsIgnored(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
- (self.call.device.RunShellCommand(
- ['pm', 'path', 'not.installed.app'], check_return=True),
+ (self.call.device.RunShellCommand(['pm', 'path', 'not.installed.app'],
+ check_return=True),
['WARNING: some warning message from pm'])):
- self.assertEquals([],
- self.device._GetApplicationPathsInternal('not.installed.app'))
+ self.assertEquals(
+ [], self.device._GetApplicationPathsInternal('not.installed.app'))
def testGetApplicationPathsInternal_fails(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
- (self.call.device.RunShellCommand(
- ['pm', 'path', 'android'], check_return=True),
+ (self.call.device.RunShellCommand(['pm', 'path', 'android'],
+ check_return=True),
self.CommandError('ERROR. Is package manager running?\n'))):
with self.assertRaises(device_errors.CommandFailedError):
self.device._GetApplicationPathsInternal('android')
class DeviceUtils_GetApplicationVersionTest(DeviceUtilsTest):
-
def test_GetApplicationVersion_exists(self):
with self.assertCalls(
(self.call.adb.Shell('dumpsys package com.android.chrome'),
@@ -511,63 +621,98 @@ class DeviceUtils_GetApplicationVersionTest(DeviceUtilsTest):
self.device.GetApplicationVersion('com.android.chrome')
-class DeviceUtils_GetPackageArchitectureTest(DeviceUtilsTest):
+class DeviceUtils_GetApplicationTargetSdkTest(DeviceUtilsTest):
+ def test_GetApplicationTargetSdk_exists(self):
+ with self.assertCalls(
+ (self.call.device.IsApplicationInstalled('com.android.chrome'), True),
+ (self.call.device._GetDumpsysOutput(['package', 'com.android.chrome'],
+ 'targetSdk='),
+ [' versionCode=413200001 minSdk=21 targetSdk=29'])):
+ self.assertEquals(
+ '29', self.device.GetApplicationTargetSdk('com.android.chrome'))
+ def test_GetApplicationTargetSdk_notExists(self):
+ with self.assertCalls(
+ (self.call.device.IsApplicationInstalled('com.android.chrome'), False)):
+ self.assertIsNone(
+ self.device.GetApplicationTargetSdk('com.android.chrome'))
+
+ def test_GetApplicationTargetSdk_fails(self):
+ with self.assertCalls(
+ (self.call.device.IsApplicationInstalled('com.android.chrome'), True),
+ (self.call.device._GetDumpsysOutput(['package', 'com.android.chrome'],
+ 'targetSdk='), [])):
+ with self.assertRaises(device_errors.CommandFailedError):
+ self.device.GetApplicationTargetSdk('com.android.chrome')
+
+ def test_GetApplicationTargetSdk_prefinalizedSdk(self):
+ with self.assertCalls(
+ (self.call.device.IsApplicationInstalled('com.android.chrome'), True),
+ (self.call.device._GetDumpsysOutput(['package', 'com.android.chrome'],
+ 'targetSdk='),
+ [' versionCode=410301483 minSdk=10000 targetSdk=10000']),
+ (self.call.device.GetProp('ro.build.version.codename',
+ cache=True), 'R')):
+ self.assertEquals(
+ 'R', self.device.GetApplicationTargetSdk('com.android.chrome'))
+
+
+class DeviceUtils_GetPackageArchitectureTest(DeviceUtilsTest):
def test_GetPackageArchitecture_exists(self):
with self.assertCall(
self.call.device._RunPipedShellCommand(
'dumpsys package com.android.chrome | grep -F primaryCpuAbi'),
[' primaryCpuAbi=armeabi-v7a']):
self.assertEquals(
- abis.ARM,
- self.device.GetPackageArchitecture('com.android.chrome'))
+ abis.ARM, self.device.GetPackageArchitecture('com.android.chrome'))
def test_GetPackageArchitecture_notExists(self):
with self.assertCall(
self.call.device._RunPipedShellCommand(
- 'dumpsys package com.android.chrome | grep -F primaryCpuAbi'),
- []):
+ 'dumpsys package com.android.chrome | grep -F primaryCpuAbi'), []):
self.assertEquals(
- None,
- self.device.GetPackageArchitecture('com.android.chrome'))
+ None, self.device.GetPackageArchitecture('com.android.chrome'))
class DeviceUtilsGetApplicationDataDirectoryTest(DeviceUtilsTest):
-
def testGetApplicationDataDirectory_exists(self):
- with self.assertCall(
- self.call.device._RunPipedShellCommand(
+ with self.assertCalls(
+ (self.call.device.IsApplicationInstalled('foo.bar.baz'), True),
+ (self.call.device._RunPipedShellCommand(
'pm dump foo.bar.baz | grep dataDir='),
- ['dataDir=/data/data/foo.bar.baz']):
- self.assertEquals(
- '/data/data/foo.bar.baz',
- self.device.GetApplicationDataDirectory('foo.bar.baz'))
+ ['dataDir=/data/data/foo.bar.baz'])):
+ self.assertEquals('/data/data/foo.bar.baz',
+ self.device.GetApplicationDataDirectory('foo.bar.baz'))
+
+ def testGetApplicationDataDirectory_notInstalled(self):
+ with self.assertCalls(
+ (self.call.device.IsApplicationInstalled('foo.bar.baz'), False)):
+ with self.assertRaises(device_errors.CommandFailedError):
+ self.device.GetApplicationDataDirectory('foo.bar.baz')
def testGetApplicationDataDirectory_notExists(self):
- with self.assertCall(
- self.call.device._RunPipedShellCommand(
- 'pm dump foo.bar.baz | grep dataDir='),
- self.ShellError()):
+ with self.assertCalls(
+ (self.call.device.IsApplicationInstalled('foo.bar.baz'), True),
+ (self.call.device._RunPipedShellCommand(
+ 'pm dump foo.bar.baz | grep dataDir='), self.ShellError())):
with self.assertRaises(device_errors.CommandFailedError):
self.device.GetApplicationDataDirectory('foo.bar.baz')
@mock.patch('time.sleep', mock.Mock())
class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
-
- def testWaitUntilFullyBooted_succeedsNoWifi(self):
+ def testWaitUntilFullyBooted_succeedsWithDefaults(self):
with self.assertCalls(
self.call.adb.WaitForDevice(),
# sd_card_ready
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- ['package:/some/fake/path']),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed', cache=False), '1')):
- self.device.WaitUntilFullyBooted(wifi=False)
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=False)
def testWaitUntilFullyBooted_succeedsWithWifi(self):
with self.assertCalls(
@@ -576,15 +721,45 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- ['package:/some/fake/path']),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed', cache=False), '1'),
# wifi_enabled
(self.call.adb.Shell('dumpsys wifi'),
'stuff\nWi-Fi is enabled\nmore stuff\n')):
- self.device.WaitUntilFullyBooted(wifi=True)
+ self.device.WaitUntilFullyBooted(wifi=True, decrypt=False)
+
+ def testWaitUntilFullyBooted_succeedsWithDecryptFDE(self):
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
+ # pm_ready
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
+ # boot_completed
+ (self.call.device.GetProp('sys.boot_completed', cache=False), '1'),
+ # decryption_completed
+ (self.call.device.GetProp('vold.decrypt', cache=False),
+ 'trigger_restart_framework')):
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=True)
+
+ def testWaitUntilFullyBooted_succeedsWithDecryptNotFDE(self):
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
+ # pm_ready
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
+ # boot_completed
+ (self.call.device.GetProp('sys.boot_completed', cache=False), '1'),
+ # decryption_completed
+ (self.call.device.GetProp('vold.decrypt', cache=False), '')):
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=True)
def testWaitUntilFullyBooted_deviceNotInitiallyAvailable(self):
with self.assertCalls(
@@ -601,12 +776,11 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- ['package:/some/fake/path']),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed', cache=False), '1')):
- self.device.WaitUntilFullyBooted(wifi=False)
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=False)
def testWaitUntilFullyBooted_deviceBrieflyOffline(self):
with self.assertCalls(
@@ -615,15 +789,14 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- ['package:/some/fake/path']),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed', cache=False),
self.AdbCommandError()),
# boot_completed
(self.call.device.GetProp('sys.boot_completed', cache=False), '1')):
- self.device.WaitUntilFullyBooted(wifi=False)
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=False)
def testWaitUntilFullyBooted_sdCardReadyFails_noPath(self):
with self.assertCalls(
@@ -631,7 +804,7 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
# sd_card_ready
(self.call.device.GetExternalStoragePath(), self.CommandError())):
with self.assertRaises(device_errors.CommandFailedError):
- self.device.WaitUntilFullyBooted(wifi=False)
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=False)
def testWaitUntilFullyBooted_sdCardReadyFails_notExists(self):
with self.assertCalls(
@@ -647,7 +820,7 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.adb.Shell('test -d /fake/storage/path'),
self.TimeoutError())):
with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=False)
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=False)
def testWaitUntilFullyBooted_devicePmFails(self):
with self.assertCalls(
@@ -656,19 +829,16 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- self.CommandError()),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), self.CommandError()),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- self.CommandError()),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), self.CommandError()),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- self.TimeoutError())):
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), self.TimeoutError())):
with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=False)
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=False)
def testWaitUntilFullyBooted_bootFails(self):
with self.assertCalls(
@@ -677,9 +847,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- ['package:/some/fake/path']),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed', cache=False), '0'),
# boot_completed
@@ -688,7 +857,7 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetProp('sys.boot_completed', cache=False),
self.TimeoutError())):
with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=False)
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=False)
def testWaitUntilFullyBooted_wifiFails(self):
with self.assertCalls(
@@ -697,9 +866,8 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
(self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
(self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- (self.call.device._GetApplicationPathsInternal('android',
- skip_cache=True),
- ['package:/some/fake/path']),
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
# boot_completed
(self.call.device.GetProp('sys.boot_completed', cache=False), '1'),
# wifi_enabled
@@ -709,331 +877,462 @@ class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
# wifi_enabled
(self.call.adb.Shell('dumpsys wifi'), self.TimeoutError())):
with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=True)
+ self.device.WaitUntilFullyBooted(wifi=True, decrypt=False)
+
+ def testWaitUntilFullyBooted_decryptFails(self):
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
+ # pm_ready
+ (self.call.device._GetApplicationPathsInternal(
+ 'android', skip_cache=True), ['package:/some/fake/path']),
+ # boot_completed
+ (self.call.device.GetProp('sys.boot_completed', cache=False), '1'),
+ # decryption_completed
+ (self.call.device.GetProp('vold.decrypt', cache=False),
+ 'trigger_restart_min_framework'),
+ # decryption_completed
+ (self.call.device.GetProp('vold.decrypt', cache=False),
+ 'trigger_restart_min_framework'),
+ # decryption_completed
+ (self.call.device.GetProp('vold.decrypt', cache=False),
+ self.TimeoutError())):
+ with self.assertRaises(device_errors.CommandTimeoutError):
+ self.device.WaitUntilFullyBooted(wifi=False, decrypt=True)
@mock.patch('time.sleep', mock.Mock())
class DeviceUtilsRebootTest(DeviceUtilsTest):
-
def testReboot_nonBlocking(self):
- with self.assertCalls(
- self.call.adb.Reboot(),
- (self.call.device.IsOnline(), True),
- (self.call.device.IsOnline(), False)):
+ with self.assertCalls(self.call.adb.Reboot(),
+ (self.call.device.IsOnline(), True),
+ (self.call.device.IsOnline(), False)):
self.device.Reboot(block=False)
def testReboot_blocking(self):
with self.assertCalls(
- self.call.adb.Reboot(),
- (self.call.device.IsOnline(), True),
+ self.call.adb.Reboot(), (self.call.device.IsOnline(), True),
(self.call.device.IsOnline(), False),
- self.call.device.WaitUntilFullyBooted(wifi=False)):
+ self.call.device.WaitUntilFullyBooted(wifi=False, decrypt=False)):
self.device.Reboot(block=True)
def testReboot_blockUntilWifi(self):
with self.assertCalls(
- self.call.adb.Reboot(),
- (self.call.device.IsOnline(), True),
+ self.call.adb.Reboot(), (self.call.device.IsOnline(), True),
(self.call.device.IsOnline(), False),
- self.call.device.WaitUntilFullyBooted(wifi=True)):
- self.device.Reboot(block=True, wifi=True)
+ self.call.device.WaitUntilFullyBooted(wifi=True, decrypt=False)):
+ self.device.Reboot(block=True, wifi=True, decrypt=False)
+
+ def testReboot_blockUntilDecrypt(self):
+ with self.assertCalls(
+ self.call.adb.Reboot(), (self.call.device.IsOnline(), True),
+ (self.call.device.IsOnline(), False),
+ self.call.device.WaitUntilFullyBooted(wifi=False, decrypt=True)):
+ self.device.Reboot(block=True, wifi=False, decrypt=True)
class DeviceUtilsInstallTest(DeviceUtilsTest):
- mock_apk = _MockApkHelper('/fake/test/app.apk', 'test.package', ['p1'])
+ mock_apk = _MockApkHelper(TEST_APK_PATH, TEST_PACKAGE, ['p1'])
def testInstall_noPriorInstall(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=23):
+ with self.patch_call(
+ self.call.device.product_name,
+ return_value='notflounder'), (self.patch_call(
+ self.call.device.build_version_sdk, return_value=23)):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False),
+ (self.call.device.GrantPermissions(TEST_PACKAGE, ['p1']), [])):
+ self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
+
+ def testInstall_noStreaming(self):
+ with self.patch_call(
+ self.call.device.product_name,
+ return_value='flounder'), (self.patch_call(
+ self.call.device.build_version_sdk, return_value=23)):
with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'), []),
- self.call.adb.Install('/fake/test/app.apk', reinstall=False,
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=False,
allow_downgrade=False),
- (self.call.device.GrantPermissions('test.package', ['p1']), [])):
+ (self.call.device.GrantPermissions(TEST_PACKAGE, ['p1']), [])):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
def testInstall_permissionsPreM(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=20):
+ with self.patch_call(
+ self.call.device.product_name,
+ return_value='notflounder'), (self.patch_call(
+ self.call.device.build_version_sdk, return_value=20)):
with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'), []),
- (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ (self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
allow_downgrade=False))):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
def testInstall_findPermissions(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=23):
+ with self.patch_call(
+ self.call.device.product_name,
+ return_value='notflounder'), (self.patch_call(
+ self.call.device.build_version_sdk, return_value=23)):
with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'), []),
- (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ (self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
allow_downgrade=False)),
- (self.call.device.GrantPermissions('test.package', ['p1']), [])):
+ (self.call.device.GrantPermissions(TEST_PACKAGE, ['p1']), [])):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
def testInstall_passPermissions(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'), []),
- (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
- allow_downgrade=False)),
- (self.call.device.GrantPermissions('test.package', ['p1', 'p2']), [])):
- self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
- permissions=['p1', 'p2'])
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ (self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False)),
+ (self.call.device.GrantPermissions(TEST_PACKAGE, ['p1', 'p2']), [])):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk,
+ retries=0,
+ permissions=['p1', 'p2'])
def testInstall_identicalPriorInstall(self):
with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
['/fake/data/app/test.package.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['/fake/test/app.apk']),
- ([], None)),
- (self.call.device.ForceStop('test.package'))):
- self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
- permissions=[])
+ (self.call.device._ComputeStaleApks(TEST_PACKAGE, [TEST_APK_PATH]),
+ ([], None)), (self.call.device.ForceStop(TEST_PACKAGE))):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk, retries=0, permissions=[])
def testInstall_differentPriorInstall(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['/fake/data/app/test.package.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['/fake/test/app.apk']),
- (['/fake/test/app.apk'], None)),
- self.call.device.Uninstall('test.package'),
- self.call.adb.Install('/fake/test/app.apk', reinstall=False,
- allow_downgrade=False)):
- self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
- permissions=[])
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
+ ['/fake/data/app/test.package.apk']),
+ (self.call.device._ComputeStaleApks(TEST_PACKAGE, [TEST_APK_PATH]),
+ ([TEST_APK_PATH], None)), self.call.device.Uninstall(TEST_PACKAGE),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False)):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk, retries=0, permissions=[])
def testInstall_differentPriorInstallSplitApk(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['/fake/data/app/test.package.apk',
- '/fake/data/app/test.package2.apk']),
- self.call.device.Uninstall('test.package'),
- self.call.adb.Install('/fake/test/app.apk', reinstall=False,
- allow_downgrade=False)):
- self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0,
- permissions=[])
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), [
+ '/fake/data/app/test.package.apk',
+ '/fake/data/app/test.package2.apk'
+ ]), self.call.device.Uninstall(TEST_PACKAGE),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False)):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk, retries=0, permissions=[])
def testInstall_differentPriorInstall_reinstall(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['/fake/data/app/test.package.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['/fake/test/app.apk']),
- (['/fake/test/app.apk'], None)),
- self.call.adb.Install('/fake/test/app.apk', reinstall=True,
- allow_downgrade=False)):
- self.device.Install(DeviceUtilsInstallTest.mock_apk,
- reinstall=True, retries=0, permissions=[])
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
+ ['/fake/data/app/test.package.apk']),
+ (self.call.device._ComputeStaleApks(TEST_PACKAGE, [TEST_APK_PATH]),
+ ([TEST_APK_PATH], None)),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=True,
+ streaming=None,
+ allow_downgrade=False)):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk,
+ reinstall=True,
+ retries=0,
+ permissions=[])
def testInstall_identicalPriorInstall_reinstall(self):
with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
['/fake/data/app/test.package.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['/fake/test/app.apk']),
- ([], None)),
- (self.call.device.ForceStop('test.package'))):
- self.device.Install(DeviceUtilsInstallTest.mock_apk,
- reinstall=True, retries=0, permissions=[])
+ (self.call.device._ComputeStaleApks(TEST_PACKAGE, [TEST_APK_PATH]),
+ ([], None)), (self.call.device.ForceStop(TEST_PACKAGE))):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk,
+ reinstall=True,
+ retries=0,
+ permissions=[])
def testInstall_missingApk(self):
with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), False)):
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), False)):
with self.assertRaises(device_errors.CommandFailedError):
self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
def testInstall_fails(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'), []),
- (self.call.adb.Install('/fake/test/app.apk', reinstall=False,
- allow_downgrade=False),
- self.CommandError('Failure\r\n'))):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ (self.call.adb.Install(
+ TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False), self.CommandError('Failure\r\n'))):
+ with self.assertRaises(device_errors.CommandFailedError):
+ self.device.Install(DeviceUtilsInstallTest.mock_apk, retries=0)
def testInstall_downgrade(self):
- with self.assertCalls(
- (mock.call.os.path.exists('/fake/test/app.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['/fake/data/app/test.package.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['/fake/test/app.apk']),
- (['/fake/test/app.apk'], None)),
- self.call.adb.Install('/fake/test/app.apk', reinstall=True,
- allow_downgrade=True)):
- self.device.Install(DeviceUtilsInstallTest.mock_apk,
- reinstall=True, retries=0, permissions=[], allow_downgrade=True)
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (self.call.device._FakeInstall(set(), None, 'test.package')),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
+ ['/fake/data/app/test.package.apk']),
+ (self.call.device._ComputeStaleApks(TEST_PACKAGE, [TEST_APK_PATH]),
+ ([TEST_APK_PATH], None)),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=True,
+ streaming=None,
+ allow_downgrade=True)):
+ self.device.Install(
+ DeviceUtilsInstallTest.mock_apk,
+ reinstall=True,
+ retries=0,
+ permissions=[],
+ allow_downgrade=True)
+
+ def testInstall_pushesFakeModulesToDevice(self):
+ @contextlib.contextmanager
+ def mock_zip_temp_dir():
+ yield '/test/tmp/dir'
- def testInstall_modulesSpecified(self):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.Install(DeviceUtilsInstallTest.mock_apk,
- modules=['base'])
+ mock_apk_with_fake = _MockApkHelper(
+ TEST_APK_PATH, TEST_PACKAGE, splits=['fake1-master.apk'])
+ fake_modules = ['fake1']
+ with self.patch_call(
+ self.call.device.product_name,
+ return_value='notflounder'), (self.patch_call(
+ self.call.device.build_version_sdk, return_value=23)):
+ with self.assertCalls(
+ (mock.call.py_utils.tempfile_ext.NamedTemporaryDirectory(),
+ mock_zip_temp_dir),
+ (mock.call.os.rename('fake1-master.apk', '/test/tmp/dir/fake1.apk')),
+ (self.call.device.PushChangedFiles(
+ [('/test/tmp/dir', '/data/local/tmp/modules/test.package')],
+ delete_device_stale=True)),
+ (mock.call.os.path.exists(TEST_APK_PATH), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ self.call.adb.Install(TEST_APK_PATH,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False),
+ (self.call.device.GrantPermissions(TEST_PACKAGE, None), [])):
+ self.device.Install(
+ mock_apk_with_fake, fake_modules=fake_modules, retries=0)
class DeviceUtilsInstallSplitApkTest(DeviceUtilsTest):
- mock_apk = _MockApkHelper('base.apk', 'test.package', ['p1'])
+ mock_apk = _MockApkHelper('base.apk', TEST_PACKAGE, ['p1'],
+ ['split1.apk', 'split2.apk'])
def testInstallSplitApk_noPriorInstall(self):
- with self.assertCalls(
- (self.call.device._CheckSdkLevel(21)),
- (mock.call.devil.android.sdk.split_select.SelectSplits(
- self.device, 'base.apk',
- ['split1.apk', 'split2.apk', 'split3.apk'],
- allow_cached_props=False),
- ['split2.apk']),
- (mock.call.os.path.exists('base.apk'), True),
- (mock.call.os.path.exists('split2.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'), []),
- (self.call.adb.InstallMultiple(
- ['base.apk', 'split2.apk'], partial=None, reinstall=False,
- allow_downgrade=False))):
- self.device.InstallSplitApk(DeviceUtilsInstallSplitApkTest.mock_apk,
- ['split1.apk', 'split2.apk', 'split3.apk'], permissions=[], retries=0)
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (mock.call.devil.android.apk_helper.ToSplitHelper(
+ 'base.apk', ['split1.apk', 'split2.apk']),
+ DeviceUtilsInstallSplitApkTest.mock_apk),
+ (self.call.device._CheckSdkLevel(21)),
+ (mock.call.os.path.exists('base.apk'), True),
+ (mock.call.os.path.exists('split1.apk'), True),
+ (mock.call.os.path.exists('split2.apk'), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ (self.call.adb.InstallMultiple(
+ ['base.apk', 'split1.apk', 'split2.apk'],
+ partial=None,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False))):
+ self.device.InstallSplitApk(
+ 'base.apk', ['split1.apk', 'split2.apk'], permissions=[], retries=0)
+
+ def testInstallSplitApk_noStreaming(self):
+ with self.patch_call(
+ self.call.device.product_name, return_value='flounder'):
+ with self.assertCalls(
+ (mock.call.devil.android.apk_helper.ToSplitHelper(
+ 'base.apk', ['split1.apk', 'split2.apk']),
+ DeviceUtilsInstallSplitApkTest.mock_apk),
+ (self.call.device._CheckSdkLevel(21)),
+ (mock.call.os.path.exists('base.apk'), True),
+ (mock.call.os.path.exists('split1.apk'), True),
+ (mock.call.os.path.exists('split2.apk'), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), []),
+ (self.call.adb.InstallMultiple(
+ ['base.apk', 'split1.apk', 'split2.apk'],
+ partial=None,
+ reinstall=False,
+ streaming=False,
+ allow_downgrade=False))):
+ self.device.InstallSplitApk(
+ 'base.apk', ['split1.apk', 'split2.apk'], permissions=[], retries=0)
def testInstallSplitApk_partialInstall(self):
- with self.assertCalls(
- (self.call.device._CheckSdkLevel(21)),
- (mock.call.devil.android.sdk.split_select.SelectSplits(
- self.device, 'base.apk',
- ['split1.apk', 'split2.apk', 'split3.apk'],
- allow_cached_props=False),
- ['split2.apk']),
- (mock.call.os.path.exists('base.apk'), True),
- (mock.call.os.path.exists('split2.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['base-on-device.apk', 'split2-on-device.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['base.apk', 'split2.apk']),
- (['split2.apk'], None)),
- (self.call.adb.InstallMultiple(
- ['split2.apk'], partial='test.package', reinstall=True,
- allow_downgrade=False))):
- self.device.InstallSplitApk(DeviceUtilsInstallSplitApkTest.mock_apk,
- ['split1.apk', 'split2.apk', 'split3.apk'],
- reinstall=True, permissions=[], retries=0)
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (mock.call.devil.android.apk_helper.ToSplitHelper(
+ DeviceUtilsInstallSplitApkTest.mock_apk,
+ ['split1.apk', 'split2.apk']),
+ DeviceUtilsInstallSplitApkTest.mock_apk),
+ (self.call.device._CheckSdkLevel(21)),
+ (mock.call.os.path.exists('base.apk'), True),
+ (mock.call.os.path.exists('split1.apk'), True),
+ (mock.call.os.path.exists('split2.apk'), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
+ ['base-on-device.apk', 'split2-on-device.apk']),
+ (self.call.device._ComputeStaleApks(
+ TEST_PACKAGE, ['base.apk', 'split1.apk', 'split2.apk']),
+ (['split2.apk'], None)),
+ (self.call.adb.InstallMultiple(['split2.apk'],
+ partial=TEST_PACKAGE,
+ reinstall=True,
+ streaming=None,
+ allow_downgrade=False))):
+ self.device.InstallSplitApk(
+ DeviceUtilsInstallSplitApkTest.mock_apk,
+ ['split1.apk', 'split2.apk'],
+ reinstall=True,
+ permissions=[],
+ retries=0)
def testInstallSplitApk_downgrade(self):
- with self.assertCalls(
- (self.call.device._CheckSdkLevel(21)),
- (mock.call.devil.android.sdk.split_select.SelectSplits(
- self.device, 'base.apk',
- ['split1.apk', 'split2.apk', 'split3.apk'],
- allow_cached_props=False),
- ['split2.apk']),
- (mock.call.os.path.exists('base.apk'), True),
- (mock.call.os.path.exists('split2.apk'), True),
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['base-on-device.apk', 'split2-on-device.apk']),
- (self.call.device._ComputeStaleApks('test.package',
- ['base.apk', 'split2.apk']),
- (['split2.apk'], None)),
- (self.call.adb.InstallMultiple(
- ['split2.apk'], partial='test.package', reinstall=True,
- allow_downgrade=True))):
- self.device.InstallSplitApk(DeviceUtilsInstallSplitApkTest.mock_apk,
- ['split1.apk', 'split2.apk', 'split3.apk'],
- reinstall=True, permissions=[], retries=0,
- allow_downgrade=True)
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
+ with self.assertCalls(
+ (mock.call.devil.android.apk_helper.ToSplitHelper(
+ DeviceUtilsInstallSplitApkTest.mock_apk,
+ ['split1.apk', 'split2.apk']),
+ DeviceUtilsInstallSplitApkTest.mock_apk),
+ (self.call.device._CheckSdkLevel(21)),
+ (mock.call.os.path.exists('base.apk'), True),
+ (mock.call.os.path.exists('split1.apk'), True),
+ (mock.call.os.path.exists('split2.apk'), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
+ ['base-on-device.apk', 'split2-on-device.apk']),
+ (self.call.device._ComputeStaleApks(
+ TEST_PACKAGE, ['base.apk', 'split1.apk', 'split2.apk']),
+ (['split2.apk'], None)),
+ (self.call.adb.InstallMultiple(['split2.apk'],
+ partial=TEST_PACKAGE,
+ reinstall=True,
+ streaming=None,
+ allow_downgrade=True))):
+ self.device.InstallSplitApk(
+ DeviceUtilsInstallSplitApkTest.mock_apk,
+ ['split1.apk', 'split2.apk'],
+ reinstall=True,
+ permissions=[],
+ retries=0,
+ allow_downgrade=True)
def testInstallSplitApk_missingSplit(self):
with self.assertCalls(
+ (mock.call.devil.android.apk_helper.ToSplitHelper(
+ DeviceUtilsInstallSplitApkTest.mock_apk,
+ ['split1.apk', 'split2.apk']),
+ DeviceUtilsInstallSplitApkTest.mock_apk),
(self.call.device._CheckSdkLevel(21)),
- (mock.call.devil.android.sdk.split_select.SelectSplits(
- self.device, 'base.apk',
- ['split1.apk', 'split2.apk', 'split3.apk'],
- allow_cached_props=False),
- ['split2.apk']),
(mock.call.os.path.exists('base.apk'), True),
- (mock.call.os.path.exists('split2.apk'), False)):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.InstallSplitApk(DeviceUtilsInstallSplitApkTest.mock_apk,
- ['split1.apk', 'split2.apk', 'split3.apk'], permissions=[],
- retries=0)
+ (mock.call.os.path.exists('split1.apk'), True),
+ (mock.call.os.path.exists('split2.apk'), False)),\
+ self.assertRaises(device_errors.CommandFailedError):
+ self.device.InstallSplitApk(
+ DeviceUtilsInstallSplitApkTest.mock_apk, ['split1.apk', 'split2.apk'],
+ permissions=[],
+ retries=0)
def testInstallSplitApk_previouslyNonSplit(self):
- with self.assertCalls(
- (self.call.device._CheckSdkLevel(21)),
- (mock.call.devil.android.sdk.split_select.SelectSplits(
- self.device, 'base.apk',
- ['split1.apk', 'split2.apk', 'split3.apk'],
- allow_cached_props=False),
- ['split2.apk']),
- (mock.call.os.path.exists('base.apk'), True),
- (mock.call.os.path.exists('split2.apk'), True),
- (self.call.device._GetApplicationPathsInternal(
- 'test.package'), ['/fake/data/app/test.package.apk']),
- self.call.device.Uninstall('test.package'),
- (self.call.adb.InstallMultiple(
- ['base.apk', 'split2.apk'], partial=None, reinstall=False,
- allow_downgrade=False))):
- self.device.InstallSplitApk(DeviceUtilsInstallSplitApkTest.mock_apk,
- ['split1.apk', 'split2.apk', 'split3.apk'], permissions=[], retries=0)
-
-
-class DeviceUtilsInstallBundleTest(DeviceUtilsTest):
- mock_apk = _MockApkHelper('/fake/test/app_bundle', 'test.package', ['p1'])
-
- def testInstallBundle_noPriorInstall(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=23):
- with self.assertCalls(
- (mock.call.devil.utils.cmd_helper.RunCmd(
- ['/fake/test/app_bundle', 'install', '--device',
- self.device.serial]), 0),
- (self.call.device.GrantPermissions('test.package', ['p1']), [])):
- self.device.Install(DeviceUtilsInstallBundleTest.mock_apk)
-
- def testInstallBundle_modulesSpecified(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=23):
- with self.assertCalls(
- (mock.call.devil.utils.cmd_helper.RunCmd(
- ['/fake/test/app_bundle', 'install', '--device',
- self.device.serial, '-m', 'base']), 0),
- (self.call.device.GrantPermissions('test.package', ['p1']), [])):
- self.device.Install(
- DeviceUtilsInstallBundleTest.mock_apk, modules=['base'])
-
- def testInstallBundle_permissionsPreM(self):
- with self.patch_call(self.call.device.build_version_sdk, return_value=20):
+ with self.patch_call(
+ self.call.device.product_name, return_value='notflounder'):
with self.assertCalls(
- (mock.call.devil.utils.cmd_helper.RunCmd(
- ['/fake/test/app_bundle', 'install', '--device',
- self.device.serial]), 0)):
- self.device.Install(DeviceUtilsInstallBundleTest.mock_apk)
-
- def testInstallBundle_splitApks(self):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.InstallSplitApk(
- DeviceUtilsInstallBundleTest.mock_apk, ['apk1', 'apk2'])
+ (mock.call.devil.android.apk_helper.ToSplitHelper(
+ DeviceUtilsInstallSplitApkTest.mock_apk,
+ ['split1.apk', 'split2.apk']),
+ DeviceUtilsInstallSplitApkTest.mock_apk),
+ (self.call.device._CheckSdkLevel(21)),
+ (mock.call.os.path.exists('base.apk'), True),
+ (mock.call.os.path.exists('split1.apk'), True),
+ (mock.call.os.path.exists('split2.apk'), True),
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
+ ['/fake/data/app/test.package.apk']),
+ self.call.device.Uninstall(TEST_PACKAGE),
+ (self.call.adb.InstallMultiple(
+ ['base.apk', 'split1.apk', 'split2.apk'],
+ partial=None,
+ reinstall=False,
+ streaming=None,
+ allow_downgrade=False))):
+ self.device.InstallSplitApk(
+ DeviceUtilsInstallSplitApkTest.mock_apk,
+ ['split1.apk', 'split2.apk'],
+ permissions=[],
+ retries=0)
class DeviceUtilsUninstallTest(DeviceUtilsTest):
-
def testUninstall_callsThrough(self):
with self.assertCalls(
- (self.call.device._GetApplicationPathsInternal('test.package'),
- ['/path.apk']),
- self.call.adb.Uninstall('test.package', True)):
- self.device.Uninstall('test.package', True)
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE),
+ ['/path.apk']), self.call.adb.Uninstall(TEST_PACKAGE, True)):
+ self.device.Uninstall(TEST_PACKAGE, True)
def testUninstall_noop(self):
with self.assertCalls(
- (self.call.device._GetApplicationPathsInternal('test.package'), [])):
- self.device.Uninstall('test.package', True)
+ (self.call.device._GetApplicationPathsInternal(TEST_PACKAGE), [])):
+ self.device.Uninstall(TEST_PACKAGE, True)
class DeviceUtilsSuTest(DeviceUtilsTest):
-
def testSu_preM(self):
with self.patch_call(
self.call.device.build_version_sdk,
@@ -1048,82 +1347,87 @@ class DeviceUtilsSuTest(DeviceUtilsTest):
class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
-
def setUp(self):
super(DeviceUtilsRunShellCommandTest, self).setUp()
self.device.NeedsSU = mock.Mock(return_value=False)
def testRunShellCommand_commandAsList(self):
with self.assertCall(self.call.adb.Shell('pm list packages'), ''):
- self.device.RunShellCommand(
- ['pm', 'list', 'packages'], check_return=True)
+ self.device.RunShellCommand(['pm', 'list', 'packages'], check_return=True)
def testRunShellCommand_commandAsListQuoted(self):
with self.assertCall(self.call.adb.Shell("echo 'hello world' '$10'"), ''):
- self.device.RunShellCommand(
- ['echo', 'hello world', '$10'], check_return=True)
+ self.device.RunShellCommand(['echo', 'hello world', '$10'],
+ check_return=True)
def testRunShellCommand_commandAsString(self):
with self.assertCall(self.call.adb.Shell('echo "$VAR"'), ''):
- self.device.RunShellCommand(
- 'echo "$VAR"', shell=True, check_return=True)
+ self.device.RunShellCommand('echo "$VAR"', shell=True, check_return=True)
def testNewRunShellImpl_withEnv(self):
with self.assertCall(
self.call.adb.Shell('VAR=some_string echo "$VAR"'), ''):
self.device.RunShellCommand(
- 'echo "$VAR"', shell=True, check_return=True,
+ 'echo "$VAR"',
+ shell=True,
+ check_return=True,
env={'VAR': 'some_string'})
def testNewRunShellImpl_withEnvQuoted(self):
with self.assertCall(
self.call.adb.Shell('PATH="$PATH:/other/path" run_this'), ''):
- self.device.RunShellCommand(
- ['run_this'], check_return=True, env={'PATH': '$PATH:/other/path'})
+ self.device.RunShellCommand(['run_this'],
+ check_return=True,
+ env={'PATH': '$PATH:/other/path'})
def testNewRunShellImpl_withEnv_failure(self):
with self.assertRaises(KeyError):
- self.device.RunShellCommand(
- ['some_cmd'], check_return=True, env={'INVALID NAME': 'value'})
+ self.device.RunShellCommand(['some_cmd'],
+ check_return=True,
+ env={'INVALID NAME': 'value'})
def testNewRunShellImpl_withCwd(self):
with self.assertCall(self.call.adb.Shell('cd /some/test/path && ls'), ''):
- self.device.RunShellCommand(
- ['ls'], check_return=True, cwd='/some/test/path')
+ self.device.RunShellCommand(['ls'],
+ check_return=True,
+ cwd='/some/test/path')
def testNewRunShellImpl_withCwdQuoted(self):
with self.assertCall(
self.call.adb.Shell("cd '/some test/path with/spaces' && ls"), ''):
- self.device.RunShellCommand(
- ['ls'], check_return=True, cwd='/some test/path with/spaces')
+ self.device.RunShellCommand(['ls'],
+ check_return=True,
+ cwd='/some test/path with/spaces')
def testRunShellCommand_withHugeCmd(self):
payload = 'hi! ' * 1024
expected_cmd = "echo '%s'" % payload
with self.assertCalls(
- (mock.call.devil.android.device_temp_file.DeviceTempFile(
- self.adb, suffix='.sh'), MockTempFile('/sdcard/temp-123.sh')),
- self.call.device._WriteFileWithPush('/sdcard/temp-123.sh', expected_cmd),
- (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')):
- self.assertEquals(
- [payload],
- self.device.RunShellCommand(['echo', payload], check_return=True))
+ (mock.call.devil.android.device_temp_file.DeviceTempFile(
+ self.adb, suffix='.sh'), MockTempFile('/sdcard/temp-123.sh')),
+ self.call.device._WriteFileWithPush('/sdcard/temp-123.sh',
+ expected_cmd),
+ (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')):
+ self.assertEquals([payload],
+ self.device.RunShellCommand(['echo', payload],
+ check_return=True))
def testRunShellCommand_withHugeCmdAndSu(self):
payload = 'hi! ' * 1024
expected_cmd_without_su = """sh -c 'echo '"'"'%s'"'"''""" % payload
expected_cmd = 'su -c %s' % expected_cmd_without_su
with self.assertCalls(
- (self.call.device.NeedsSU(), True),
- (self.call.device._Su(expected_cmd_without_su), expected_cmd),
- (mock.call.devil.android.device_temp_file.DeviceTempFile(
- self.adb, suffix='.sh'), MockTempFile('/sdcard/temp-123.sh')),
- self.call.device._WriteFileWithPush('/sdcard/temp-123.sh', expected_cmd),
- (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')):
- self.assertEquals(
- [payload],
- self.device.RunShellCommand(
- ['echo', payload], check_return=True, as_root=True))
+ (self.call.device.NeedsSU(), True),
+ (self.call.device._Su(expected_cmd_without_su), expected_cmd),
+ (mock.call.devil.android.device_temp_file.DeviceTempFile(
+ self.adb, suffix='.sh'), MockTempFile('/sdcard/temp-123.sh')),
+ self.call.device._WriteFileWithPush('/sdcard/temp-123.sh',
+ expected_cmd),
+ (self.call.adb.Shell('sh /sdcard/temp-123.sh'), payload + '\n')):
+ self.assertEquals([payload],
+ self.device.RunShellCommand(['echo', payload],
+ check_return=True,
+ as_root=True))
def testRunShellCommand_withSu(self):
expected_cmd_without_su = "sh -c 'setprop service.adb.root 0'"
@@ -1132,18 +1436,18 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
(self.call.device.NeedsSU(), True),
(self.call.device._Su(expected_cmd_without_su), expected_cmd),
(self.call.adb.Shell(expected_cmd), '')):
- self.device.RunShellCommand(
- ['setprop', 'service.adb.root', '0'],
- check_return=True, as_root=True)
+ self.device.RunShellCommand(['setprop', 'service.adb.root', '0'],
+ check_return=True,
+ as_root=True)
def testRunShellCommand_withRunAs(self):
expected_cmd_without_run_as = "sh -c 'mkdir -p files'"
expected_cmd = (
'run-as org.devil.test_package %s' % expected_cmd_without_run_as)
with self.assertCall(self.call.adb.Shell(expected_cmd), ''):
- self.device.RunShellCommand(
- ['mkdir', '-p', 'files'],
- check_return=True, run_as='org.devil.test_package')
+ self.device.RunShellCommand(['mkdir', '-p', 'files'],
+ check_return=True,
+ run_as='org.devil.test_package')
def testRunShellCommand_withRunAsAndSu(self):
expected_cmd_with_nothing = "sh -c 'mkdir -p files'"
@@ -1156,17 +1460,17 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
(self.call.device.NeedsSU(), True),
(self.call.device._Su(expected_cmd_without_su), expected_cmd),
(self.call.adb.Shell(expected_cmd), '')):
- self.device.RunShellCommand(
- ['mkdir', '-p', 'files'],
- check_return=True, run_as='org.devil.test_package',
- as_root=True)
+ self.device.RunShellCommand(['mkdir', '-p', 'files'],
+ check_return=True,
+ run_as='org.devil.test_package',
+ as_root=True)
def testRunShellCommand_manyLines(self):
cmd = 'ls /some/path'
with self.assertCall(self.call.adb.Shell(cmd), 'file1\nfile2\nfile3\n'):
- self.assertEquals(
- ['file1', 'file2', 'file3'],
- self.device.RunShellCommand(cmd.split(), check_return=True))
+ self.assertEquals(['file1', 'file2', 'file3'],
+ self.device.RunShellCommand(
+ cmd.split(), check_return=True))
def testRunShellCommand_manyLinesRawOutput(self):
cmd = 'ls /some/path'
@@ -1210,8 +1514,8 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
def testRunShellCommand_singleLine_failTooManyLines(self):
cmd = 'echo $VALUE'
- with self.assertCall(self.call.adb.Shell(cmd),
- 'some value\nanother value\n'):
+ with self.assertCall(
+ self.call.adb.Shell(cmd), 'some value\nanother value\n'):
with self.assertRaises(device_errors.CommandFailedError):
self.device.RunShellCommand(
cmd, shell=True, check_return=True, single_line=True)
@@ -1220,9 +1524,9 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
cmd = 'echo $ANDROID_DATA'
output = '/data\n'
with self.assertCall(self.call.adb.Shell(cmd), output):
- self.assertEquals(
- [output.rstrip()],
- self.device.RunShellCommand(cmd, shell=True, check_return=True))
+ self.assertEquals([output.rstrip()],
+ self.device.RunShellCommand(
+ cmd, shell=True, check_return=True))
def testRunShellCommand_checkReturn_failure(self):
cmd = 'ls /root'
@@ -1235,9 +1539,9 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
cmd = 'ls /root'
output = 'opendir failed, Permission denied\n'
with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)):
- self.assertEquals(
- [output.rstrip()],
- self.device.RunShellCommand(cmd.split(), check_return=False))
+ self.assertEquals([output.rstrip()],
+ self.device.RunShellCommand(
+ cmd.split(), check_return=False))
def testRunShellCommand_largeOutput_enabled(self):
cmd = 'echo $VALUE'
@@ -1245,14 +1549,15 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
cmd_redirect = '( %s )>%s 2>&1' % (cmd, temp_file.name)
with self.assertCalls(
(mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb),
- temp_file),
- (self.call.adb.Shell(cmd_redirect)),
- (self.call.device.ReadFile(temp_file.name, force_pull=True),
- 'something')):
- self.assertEquals(
- ['something'],
- self.device.RunShellCommand(
- cmd, shell=True, large_output=True, check_return=True))
+ temp_file),
+ (self.call.adb.Shell(cmd_redirect)), (self.call.device.ReadFile(
+ temp_file.name, force_pull=True), 'something')):
+ self.assertEquals(['something'],
+ self.device.RunShellCommand(
+ cmd,
+ shell=True,
+ large_output=True,
+ check_return=True))
def testRunShellCommand_largeOutput_disabledNoTrigger(self):
cmd = 'something'
@@ -1267,23 +1572,20 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
with self.assertCalls(
(self.call.adb.Shell(cmd), self.ShellError('', None)),
(mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb),
- temp_file),
- (self.call.adb.Shell(cmd_redirect)),
- (self.call.device.ReadFile(mock.ANY, force_pull=True),
- 'something')):
- self.assertEquals(
- ['something'],
- self.device.RunShellCommand(cmd, shell=True, check_return=True))
+ temp_file), (self.call.adb.Shell(cmd_redirect)),
+ (self.call.device.ReadFile(mock.ANY, force_pull=True), 'something')):
+ self.assertEquals(['something'],
+ self.device.RunShellCommand(
+ cmd, shell=True, check_return=True))
class DeviceUtilsRunPipedShellCommandTest(DeviceUtilsTest):
-
def testRunPipedShellCommand_success(self):
with self.assertCall(
self.call.device.RunShellCommand(
'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
- shell=True, check_return=True),
- ['This line contains foo', 'PIPESTATUS: 0 0']):
+ shell=True,
+ check_return=True), ['This line contains foo', 'PIPESTATUS: 0 0']):
self.assertEquals(['This line contains foo'],
self.device._RunPipedShellCommand('ps | grep foo'))
@@ -1291,8 +1593,8 @@ class DeviceUtilsRunPipedShellCommandTest(DeviceUtilsTest):
with self.assertCall(
self.call.device.RunShellCommand(
'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
- shell=True, check_return=True),
- ['PIPESTATUS: 1 0']):
+ shell=True,
+ check_return=True), ['PIPESTATUS: 1 0']):
with self.assertRaises(device_errors.AdbShellCommandFailedError) as ec:
self.device._RunPipedShellCommand('ps | grep foo')
self.assertEquals([1, 0], ec.exception.status)
@@ -1301,8 +1603,8 @@ class DeviceUtilsRunPipedShellCommandTest(DeviceUtilsTest):
with self.assertCall(
self.call.device.RunShellCommand(
'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
- shell=True, check_return=True),
- ['PIPESTATUS: 0 1']):
+ shell=True,
+ check_return=True), ['PIPESTATUS: 0 1']):
with self.assertRaises(device_errors.AdbShellCommandFailedError) as ec:
self.device._RunPipedShellCommand('ps | grep foo')
self.assertEquals([0, 1], ec.exception.status)
@@ -1311,8 +1613,8 @@ class DeviceUtilsRunPipedShellCommandTest(DeviceUtilsTest):
with self.assertCall(
self.call.device.RunShellCommand(
'ps | grep foo; echo "PIPESTATUS: ${PIPESTATUS[@]}"',
- shell=True, check_return=True),
- ['foo.bar'] * 256 + ['foo.ba']):
+ shell=True,
+ check_return=True), ['foo.bar'] * 256 + ['foo.ba']):
with self.assertRaises(device_errors.AdbShellCommandFailedError) as ec:
self.device._RunPipedShellCommand('ps | grep foo')
self.assertIs(None, ec.exception.status)
@@ -1320,7 +1622,6 @@ class DeviceUtilsRunPipedShellCommandTest(DeviceUtilsTest):
@mock.patch('time.sleep', mock.Mock())
class DeviceUtilsKillAllTest(DeviceUtilsTest):
-
def testKillAll_noMatchingProcessesFailure(self):
with self.assertCall(self.call.device.ListProcesses('test_process'), []):
with self.assertRaises(device_errors.CommandFailedError):
@@ -1331,12 +1632,11 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest):
self.assertEqual(0, self.device.KillAll('test_process', quiet=True))
def testKillAll_nonblocking(self):
- with self.assertCalls(
- (self.call.device.ListProcesses('some.process'),
- Processes(('some.process', 1234), ('some.process.thing', 5678))),
- (self.call.adb.Shell('kill -9 1234 5678'), '')):
- self.assertEquals(
- 2, self.device.KillAll('some.process', blocking=False))
+ with self.assertCalls((self.call.device.ListProcesses('some.process'),
+ Processes(('some.process', 1234),
+ ('some.process.thing', 5678))),
+ (self.call.adb.Shell('kill -9 1234 5678'), '')):
+ self.assertEquals(2, self.device.KillAll('some.process', blocking=False))
def testKillAll_blocking(self):
with self.assertCalls(
@@ -1345,62 +1645,59 @@ class DeviceUtilsKillAllTest(DeviceUtilsTest):
(self.call.adb.Shell('kill -9 1234 5678'), ''),
(self.call.device.ListProcesses('some.process'),
Processes(('some.process.thing', 5678))),
- (self.call.device.ListProcesses('some.process'),
- # Other instance with different pid.
- Processes(('some.process', 111)))):
- self.assertEquals(
- 2, self.device.KillAll('some.process', blocking=True))
+ (
+ self.call.device.ListProcesses('some.process'),
+ # Other instance with different pid.
+ Processes(('some.process', 111)))):
+ self.assertEquals(2, self.device.KillAll('some.process', blocking=True))
def testKillAll_exactNonblocking(self):
- with self.assertCalls(
- (self.call.device.ListProcesses('some.process'),
- Processes(('some.process', 1234), ('some.process.thing', 5678))),
- (self.call.adb.Shell('kill -9 1234'), '')):
+ with self.assertCalls((self.call.device.ListProcesses('some.process'),
+ Processes(('some.process', 1234),
+ ('some.process.thing', 5678))),
+ (self.call.adb.Shell('kill -9 1234'), '')):
self.assertEquals(
1, self.device.KillAll('some.process', exact=True, blocking=False))
def testKillAll_exactBlocking(self):
- with self.assertCalls(
- (self.call.device.ListProcesses('some.process'),
- Processes(('some.process', 1234), ('some.process.thing', 5678))),
- (self.call.adb.Shell('kill -9 1234'), ''),
- (self.call.device.ListProcesses('some.process'),
- Processes(('some.process', 1234), ('some.process.thing', 5678))),
- (self.call.device.ListProcesses('some.process'),
- Processes(('some.process.thing', 5678)))):
+ with self.assertCalls((self.call.device.ListProcesses('some.process'),
+ Processes(('some.process', 1234),
+ ('some.process.thing', 5678))),
+ (self.call.adb.Shell('kill -9 1234'), ''),
+ (self.call.device.ListProcesses('some.process'),
+ Processes(('some.process', 1234),
+ ('some.process.thing', 5678))),
+ (self.call.device.ListProcesses('some.process'),
+ Processes(('some.process.thing', 5678)))):
self.assertEquals(
1, self.device.KillAll('some.process', exact=True, blocking=True))
def testKillAll_root(self):
with self.assertCalls(
(self.call.device.ListProcesses('some.process'),
- Processes(('some.process', 1234))),
- (self.call.device.NeedsSU(), True),
+ Processes(('some.process', 1234))), (self.call.device.NeedsSU(), True),
(self.call.device._Su("sh -c 'kill -9 1234'"),
"su -c sh -c 'kill -9 1234'"),
(self.call.adb.Shell("su -c sh -c 'kill -9 1234'"), '')):
- self.assertEquals(
- 1, self.device.KillAll('some.process', as_root=True))
+ self.assertEquals(1, self.device.KillAll('some.process', as_root=True))
def testKillAll_sigterm(self):
- with self.assertCalls(
- (self.call.device.ListProcesses('some.process'),
- Processes(('some.process', 1234))),
- (self.call.adb.Shell('kill -15 1234'), '')):
+ with self.assertCalls((self.call.device.ListProcesses('some.process'),
+ Processes(('some.process', 1234))),
+ (self.call.adb.Shell('kill -15 1234'), '')):
self.assertEquals(
1, self.device.KillAll('some.process', signum=device_signal.SIGTERM))
def testKillAll_multipleInstances(self):
- with self.assertCalls(
- (self.call.device.ListProcesses('some.process'),
- Processes(('some.process', 1234), ('some.process', 4567))),
- (self.call.adb.Shell('kill -15 1234 4567'), '')):
+ with self.assertCalls((self.call.device.ListProcesses('some.process'),
+ Processes(('some.process', 1234),
+ ('some.process', 4567))),
+ (self.call.adb.Shell('kill -15 1234 4567'), '')):
self.assertEquals(
2, self.device.KillAll('some.process', signum=device_signal.SIGTERM))
class DeviceUtilsStartActivityTest(DeviceUtilsTest):
-
def testStartActivity_actionOnly(self):
test_intent = intent.Intent(action='android.intent.action.VIEW')
with self.assertCall(
@@ -1410,9 +1707,10 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_success(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main')
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main')
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1421,9 +1719,10 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_failure(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main')
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main')
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1433,9 +1732,10 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_blocking(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main')
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main')
with self.assertCall(
self.call.adb.Shell('am start '
'-W '
@@ -1445,10 +1745,11 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent, blocking=True)
def testStartActivity_withCategory(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main',
- category='android.intent.category.HOME')
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main',
+ category='android.intent.category.HOME')
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1458,11 +1759,13 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_withMultipleCategories(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main',
- category=['android.intent.category.HOME',
- 'android.intent.category.BROWSABLE'])
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main',
+ category=[
+ 'android.intent.category.HOME', 'android.intent.category.BROWSABLE'
+ ])
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1473,10 +1776,11 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_withData(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main',
- data='http://www.google.com/')
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main',
+ data='http://www.google.com/')
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1486,10 +1790,11 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_withStringExtra(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main',
- extras={'foo': 'test'})
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main',
+ extras={'foo': 'test'})
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1499,10 +1804,11 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_withBoolExtra(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main',
- extras={'foo': True})
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main',
+ extras={'foo': True})
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1512,10 +1818,11 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_withIntExtra(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main',
- extras={'foo': 123})
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main',
+ extras={'foo': 123})
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1525,22 +1832,24 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent)
def testStartActivity_withTraceFile(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main')
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main')
with self.assertCall(
self.call.adb.Shell('am start '
'--start-profiler test_trace_file.out '
'-a android.intent.action.VIEW '
'-n test.package/.Main'),
'Starting: Intent { act=android.intent.action.VIEW }'):
- self.device.StartActivity(test_intent,
- trace_file_name='test_trace_file.out')
+ self.device.StartActivity(
+ test_intent, trace_file_name='test_trace_file.out')
def testStartActivity_withForceStop(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main')
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main')
with self.assertCall(
self.call.adb.Shell('am start '
'-S '
@@ -1550,13 +1859,14 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
self.device.StartActivity(test_intent, force_stop=True)
def testStartActivity_withFlags(self):
- test_intent = intent.Intent(action='android.intent.action.VIEW',
- package='test.package',
- activity='.Main',
- flags=[
- intent.FLAG_ACTIVITY_NEW_TASK,
- intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- ])
+ test_intent = intent.Intent(
+ action='android.intent.action.VIEW',
+ package=TEST_PACKAGE,
+ activity='.Main',
+ flags=[
+ intent.FLAG_ACTIVITY_NEW_TASK,
+ intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ ])
with self.assertCall(
self.call.adb.Shell('am start '
'-a android.intent.action.VIEW '
@@ -1568,11 +1878,12 @@ class DeviceUtilsStartActivityTest(DeviceUtilsTest):
class DeviceUtilsStartServiceTest(DeviceUtilsTest):
def testStartService_success(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
+ test_intent = intent.Intent(
+ action='android.intent.action.START',
+ package=TEST_PACKAGE,
+ activity='.Main')
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.NOUGAT):
with self.assertCall(
self.call.adb.Shell('am startservice '
'-a android.intent.action.START '
@@ -1581,11 +1892,12 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest):
self.device.StartService(test_intent)
def testStartService_failure(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
+ test_intent = intent.Intent(
+ action='android.intent.action.START',
+ package=TEST_PACKAGE,
+ activity='.Main')
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.NOUGAT):
with self.assertCall(
self.call.adb.Shell('am startservice '
'-a android.intent.action.START '
@@ -1595,11 +1907,12 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest):
self.device.StartService(test_intent)
def testStartService_withUser(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
+ test_intent = intent.Intent(
+ action='android.intent.action.START',
+ package=TEST_PACKAGE,
+ activity='.Main')
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.NOUGAT):
with self.assertCall(
self.call.adb.Shell('am startservice '
'--user TestUser '
@@ -1609,11 +1922,12 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest):
self.device.StartService(test_intent, user_id='TestUser')
def testStartService_onOreo(self):
- test_intent = intent.Intent(action='android.intent.action.START',
- package='test.package',
- activity='.Main')
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
+ test_intent = intent.Intent(
+ action='android.intent.action.START',
+ package=TEST_PACKAGE,
+ activity='.Main')
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.OREO):
with self.assertCall(
self.call.adb.Shell('am start-service '
'-a android.intent.action.START '
@@ -1623,50 +1937,64 @@ class DeviceUtilsStartServiceTest(DeviceUtilsTest):
class DeviceUtilsStartInstrumentationTest(DeviceUtilsTest):
-
def testStartInstrumentation_nothing(self):
with self.assertCalls(
self.call.device.RunShellCommand(
'p=test.package;am instrument "$p"/.TestInstrumentation',
- shell=True, check_return=True, large_output=True)):
+ shell=True,
+ check_return=True,
+ large_output=True)):
self.device.StartInstrumentation(
'test.package/.TestInstrumentation',
- finish=False, raw=False, extras=None)
+ finish=False,
+ raw=False,
+ extras=None)
def testStartInstrumentation_finish(self):
- with self.assertCalls(
- (self.call.device.RunShellCommand(
- 'p=test.package;am instrument -w "$p"/.TestInstrumentation',
- shell=True, check_return=True, large_output=True),
- ['OK (1 test)'])):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ 'p=test.package;am instrument -w "$p"/.TestInstrumentation',
+ shell=True,
+ check_return=True,
+ large_output=True), ['OK (1 test)'])):
output = self.device.StartInstrumentation(
'test.package/.TestInstrumentation',
- finish=True, raw=False, extras=None)
+ finish=True,
+ raw=False,
+ extras=None)
self.assertEquals(['OK (1 test)'], output)
def testStartInstrumentation_raw(self):
with self.assertCalls(
self.call.device.RunShellCommand(
'p=test.package;am instrument -r "$p"/.TestInstrumentation',
- shell=True, check_return=True, large_output=True)):
+ shell=True,
+ check_return=True,
+ large_output=True)):
self.device.StartInstrumentation(
'test.package/.TestInstrumentation',
- finish=False, raw=True, extras=None)
+ finish=False,
+ raw=True,
+ extras=None)
def testStartInstrumentation_extras(self):
with self.assertCalls(
self.call.device.RunShellCommand(
'p=test.package;am instrument -e "$p".foo Foo -e bar \'Val \'"$p" '
'"$p"/.TestInstrumentation',
- shell=True, check_return=True, large_output=True)):
+ shell=True,
+ check_return=True,
+ large_output=True)):
self.device.StartInstrumentation(
'test.package/.TestInstrumentation',
- finish=False, raw=False, extras={'test.package.foo': 'Foo',
- 'bar': 'Val test.package'})
+ finish=False,
+ raw=False,
+ extras={
+ 'test.package.foo': 'Foo',
+ 'bar': 'Val test.package'
+ })
class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest):
-
def testBroadcastIntent_noExtras(self):
test_intent = intent.Intent(action='test.package.with.an.INTENT')
with self.assertCall(
@@ -1675,8 +2003,8 @@ class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest):
self.device.BroadcastIntent(test_intent)
def testBroadcastIntent_withExtra(self):
- test_intent = intent.Intent(action='test.package.with.an.INTENT',
- extras={'foo': 'bar value'})
+ test_intent = intent.Intent(
+ action='test.package.with.an.INTENT', extras={'foo': 'bar value'})
with self.assertCall(
self.call.adb.Shell(
"am broadcast -a test.package.with.an.INTENT --es foo 'bar value'"),
@@ -1684,8 +2012,8 @@ class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest):
self.device.BroadcastIntent(test_intent)
def testBroadcastIntent_withExtra_noValue(self):
- test_intent = intent.Intent(action='test.package.with.an.INTENT',
- extras={'foo': None})
+ test_intent = intent.Intent(
+ action='test.package.with.an.INTENT', extras={'foo': None})
with self.assertCall(
self.call.adb.Shell(
'am broadcast -a test.package.with.an.INTENT --esn foo'),
@@ -1694,137 +2022,136 @@ class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest):
class DeviceUtilsGoHomeTest(DeviceUtilsTest):
-
def testGoHome_popupsExist(self):
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), []),
- (self.call.device.RunShellCommand(
- ['am', 'start', '-W', '-a', 'android.intent.action.MAIN',
- '-c', 'android.intent.category.HOME'], check_return=True),
- 'Starting: Intent { act=android.intent.action.MAIN }\r\n'''),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), []),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '66'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '4'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True),
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), []),
+ (self.call.device.RunShellCommand([
+ 'am', 'start', '-W', '-a', 'android.intent.action.MAIN', '-c',
+ 'android.intent.category.HOME'
+ ],
+ check_return=True),
+ 'Starting: Intent { act=android.intent.action.MAIN }\r\n'
+ ''),
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), []),
+ (self.call.device.RunShellCommand(['input', 'keyevent', '66'],
+ check_return=True)),
+ (self.call.device.RunShellCommand(['input', 'keyevent', '4'],
+ check_return=True)),
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True),
['mCurrentFocus Launcher'])):
self.device.GoHome()
def testGoHome_willRetry(self):
with self.assertCalls(
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), []),
+ (self.call.device.RunShellCommand([
+ 'am', 'start', '-W', '-a', 'android.intent.action.MAIN', '-c',
+ 'android.intent.category.HOME'
+ ],
+ check_return=True),
+ 'Starting: Intent { act=android.intent.action.MAIN }\r\n'
+ ''),
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), []),
(self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), []),
- (self.call.device.RunShellCommand(
- ['am', 'start', '-W', '-a', 'android.intent.action.MAIN',
- '-c', 'android.intent.category.HOME'], check_return=True),
- 'Starting: Intent { act=android.intent.action.MAIN }\r\n'''),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), []),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '66'], check_return=True,)),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '4'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), []),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '66'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '4'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True),
+ ['input', 'keyevent', '66'],
+ check_return=True,
+ )), (self.call.device.RunShellCommand(['input', 'keyevent', '4'],
+ check_return=True)),
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), []),
+ (self.call.device.RunShellCommand(['input', 'keyevent', '66'],
+ check_return=True)),
+ (self.call.device.RunShellCommand(['input', 'keyevent', '4'],
+ check_return=True)),
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True),
self.TimeoutError())):
with self.assertRaises(device_errors.CommandTimeoutError):
self.device.GoHome()
def testGoHome_alreadyFocused(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True),
+ self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True),
['mCurrentFocus Launcher']):
self.device.GoHome()
def testGoHome_alreadyFocusedAlternateCase(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True),
+ self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True),
[' mCurrentFocus .launcher/.']):
self.device.GoHome()
def testGoHome_obtainsFocusAfterGoingHome(self):
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), []),
- (self.call.device.RunShellCommand(
- ['am', 'start', '-W', '-a', 'android.intent.action.MAIN',
- '-c', 'android.intent.category.HOME'], check_return=True),
- 'Starting: Intent { act=android.intent.action.MAIN }\r\n'''),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True),
- ['mCurrentFocus Launcher'])):
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), []),
+ (self.call.device.RunShellCommand([
+ 'am', 'start', '-W', '-a', 'android.intent.action.MAIN', '-c',
+ 'android.intent.category.HOME'
+ ],
+ check_return=True),
+ 'Starting: Intent { act=android.intent.action.MAIN }\r\n'
+ ''), (self.call.device.RunShellCommand(
+ ['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), ['mCurrentFocus Launcher'])):
self.device.GoHome()
class DeviceUtilsForceStopTest(DeviceUtilsTest):
-
def testForceStop(self):
with self.assertCalls(
- (self.call.device.GetApplicationPids('test.package'), [1111]),
- (self.call.device.RunShellCommand(
- ['am', 'force-stop', 'test.package'],
- check_return=True),
- ['Success'])):
- self.device.ForceStop('test.package')
+ (self.call.device.GetApplicationPids(TEST_PACKAGE), [1111]),
+ (self.call.device.RunShellCommand(['am', 'force-stop', TEST_PACKAGE],
+ check_return=True), ['Success'])):
+ self.device.ForceStop(TEST_PACKAGE)
def testForceStop_NoProcessFound(self):
- with self.assertCall(
- self.call.device.GetApplicationPids('test.package'), []):
- self.device.ForceStop('test.package')
+ with self.assertCall(self.call.device.GetApplicationPids(TEST_PACKAGE), []):
+ self.device.ForceStop(TEST_PACKAGE)
class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest):
-
def testClearApplicationState_setPermissions(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '17'),
(self.call.device._GetApplicationPathsInternal('this.package.exists'),
['/data/app/this.package.exists.apk']),
(self.call.device.RunShellCommand(
- ['pm', 'clear', 'this.package.exists'],
- check_return=True),
+ ['pm', 'clear', 'this.package.exists'], check_return=True),
['Success']),
- (self.call.device.GrantPermissions(
- 'this.package.exists', ['p1']), [])):
+ (self.call.device.GrantPermissions('this.package.exists', ['p1']), [])):
self.device.ClearApplicationState(
'this.package.exists', permissions=['p1'])
def testClearApplicationState_packageDoesntExist(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '11'),
- (self.call.device._GetApplicationPathsInternal('does.not.exist'),
- [])):
+ (self.call.device._GetApplicationPathsInternal('does.not.exist'), [])):
self.device.ClearApplicationState('does.not.exist')
def testClearApplicationState_packageDoesntExistOnAndroidJBMR2OrAbove(self):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '18'),
(self.call.device.RunShellCommand(
- ['pm', 'clear', 'this.package.does.not.exist'],
- check_return=True),
+ ['pm', 'clear', 'this.package.does.not.exist'], check_return=True),
['Failed'])):
self.device.ClearApplicationState('this.package.does.not.exist')
@@ -1834,8 +2161,7 @@ class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest):
(self.call.device._GetApplicationPathsInternal('this.package.exists'),
['/data/app/this.package.exists.apk']),
(self.call.device.RunShellCommand(
- ['pm', 'clear', 'this.package.exists'],
- check_return=True),
+ ['pm', 'clear', 'this.package.exists'], check_return=True),
['Success'])):
self.device.ClearApplicationState('this.package.exists')
@@ -1843,21 +2169,18 @@ class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest):
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '18'),
(self.call.device.RunShellCommand(
- ['pm', 'clear', 'this.package.exists'],
- check_return=True),
+ ['pm', 'clear', 'this.package.exists'], check_return=True),
['Success'])):
self.device.ClearApplicationState('this.package.exists')
class DeviceUtilsSendKeyEventTest(DeviceUtilsTest):
-
def testSendKeyEvent(self):
with self.assertCall(self.call.adb.Shell('input keyevent 66'), ''):
self.device.SendKeyEvent(66)
class DeviceUtilsPushChangedFilesIndividuallyTest(DeviceUtilsTest):
-
def testPushChangedFilesIndividually_empty(self):
test_files = []
with self.assertCalls():
@@ -1869,23 +2192,19 @@ class DeviceUtilsPushChangedFilesIndividuallyTest(DeviceUtilsTest):
self.device._PushChangedFilesIndividually(test_files)
def testPushChangedFilesIndividually_multiple(self):
- test_files = [
- ('/test/host/path/file1', '/test/device/path/file1'),
- ('/test/host/path/file2', '/test/device/path/file2')]
+ test_files = [('/test/host/path/file1', '/test/device/path/file1'),
+ ('/test/host/path/file2', '/test/device/path/file2')]
with self.assertCalls(
- self.call.adb.Push(*test_files[0]),
- self.call.adb.Push(*test_files[1])):
+ self.call.adb.Push(*test_files[0]), self.call.adb.Push(*test_files[1])):
self.device._PushChangedFilesIndividually(test_files)
class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsTest):
-
def testPushChangedFilesZipped_noUnzipCommand(self):
test_files = [('/test/host/path/file1', '/test/device/path/file1')]
- with self.assertCalls(
- (self.call.device._MaybeInstallCommands(), False)):
- self.assertFalse(self.device._PushChangedFilesZipped(test_files,
- ['/test/dir']))
+ with self.assertCalls((self.call.device._MaybeInstallCommands(), False)):
+ self.assertFalse(
+ self.device._PushChangedFilesZipped(test_files, ['/test/dir']))
def _testPushChangedFilesZipped_spec(self, test_files):
@contextlib.contextmanager
@@ -1895,28 +2214,25 @@ class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsTest):
with self.assertCalls(
(self.call.device._MaybeInstallCommands(), True),
(mock.call.py_utils.tempfile_ext.NamedTemporaryDirectory(),
- mock_zip_temp_dir),
- (mock.call.devil.utils.zip_utils.WriteZipFile(
- '/test/temp/dir/tmp.zip', test_files)),
- (mock.call.os.path.getsize(
- '/test/temp/dir/tmp.zip'), 123),
+ mock_zip_temp_dir), (mock.call.devil.utils.zip_utils.WriteZipFile(
+ '/test/temp/dir/tmp.zip', test_files)),
+ (mock.call.os.path.getsize('/test/temp/dir/tmp.zip'), 123),
(self.call.device.NeedsSU(), True),
- (mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb,
- suffix='.zip'),
- MockTempFile('/test/sdcard/foo123.zip')),
- self.call.adb.Push(
- '/test/temp/dir/tmp.zip', '/test/sdcard/foo123.zip'),
+ (mock.call.devil.android.device_temp_file.DeviceTempFile(
+ self.adb, suffix='.zip'), MockTempFile('/test/sdcard/foo123.zip')),
+ self.call.adb.Push('/test/temp/dir/tmp.zip', '/test/sdcard/foo123.zip'),
self.call.device.RunShellCommand(
'unzip /test/sdcard/foo123.zip&&chmod -R 777 /test/dir',
- shell=True, as_root=True,
+ shell=True,
+ as_root=True,
env={'PATH': '/data/local/tmp/bin:$PATH'},
check_return=True)):
- self.assertTrue(self.device._PushChangedFilesZipped(test_files,
- ['/test/dir']))
+ self.assertTrue(
+ self.device._PushChangedFilesZipped(test_files, ['/test/dir']))
def testPushChangedFilesZipped_single(self):
- self._testPushChangedFilesZipped_spec(
- [('/test/host/path/file1', '/test/device/path/file1')])
+ self._testPushChangedFilesZipped_spec([('/test/host/path/file1',
+ '/test/device/path/file1')])
def testPushChangedFilesZipped_multiple(self):
self._testPushChangedFilesZipped_spec(
@@ -1925,37 +2241,42 @@ class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsTest):
class DeviceUtilsPathExistsTest(DeviceUtilsTest):
-
def testPathExists_pathExists(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['test', '-e', '/path/file exists'],
- as_root=False, check_return=True, timeout=10, retries=0),
- []):
+ self.call.device.RunShellCommand(['test', '-e', '/path/file exists'],
+ as_root=False,
+ check_return=True,
+ timeout=10,
+ retries=0), []):
self.assertTrue(self.device.PathExists('/path/file exists'))
def testPathExists_multiplePathExists(self):
with self.assertCall(
self.call.device.RunShellCommand(
['test', '-e', '/path 1', '-a', '-e', '/path2'],
- as_root=False, check_return=True, timeout=10, retries=0),
- []):
+ as_root=False,
+ check_return=True,
+ timeout=10,
+ retries=0), []):
self.assertTrue(self.device.PathExists(('/path 1', '/path2')))
def testPathExists_pathDoesntExist(self):
with self.assertCall(
self.call.device.RunShellCommand(
['test', '-e', '/path/file.not.exists'],
- as_root=False, check_return=True, timeout=10, retries=0),
- self.ShellError()):
+ as_root=False,
+ check_return=True,
+ timeout=10,
+ retries=0), self.ShellError()):
self.assertFalse(self.device.PathExists('/path/file.not.exists'))
def testPathExists_asRoot(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['test', '-e', '/root/path/exists'],
- as_root=True, check_return=True, timeout=10, retries=0),
- self.ShellError()):
+ self.call.device.RunShellCommand(['test', '-e', '/root/path/exists'],
+ as_root=True,
+ check_return=True,
+ timeout=10,
+ retries=0), self.ShellError()):
self.assertFalse(
self.device.PathExists('/root/path/exists', as_root=True))
@@ -1963,52 +2284,51 @@ class DeviceUtilsPathExistsTest(DeviceUtilsTest):
with self.assertCall(
self.call.device.RunShellCommand(
['test', '-e', '/path/file.not.exists'],
- as_root=False, check_return=True, timeout=10, retries=0),
- self.ShellError()):
+ as_root=False,
+ check_return=True,
+ timeout=10,
+ retries=0), self.ShellError()):
self.assertFalse(self.device.FileExists('/path/file.not.exists'))
class DeviceUtilsRemovePathTest(DeviceUtilsTest):
-
def testRemovePath_regular(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['rm', 'some file'], as_root=False, check_return=True),
- []):
+ self.call.device.RunShellCommand(['rm', 'some file'],
+ as_root=False,
+ check_return=True), []):
self.device.RemovePath('some file')
def testRemovePath_withForce(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['rm', '-f', 'some file'], as_root=False, check_return=True),
- []):
+ self.call.device.RunShellCommand(['rm', '-f', 'some file'],
+ as_root=False,
+ check_return=True), []):
self.device.RemovePath('some file', force=True)
def testRemovePath_recursively(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['rm', '-r', '/remove/this/dir'], as_root=False, check_return=True),
- []):
+ self.call.device.RunShellCommand(['rm', '-r', '/remove/this/dir'],
+ as_root=False,
+ check_return=True), []):
self.device.RemovePath('/remove/this/dir', recursive=True)
def testRemovePath_withRoot(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['rm', 'some file'], as_root=True, check_return=True),
- []):
+ self.call.device.RunShellCommand(['rm', 'some file'],
+ as_root=True,
+ check_return=True), []):
self.device.RemovePath('some file', as_root=True)
def testRemovePath_manyPaths(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['rm', 'eeny', 'meeny', 'miny', 'moe'],
- as_root=False, check_return=True),
- []):
+ self.call.device.RunShellCommand(['rm', 'eeny', 'meeny', 'miny', 'moe'],
+ as_root=False,
+ check_return=True), []):
self.device.RemovePath(['eeny', 'meeny', 'miny', 'moe'])
class DeviceUtilsPullFileTest(DeviceUtilsTest):
-
def testPullFile_existsOnDevice(self):
with mock.patch('os.path.exists', return_value=True):
with self.assertCall(
@@ -2030,33 +2350,35 @@ class DeviceUtilsPullFileTest(DeviceUtilsTest):
def testPullFile_asRoot(self):
with mock.patch('os.path.exists', return_value=True):
with self.assertCalls(
- (self.call.device.NeedsSU(), True),
- (self.call.device.PathExists('/this/file/can.be.read.with.su',
- as_root=True), True),
+ (self.call.device.NeedsSU(), True), (self.call.device.PathExists(
+ '/this/file/can.be.read.with.su', as_root=True), True),
(mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb),
MockTempFile('/sdcard/tmp/on.device')),
self.call.device.RunShellCommand(
'SRC=/this/file/can.be.read.with.su DEST=/sdcard/tmp/on.device;'
'cp "$SRC" "$DEST" && chmod 666 "$DEST"',
- shell=True, as_root=True, check_return=True),
- (self.call.adb.Pull('/sdcard/tmp/on.device',
- '/test/file/host/path'))):
- self.device.PullFile('/this/file/can.be.read.with.su',
- '/test/file/host/path', as_root=True)
+ shell=True,
+ as_root=True,
+ check_return=True), (self.call.adb.Pull('/sdcard/tmp/on.device',
+ '/test/file/host/path'))):
+ self.device.PullFile(
+ '/this/file/can.be.read.with.su',
+ '/test/file/host/path',
+ as_root=True)
def testPullFile_asRootDoesntExistOnDevice(self):
with mock.patch('os.path.exists', return_value=True):
with self.assertCalls(
- (self.call.device.NeedsSU(), True),
- (self.call.device.PathExists('/data/app/test.file.does.not.exist',
- as_root=True), False)):
+ (self.call.device.NeedsSU(), True), (self.call.device.PathExists(
+ '/data/app/test.file.does.not.exist', as_root=True), False)):
with self.assertRaises(device_errors.CommandFailedError):
- self.device.PullFile('/data/app/test.file.does.not.exist',
- '/test/file/host/path', as_root=True)
+ self.device.PullFile(
+ '/data/app/test.file.does.not.exist',
+ '/test/file/host/path',
+ as_root=True)
class DeviceUtilsReadFileTest(DeviceUtilsTest):
-
def testReadFileWithPull_success(self):
tmp_host_dir = '/tmp/dir/on.host/'
tmp_host = MockTempFile('/tmp/dir/on.host/tmp_ReadFileWithPull')
@@ -2073,93 +2395,82 @@ class DeviceUtilsReadFileTest(DeviceUtilsTest):
def testReadFileWithPull_rejected(self):
tmp_host_dir = '/tmp/dir/on.host/'
- with self.assertCalls(
- (mock.call.tempfile.mkdtemp(), tmp_host_dir),
- (self.call.adb.Pull('/path/to/device/file', mock.ANY),
- self.CommandError()),
- (mock.call.os.path.exists(tmp_host_dir), True),
- (mock.call.shutil.rmtree(tmp_host_dir), None)):
+ with self.assertCalls((mock.call.tempfile.mkdtemp(), tmp_host_dir),
+ (self.call.adb.Pull('/path/to/device/file', mock.ANY),
+ self.CommandError()),
+ (mock.call.os.path.exists(tmp_host_dir), True),
+ (mock.call.shutil.rmtree(tmp_host_dir), None)):
with self.assertRaises(device_errors.CommandFailedError):
self.device._ReadFileWithPull('/path/to/device/file')
- def testReadFile_exists(self):
- with self.assertCalls(
- (self.call.device.FileSize('/read/this/test/file', as_root=False), 256),
- (self.call.device.RunShellCommand(
- ['cat', '/read/this/test/file'],
- as_root=False, check_return=True),
- ['this is a test file'])):
- self.assertEqual('this is a test file\n',
- self.device.ReadFile('/read/this/test/file'))
-
- def testReadFile_exists2(self):
- # Same as testReadFile_exists, but uses Android N ls output.
- with self.assertCalls(
- (self.call.device.FileSize('/read/this/test/file', as_root=False), 256),
- (self.call.device.RunShellCommand(
- ['cat', '/read/this/test/file'],
- as_root=False, check_return=True),
- ['this is a test file'])):
- self.assertEqual('this is a test file\n',
- self.device.ReadFile('/read/this/test/file'))
-
- def testReadFile_doesNotExist(self):
- with self.assertCall(
- self.call.device.FileSize('/this/file/does.not.exist', as_root=False),
- self.CommandError('File does not exist')):
- with self.assertRaises(device_errors.CommandFailedError):
- self.device.ReadFile('/this/file/does.not.exist')
-
- def testReadFile_zeroSize(self):
+ def testReadFile_withSU_zeroSize(self):
with self.assertCalls(
- (self.call.device.FileSize('/this/file/has/zero/size', as_root=False),
- 0),
- (self.call.device._ReadFileWithPull('/this/file/has/zero/size'),
+ (self.call.device.NeedsSU(), True),
+ (self.call.device.FileSize(
+ '/this/file/has/zero/size', as_root=True), 0),
+ (mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb),
+ MockTempFile('/sdcard/tmp/on.device')),
+ self.call.device.RunShellCommand(
+ 'SRC=/this/file/has/zero/size DEST=/sdcard/tmp/on.device;'
+ 'cp "$SRC" "$DEST" && chmod 666 "$DEST"',
+ shell=True,
+ as_root=True,
+ check_return=True),
+ (self.call.device._ReadFileWithPull('/sdcard/tmp/on.device'),
'but it has contents\n')):
self.assertEqual('but it has contents\n',
- self.device.ReadFile('/this/file/has/zero/size'))
+ self.device.ReadFile('/this/file/has/zero/size',
+ as_root=True))
def testReadFile_withSU(self):
with self.assertCalls(
+ (self.call.device.NeedsSU(), True),
(self.call.device.FileSize(
'/this/file/can.be.read.with.su', as_root=True), 256),
(self.call.device.RunShellCommand(
['cat', '/this/file/can.be.read.with.su'],
- as_root=True, check_return=True),
- ['this is a test file', 'read with su'])):
+ as_root=True,
+ check_return=True), ['this is a test file', 'read with su'])):
self.assertEqual(
'this is a test file\nread with su\n',
- self.device.ReadFile('/this/file/can.be.read.with.su',
- as_root=True))
+ self.device.ReadFile('/this/file/can.be.read.with.su', as_root=True))
+
+ def testReadFile_withSU_doesNotExist(self):
+ with self.assertCalls(
+ (self.call.device.NeedsSU(), True),
+ (self.call.device.FileSize('/this/file/does.not.exist', as_root=True),
+ self.CommandError('File does not exist'))):
+ with self.assertRaises(device_errors.CommandFailedError):
+ self.device.ReadFile('/this/file/does.not.exist', as_root=True)
def testReadFile_withPull(self):
contents = 'a' * 123456
with self.assertCalls(
- (self.call.device.FileSize('/read/this/big/test/file', as_root=False),
- 123456),
(self.call.device._ReadFileWithPull('/read/this/big/test/file'),
contents)):
- self.assertEqual(
- contents, self.device.ReadFile('/read/this/big/test/file'))
+ self.assertEqual(contents,
+ self.device.ReadFile('/read/this/big/test/file'))
def testReadFile_withPullAndSU(self):
contents = 'b' * 123456
with self.assertCalls(
+ (self.call.device.NeedsSU(), True),
(self.call.device.FileSize(
'/this/big/file/can.be.read.with.su', as_root=True), 123456),
- (self.call.device.NeedsSU(), True),
(mock.call.devil.android.device_temp_file.DeviceTempFile(self.adb),
MockTempFile('/sdcard/tmp/on.device')),
self.call.device.RunShellCommand(
'SRC=/this/big/file/can.be.read.with.su DEST=/sdcard/tmp/on.device;'
'cp "$SRC" "$DEST" && chmod 666 "$DEST"',
- shell=True, as_root=True, check_return=True),
+ shell=True,
+ as_root=True,
+ check_return=True),
(self.call.device._ReadFileWithPull('/sdcard/tmp/on.device'),
contents)):
self.assertEqual(
contents,
- self.device.ReadFile('/this/big/file/can.be.read.with.su',
- as_root=True))
+ self.device.ReadFile(
+ '/this/big/file/can.be.read.with.su', as_root=True))
def testReadFile_forcePull(self):
contents = 'a' * 123456
@@ -2172,13 +2483,12 @@ class DeviceUtilsReadFileTest(DeviceUtilsTest):
class DeviceUtilsWriteFileTest(DeviceUtilsTest):
-
def testWriteFileWithPush_success(self):
tmp_host = MockTempFile('/tmp/file/on.host')
contents = 'some interesting contents'
- with self.assertCalls(
- (mock.call.tempfile.NamedTemporaryFile(), tmp_host),
- self.call.adb.Push('/tmp/file/on.host', '/path/to/device/file')):
+ with self.assertCalls((mock.call.tempfile.NamedTemporaryFile(), tmp_host),
+ self.call.adb.Push('/tmp/file/on.host',
+ '/path/to/device/file')):
self.device._WriteFileWithPush('/path/to/device/file', contents)
tmp_host.file.write.assert_called_once_with(contents)
@@ -2213,17 +2523,19 @@ class DeviceUtilsWriteFileTest(DeviceUtilsTest):
self.call.device._WriteFileWithPush('/sdcard/tmp/on.device', contents),
self.call.device.RunShellCommand(
['cp', '/sdcard/tmp/on.device', '/path/to/device/file'],
- as_root=True, check_return=True)):
+ as_root=True,
+ check_return=True)):
self.device.WriteFile('/path/to/device/file', contents, as_root=True)
def testWriteFile_withEcho(self):
- with self.assertCall(self.call.adb.Shell(
- "echo -n the.contents > /test/file/to.write"), ''):
+ with self.assertCall(
+ self.call.adb.Shell("echo -n the.contents > /test/file/to.write"), ''):
self.device.WriteFile('/test/file/to.write', 'the.contents')
def testWriteFile_withEchoAndQuotes(self):
- with self.assertCall(self.call.adb.Shell(
- "echo -n 'the contents' > '/test/file/to write'"), ''):
+ with self.assertCall(
+ self.call.adb.Shell("echo -n 'the contents' > '/test/file/to write'"),
+ ''):
self.device.WriteFile('/test/file/to write', 'the contents')
def testWriteFile_withEchoAndSU(self):
@@ -2232,8 +2544,7 @@ class DeviceUtilsWriteFileTest(DeviceUtilsTest):
with self.assertCalls(
(self.call.device.NeedsSU(), True),
(self.call.device._Su(expected_cmd_without_su), expected_cmd),
- (self.call.adb.Shell(expected_cmd),
- '')):
+ (self.call.adb.Shell(expected_cmd), '')):
self.device.WriteFile('/test/file', 'contents', as_root=True)
@@ -2241,42 +2552,45 @@ class DeviceUtilsStatDirectoryTest(DeviceUtilsTest):
# Note: Also tests ListDirectory in testStatDirectory_fileList.
EXAMPLE_LS_OUTPUT = [
- 'total 12345',
- 'drwxr-xr-x 19 root root 0 1970-04-06 18:03 .',
- 'drwxr-xr-x 19 root root 0 1970-04-06 18:03 ..',
- 'drwxr-xr-x 6 root root 1970-01-01 00:00 some_dir',
- '-rw-r--r-- 1 root root 723 1971-01-01 07:04 some_file',
- '-rw-r----- 1 root root 327 2009-02-13 23:30 My Music File',
- # Some Android versions escape spaces in file names
- '-rw-rw-rw- 1 root root 0 2018-01-11 13:35 Local\\ State',
- # Older Android versions do not print st_nlink
- 'lrwxrwxrwx root root 1970-01-01 00:00 lnk -> /some/path',
- 'srwxrwx--- system system 2016-05-31 17:25 a_socket1',
- 'drwxrwxrwt system misc 1970-11-23 02:25 tmp',
- 'drwxr-s--- system shell 1970-11-23 02:24 my_cmd',
- 'cr--r----- root system 10, 183 1971-01-01 07:04 random',
- 'brw------- root root 7, 0 1971-01-01 07:04 block_dev',
- '-rwS------ root shell 157404 2015-04-13 15:44 silly',
+ 'total 12345',
+ 'drwxr-xr-x 19 root root 0 1970-04-06 18:03 .',
+ 'drwxr-xr-x 19 root root 0 1970-04-06 18:03 ..',
+ 'drwxr-xr-x 6 root root 1970-01-01 00:00 some_dir',
+ '-rw-r--r-- 1 root root 723 1971-01-01 07:04 some_file',
+ '-rw-r----- 1 root root 327 2009-02-13 23:30 My Music File',
+ # Some Android versions escape spaces in file names
+ '-rw-rw-rw- 1 root root 0 2018-01-11 13:35 Local\\ State',
+ # Older Android versions do not print st_nlink
+ 'lrwxrwxrwx root root 1970-01-01 00:00 lnk -> /a/path',
+ 'srwxrwx--- system system 2016-05-31 17:25 a_socket1',
+ 'drwxrwxrwt system misc 1970-11-23 02:25 tmp',
+ 'drwxr-s--- system shell 1970-11-23 02:24 my_cmd',
+ 'cr--r----- root system 10, 183 1971-01-01 07:04 random',
+ 'brw------- root root 7, 0 1971-01-01 07:04 block_dev',
+ '-rwS------ root shell 157404 2015-04-13 15:44 silly',
]
FILENAMES = [
- 'some_dir', 'some_file', 'My Music File', 'Local State', 'lnk',
- 'a_socket1', 'tmp', 'my_cmd', 'random', 'block_dev', 'silly']
+ 'some_dir', 'some_file', 'My Music File', 'Local State', 'lnk',
+ 'a_socket1', 'tmp', 'my_cmd', 'random', 'block_dev', 'silly'
+ ]
def getStatEntries(self, path_given='/', path_listed='/'):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['ls', '-a', '-l', path_listed],
- check_return=True, as_root=False, env={'TZ': 'utc'}),
+ self.call.device.RunShellCommand(['ls', '-a', '-l', path_listed],
+ check_return=True,
+ as_root=False,
+ env={'TZ': 'utc'}),
self.EXAMPLE_LS_OUTPUT):
entries = self.device.StatDirectory(path_given)
return {f['filename']: f for f in entries}
def getListEntries(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['ls', '-a', '-l', '/'],
- check_return=True, as_root=False, env={'TZ': 'utc'}),
+ self.call.device.RunShellCommand(['ls', '-a', '-l', '/'],
+ check_return=True,
+ as_root=False,
+ env={'TZ': 'utc'}),
self.EXAMPLE_LS_OUTPUT):
return self.device.ListDirectory('/')
@@ -2290,12 +2604,12 @@ class DeviceUtilsStatDirectoryTest(DeviceUtilsTest):
def testStatDirectory_fileModes(self):
expected_modes = (
- ('some_dir', stat.S_ISDIR),
- ('some_file', stat.S_ISREG),
- ('lnk', stat.S_ISLNK),
- ('a_socket1', stat.S_ISSOCK),
- ('block_dev', stat.S_ISBLK),
- ('random', stat.S_ISCHR),
+ ('some_dir', stat.S_ISDIR),
+ ('some_file', stat.S_ISREG),
+ ('lnk', stat.S_ISLNK),
+ ('a_socket1', stat.S_ISSOCK),
+ ('block_dev', stat.S_ISBLK),
+ ('random', stat.S_ISCHR),
)
entries = self.getStatEntries()
for filename, check in expected_modes:
@@ -2303,16 +2617,16 @@ class DeviceUtilsStatDirectoryTest(DeviceUtilsTest):
def testStatDirectory_filePermissions(self):
should_have = (
- ('some_file', stat.S_IWUSR), # Owner can write.
- ('tmp', stat.S_IXOTH), # Others can execute.
- ('tmp', stat.S_ISVTX), # Has sticky bit.
- ('my_cmd', stat.S_ISGID), # Has set-group-ID bit.
- ('silly', stat.S_ISUID), # Has set UID bit.
+ ('some_file', stat.S_IWUSR), # Owner can write.
+ ('tmp', stat.S_IXOTH), # Others can execute.
+ ('tmp', stat.S_ISVTX), # Has sticky bit.
+ ('my_cmd', stat.S_ISGID), # Has set-group-ID bit.
+ ('silly', stat.S_ISUID), # Has set UID bit.
)
should_not_have = (
- ('some_file', stat.S_IWOTH), # Others can't write.
- ('block_dev', stat.S_IRGRP), # Group can't read.
- ('silly', stat.S_IXUSR), # Owner can't execute.
+ ('some_file', stat.S_IWOTH), # Others can't write.
+ ('block_dev', stat.S_IRGRP), # Group can't read.
+ ('silly', stat.S_IXUSR), # Owner can't execute.
)
entries = self.getStatEntries()
for filename, bit in should_have:
@@ -2353,17 +2667,21 @@ class DeviceUtilsStatDirectoryTest(DeviceUtilsTest):
def testStatDirectory_symbolicLinks(self):
entries = self.getStatEntries()
- self.assertEqual(entries['lnk']['symbolic_link_to'], '/some/path')
+ self.assertEqual(entries['lnk']['symbolic_link_to'], '/a/path')
for d in entries.itervalues():
self.assertEqual('symbolic_link_to' in d, stat.S_ISLNK(d['st_mode']))
class DeviceUtilsStatPathTest(DeviceUtilsTest):
- EXAMPLE_DIRECTORY = [
- {'filename': 'foo.txt', 'st_size': 123, 'st_time': 456},
- {'filename': 'some_dir', 'st_time': 0}
- ]
+ EXAMPLE_DIRECTORY = [{
+ 'filename': 'foo.txt',
+ 'st_size': 123,
+ 'st_time': 456
+ }, {
+ 'filename': 'some_dir',
+ 'st_time': 0
+ }]
INDEX = {e['filename']: e for e in EXAMPLE_DIRECTORY}
def testStatPath_file(self):
@@ -2397,17 +2715,20 @@ class DeviceUtilsStatPathTest(DeviceUtilsTest):
class DeviceUtilsFileSizeTest(DeviceUtilsTest):
- EXAMPLE_DIRECTORY = [
- {'filename': 'foo.txt', 'st_size': 123, 'st_mtime': 456},
- {'filename': 'some_dir', 'st_mtime': 0}
- ]
+ EXAMPLE_DIRECTORY = [{
+ 'filename': 'foo.txt',
+ 'st_size': 123,
+ 'st_mtime': 456
+ }, {
+ 'filename': 'some_dir',
+ 'st_mtime': 0
+ }]
def testFileSize_file(self):
with self.assertCall(
self.call.device.StatDirectory('/data/local/tmp', as_root=False),
self.EXAMPLE_DIRECTORY):
- self.assertEquals(123,
- self.device.FileSize('/data/local/tmp/foo.txt'))
+ self.assertEquals(123, self.device.FileSize('/data/local/tmp/foo.txt'))
def testFileSize_doesNotExist(self):
with self.assertCall(
@@ -2425,7 +2746,6 @@ class DeviceUtilsFileSizeTest(DeviceUtilsTest):
class DeviceUtilsSetJavaAssertsTest(DeviceUtilsTest):
-
def testSetJavaAsserts_enable(self):
with self.assertCalls(
(self.call.device.ReadFile(self.device.LOCAL_PROPERTIES_PATH),
@@ -2475,14 +2795,14 @@ class DeviceUtilsSetJavaAssertsTest(DeviceUtilsTest):
class DeviceUtilsEnsureCacheInitializedTest(DeviceUtilsTest):
-
def testEnsureCacheInitialized_noCache_success(self):
self.assertIsNone(self.device._cache['token'])
with self.assertCall(
self.call.device.RunShellCommand(
AnyStringWith('getprop'),
- shell=True, check_return=True, large_output=True),
- ['/sdcard', 'TOKEN']):
+ shell=True,
+ check_return=True,
+ large_output=True), ['/sdcard', 'TOKEN']):
self.device._EnsureCacheInitialized()
self.assertIsNotNone(self.device._cache['token'])
@@ -2491,8 +2811,9 @@ class DeviceUtilsEnsureCacheInitializedTest(DeviceUtilsTest):
with self.assertCall(
self.call.device.RunShellCommand(
AnyStringWith('getprop'),
- shell=True, check_return=True, large_output=True),
- self.TimeoutError()):
+ shell=True,
+ check_return=True,
+ large_output=True), self.TimeoutError()):
with self.assertRaises(device_errors.CommandTimeoutError):
self.device._EnsureCacheInitialized()
self.assertIsNone(self.device._cache['token'])
@@ -2505,24 +2826,23 @@ class DeviceUtilsEnsureCacheInitializedTest(DeviceUtilsTest):
class DeviceUtilsGetPropTest(DeviceUtilsTest):
-
def testGetProp_exists(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['getprop', 'test.property'], check_return=True, single_line=True,
- timeout=self.device._default_timeout,
- retries=self.device._default_retries),
+ self.call.device.RunShellCommand(['getprop', 'test.property'],
+ check_return=True,
+ single_line=True,
+ timeout=self.device._default_timeout,
+ retries=self.device._default_retries),
'property_value'):
- self.assertEqual('property_value',
- self.device.GetProp('test.property'))
+ self.assertEqual('property_value', self.device.GetProp('test.property'))
def testGetProp_doesNotExist(self):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['getprop', 'property.does.not.exist'],
- check_return=True, single_line=True,
- timeout=self.device._default_timeout,
- retries=self.device._default_retries),
+ self.call.device.RunShellCommand(['getprop', 'property.does.not.exist'],
+ check_return=True,
+ single_line=True,
+ timeout=self.device._default_timeout,
+ retries=self.device._default_retries),
''):
self.assertEqual('', self.device.GetProp('property.does.not.exist'))
@@ -2536,7 +2856,6 @@ class DeviceUtilsGetPropTest(DeviceUtilsTest):
class DeviceUtilsSetPropTest(DeviceUtilsTest):
-
def testSetProp(self):
with self.assertCall(
self.call.device.RunShellCommand(
@@ -2576,26 +2895,28 @@ class DeviceUtilsListProcessesTest(DeviceUtilsTest):
return [line for line in self.sample_output if substring in line]
def testListProcesses_sdkGreaterThanNougatMR1(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=(version_codes.NOUGAT_MR1 + 1)):
- with self.patch_call(self.call.device.build_id,
- return_value='ZZZ99Z'):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=(version_codes.NOUGAT_MR1 + 1)):
+ with self.patch_call(self.call.device.build_id, return_value='ZZZ99Z'):
with self.assertCall(
self.call.device._RunPipedShellCommand(
'ps -e | grep -F example.process'), []):
self.device.ListProcesses('example.process')
def testListProcesses_noMatches(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F does.not.match'),
self._grepOutput('does.not.match')):
self.assertEqual([], self.device.ListProcesses('does.not.match'))
def testListProcesses_oneMatch(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F one.match'),
self._grepOutput('one.match')):
@@ -2604,20 +2925,21 @@ class DeviceUtilsListProcessesTest(DeviceUtilsTest):
self.device.ListProcesses('one.match'))
def testListProcesses_multipleMatches(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F match'),
self._grepOutput('match')):
self.assertEqual(
- Processes(('one.match', 1001, 100),
- ('two.match', 1002, 100),
+ Processes(('one.match', 1001, 100), ('two.match', 1002, 100),
('three.match', 1003, 101)),
self.device.ListProcesses('match'))
def testListProcesses_quotable(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand("ps | grep -F 'my$process'"),
self._grepOutput('my$process')):
@@ -2627,34 +2949,36 @@ class DeviceUtilsListProcessesTest(DeviceUtilsTest):
# Tests for the GetPids wrapper interface.
def testGetPids_multipleInstances(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F foo'),
self._grepOutput('foo')):
- self.assertEqual(
- {'foo': ['1236', '1578']},
- self.device.GetPids('foo'))
+ self.assertEqual({'foo': ['1236', '1578']}, self.device.GetPids('foo'))
def testGetPids_allProcesses(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
- self.call.device.RunShellCommand(
- ['ps'], check_return=True, large_output=True),
+ self.call.device.RunShellCommand(['ps'],
+ check_return=True,
+ large_output=True),
self.sample_output):
- self.assertEqual(
- {'one.match': ['1001'],
- 'two.match': ['1002'],
- 'three.match': ['1003'],
- 'my$process': ['1234'],
- 'foo': ['1236', '1578']},
- self.device.GetPids())
+ self.assertEqual({
+ 'one.match': ['1001'],
+ 'two.match': ['1002'],
+ 'three.match': ['1003'],
+ 'my$process': ['1234'],
+ 'foo': ['1236', '1578']
+ }, self.device.GetPids())
# Tests for the GetApplicationPids wrapper interface.
def testGetApplicationPids_notFound(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F match'),
self._grepOutput('match')):
@@ -2662,47 +2986,48 @@ class DeviceUtilsListProcessesTest(DeviceUtilsTest):
self.assertEqual([], self.device.GetApplicationPids('match'))
def testGetApplicationPids_foundOne(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F one.match'),
self._grepOutput('one.match')):
self.assertEqual([1001], self.device.GetApplicationPids('one.match'))
def testGetApplicationPids_foundMany(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F foo'),
self._grepOutput('foo')):
- self.assertEqual(
- [1236, 1578],
- self.device.GetApplicationPids('foo'))
+ self.assertEqual([1236, 1578], self.device.GetApplicationPids('foo'))
def testGetApplicationPids_atMostOneNotFound(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F match'),
self._grepOutput('match')):
# No PIDs found, process name should be exact match.
self.assertEqual(
- None,
- self.device.GetApplicationPids('match', at_most_one=True))
+ None, self.device.GetApplicationPids('match', at_most_one=True))
def testGetApplicationPids_atMostOneFound(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F one.match'),
self._grepOutput('one.match')):
self.assertEqual(
- 1001,
- self.device.GetApplicationPids('one.match', at_most_one=True))
+ 1001, self.device.GetApplicationPids('one.match', at_most_one=True))
def testGetApplicationPids_atMostOneFoundTooMany(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
with self.assertRaises(device_errors.CommandFailedError):
with self.assertCall(
self.call.device._RunPipedShellCommand('ps | grep -F foo'),
@@ -2711,7 +3036,6 @@ class DeviceUtilsListProcessesTest(DeviceUtilsTest):
class DeviceUtilsGetSetEnforce(DeviceUtilsTest):
-
def testGetEnforce_Enforcing(self):
with self.assertCall(self.call.adb.Shell('getenforce'), 'Enforcing'):
self.assertEqual(True, self.device.GetEnforce())
@@ -2725,44 +3049,37 @@ class DeviceUtilsGetSetEnforce(DeviceUtilsTest):
self.assertEqual(None, self.device.GetEnforce())
def testSetEnforce_Enforcing(self):
- with self.assertCalls(
- (self.call.device.NeedsSU(), False),
- (self.call.adb.Shell('setenforce 1'), '')):
+ with self.assertCalls((self.call.device.NeedsSU(), False),
+ (self.call.adb.Shell('setenforce 1'), '')):
self.device.SetEnforce(enabled=True)
def testSetEnforce_Permissive(self):
- with self.assertCalls(
- (self.call.device.NeedsSU(), False),
- (self.call.adb.Shell('setenforce 0'), '')):
+ with self.assertCalls((self.call.device.NeedsSU(), False),
+ (self.call.adb.Shell('setenforce 0'), '')):
self.device.SetEnforce(enabled=False)
def testSetEnforce_EnforcingWithInt(self):
- with self.assertCalls(
- (self.call.device.NeedsSU(), False),
- (self.call.adb.Shell('setenforce 1'), '')):
+ with self.assertCalls((self.call.device.NeedsSU(), False),
+ (self.call.adb.Shell('setenforce 1'), '')):
self.device.SetEnforce(enabled=1)
def testSetEnforce_PermissiveWithInt(self):
- with self.assertCalls(
- (self.call.device.NeedsSU(), False),
- (self.call.adb.Shell('setenforce 0'), '')):
+ with self.assertCalls((self.call.device.NeedsSU(), False),
+ (self.call.adb.Shell('setenforce 0'), '')):
self.device.SetEnforce(enabled=0)
def testSetEnforce_EnforcingWithStr(self):
- with self.assertCalls(
- (self.call.device.NeedsSU(), False),
- (self.call.adb.Shell('setenforce 1'), '')):
+ with self.assertCalls((self.call.device.NeedsSU(), False),
+ (self.call.adb.Shell('setenforce 1'), '')):
self.device.SetEnforce(enabled='1')
def testSetEnforce_PermissiveWithStr(self):
- with self.assertCalls(
- (self.call.device.NeedsSU(), False),
- (self.call.adb.Shell('setenforce 0'), '')):
+ with self.assertCalls((self.call.device.NeedsSU(), False),
+ (self.call.adb.Shell('setenforce 0'), '')):
self.device.SetEnforce(enabled='0') # Not recommended but it works!
class DeviceUtilsGetWebViewUpdateServiceDumpTest(DeviceUtilsTest):
-
def testGetWebViewUpdateServiceDump_success(self):
# Some of the lines of adb shell dumpsys webviewupdate:
dumpsys_lines = [
@@ -2782,15 +3099,14 @@ class DeviceUtilsGetWebViewUpdateServiceDumpTest(DeviceUtilsTest):
'version too low'),
('com.chrome.canary is NOT installed.'),
]
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.OREO):
with self.assertCall(
self.call.adb.Shell('dumpsys webviewupdate'),
'\n'.join(dumpsys_lines)):
update = self.device.GetWebViewUpdateServiceDump()
self.assertTrue(update['FallbackLogicEnabled'])
- self.assertEqual('com.android.chrome',
- update['CurrentWebViewPackage'])
+ self.assertEqual('com.android.chrome', update['CurrentWebViewPackage'])
self.assertEqual(12345, update['MinimumWebViewVersionCode'])
# Order isn't really important, and we shouldn't have duplicates, so we
# convert to sets.
@@ -2799,49 +3115,49 @@ class DeviceUtilsGetWebViewUpdateServiceDumpTest(DeviceUtilsTest):
'com.google.android.apps.chrome', 'com.chrome.canary'
}
self.assertSetEqual(expected, set(update['WebViewPackages'].keys()))
- self.assertEquals(
- 'is installed/enabled for all users',
- update['WebViewPackages']['com.android.chrome'])
+ self.assertEquals('is installed/enabled for all users',
+ update['WebViewPackages']['com.android.chrome'])
self.assertEquals(
'is NOT installed/enabled for all users',
update['WebViewPackages']['com.google.android.webview'])
self.assertEquals(
'reason: SDK version too low',
update['WebViewPackages']['com.google.android.apps.chrome'])
- self.assertEquals(
- 'is NOT installed.',
- update['WebViewPackages']['com.chrome.canary'])
+ self.assertEquals('is NOT installed.',
+ update['WebViewPackages']['com.chrome.canary'])
def testGetWebViewUpdateServiceDump_missingkey(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
- with self.assertCall(self.call.adb.Shell('dumpsys webviewupdate'),
- 'Fallback logic enabled: true'):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.OREO):
+ with self.assertCall(
+ self.call.adb.Shell('dumpsys webviewupdate'),
+ 'Fallback logic enabled: true'):
with self.assertRaises(device_errors.CommandFailedError):
self.device.GetWebViewUpdateServiceDump()
def testGetWebViewUpdateServiceDump_noop(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT_MR1):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.NOUGAT_MR1):
with self.assertCalls():
self.device.GetWebViewUpdateServiceDump()
def testGetWebViewUpdateServiceDump_noPackage(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.OREO):
- with self.assertCall(self.call.adb.Shell('dumpsys webviewupdate'),
- 'Fallback logic enabled: true\n'
- 'Current WebView package is null'):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.OREO):
+ with self.assertCall(
+ self.call.adb.Shell('dumpsys webviewupdate'),
+ 'Fallback logic enabled: true\n'
+ 'Current WebView package is null'):
update = self.device.GetWebViewUpdateServiceDump()
self.assertEqual(True, update['FallbackLogicEnabled'])
self.assertEqual(None, update['CurrentWebViewPackage'])
class DeviceUtilsSetWebViewImplementationTest(DeviceUtilsTest):
-
def testSetWebViewImplementation_success(self):
with self.patch_call(
- self.call.device.GetApplicationPaths, return_value=['/any/path']):
+ self.call.device.IsApplicationInstalled, return_value=True):
with self.assertCall(
self.call.adb.Shell(
'cmd webviewupdate set-webview-implementation foo.org'),
@@ -2849,7 +3165,8 @@ class DeviceUtilsSetWebViewImplementationTest(DeviceUtilsTest):
self.device.SetWebViewImplementation('foo.org')
def testSetWebViewImplementation_uninstalled(self):
- with self.patch_call(self.call.device.GetApplicationPaths, return_value=[]):
+ with self.patch_call(
+ self.call.device.IsApplicationInstalled, return_value=False):
with self.assertRaises(device_errors.CommandFailedError) as cfe:
self.device.SetWebViewImplementation('foo.org')
self.assertIn('is not installed', cfe.exception.message)
@@ -2857,7 +3174,7 @@ class DeviceUtilsSetWebViewImplementationTest(DeviceUtilsTest):
def _testSetWebViewImplementationHelper(self, mock_dump_sys,
exception_message_substr):
with self.patch_call(
- self.call.device.GetApplicationPaths, return_value=['/any/path']):
+ self.call.device.IsApplicationInstalled, return_value=True):
with self.assertCall(
self.call.adb.Shell(
'cmd webviewupdate set-webview-implementation foo.org'), 'Oops!'):
@@ -2894,11 +3211,28 @@ class DeviceUtilsSetWebViewImplementationTest(DeviceUtilsTest):
self._testSetWebViewImplementationHelper(mock_dump_sys,
'WebView native library')
- def testSetWebViewImplementation_lowTargetSdkVersion(self):
- mock_dump_sys = {'WebViewPackages': {'foo.org': 'SDK version too low',}}
- with self.patch_call(self.call.device.build_version_sdk, return_value=26):
- self._testSetWebViewImplementationHelper(mock_dump_sys,
- 'higher targetSdkVersion')
+ def testSetWebViewImplementation_lowTargetSdkVersion_finalizedSdk(self):
+ mock_dump_sys = {'WebViewPackages': {'foo.org': 'SDK version too low', }}
+ with self.assertCalls(
+ (self.call.device.GetApplicationTargetSdk('foo.org'), '29'),
+ (self.call.device.GetProp('ro.build.version.preview_sdk'), '0')):
+ with self.patch_call(self.call.device.build_version_sdk, return_value=30):
+ self._testSetWebViewImplementationHelper(
+ mock_dump_sys,
+ "has targetSdkVersion '29', but valid WebView providers must "
+ "target >= 30 on this device")
+
+ def testSetWebViewImplementation_lowTargetSdkVersion_prefinalizedSdk(self):
+ mock_dump_sys = {'WebViewPackages': {'foo.org': 'SDK version too low', }}
+ with self.assertCalls(
+ (self.call.device.GetApplicationTargetSdk('foo.org'), '29'),
+ (self.call.device.GetProp('ro.build.version.preview_sdk'), '1'),
+ (self.call.device.GetProp('ro.build.version.codename'), 'R')):
+ with self.patch_call(self.call.device.build_version_sdk, return_value=29):
+ self._testSetWebViewImplementationHelper(
+ mock_dump_sys,
+ "targets a finalized SDK ('29'), but valid WebView providers must "
+ "target a pre-finalized SDK ('R') on this device")
def testSetWebViewImplementation_lowVersionCode(self):
mock_dump_sys = {
@@ -2911,62 +3245,56 @@ class DeviceUtilsSetWebViewImplementationTest(DeviceUtilsTest):
'higher versionCode')
def testSetWebViewImplementation_invalidSignature(self):
- mock_dump_sys = {
- 'WebViewPackages': {
- 'foo.org': 'Incorrect signature',
- }
- }
+ mock_dump_sys = {'WebViewPackages': {'foo.org': 'Incorrect signature'}}
self._testSetWebViewImplementationHelper(mock_dump_sys,
'signed with release keys')
class DeviceUtilsSetWebViewFallbackLogicTest(DeviceUtilsTest):
-
def testSetWebViewFallbackLogic_False_success(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(self.call.adb.Shell(
- 'cmd webviewupdate enable-redundant-packages'), 'Success'):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.NOUGAT):
+ with self.assertCall(
+ self.call.adb.Shell('cmd webviewupdate enable-redundant-packages'),
+ 'Success'):
self.device.SetWebViewFallbackLogic(False)
def testSetWebViewFallbackLogic_True_success(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(self.call.adb.Shell(
- 'cmd webviewupdate disable-redundant-packages'), 'Success'):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.NOUGAT):
+ with self.assertCall(
+ self.call.adb.Shell('cmd webviewupdate disable-redundant-packages'),
+ 'Success'):
self.device.SetWebViewFallbackLogic(True)
def testSetWebViewFallbackLogic_failure(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.NOUGAT):
- with self.assertCall(self.call.adb.Shell(
- 'cmd webviewupdate enable-redundant-packages'), 'Oops!'):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.NOUGAT):
+ with self.assertCall(
+ self.call.adb.Shell('cmd webviewupdate enable-redundant-packages'),
+ 'Oops!'):
with self.assertRaises(device_errors.CommandFailedError):
self.device.SetWebViewFallbackLogic(False)
def testSetWebViewFallbackLogic_beforeNougat(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.MARSHMALLOW):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.MARSHMALLOW):
with self.assertCalls():
self.device.SetWebViewFallbackLogic(False)
def testSetWebViewFallbackLogic_afterPie(self):
- # TODO(ntfschr): replace this with the Q constant when the SDK is public and
- # the codename is finalized.
- q_version_code = version_codes.PIE + 1
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=q_version_code):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.Q):
with self.assertCalls():
self.device.SetWebViewFallbackLogic(False)
class DeviceUtilsTakeScreenshotTest(DeviceUtilsTest):
-
def testTakeScreenshot_fileNameProvided(self):
with self.assertCalls(
(mock.call.devil.android.device_temp_file.DeviceTempFile(
- self.adb, suffix='.png'),
- MockTempFile('/tmp/path/temp-123.png')),
+ self.adb, suffix='.png'), MockTempFile('/tmp/path/temp-123.png')),
(self.call.adb.Shell('/system/bin/screencap -p /tmp/path/temp-123.png'),
''),
self.call.device.PullFile('/tmp/path/temp-123.png',
@@ -2975,7 +3303,6 @@ class DeviceUtilsTakeScreenshotTest(DeviceUtilsTest):
class DeviceUtilsDismissCrashDialogIfNeededTest(DeviceUtilsTest):
-
def testDismissCrashDialogIfNeeded_crashedPageckageNotFound(self):
sample_dumpsys_output = '''
WINDOW MANAGER WINDOWS (dumpsys window windows)
@@ -2987,9 +3314,10 @@ WINDOW MANAGER WINDOWS (dumpsys window windows)
mBaseLayer=211000 mSubLayer=0 mAnimLayer=211000+0=211000 mLastLayer=211000
'''
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), sample_dumpsys_output.split('\n'))):
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True),
+ sample_dumpsys_output.split('\n'))):
package_name = self.device.DismissCrashDialogIfNeeded()
self.assertIsNone(package_name)
@@ -3007,24 +3335,23 @@ WINDOW MANAGER WINDOWS (dumpsys window windows)
mFocusedApp=AppWindowToken{470af6f token=Token{272ec24e ActivityRecord{t894}}}
'''
with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), sample_dumpsys_output.split('\n')),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '22'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '22'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['input', 'keyevent', '66'], check_return=True)),
- (self.call.device.RunShellCommand(
- ['dumpsys', 'window', 'windows'], check_return=True,
- large_output=True), [])):
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True),
+ sample_dumpsys_output.split('\n')), (self.call.device.RunShellCommand(
+ ['input', 'keyevent', '22'], check_return=True)),
+ (self.call.device.RunShellCommand(['input', 'keyevent', '22'],
+ check_return=True)),
+ (self.call.device.RunShellCommand(['input', 'keyevent', '66'],
+ check_return=True)),
+ (self.call.device.RunShellCommand(['dumpsys', 'window', 'windows'],
+ check_return=True,
+ large_output=True), [])):
package_name = self.device.DismissCrashDialogIfNeeded()
self.assertEqual(package_name, 'com.android.chrome')
class DeviceUtilsClientCache(DeviceUtilsTest):
-
def testClientCache_twoCaches(self):
self.device._cache['test'] = 0
client_cache_one = self.device.GetClientCache('ClientOne')
@@ -3051,32 +3378,28 @@ class DeviceUtilsClientCache(DeviceUtilsTest):
class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
-
- def testHealthyDevices_emptyBlacklist_defaultDeviceArg(self):
+ def testHealthyDevices_emptyDenylist_defaultDeviceArg(self):
test_serials = ['0123456789abcdef', 'fedcba9876543210']
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
- blacklist = mock.NonCallableMock(**{'Read.return_value': []})
- devices = device_utils.DeviceUtils.HealthyDevices(blacklist)
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM),
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM)):
+ denylist = mock.NonCallableMock(**{'Read.return_value': []})
+ devices = device_utils.DeviceUtils.HealthyDevices(denylist)
for serial, device in zip(test_serials, devices):
self.assertTrue(isinstance(device, device_utils.DeviceUtils))
self.assertEquals(serial, device.adb.GetDeviceSerial())
- def testHealthyDevices_blacklist_defaultDeviceArg(self):
+ def testHealthyDevices_denylist_defaultDeviceArg(self):
test_serials = ['0123456789abcdef', 'fedcba9876543210']
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
- blacklist = mock.NonCallableMock(
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM)):
+ denylist = mock.NonCallableMock(
**{'Read.return_value': ['fedcba9876543210']})
- devices = device_utils.DeviceUtils.HealthyDevices(blacklist)
+ devices = device_utils.DeviceUtils.HealthyDevices(denylist)
self.assertEquals(1, len(devices))
self.assertTrue(isinstance(devices[0], device_utils.DeviceUtils))
self.assertEquals('0123456789abcdef', devices[0].adb.GetDeviceSerial())
@@ -3086,10 +3409,8 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM),
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM),
(mock.call.devil.android.device_errors.MultipleDevicesError(mock.ANY),
_MockMultipleDevicesError())):
with self.assertRaises(_MockMultipleDevicesError):
@@ -3100,8 +3421,7 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM)):
devices = device_utils.DeviceUtils.HealthyDevices(device_arg=None)
self.assertEquals(1, len(devices))
@@ -3132,10 +3452,8 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM),
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM)):
devices = device_utils.DeviceUtils.HealthyDevices(device_arg=())
self.assertEquals(2, len(devices))
@@ -3169,8 +3487,10 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
with self.assertRaises(device_errors.NoDevicesError):
device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=4)
self.assertEquals(mock_restart.call_count, 4)
- self.assertEquals(mock_sleep.call_args_list, [
- mock.call(2), mock.call(4), mock.call(8), mock.call(16)])
+ self.assertEquals(
+ mock_sleep.call_args_list,
+ [mock.call(2), mock.call(4),
+ mock.call(8), mock.call(16)])
@mock.patch('time.sleep')
@mock.patch('devil.android.device_utils.RestartServer')
@@ -3187,14 +3507,16 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), []),
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(), [])):
with self.assertRaises(device_errors.NoDevicesError):
- with mock.patch.object(
- mock_reset_import, 'reset_all_android_devices') as mock_reset:
- device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=4,
- enable_usb_resets=True)
+ with mock.patch.object(mock_reset_import,
+ 'reset_all_android_devices') as mock_reset:
+ device_utils.DeviceUtils.HealthyDevices(
+ device_arg=[], retries=4, enable_usb_resets=True)
self.assertEquals(mock_reset.call_count, 1)
self.assertEquals(mock_restart.call_count, 4)
- self.assertEquals(mock_sleep.call_args_list, [
- mock.call(2), mock.call(4), mock.call(8), mock.call(16)])
+ self.assertEquals(
+ mock_sleep.call_args_list,
+ [mock.call(2), mock.call(4),
+ mock.call(8), mock.call(16)])
def testHealthyDevices_ListDeviceArg(self):
device_arg = ['0123456789abcdef', 'fedcba9876543210']
@@ -3211,13 +3533,11 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
with self.assertCalls(
(mock.call.devil.android.sdk.adb_wrapper.AdbWrapper.Devices(),
[_AdbWrapperMock(s) for s in test_serials]),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM),
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM)):
with self.assertRaises(device_errors.NoDevicesError):
- device_utils.DeviceUtils.HealthyDevices(device_arg=[], retries=0,
- abis=[abis.ARM_64])
+ device_utils.DeviceUtils.HealthyDevices(
+ device_arg=[], retries=0, abis=[abis.ARM_64])
def testHealthyDevices_abisArg_filter_on_abi(self):
test_serials = ['0123456789abcdef', 'fedcba9876543210']
@@ -3226,16 +3546,13 @@ class DeviceUtilsHealthyDevicesTest(mock_calls.TestCase):
[_AdbWrapperMock(s) for s in test_serials]),
(mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
abis.ARM_64),
- (mock.call.devil.android.device_utils.DeviceUtils.GetABI(),
- abis.ARM)):
- devices = device_utils.DeviceUtils.HealthyDevices(device_arg=[],
- retries=0,
- abis=[abis.ARM_64])
+ (mock.call.devil.android.device_utils.DeviceUtils.GetABI(), abis.ARM)):
+ devices = device_utils.DeviceUtils.HealthyDevices(
+ device_arg=[], retries=0, abis=[abis.ARM_64])
self.assertEquals(1, len(devices))
class DeviceUtilsRestartAdbdTest(DeviceUtilsTest):
-
def testAdbdRestart(self):
mock_temp_file = '/sdcard/temp-123.sh'
with self.assertCalls(
@@ -3257,60 +3574,62 @@ class DeviceUtilsGrantPermissionsTest(DeviceUtilsTest):
output, status = result + '\n', 1
else:
output, status = '', 0
- results.append(
- '{output}{sep}{permission}{sep}{status}{sep}\n'.format(
- output=output,
- permission=permission,
- status=status,
- sep=device_utils._SHELL_OUTPUT_SEPARATOR
- ))
- return (
- self.call.device.RunShellCommand(
- AnyStringWith(fragment),
- shell=True, raw_output=True, large_output=True, check_return=True),
- ''.join(results))
+ results.append('{output}{sep}{permission}{sep}{status}{sep}\n'.format(
+ output=output,
+ permission=permission,
+ status=status,
+ sep=device_utils._SHELL_OUTPUT_SEPARATOR))
+ return (self.call.device.RunShellCommand(
+ AnyStringWith(fragment),
+ shell=True,
+ raw_output=True,
+ large_output=True,
+ check_return=True), ''.join(results))
def testGrantPermissions_none(self):
self.device.GrantPermissions('package', [])
- def testGrantPermissions_underM(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
- self.device.GrantPermissions('package', ['p1'])
-
def testGrantPermissions_one(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.MARSHMALLOW):
- with self.assertCalls(
- self._PmGrantShellCall('package', {'p1': 0})):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.MARSHMALLOW):
+ with self.assertCalls(self._PmGrantShellCall('package', {'p1': 0})):
self.device.GrantPermissions('package', ['p1'])
def testGrantPermissions_multiple(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.MARSHMALLOW):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.MARSHMALLOW):
with self.assertCalls(
- self._PmGrantShellCall('package', {'p1': 0, 'p2': 0})):
+ self._PmGrantShellCall('package', {
+ 'p1': 0,
+ 'p2': 0
+ })):
self.device.GrantPermissions('package', ['p1', 'p2'])
def testGrantPermissions_WriteExtrnalStorage(self):
WRITE = 'android.permission.WRITE_EXTERNAL_STORAGE'
READ = 'android.permission.READ_EXTERNAL_STORAGE'
with PatchLogger() as logger:
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.MARSHMALLOW):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.MARSHMALLOW):
with self.assertCalls(
- self._PmGrantShellCall('package', {READ: 0, WRITE: 0})):
+ self._PmGrantShellCall('package', {
+ READ: 0,
+ WRITE: 0
+ })):
self.device.GrantPermissions('package', [WRITE])
self.assertEqual(logger.warnings, [])
- def testGrantPermissions_BlackList(self):
+ def testGrantPermissions_DenyList(self):
with PatchLogger() as logger:
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.MARSHMALLOW):
- with self.assertCalls(
- self._PmGrantShellCall('package', {'p1': 0})):
- self.device.GrantPermissions(
- 'package', ['p1', 'foo.permission.C2D_MESSAGE'])
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.MARSHMALLOW):
+ with self.assertCalls(self._PmGrantShellCall('package', {'p1': 0})):
+ self.device.GrantPermissions('package',
+ ['p1', 'foo.permission.C2D_MESSAGE'])
self.assertEqual(logger.warnings, [])
def testGrantPermissions_unchangeablePermision(self):
@@ -3318,13 +3637,14 @@ class DeviceUtilsGrantPermissionsTest(DeviceUtilsTest):
'Operation not allowed: java.lang.SecurityException: '
'Permission UNCHANGEABLE is not a changeable permission type')
with PatchLogger() as logger:
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.MARSHMALLOW):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.MARSHMALLOW):
with self.assertCalls(
self._PmGrantShellCall('package', {'UNCHANGEABLE': error_message})):
self.device.GrantPermissions('package', ['UNCHANGEABLE'])
- self.assertEqual(
- logger.warnings, [mock.ANY, AnyStringWith('UNCHANGEABLE')])
+ self.assertEqual(logger.warnings,
+ [mock.ANY, AnyStringWith('UNCHANGEABLE')])
class DeviecUtilsIsScreenOn(DeviceUtilsTest):
@@ -3335,53 +3655,49 @@ class DeviecUtilsIsScreenOn(DeviceUtilsTest):
_K_SCREEN_OFF = ['mScreenOn=false']
def testIsScreenOn_onPreL(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.KITKAT):
- with self.assertCalls(
- (self.call.device._RunPipedShellCommand(
- 'dumpsys input_method | grep mScreenOn'), self._K_SCREEN_ON)):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.KITKAT):
+ with self.assertCalls((self.call.device._RunPipedShellCommand(
+ 'dumpsys input_method | grep mScreenOn'), self._K_SCREEN_ON)):
self.assertTrue(self.device.IsScreenOn())
def testIsScreenOn_onL(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
- with self.assertCalls(
- (self.call.device._RunPipedShellCommand(
- 'dumpsys input_method | grep mInteractive'), self._L_SCREEN_ON)):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
+ with self.assertCalls((self.call.device._RunPipedShellCommand(
+ 'dumpsys input_method | grep mInteractive'), self._L_SCREEN_ON)):
self.assertTrue(self.device.IsScreenOn())
def testIsScreenOn_offPreL(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.KITKAT):
- with self.assertCalls(
- (self.call.device._RunPipedShellCommand(
- 'dumpsys input_method | grep mScreenOn'), self._K_SCREEN_OFF)):
+ with self.patch_call(
+ self.call.device.build_version_sdk, return_value=version_codes.KITKAT):
+ with self.assertCalls((self.call.device._RunPipedShellCommand(
+ 'dumpsys input_method | grep mScreenOn'), self._K_SCREEN_OFF)):
self.assertFalse(self.device.IsScreenOn())
def testIsScreenOn_offL(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
- with self.assertCalls(
- (self.call.device._RunPipedShellCommand(
- 'dumpsys input_method | grep mInteractive'), self._L_SCREEN_OFF)):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
+ with self.assertCalls((self.call.device._RunPipedShellCommand(
+ 'dumpsys input_method | grep mInteractive'), self._L_SCREEN_OFF)):
self.assertFalse(self.device.IsScreenOn())
def testIsScreenOn_noOutput(self):
- with self.patch_call(self.call.device.build_version_sdk,
- return_value=version_codes.LOLLIPOP):
- with self.assertCalls(
- (self.call.device._RunPipedShellCommand(
- 'dumpsys input_method | grep mInteractive'), [])):
+ with self.patch_call(
+ self.call.device.build_version_sdk,
+ return_value=version_codes.LOLLIPOP):
+ with self.assertCalls((self.call.device._RunPipedShellCommand(
+ 'dumpsys input_method | grep mInteractive'), [])):
with self.assertRaises(device_errors.CommandFailedError):
self.device.IsScreenOn()
class DeviecUtilsSetScreen(DeviceUtilsTest):
-
@mock.patch('time.sleep', mock.Mock())
def testSetScren_alreadySet(self):
- with self.assertCalls(
- (self.call.device.IsScreenOn(), False)):
+ with self.assertCalls((self.call.device.IsScreenOn(), False)):
self.device.SetScreen(False)
@mock.patch('time.sleep', mock.Mock())
@@ -3410,38 +3726,35 @@ class DeviecUtilsSetScreen(DeviceUtilsTest):
(self.call.device.IsScreenOn(), False)):
self.device.SetScreen(False)
+
class DeviecUtilsLoadCacheData(DeviceUtilsTest):
+ def testInvalidJson(self):
+ self.assertFalse(self.device.LoadCacheData(''))
def testTokenMissing(self):
- with self.assertCalls(
- self.EnsureCacheInitialized()):
+ with self.assertCalls(self.EnsureCacheInitialized()):
self.assertFalse(self.device.LoadCacheData('{}'))
def testTokenStale(self):
- with self.assertCalls(
- self.EnsureCacheInitialized()):
+ with self.assertCalls(self.EnsureCacheInitialized()):
self.assertFalse(self.device.LoadCacheData('{"token":"foo"}'))
def testTokenMatches(self):
- with self.assertCalls(
- self.EnsureCacheInitialized()):
+ with self.assertCalls(self.EnsureCacheInitialized()):
self.assertTrue(self.device.LoadCacheData('{"token":"TOKEN"}'))
def testDumpThenLoad(self):
- with self.assertCalls(
- self.EnsureCacheInitialized()):
+ with self.assertCalls(self.EnsureCacheInitialized()):
data = json.loads(self.device.DumpCacheData())
data['token'] = 'TOKEN'
self.assertTrue(self.device.LoadCacheData(json.dumps(data)))
class DeviceUtilsGetIMEITest(DeviceUtilsTest):
-
def testSuccessfulDumpsys(self):
- dumpsys_output = (
- 'Phone Subscriber Info:'
- ' Phone Type = GSM'
- ' Device ID = 123454321')
+ dumpsys_output = ('Phone Subscriber Info:'
+ ' Phone Type = GSM'
+ ' Device ID = 123454321')
with self.assertCalls(
(self.call.device.GetProp('ro.build.version.sdk', cache=True), '19'),
(self.call.adb.Shell('dumpsys iphonesubinfo'), dumpsys_output)):
@@ -3476,7 +3789,6 @@ class DeviceUtilsGetIMEITest(DeviceUtilsTest):
class DeviceUtilsChangeOwner(DeviceUtilsTest):
-
def testChangeOwner(self):
with self.assertCalls(
(self.call.device.RunShellCommand(
@@ -3486,18 +3798,16 @@ class DeviceUtilsChangeOwner(DeviceUtilsTest):
class DeviceUtilsChangeSecurityContext(DeviceUtilsTest):
-
def testChangeSecurityContext(self):
- with self.assertCalls(
- (self.call.device.RunShellCommand(
- ['chcon', 'u:object_r:system_data_file:s0', '/path', '/path2'],
- as_root=device_utils._FORCE_SU, check_return=True))):
+ with self.assertCalls((self.call.device.RunShellCommand(
+ ['chcon', 'u:object_r:system_data_file:s0', '/path', '/path2'],
+ as_root=device_utils._FORCE_SU,
+ check_return=True))):
self.device.ChangeSecurityContext('u:object_r:system_data_file:s0',
['/path', '/path2'])
class DeviceUtilsLocale(DeviceUtilsTest):
-
def testLocaleLegacy(self):
with self.assertCalls(
(self.call.device.GetProp('persist.sys.locale', cache=False), ''),
@@ -3514,11 +3824,10 @@ class DeviceUtilsLocale(DeviceUtilsTest):
self.assertEquals(self.device.GetLocale(), ('en', 'US-sw'))
def testBadLocale(self):
- with self.assertCalls(
- (self.call.device.GetProp('persist.sys.locale', cache=False), 'en')):
+ with self.assertCalls((self.call.device.GetProp(
+ 'persist.sys.locale', cache=False), 'en')):
self.assertEquals(self.device.GetLocale(), ('', ''))
-
def testLanguageAndCountryLegacy(self):
with self.assertCalls(
(self.call.device.GetProp('persist.sys.locale', cache=False), ''),
@@ -3538,6 +3847,138 @@ class DeviceUtilsLocale(DeviceUtilsTest):
self.assertEquals(self.device.GetCountry(), 'US')
+class IterPushableComponentsTest(unittest.TestCase):
+ @classmethod
+ @contextlib.contextmanager
+ def sampleLayout(cls):
+ Layout = collections.namedtuple('Layout', [
+ 'root', 'basic_file', 'symlink_file', 'symlink_dir',
+ 'dir_with_symlinks', 'dir_without_symlinks'
+ ])
+
+ with tempfile_ext.NamedTemporaryDirectory() as layout_root:
+ dir1 = os.path.join(layout_root, 'dir1')
+ os.makedirs(dir1)
+
+ basic_file = os.path.join(dir1, 'file1.txt')
+ with open(basic_file, 'w') as f:
+ f.write('hello world')
+
+ symlink = os.path.join(dir1, 'symlink.txt')
+ os.symlink(basic_file, symlink)
+
+ dir2 = os.path.join(layout_root, 'dir2')
+ os.makedirs(dir2)
+
+ with open(os.path.join(dir2, 'file2.txt'), 'w') as f:
+ f.write('goodnight moon')
+
+ symlink_dir = os.path.join(layout_root, 'dir3')
+ os.symlink(dir2, symlink_dir)
+
+ yield Layout(layout_root, basic_file, symlink, symlink_dir, dir1, dir2)
+
+ def testFile(self):
+ with self.sampleLayout() as layout:
+ device_path = '/sdcard/basic_file'
+
+ expected = [(layout.basic_file, device_path, True)]
+ actual = list(
+ device_utils._IterPushableComponents(layout.basic_file, device_path))
+ self.assertItemsEqual(expected, actual)
+
+ def testSymlinkFile(self):
+ with self.sampleLayout() as layout:
+ device_path = '/sdcard/basic_symlink'
+
+ expected = [(os.path.realpath(layout.symlink_file), device_path, False)]
+ actual = list(
+ device_utils._IterPushableComponents(layout.symlink_file,
+ device_path))
+ self.assertItemsEqual(expected, actual)
+
+ def testDirectoryWithNoSymlink(self):
+ with self.sampleLayout() as layout:
+ device_path = '/sdcard/basic_directory'
+
+ expected = [(layout.dir_without_symlinks, device_path, True)]
+ actual = list(
+ device_utils._IterPushableComponents(layout.dir_without_symlinks,
+ device_path))
+ self.assertItemsEqual(expected, actual)
+
+ def testDirectoryWithSymlink(self):
+ with self.sampleLayout() as layout:
+ device_path = '/sdcard/directory'
+
+ expected = [
+ (layout.basic_file,
+ posixpath.join(device_path, os.path.basename(layout.basic_file)),
+ True),
+ (os.path.realpath(layout.symlink_file),
+ posixpath.join(device_path, os.path.basename(layout.symlink_file)),
+ False),
+ ]
+ actual = list(
+ device_utils._IterPushableComponents(layout.dir_with_symlinks,
+ device_path))
+ self.assertItemsEqual(expected, actual)
+
+ def testSymlinkDirectory(self):
+ with self.sampleLayout() as layout:
+ device_path = '/sdcard/directory'
+
+ expected = [(os.path.realpath(layout.symlink_dir), device_path, False)]
+ actual = list(
+ device_utils._IterPushableComponents(layout.symlink_dir, device_path))
+ self.assertItemsEqual(expected, actual)
+
+ def testDirectoryWithNestedSymlink(self):
+ with self.sampleLayout() as layout:
+ device_path = '/sdcard/directory'
+
+ expected = [
+ (layout.dir_without_symlinks,
+ posixpath.join(device_path,
+ os.path.basename(layout.dir_without_symlinks)), True),
+ (layout.basic_file,
+ posixpath.join(
+ device_path,
+ *os.path.split(os.path.relpath(layout.basic_file, layout.root))),
+ True),
+ (os.path.realpath(layout.symlink_file),
+ posixpath.join(
+ device_path,
+ *os.path.split(
+ os.path.relpath(layout.symlink_file, layout.root))), False),
+ (os.path.realpath(layout.symlink_dir),
+ posixpath.join(
+ device_path,
+ *os.path.split(os.path.relpath(layout.symlink_dir,
+ layout.root))), False),
+ ]
+ actual = list(
+ device_utils._IterPushableComponents(layout.root, device_path))
+ self.assertItemsEqual(expected, actual)
+
+
+class DeviceUtilsGetTracingPathTest(DeviceUtilsTest):
+ def testGetTracingPath_hasDebugfs(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(['mount'], retries=0,
+ timeout=10, check_return=True),
+ ['debugfs on /sys/kernel/debug', 'proc on /proc'])):
+ self.assertEquals('/sys/kernel/debug/tracing',
+ self.device.GetTracingPath())
+
+ def testGetTracingPath_noDebugfs(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(['mount'], retries=0,
+ timeout=10, check_return=True),
+ ['proc on /proc'])):
+ self.assertEquals('/sys/kernel/tracing', self.device.GetTracingPath())
+
+
if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
unittest.main(verbosity=2)
diff --git a/catapult/devil/devil/android/fastboot_utils.py b/catapult/devil/devil/android/fastboot_utils.py
index 3621d7fb..d8ca7d20 100644
--- a/catapult/devil/devil/android/fastboot_utils.py
+++ b/catapult/devil/devil/android/fastboot_utils.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Provides a variety of device interactions based on fastboot."""
# pylint: disable=unused-argument
@@ -14,6 +13,7 @@ import re
from devil.android import decorators
from devil.android import device_errors
+from devil.android import device_utils
from devil.android.sdk import fastboot
from devil.utils import timeout_retry
@@ -23,58 +23,62 @@ _DEFAULT_TIMEOUT = 30
_DEFAULT_RETRIES = 3
_FASTBOOT_REBOOT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
_KNOWN_PARTITIONS = collections.OrderedDict([
- ('bootloader', {'image': 'bootloader*.img', 'restart': True}),
- ('radio', {'image': 'radio*.img', 'restart': True}),
- ('boot', {'image': 'boot.img'}),
- ('recovery', {'image': 'recovery.img'}),
- ('system', {'image': 'system.img'}),
- ('userdata', {'image': 'userdata.img', 'wipe_only': True}),
- ('cache', {'image': 'cache.img', 'wipe_only': True}),
- ('vendor', {'image': 'vendor*.img', 'optional': True}),
- ])
+ ('bootloader', {
+ 'image': 'bootloader*.img',
+ 'restart': True
+ }),
+ ('radio', {
+ 'image': 'radio*.img',
+ 'restart': True
+ }),
+ ('boot', {
+ 'image': 'boot.img'
+ }),
+ # recovery.img moved into boot.img for A/B devices. See:
+ # https://source.android.com/devices/tech/ota/ab/ab_implement#recovery
+ ('recovery', {
+ 'image': 'recovery.img',
+ 'optional': lambda fu: fu.supports_ab
+ }),
+ ('system', {
+ 'image': 'system.img'
+ }),
+ ('userdata', {
+ 'image': 'userdata.img',
+ 'wipe_only': True
+ }),
+ # cache.img deprecated for A/B devices. See:
+ # https://source.android.com/devices/tech/ota/ab/ab_implement#cache
+ ('cache', {
+ 'image': 'cache.img',
+ 'wipe_only': True,
+ 'optional': lambda fu: fu.supports_ab
+ }),
+ ('vendor', {
+ 'image': 'vendor*.img',
+ 'optional': lambda _: True
+ }),
+ ('dtbo', {
+ 'image': 'dtbo.img',
+ 'optional': lambda fu: not fu.requires_dtbo
+ }),
+ ('vbmeta', {
+ 'image': 'vbmeta.img',
+ 'optional': lambda fu: not fu.requires_vbmeta
+ }),
+])
ALL_PARTITIONS = _KNOWN_PARTITIONS.keys()
-def _FindAndVerifyPartitionsAndImages(partitions, directory):
- """Validate partitions and images.
-
- Validate all partition names and partition directories. Cannot stop mid
- flash so its important to validate everything first.
-
- Args:
- Partitions: partitions to be tested.
- directory: directory containing the images.
-
- Returns:
- Dictionary with exact partition, image name mapping.
- """
-
- files = os.listdir(directory)
- return_dict = collections.OrderedDict()
-
- def find_file(pattern):
- for filename in files:
- if fnmatch.fnmatch(filename, pattern):
- return os.path.join(directory, filename)
- return None
- for partition in partitions:
- partition_info = _KNOWN_PARTITIONS[partition]
- image_file = find_file(partition_info['image'])
- if image_file:
- return_dict[partition] = image_file
- elif not partition_info.get('optional'):
- raise device_errors.FastbootCommandFailedError(
- 'Failed to flash device. Could not find image for %s.',
- partition_info['image'])
- return return_dict
-
-
class FastbootUtils(object):
_FASTBOOT_WAIT_TIME = 1
_BOARD_VERIFICATION_FILE = 'android-info.txt'
- def __init__(self, device, fastbooter=None, default_timeout=_DEFAULT_TIMEOUT,
+ def __init__(self,
+ device=None,
+ fastbooter=None,
+ default_timeout=_DEFAULT_TIMEOUT,
default_retries=_DEFAULT_RETRIES):
"""FastbootUtils constructor.
@@ -83,23 +87,99 @@ class FastbootUtils(object):
fastboot.FlashDevice('/path/to/build/directory')
Args:
- device: A DeviceUtils instance.
- fastbooter: Optional fastboot object. If none is passed, one will
- be created.
+ device: A DeviceUtils instance. Optional if a Fastboot instance was
+ passed.
+ fastbooter: A fastboot.Fastboot instance. Optional if a DeviceUtils
+ instance was passed.
default_timeout: An integer containing the default number of seconds to
wait for an operation to complete if no explicit value is provided.
default_retries: An integer containing the default number or times an
operation should be retried on failure if no explicit value is provided.
"""
- self._device = device
- self._board = device.product_board
- self._serial = str(device)
- self._default_timeout = default_timeout
- self._default_retries = default_retries
+ if not device and not fastbooter:
+ raise ValueError("One of 'device' or 'fastbooter' must be passed.")
+
+ if device:
+ self._device = device
+ self._serial = str(device)
+ self._board = device.product_board
+ if not fastbooter:
+ self.fastboot = fastboot.Fastboot(self._serial)
+
if fastbooter:
+ self._serial = str(fastbooter)
self.fastboot = fastbooter
- else:
- self.fastboot = fastboot.Fastboot(self._serial)
+ self._board = fastbooter.GetVar('product')
+ if not device:
+ self._device = device_utils.DeviceUtils(self._serial)
+
+ self._default_timeout = default_timeout
+ self._default_retries = default_retries
+
+ self._supports_ab = None
+ self._requires_dtbo = None
+ self._requires_vbmeta = None
+
+ @property
+ def supports_ab(self):
+ """returns boolean to indicate if a device supports A/B updates.
+
+ It appears that boards which support A/B updates have different partition
+ requirements when flashing.
+ """
+ if self._supports_ab is None:
+ if self.IsFastbootMode():
+ try:
+ # According to https://bit.ly/2XIuICQ, slot-count is used to
+ # determine if a device supports A/B updates.
+ slot_count = self.fastboot.GetVar('slot-count') or '0'
+ self._supports_ab = int(slot_count) >= 2
+ except device_errors.FastbootCommandFailedError:
+ self._supports_ab = False
+ else:
+ # According to https://bit.ly/2UlJkGa and https://bit.ly/2MG8CL0,
+ # the property 'ro.build.ab_update' will be defined if the device
+ # supports A/B system updates.
+ self._supports_ab = self._device.GetProp('ro.build.ab_update') == 'true'
+
+ return self._supports_ab
+
+ @property
+ def requires_dtbo(self):
+ if self._requires_dtbo is None:
+ if self.IsFastbootMode():
+ try:
+ self._requires_dtbo = self.fastboot.GetVar('has-slot:dtbo') == 'yes'
+ except device_errors.FastbootCommandFailedError:
+ self._requires_dtbo = False
+ else:
+ # This prop will be set when a device supports dtbo.
+ # See https://bit.ly/2VUjBp0.
+ # Checking if this prop has a non-empty value should be good enough.
+ self._requires_dtbo = len(self._device.GetProp('ro.boot.dtbo_idx')) > 0
+
+ return self._requires_dtbo
+
+ @property
+ def requires_vbmeta(self):
+ if self._requires_vbmeta is None:
+ if self.IsFastbootMode():
+ try:
+ self._requires_vbmeta = self.fastboot.GetVar(
+ 'has-slot:vbmeta') == 'yes'
+ except device_errors.FastbootCommandFailedError:
+ self._requires_vbmeta = False
+ else:
+ # This prop will be set when a device uses Android Verified Boot (avb).
+ # See https://bit.ly/2CbsO5z.
+ # Checking if this prop has a non-empty value should be good enough.
+ self._requires_vbmeta = len(
+ self._device.GetProp('ro.boot.vbmeta.digest')) > 0
+
+ return self._requires_vbmeta
+
+ def IsFastbootMode(self):
+ return self._serial in (str(d) for d in self.fastboot.Devices())
@decorators.WithTimeoutAndRetriesFromInstance()
def WaitForFastbootMode(self, timeout=None, retries=None):
@@ -107,10 +187,8 @@ class FastbootUtils(object):
This waits for the device serial to show up in fastboot devices output.
"""
- def fastboot_mode():
- return any(self._serial == str(d) for d in self.fastboot.Devices())
-
- timeout_retry.WaitFor(fastboot_mode, wait_period=self._FASTBOOT_WAIT_TIME)
+ timeout_retry.WaitFor(self.IsFastbootMode,
+ wait_period=self._FASTBOOT_WAIT_TIME)
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=_FASTBOOT_REBOOT_TIMEOUT)
@@ -119,14 +197,19 @@ class FastbootUtils(object):
Roots phone if needed, then reboots phone into fastboot mode and waits.
"""
+ if self.IsFastbootMode():
+ return
self._device.EnableRoot()
self._device.adb.Reboot(to_bootloader=True)
self.WaitForFastbootMode()
@decorators.WithTimeoutAndRetriesFromInstance(
min_default_timeout=_FASTBOOT_REBOOT_TIMEOUT)
- def Reboot(
- self, bootloader=False, wait_for_reboot=True, timeout=None, retries=None):
+ def Reboot(self,
+ bootloader=False,
+ wait_for_reboot=True,
+ timeout=None,
+ retries=None):
"""Reboots out of fastboot mode.
It reboots the phone either back into fastboot, or to a regular boot. It
@@ -153,20 +236,16 @@ class FastbootUtils(object):
directory: directory where build files are located.
"""
files = os.listdir(directory)
- board_regex = re.compile(r'require board=(\w+)')
+ board_regex = re.compile(r'require board=([\w|]+)')
if self._BOARD_VERIFICATION_FILE in files:
with open(os.path.join(directory, self._BOARD_VERIFICATION_FILE)) as f:
for line in f:
m = board_regex.match(line)
- if m:
- board_name = m.group(1)
- if board_name == self._board:
- return True
- elif board_name:
- return False
- else:
- logger.warning('No board type found in %s.',
- self._BOARD_VERIFICATION_FILE)
+ if m and m.group(1):
+ return self._board in m.group(1).split('|')
+ else:
+ logger.warning('No board type found in %s.',
+ self._BOARD_VERIFICATION_FILE)
else:
logger.warning('%s not found. Unable to use it to verify device.',
self._BOARD_VERIFICATION_FILE)
@@ -203,19 +282,58 @@ class FastbootUtils(object):
'device type. Run again with force=True to force flashing with an '
'unverified board.')
- flash_image_files = _FindAndVerifyPartitionsAndImages(partitions, directory)
+ flash_image_files = self._FindAndVerifyPartitionsAndImages(
+ partitions, directory)
partitions = flash_image_files.keys()
for partition in partitions:
if _KNOWN_PARTITIONS[partition].get('wipe_only') and not wipe:
- logger.info(
- 'Not flashing in wipe mode. Skipping partition %s.', partition)
+ logger.info('Not flashing in wipe mode. Skipping partition %s.',
+ partition)
else:
- logger.info(
- 'Flashing %s with %s', partition, flash_image_files[partition])
+ logger.info('Flashing %s with %s', partition,
+ flash_image_files[partition])
self.fastboot.Flash(partition, flash_image_files[partition])
if _KNOWN_PARTITIONS[partition].get('restart', False):
self.Reboot(bootloader=True)
+ def _FindAndVerifyPartitionsAndImages(self, partitions, directory):
+ """Validate partitions and images.
+
+ Validate all partition names and partition directories. Cannot stop mid
+ flash so its important to validate everything first.
+
+ Args:
+ Partitions: partitions to be tested.
+ directory: directory containing the images.
+
+ Returns:
+ Dictionary with exact partition, image name mapping.
+ """
+
+ files = os.listdir(directory)
+ return_dict = collections.OrderedDict()
+
+ def find_file(pattern):
+ for filename in files:
+ if fnmatch.fnmatch(filename, pattern):
+ return os.path.join(directory, filename)
+ return None
+
+ for partition in partitions:
+ partition_info = _KNOWN_PARTITIONS[partition]
+ image_file = find_file(partition_info['image'])
+ if image_file:
+ return_dict[partition] = image_file
+ elif (not 'optional' in partition_info
+ or not partition_info['optional'](self)):
+ raise device_errors.FastbootCommandFailedError(
+ [],
+ '',
+ message='Failed to flash device%s. Could not find image for %s.' %
+ (' which supports A/B updates' if self.supports_ab else '',
+ partition_info['image']))
+ return return_dict
+
@contextlib.contextmanager
def FastbootMode(self, wait_for_reboot=True, timeout=None, retries=None):
"""Context manager that enables fastboot mode, and reboots after.
@@ -227,11 +345,12 @@ class FastbootUtils(object):
"""
self.EnableFastbootMode()
self.fastboot.SetOemOffModeCharge(False)
- try:
- yield self
- finally:
- self.fastboot.SetOemOffModeCharge(True)
- self.Reboot(wait_for_reboot=wait_for_reboot)
+ yield self
+ # If something went wrong while it was in fastboot mode (eg: a failed
+ # flash) rebooting may be harmful or cause boot loops. So only reboot if
+ # no exception was thrown.
+ self.fastboot.SetOemOffModeCharge(True)
+ self.Reboot(wait_for_reboot=wait_for_reboot)
def FlashDevice(self, directory, partitions=None, wipe=False):
"""Flash device with build in |directory|.
@@ -241,7 +360,6 @@ class FastbootUtils(object):
use with care.
Args:
- fastboot: A FastbootUtils instance.
directory: Directory with build files.
wipe: Wipes cache and userdata if set to true.
partitions: List of partitions to flash. Defaults to all.
diff --git a/catapult/devil/devil/android/fastboot_utils_test.py b/catapult/devil/devil/android/fastboot_utils_test.py
index 05629746..1ad73190 100755
--- a/catapult/devil/devil/android/fastboot_utils_test.py
+++ b/catapult/devil/devil/android/fastboot_utils_test.py
@@ -2,7 +2,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for the contents of fastboot_utils.py
"""
@@ -27,7 +26,8 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
_BOARD = 'board_type'
_SERIAL = '0123456789abcdef'
_PARTITIONS = [
- 'bootloader', 'radio', 'boot', 'recovery', 'system', 'userdata', 'cache']
+ 'bootloader', 'radio', 'boot', 'recovery', 'system', 'userdata', 'cache'
+]
_IMAGES = collections.OrderedDict([
('bootloader', 'bootloader.img'),
('radio', 'radio.img'),
@@ -35,14 +35,13 @@ _IMAGES = collections.OrderedDict([
('recovery', 'recovery.img'),
('system', 'system.img'),
('userdata', 'userdata.img'),
- ('cache', 'cache.img')
+ ('cache', 'cache.img'),
])
_VALID_FILES = [_BOARD + '.zip', 'android-info.txt']
_INVALID_FILES = ['test.zip', 'android-info.txt']
class MockFile(object):
-
def __init__(self, name='/tmp/some/file'):
self.file = mock.MagicMock(spec=file)
self.file.name = name
@@ -74,32 +73,35 @@ def _DeviceUtilsMock(test_serial):
class FastbootUtilsTest(mock_calls.TestCase):
-
def setUp(self):
self.device_utils_mock = _DeviceUtilsMock(_SERIAL)
self.fastboot_wrapper = _FastbootWrapperMock(_SERIAL)
self.fastboot = fastboot_utils.FastbootUtils(
- self.device_utils_mock, fastbooter=self.fastboot_wrapper,
- default_timeout=2, default_retries=0)
+ device=self.device_utils_mock,
+ fastbooter=self.fastboot_wrapper,
+ default_timeout=2,
+ default_retries=0)
self.fastboot._board = _BOARD
+ def FastbootCommandFailedError(self,
+ args=None,
+ output=None,
+ status=None,
+ msg=None):
+ return mock.Mock(side_effect=device_errors.FastbootCommandFailedError(
+ args, output, status, msg, str(self.device_utils_mock)))
-class FastbootUtilsInitTest(FastbootUtilsTest):
+class FastbootUtilsInitTest(FastbootUtilsTest):
def testInitWithDeviceUtil(self):
f = fastboot_utils.FastbootUtils(self.device_utils_mock)
self.assertEqual(str(self.device_utils_mock), str(f._device))
def testInitWithMissing_fails(self):
+ with self.assertRaises(ValueError):
+ fastboot_utils.FastbootUtils(device=None, fastbooter=None)
with self.assertRaises(AttributeError):
- fastboot_utils.FastbootUtils(None)
- with self.assertRaises(AttributeError):
- fastboot_utils.FastbootUtils('')
-
- def testPartitionOrdering(self):
- parts = ['bootloader', 'radio', 'boot', 'recovery', 'system', 'userdata',
- 'cache', 'vendor']
- self.assertListEqual(fastboot_utils.ALL_PARTITIONS, parts)
+ fastboot_utils.FastbootUtils('abc')
class FastbootUtilsWaitForFastbootMode(FastbootUtilsTest):
@@ -110,10 +112,123 @@ class FastbootUtilsWaitForFastbootMode(FastbootUtilsTest):
self.fastboot.WaitForFastbootMode()
-class FastbootUtilsEnableFastbootMode(FastbootUtilsTest):
+class FastbootUtilsIsFastbootMode(FastbootUtilsTest):
+ def testIsFastbootMode_True(self):
+ self.assertEqual(True, self.fastboot.IsFastbootMode())
+
+ def testIsFastbootMode_False(self):
+ self.fastboot._serial = 'not' + _SERIAL
+ self.assertEqual(False, self.fastboot.IsFastbootMode())
+
+
+class FastbootUtils_supports_ab(FastbootUtilsTest):
+ def test_supports_ab_fastboot_True(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('slot-count'), '2')):
+ self.assertEqual(True, self.fastboot.supports_ab)
+
+ def test_supports_ab_fastboot_False(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('slot-count'), '1')):
+ self.assertEqual(False, self.fastboot.supports_ab)
+
+ def test_supports_ab_fastboot_FalseWithEmpty(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('slot-count'), '')):
+ self.assertEqual(False, self.fastboot.supports_ab)
+
+ def test_supports_ab_fastboot_FalseWithError(self):
+ with self.assertCalls((self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('slot-count'),
+ self.FastbootCommandFailedError([], ''))):
+ self.assertEqual(False, self.fastboot.supports_ab)
+
+ def test_supports_ab_device_True(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), False),
+ (self.call.fastboot._device.GetProp('ro.build.ab_update'), 'true')):
+ self.assertEqual(True, self.fastboot.supports_ab)
+
+ def test_supports_ab_device_False(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), False),
+ (self.call.fastboot._device.GetProp('ro.build.ab_update'), '')):
+ self.assertEqual(False, self.fastboot.supports_ab)
+
+
+class FastbootUtils_requires_dtbo(FastbootUtilsTest):
+ def test_requires_dtbo_fastboot_True(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('has-slot:dtbo'), 'yes')):
+ self.assertEqual(True, self.fastboot.requires_dtbo)
+
+ def test_requires_dtbo_fastboot_FalseWithEmpty(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('has-slot:dtbo'), '')):
+ self.assertEqual(False, self.fastboot.requires_dtbo)
+
+ def test_requires_dtbo_fastboot_FalseWithError(self):
+ with self.assertCalls((self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('has-slot:dtbo'),
+ self.FastbootCommandFailedError([], ''))):
+ self.assertEqual(False, self.fastboot.requires_dtbo)
+
+ def test_requires_dtbo_device_True(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), False),
+ (self.call.fastboot._device.GetProp('ro.boot.dtbo_idx'), '1')):
+ self.assertEqual(True, self.fastboot.requires_dtbo)
+
+ def test_requires_dtbo_device_False(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), False),
+ (self.call.fastboot._device.GetProp('ro.boot.dtbo_idx'), '')):
+ self.assertEqual(False, self.fastboot.requires_dtbo)
+
+
+class FastbootUtils_requires_vbmeta(FastbootUtilsTest):
+ def test_requires_vbmeta_fastboot_True(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('has-slot:vbmeta'), 'yes')):
+ self.assertEqual(True, self.fastboot.requires_vbmeta)
+
+ def test_requires_vbmeta_fastboot_FalseWithEmpty(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('has-slot:vbmeta'), '')):
+ self.assertEqual(False, self.fastboot.requires_vbmeta)
+
+ def test_requires_vbmeta_fastboot_FalseWithError(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), True),
+ (self.call.fastboot.fastboot.GetVar('has-slot:vbmeta'),
+ self.FastbootCommandFailedError([], ''))):
+ self.assertEqual(False, self.fastboot.requires_vbmeta)
+
+ def test_requires_vbmeta_device_True(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), False),
+ (self.call.fastboot._device.GetProp('ro.boot.vbmeta.digest'),
+ '1')):
+ self.assertEqual(True, self.fastboot.requires_vbmeta)
+
+ def test_requires_vbmeta_device_False(self):
+ with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), False),
+ (self.call.fastboot._device.GetProp('ro.boot.vbmeta.digest'), '')):
+ self.assertEqual(False, self.fastboot.requires_vbmeta)
+
+class FastbootUtilsEnableFastbootMode(FastbootUtilsTest):
def testEnableFastbootMode(self):
with self.assertCalls(
+ (self.call.fastboot.IsFastbootMode(), False),
self.call.fastboot._device.EnableRoot(),
self.call.fastboot._device.adb.Reboot(to_bootloader=True),
self.call.fastboot.WaitForFastbootMode()):
@@ -121,11 +236,9 @@ class FastbootUtilsEnableFastbootMode(FastbootUtilsTest):
class FastbootUtilsReboot(FastbootUtilsTest):
-
def testReboot_bootloader(self):
- with self.assertCalls(
- self.call.fastboot.fastboot.RebootBootloader(),
- self.call.fastboot.WaitForFastbootMode()):
+ with self.assertCalls(self.call.fastboot.fastboot.RebootBootloader(),
+ self.call.fastboot.WaitForFastbootMode()):
self.fastboot.Reboot(bootloader=True)
def testReboot_normal(self):
@@ -136,40 +249,69 @@ class FastbootUtilsReboot(FastbootUtilsTest):
class FastbootUtilsFlashPartitions(FastbootUtilsTest):
-
def testFlashPartitions_wipe(self):
- with self.assertCalls(
- (self.call.fastboot._VerifyBoard('test'), True),
- (mock.call.devil.android.fastboot_utils.
- _FindAndVerifyPartitionsAndImages(_PARTITIONS, 'test'), _IMAGES),
- (self.call.fastboot.fastboot.Flash('bootloader', 'bootloader.img')),
- (self.call.fastboot.Reboot(bootloader=True)),
- (self.call.fastboot.fastboot.Flash('radio', 'radio.img')),
- (self.call.fastboot.Reboot(bootloader=True)),
- (self.call.fastboot.fastboot.Flash('boot', 'boot.img')),
- (self.call.fastboot.fastboot.Flash('recovery', 'recovery.img')),
- (self.call.fastboot.fastboot.Flash('system', 'system.img')),
- (self.call.fastboot.fastboot.Flash('userdata', 'userdata.img')),
- (self.call.fastboot.fastboot.Flash('cache', 'cache.img'))):
- self.fastboot._FlashPartitions(_PARTITIONS, 'test', wipe=True)
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=False):
+ with self.assertCalls(
+ (self.call.fastboot._VerifyBoard('test'), True),
+ (self.call.fastboot._FindAndVerifyPartitionsAndImages(
+ _PARTITIONS, 'test'), _IMAGES),
+ (self.call.fastboot.fastboot.Flash('bootloader', 'bootloader.img')),
+ (self.call.fastboot.Reboot(bootloader=True)),
+ (self.call.fastboot.fastboot.Flash('radio', 'radio.img')),
+ (self.call.fastboot.Reboot(bootloader=True)),
+ (self.call.fastboot.fastboot.Flash('boot', 'boot.img')),
+ (self.call.fastboot.fastboot.Flash('recovery', 'recovery.img')),
+ (self.call.fastboot.fastboot.Flash('system', 'system.img')),
+ (self.call.fastboot.fastboot.Flash('userdata', 'userdata.img')),
+ (self.call.fastboot.fastboot.Flash('cache', 'cache.img'))):
+ self.fastboot._FlashPartitions(_PARTITIONS, 'test', wipe=True)
def testFlashPartitions_noWipe(self):
- with self.assertCalls(
- (self.call.fastboot._VerifyBoard('test'), True),
- (mock.call.devil.android.fastboot_utils.
- _FindAndVerifyPartitionsAndImages(_PARTITIONS, 'test'), _IMAGES),
- (self.call.fastboot.fastboot.Flash('bootloader', 'bootloader.img')),
- (self.call.fastboot.Reboot(bootloader=True)),
- (self.call.fastboot.fastboot.Flash('radio', 'radio.img')),
- (self.call.fastboot.Reboot(bootloader=True)),
- (self.call.fastboot.fastboot.Flash('boot', 'boot.img')),
- (self.call.fastboot.fastboot.Flash('recovery', 'recovery.img')),
- (self.call.fastboot.fastboot.Flash('system', 'system.img'))):
- self.fastboot._FlashPartitions(_PARTITIONS, 'test')
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=False):
+ with self.assertCalls(
+ (self.call.fastboot._VerifyBoard('test'), True),
+ (self.call.fastboot._FindAndVerifyPartitionsAndImages(
+ _PARTITIONS, 'test'), _IMAGES),
+ (self.call.fastboot.fastboot.Flash('bootloader', 'bootloader.img')),
+ (self.call.fastboot.Reboot(bootloader=True)),
+ (self.call.fastboot.fastboot.Flash('radio', 'radio.img')),
+ (self.call.fastboot.Reboot(bootloader=True)),
+ (self.call.fastboot.fastboot.Flash('boot', 'boot.img')),
+ (self.call.fastboot.fastboot.Flash('recovery', 'recovery.img')),
+ (self.call.fastboot.fastboot.Flash('system', 'system.img'))):
+ self.fastboot._FlashPartitions(_PARTITIONS, 'test')
+
+ def testFlashPartitions_AB_device(self):
+ ab_images = _IMAGES.copy()
+ ab_images['dtbo'] = 'dtbo.img'
+ ab_images['vbmeta'] = 'vbmeta.img'
+ ab_partitions = _PARTITIONS[:]
+ ab_partitions.append('dtbo')
+ ab_partitions.append('vbmeta')
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=True):
+ with self.patch_call(self.call.fastboot.requires_dtbo, return_value=True):
+ with self.patch_call(self.call.fastboot.requires_vbmeta,
+ return_value=True):
+ with self.assertCalls(
+ (self.call.fastboot._VerifyBoard('test'), True),
+ (self.call.fastboot._FindAndVerifyPartitionsAndImages(
+ ab_partitions, 'test'), ab_images),
+ (self.call.fastboot.fastboot.Flash('bootloader',
+ 'bootloader.img')),
+ (self.call.fastboot.Reboot(bootloader=True)),
+ (self.call.fastboot.fastboot.Flash('radio', 'radio.img')),
+ (self.call.fastboot.Reboot(bootloader=True)),
+ (self.call.fastboot.fastboot.Flash('boot', 'boot.img')),
+ (self.call.fastboot.fastboot.Flash('recovery', 'recovery.img')),
+ (self.call.fastboot.fastboot.Flash('system', 'system.img')),
+ (self.call.fastboot.fastboot.Flash('userdata', 'userdata.img')),
+ (self.call.fastboot.fastboot.Flash('cache', 'cache.img')),
+ (self.call.fastboot.fastboot.Flash('dtbo', 'dtbo.img')),
+ (self.call.fastboot.fastboot.Flash('vbmeta', 'vbmeta.img'))):
+ self.fastboot._FlashPartitions(ab_partitions, 'test', wipe=True)
class FastbootUtilsFastbootMode(FastbootUtilsTest):
-
def testFastbootMode_goodWait(self):
with self.assertCalls(
self.call.fastboot.EnableFastbootMode(),
@@ -191,9 +333,7 @@ class FastbootUtilsFastbootMode(FastbootUtilsTest):
def testFastbootMode_exception(self):
with self.assertCalls(
self.call.fastboot.EnableFastbootMode(),
- self.call.fastboot.fastboot.SetOemOffModeCharge(False),
- self.call.fastboot.fastboot.SetOemOffModeCharge(True),
- self.call.fastboot.Reboot(wait_for_reboot=True)):
+ self.call.fastboot.fastboot.SetOemOffModeCharge(False)):
with self.assertRaises(NotImplementedError):
with self.fastboot.FastbootMode() as fbm:
self.assertEqual(self.fastboot, fbm)
@@ -208,7 +348,6 @@ class FastbootUtilsFastbootMode(FastbootUtilsTest):
class FastbootUtilsVerifyBoard(FastbootUtilsTest):
-
def testVerifyBoard_bothValid(self):
mock_file = io.StringIO(u'require board=%s\n' % _BOARD)
with mock.patch('__builtin__.open', return_value=mock_file, create=True):
@@ -257,37 +396,29 @@ class FastbootUtilsVerifyBoard(FastbootUtilsTest):
class FastbootUtilsFindAndVerifyPartitionsAndImages(FastbootUtilsTest):
-
def testFindAndVerifyPartitionsAndImages_validNoVendor(self):
PARTITIONS = [
'bootloader', 'radio', 'boot', 'recovery', 'system', 'userdata',
'cache', 'vendor'
]
files = [
- 'bootloader-test-.img',
- 'radio123.img',
- 'boot.img',
- 'recovery.img',
- 'system.img',
- 'userdata.img',
- 'cache.img'
+ 'bootloader-test-.img', 'radio123.img', 'boot.img', 'recovery.img',
+ 'system.img', 'userdata.img', 'cache.img'
]
img_check = collections.OrderedDict([
- ('bootloader', 'test/bootloader-test-.img'),
- ('radio', 'test/radio123.img'),
- ('boot', 'test/boot.img'),
- ('recovery', 'test/recovery.img'),
- ('system', 'test/system.img'),
- ('userdata', 'test/userdata.img'),
- ('cache', 'test/cache.img'),
+ ('bootloader', 'test/bootloader-test-.img'),
+ ('radio', 'test/radio123.img'),
+ ('boot', 'test/boot.img'),
+ ('recovery', 'test/recovery.img'),
+ ('system', 'test/system.img'),
+ ('userdata', 'test/userdata.img'),
+ ('cache', 'test/cache.img'),
])
parts_check = [
- 'bootloader', 'radio', 'boot', 'recovery', 'system', 'userdata',
- 'cache'
+ 'bootloader', 'radio', 'boot', 'recovery', 'system', 'userdata', 'cache'
]
with mock.patch('os.listdir', return_value=files):
- imgs = fastboot_utils._FindAndVerifyPartitionsAndImages(
- PARTITIONS, 'test')
+ imgs = self.fastboot._FindAndVerifyPartitionsAndImages(PARTITIONS, 'test')
parts = imgs.keys()
self.assertDictEqual(imgs, img_check)
self.assertListEqual(parts, parts_check)
@@ -298,24 +429,18 @@ class FastbootUtilsFindAndVerifyPartitionsAndImages(FastbootUtilsTest):
'cache', 'vendor'
]
files = [
- 'bootloader-test-.img',
- 'radio123.img',
- 'boot.img',
- 'recovery.img',
- 'system.img',
- 'userdata.img',
- 'cache.img',
- 'vendor.img'
+ 'bootloader-test-.img', 'radio123.img', 'boot.img', 'recovery.img',
+ 'system.img', 'userdata.img', 'cache.img', 'vendor.img'
]
img_check = {
- 'bootloader': 'test/bootloader-test-.img',
- 'radio': 'test/radio123.img',
- 'boot': 'test/boot.img',
- 'recovery': 'test/recovery.img',
- 'system': 'test/system.img',
- 'userdata': 'test/userdata.img',
- 'cache': 'test/cache.img',
- 'vendor': 'test/vendor.img',
+ 'bootloader': 'test/bootloader-test-.img',
+ 'radio': 'test/radio123.img',
+ 'boot': 'test/boot.img',
+ 'recovery': 'test/recovery.img',
+ 'system': 'test/system.img',
+ 'userdata': 'test/userdata.img',
+ 'cache': 'test/cache.img',
+ 'vendor': 'test/vendor.img',
}
parts_check = [
'bootloader', 'radio', 'boot', 'recovery', 'system', 'userdata',
@@ -323,25 +448,58 @@ class FastbootUtilsFindAndVerifyPartitionsAndImages(FastbootUtilsTest):
]
with mock.patch('os.listdir', return_value=files):
- imgs = fastboot_utils._FindAndVerifyPartitionsAndImages(
- PARTITIONS, 'test')
- parts = imgs.keys()
- self.assertDictEqual(imgs, img_check)
- self.assertListEqual(parts, parts_check)
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=False):
+ imgs = self.fastboot._FindAndVerifyPartitionsAndImages(
+ PARTITIONS, 'test')
+ parts = imgs.keys()
+ self.assertDictEqual(imgs, img_check)
+ self.assertListEqual(parts, parts_check)
def testFindAndVerifyPartitionsAndImages_badPartition(self):
with mock.patch('os.listdir', return_value=['test']):
- with self.assertRaises(KeyError):
- fastboot_utils._FindAndVerifyPartitionsAndImages(['test'], 'test')
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=False):
+ with self.assertRaises(KeyError):
+ self.fastboot._FindAndVerifyPartitionsAndImages(['test'], 'test')
- def testFindAndVerifyPartitionsAndImages_noFile(self):
+ def testFindAndVerifyPartitionsAndImages_noFile_RequiredImage(self):
with mock.patch('os.listdir', return_value=['test']):
- with self.assertRaises(device_errors.FastbootCommandFailedError):
- fastboot_utils._FindAndVerifyPartitionsAndImages(['cache'], 'test')
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=False):
+ with self.assertRaises(device_errors.FastbootCommandFailedError):
+ self.fastboot._FindAndVerifyPartitionsAndImages(['boot'], 'test')
+ def testFindAndVerifyPartitionsAndImages_noFile_RequiredImageAB(self):
+ with mock.patch('os.listdir', return_value=['test']):
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=True):
+ with self.assertRaises(device_errors.FastbootCommandFailedError):
+ self.fastboot._FindAndVerifyPartitionsAndImages(['boot'], 'test')
-class FastbootUtilsFlashDevice(FastbootUtilsTest):
+ def testFindAndVerifyPartitionsAndImages_noFile_NotRequiredImage(self):
+ with mock.patch('os.listdir', return_value=['test']):
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=False):
+ with self.patch_call(self.call.fastboot.requires_dtbo,
+ return_value=False):
+ self.assertFalse(
+ self.fastboot._FindAndVerifyPartitionsAndImages(['vendor'],
+ 'test'))
+ self.assertFalse(
+ self.fastboot._FindAndVerifyPartitionsAndImages(['dtbo'], 'test'))
+
+ def testFindAndVerifyPartitionsAndImages_noFile_NotRequiredImageAB(self):
+ with mock.patch('os.listdir', return_value=['test']):
+ with self.patch_call(self.call.fastboot.supports_ab, return_value=True):
+ with self.patch_call(self.call.fastboot.requires_dtbo,
+ return_value=False):
+ self.assertFalse(
+ self.fastboot._FindAndVerifyPartitionsAndImages(['vendor'],
+ 'test'))
+ self.assertFalse(
+ self.fastboot._FindAndVerifyPartitionsAndImages(['cache'],
+ 'test'))
+ self.assertFalse(
+ self.fastboot._FindAndVerifyPartitionsAndImages(['dtbo'], 'test'))
+
+class FastbootUtilsFlashDevice(FastbootUtilsTest):
def testFlashDevice_wipe(self):
with self.assertCalls(
self.call.fastboot.EnableFastbootMode(),
diff --git a/catapult/devil/devil/android/flag_changer.py b/catapult/devil/devil/android/flag_changer.py
index 110cf827..5dabc4c8 100644
--- a/catapult/devil/devil/android/flag_changer.py
+++ b/catapult/devil/devil/android/flag_changer.py
@@ -9,10 +9,8 @@ import re
from devil.android.sdk import version_codes
-
logger = logging.getLogger(__name__)
-
_CMDLINE_DIR = '/data/local/tmp'
_CMDLINE_DIR_LEGACY = '/data/local'
_RE_NEEDS_QUOTING = re.compile(r'[^\w-]') # Not in: alphanumeric or hyphens.
@@ -72,15 +70,15 @@ class FlagChanger(object):
alternate_cmdline_path = posixpath.join(_CMDLINE_DIR_LEGACY, cmdline_file)
if use_legacy_path:
- cmdline_path, alternate_cmdline_path = (
- alternate_cmdline_path, cmdline_path)
+ cmdline_path, alternate_cmdline_path = (alternate_cmdline_path,
+ cmdline_path)
if not self._device.HasRoot():
raise ValueError('use_legacy_path requires a rooted device')
self._cmdline_path = cmdline_path
if self._device.PathExists(alternate_cmdline_path):
- logger.warning(
- 'Removing alternate command line file %r.', alternate_cmdline_path)
+ logger.warning('Removing alternate command line file %r.',
+ alternate_cmdline_path)
self._device.RemovePath(alternate_cmdline_path, as_root=True)
self._state_stack = [None] # Actual state is set by GetCurrentFlags().
@@ -189,8 +187,8 @@ class FlagChanger(object):
# permissions are needed, and document them in the code.
if not self._device.HasRoot():
return
- if (self._device.build_version_sdk >= version_codes.NOUGAT and
- self._device.GetEnforce()):
+ if (self._device.build_version_sdk >= version_codes.NOUGAT
+ and self._device.GetEnforce()):
self._device.SetEnforce(enabled=False)
self._should_reset_enforce = True
diff --git a/catapult/devil/devil/android/flag_changer_devicetest.py b/catapult/devil/devil/android/flag_changer_devicetest.py
index b75504b5..4926ae3a 100644
--- a/catapult/devil/devil/android/flag_changer_devicetest.py
+++ b/catapult/devil/devil/android/flag_changer_devicetest.py
@@ -14,19 +14,21 @@ import unittest
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', )))
+ os.path.abspath(os.path.join(
+ os.path.dirname(__file__),
+ '..',
+ '..',
+ )))
from devil.android import device_test_case
from devil.android import device_utils
from devil.android import flag_changer
from devil.android.sdk import adb_wrapper
-
_CMDLINE_FILE = 'dummy-command-line'
class FlagChangerTest(device_test_case.DeviceTestCase):
-
def setUp(self):
super(FlagChangerTest, self).setUp()
self.adb = adb_wrapper.AdbWrapper(self.serial)
@@ -35,21 +37,21 @@ class FlagChangerTest(device_test_case.DeviceTestCase):
self.adb, default_timeout=10, default_retries=0)
# pylint: disable=protected-access
self.cmdline_path = posixpath.join(flag_changer._CMDLINE_DIR, _CMDLINE_FILE)
- self.cmdline_path_legacy = posixpath.join(
- flag_changer._CMDLINE_DIR_LEGACY, _CMDLINE_FILE)
+ self.cmdline_path_legacy = posixpath.join(flag_changer._CMDLINE_DIR_LEGACY,
+ _CMDLINE_FILE)
def tearDown(self):
super(FlagChangerTest, self).tearDown()
- self.device.RemovePath(
- [self.cmdline_path, self.cmdline_path_legacy], force=True, as_root=True)
+ self.device.RemovePath([self.cmdline_path, self.cmdline_path_legacy],
+ force=True,
+ as_root=True)
def testFlagChanger_restoreFlags(self):
if not self.device.HasRoot():
self.skipTest('Test needs a rooted device')
# Write some custom chrome command line flags.
- self.device.WriteFile(
- self.cmdline_path, 'chrome --some --old --flags')
+ self.device.WriteFile(self.cmdline_path, 'chrome --some --old --flags')
# Write some more flags on a command line file in the legacy location.
self.device.WriteFile(
@@ -64,21 +66,17 @@ class FlagChangerTest(device_test_case.DeviceTestCase):
# Write some new files, and check they are set.
new_flags = ['--my', '--new', '--flags=with special value']
- self.assertItemsEqual(
- changer.ReplaceFlags(new_flags),
- new_flags)
+ self.assertItemsEqual(changer.ReplaceFlags(new_flags), new_flags)
# Restore and go back to the old flags.
- self.assertItemsEqual(
- changer.Restore(),
- ['--some', '--old', '--flags'])
+ self.assertItemsEqual(changer.Restore(), ['--some', '--old', '--flags'])
def testFlagChanger_removeFlags(self):
self.device.RemovePath(self.cmdline_path, force=True)
self.assertFalse(self.device.PathExists(self.cmdline_path))
- with flag_changer.CustomCommandLineFlags(
- self.device, _CMDLINE_FILE, ['--some', '--flags']):
+ with flag_changer.CustomCommandLineFlags(self.device, _CMDLINE_FILE,
+ ['--some', '--flags']):
self.assertTrue(self.device.PathExists(self.cmdline_path))
self.assertFalse(self.device.PathExists(self.cmdline_path))
diff --git a/catapult/devil/devil/android/flag_changer_test.py b/catapult/devil/devil/android/flag_changer_test.py
index dbe6facc..564ead6e 100755
--- a/catapult/devil/devil/android/flag_changer_test.py
+++ b/catapult/devil/devil/android/flag_changer_test.py
@@ -8,7 +8,6 @@ import unittest
from devil.android import flag_changer
-
_CMDLINE_FILE = 'chrome-command-line'
@@ -39,8 +38,8 @@ class FlagChangerTest(unittest.TestCase):
self.device = _FakeDevice()
# pylint: disable=protected-access
self.cmdline_path = posixpath.join(flag_changer._CMDLINE_DIR, _CMDLINE_FILE)
- self.cmdline_path_legacy = posixpath.join(
- flag_changer._CMDLINE_DIR_LEGACY, _CMDLINE_FILE)
+ self.cmdline_path_legacy = posixpath.join(flag_changer._CMDLINE_DIR_LEGACY,
+ _CMDLINE_FILE)
def testFlagChanger_removeAlternateCmdLine(self):
self.device.WriteFile(self.cmdline_path_legacy, 'chrome --old --stuff')
@@ -56,11 +55,11 @@ class FlagChangerTest(unittest.TestCase):
self.device.WriteFile(self.cmdline_path, 'chrome --old --stuff')
self.assertTrue(self.device.PathExists(self.cmdline_path))
- changer = flag_changer.FlagChanger(self.device, 'chrome-command-line',
- use_legacy_path=True)
+ changer = flag_changer.FlagChanger(
+ self.device, 'chrome-command-line', use_legacy_path=True)
self.assertEquals(
- changer._cmdline_path, # pylint: disable=protected-access
- self.cmdline_path_legacy)
+ changer._cmdline_path, # pylint: disable=protected-access
+ self.cmdline_path_legacy)
self.assertFalse(self.device.PathExists(self.cmdline_path))
def testFlagChanger_mustBeFileName(self):
@@ -89,21 +88,20 @@ class ParseSerializeFlagsTest(unittest.TestCase):
self._testQuoteFlag('--key=valueA valueB', '--key="valueA valueB"')
def testQuoteFlag_withQuotedValue2(self):
- self._testQuoteFlag(
- '--key=this "should" work', r'--key="this \"should\" work"')
+ self._testQuoteFlag('--key=this "should" work',
+ r'--key="this \"should\" work"')
def testQuoteFlag_withQuotedValue3(self):
- self._testQuoteFlag(
- "--key=this is 'fine' too", '''--key="this is 'fine' too"''')
+ self._testQuoteFlag("--key=this is 'fine' too",
+ '''--key="this is 'fine' too"''')
def testQuoteFlag_withQuotedValue4(self):
- self._testQuoteFlag(
- "--key='I really want to keep these quotes'",
- '''--key="'I really want to keep these quotes'"''')
+ self._testQuoteFlag("--key='I really want to keep these quotes'",
+ '''--key="'I really want to keep these quotes'"''')
def testQuoteFlag_withQuotedValue5(self):
- self._testQuoteFlag(
- "--this is a strange=flag", '"--this is a strange=flag"')
+ self._testQuoteFlag("--this is a strange=flag",
+ '"--this is a strange=flag"')
def testQuoteFlag_withEmptyValue(self):
self._testQuoteFlag('--some-flag=', '--some-flag=')
@@ -122,24 +120,22 @@ class ParseSerializeFlagsTest(unittest.TestCase):
self.assertItemsEqual(new_flags, expected_flags)
def testParseCmdLine_simple(self):
- self._testParseCmdLine(
- 'chrome --foo --bar="a b" --baz=true --fine="ok"',
- ['--foo', '--bar=a b', '--baz=true', '--fine=ok'])
+ self._testParseCmdLine('chrome --foo --bar="a b" --baz=true --fine="ok"',
+ ['--foo', '--bar=a b', '--baz=true', '--fine=ok'])
def testParseCmdLine_withFancyQuotes(self):
self._testParseCmdLine(
r'''_ --foo="this 'is' ok"
--bar='this \'is\' too'
--baz="this \'is\' tricky"
- ''',
- ["--foo=this 'is' ok",
- "--bar=this 'is' too",
- r"--baz=this \'is\' tricky"])
+ ''', [
+ "--foo=this 'is' ok", "--bar=this 'is' too",
+ r"--baz=this \'is\' tricky"
+ ])
def testParseCmdLine_withUnterminatedQuote(self):
- self._testParseCmdLine(
- '_ --foo --bar="I forgot something',
- ['--foo', '--bar=I forgot something'])
+ self._testParseCmdLine('_ --foo --bar="I forgot something',
+ ['--foo', '--bar=I forgot something'])
if __name__ == '__main__':
diff --git a/catapult/devil/devil/android/forwarder.py b/catapult/devil/devil/android/forwarder.py
index 6be46516..cdb7e4d5 100644
--- a/catapult/devil/devil/android/forwarder.py
+++ b/catapult/devil/devil/android/forwarder.py
@@ -9,6 +9,8 @@ import inspect
import logging
import os
import psutil
+import re
+import textwrap
from devil import base_error
from devil import devil_env
@@ -25,6 +27,8 @@ logger = logging.getLogger(__name__)
# Forwarder.DevicePortForHostPort.
DYNAMIC_DEVICE_PORT = 0
+PORT_REGEX = re.compile(r'(?P<device_port>\d+):(?P<host_port>\d+)')
+
def _GetProcessStartTime(pid):
p = psutil.Process(pid)
@@ -34,7 +38,7 @@ def _GetProcessStartTime(pid):
return p.create_time
-def _LogMapFailureDiagnostics(device):
+def _DumpHostLog():
# The host forwarder daemon logs to /tmp/host_forwarder_log, so print the end
# of that.
try:
@@ -42,10 +46,14 @@ def _LogMapFailureDiagnostics(device):
logger.info('Last 50 lines of the host forwarder daemon log:')
for line in host_forwarder_log.read().splitlines()[-50:]:
logger.info(' %s', line)
- except Exception: # pylint: disable=broad-except
+ except Exception: # pylint: disable=broad-except
# Grabbing the host forwarder log is best-effort. Ignore all errors.
logger.warning('Failed to get the contents of host_forwarder_log.')
+
+def _LogMapFailureDiagnostics(device):
+ _DumpHostLog()
+
# The device forwarder daemon logs to the logcat, so print the end of that.
try:
logger.info('Last 50 lines of logcat:')
@@ -101,10 +109,9 @@ class HostForwarderError(base_error.BaseError):
class Forwarder(object):
"""Thread-safe class to manage port forwards from the device to the host."""
- _DEVICE_FORWARDER_FOLDER = (file_system.TEST_EXECUTABLE_DIR +
- '/forwarder/')
- _DEVICE_FORWARDER_PATH = (file_system.TEST_EXECUTABLE_DIR +
- '/forwarder/device_forwarder')
+ _DEVICE_FORWARDER_FOLDER = (file_system.TEST_EXECUTABLE_DIR + '/forwarder/')
+ _DEVICE_FORWARDER_PATH = (
+ file_system.TEST_EXECUTABLE_DIR + '/forwarder/device_forwarder')
_LOCK_PATH = '/tmp/chrome.forwarder.lock'
# Defined in host_forwarder_main.cc
_HOST_FORWARDER_LOG = '/tmp/host_forwarder_log'
@@ -137,11 +144,12 @@ class Forwarder(object):
instance._InitDeviceLocked(device, tool)
device_serial = str(device)
- map_arg_lists = [
- ['--adb=' + adb_wrapper.AdbWrapper.GetAdbPath(),
- '--serial-id=' + device_serial,
- '--map', str(device_port), str(host_port)]
- for device_port, host_port in port_pairs]
+ map_arg_lists = [[
+ '--adb=' + adb_wrapper.AdbWrapper.GetAdbPath(),
+ '--serial-id=' + device_serial, '--map',
+ str(device_port),
+ str(host_port)
+ ] for device_port, host_port in port_pairs]
logger.info('Forwarding using commands: %s', map_arg_lists)
for map_arg_list in map_arg_lists:
@@ -154,10 +162,10 @@ class Forwarder(object):
'`%s` timed out:\n%s' % (' '.join(map_cmd), e.output))
except OSError as e:
if e.errno == 2:
- raise HostForwarderError(
- 'Unable to start host forwarder. '
- 'Make sure you have built host_forwarder.')
- else: raise
+ raise HostForwarderError('Unable to start host forwarder. '
+ 'Make sure you have built host_forwarder.')
+ else:
+ raise
if exit_code != 0:
try:
instance._KillDeviceLocked(device, tool)
@@ -169,25 +177,25 @@ class Forwarder(object):
'Failed to kill the device forwarder after map failure: %s',
str(e))
_LogMapFailureDiagnostics(device)
- formatted_output = ('\n'.join(output) if isinstance(output, list)
- else output)
+ formatted_output = ('\n'.join(output)
+ if isinstance(output, list) else output)
raise HostForwarderError(
- '`%s` exited with %d:\n%s' % (
- ' '.join(map_cmd),
- exit_code,
- formatted_output))
- tokens = output.split(':')
- if len(tokens) != 2:
- raise HostForwarderError(
- 'Unexpected host forwarder output "%s", '
- 'expected "device_port:host_port"' % output)
- device_port = int(tokens[0])
- host_port = int(tokens[1])
+ '`%s` exited with %d:\n%s' % (' '.join(map_cmd), exit_code,
+ formatted_output))
+ for line in output.splitlines():
+ match = PORT_REGEX.match(line)
+ if match:
+ break
+ if not match:
+ raise HostForwarderError('Unable to find device_port:host_port in '
+ 'host forwarder output: %s' % output)
+ device_port = int(match.groupdict()['device_port'])
+ host_port = int(match.groupdict()['host_port'])
serial_with_port = (device_serial, device_port)
instance._device_to_host_port_map[serial_with_port] = host_port
instance._host_to_device_port_map[host_port] = serial_with_port
- logger.info('Forwarding device port: %d to host port: %d.',
- device_port, host_port)
+ logger.info('Forwarding device port: %d to host port: %d.', device_port,
+ host_port)
@staticmethod
def UnmapDevicePort(device_port, device):
@@ -213,8 +221,7 @@ class Forwarder(object):
unmap_all_cmd = [
instance._host_forwarder_path,
'--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(),
- '--serial-id=%s' % device.serial,
- '--unmap-all'
+ '--serial-id=%s' % device.serial, '--unmap-all'
]
try:
exit_code, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
@@ -224,7 +231,8 @@ class Forwarder(object):
'`%s` timed out:\n%s' % (' '.join(unmap_all_cmd), e.output))
if exit_code != 0:
error_msg = [
- '`%s` exited with %d' % (' '.join(unmap_all_cmd), exit_code)]
+ '`%s` exited with %d' % (' '.join(unmap_all_cmd), exit_code)
+ ]
if isinstance(output, list):
error_msg += output
else:
@@ -317,8 +325,8 @@ class Forwarder(object):
unmap_cmd = [
instance._host_forwarder_path,
'--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(),
- '--serial-id=%s' % serial,
- '--unmap', str(device_port)
+ '--serial-id=%s' % serial, '--unmap',
+ str(device_port)
]
try:
(exit_code, output) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
@@ -327,11 +335,8 @@ class Forwarder(object):
raise HostForwarderError(
'`%s` timed out:\n%s' % (' '.join(unmap_cmd), e.output))
if exit_code != 0:
- logger.error(
- '`%s` exited with %d:\n%s',
- ' '.join(unmap_cmd),
- exit_code,
- '\n'.join(output) if isinstance(output, list) else output)
+ logger.error('`%s` exited with %d:\n%s', ' '.join(unmap_cmd), exit_code,
+ '\n'.join(output) if isinstance(output, list) else output)
@staticmethod
def _GetPidForLock():
@@ -393,18 +398,18 @@ class Forwarder(object):
'forwarder_device', device=device)
forwarder_device_path_on_device = (
Forwarder._DEVICE_FORWARDER_FOLDER
- if os.path.isdir(forwarder_device_path_on_host)
- else Forwarder._DEVICE_FORWARDER_PATH)
- device.PushChangedFiles([(
- forwarder_device_path_on_host,
- forwarder_device_path_on_device)])
+ if os.path.isdir(forwarder_device_path_on_host) else
+ Forwarder._DEVICE_FORWARDER_PATH)
+ device.PushChangedFiles([(forwarder_device_path_on_host,
+ forwarder_device_path_on_device)])
cmd = [Forwarder._DEVICE_FORWARDER_PATH]
wrapper = tool.GetUtilWrapper()
if wrapper:
cmd.insert(0, wrapper)
device.RunShellCommand(
- cmd, env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER},
+ cmd,
+ env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER},
check_return=True)
self._initialized_devices.add(device_serial)
@@ -429,12 +434,41 @@ class Forwarder(object):
kill_cmd = ['pkill', '-9', 'host_forwarder']
(exit_code, output) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
kill_cmd, Forwarder._TIMEOUT)
- if exit_code != 0:
- raise HostForwarderError(
- '%s exited with %d:\n%s' % (
- self._host_forwarder_path,
- exit_code,
- '\n'.join(output) if isinstance(output, list) else output))
+ if exit_code == -9:
+ # pkill can exit with -9, seemingly in cases where the process it's
+ # asked to kill dies sometime during pkill running. In this case,
+ # re-running should result in pkill succeeding.
+ logging.warning(
+ 'pkilling host forwarder returned -9, retrying. Output: %s',
+ output)
+ exit_code, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+ kill_cmd, Forwarder._TIMEOUT)
+ if exit_code in (0, 1):
+ # pkill exits with a 0 if it was able to signal at least one process.
+ # pkill exits with a 1 if it wasn't able to singal a process because
+ # no matching process existed. We're ok with either.
+ return
+
+ _, ps_output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
+ ['ps', 'aux'], Forwarder._TIMEOUT)
+ host_forwarder_lines = [line for line in ps_output.splitlines()
+ if 'host_forwarder' in line]
+ if host_forwarder_lines:
+ logger.error('Remaining host_forwarder processes:\n %s',
+ '\n '.join(host_forwarder_lines))
+ else:
+ logger.error('No remaining host_forwarder processes?')
+ _DumpHostLog()
+ error_msg = textwrap.dedent("""\
+ `{kill_cmd}` failed to kill host_forwarder.
+ exit_code: {exit_code}
+ output:
+ {output}
+ """)
+ raise HostForwarderError(
+ error_msg.format(
+ kill_cmd=' '.join(kill_cmd), exit_code=str(exit_code),
+ output='\n'.join(' %s' % l for l in output.splitlines())))
except cmd_helper.TimeoutError as e:
raise HostForwarderError(
'`%s` timed out:\n%s' % (' '.join(kill_cmd), e.output))
@@ -472,5 +506,6 @@ class Forwarder(object):
if wrapper:
cmd.insert(0, wrapper)
device.RunShellCommand(
- cmd, env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER},
+ cmd,
+ env={'LD_LIBRARY_PATH': Forwarder._DEVICE_FORWARDER_FOLDER},
check_return=True)
diff --git a/catapult/devil/devil/android/install_commands.py b/catapult/devil/devil/android/install_commands.py
index c8da8696..c87fc9bd 100644
--- a/catapult/devil/devil/android/install_commands.py
+++ b/catapult/devil/devil/android/install_commands.py
@@ -13,11 +13,10 @@ BIN_DIR = '%s/bin' % file_system.TEST_EXECUTABLE_DIR
_FRAMEWORK_DIR = '%s/framework' % file_system.TEST_EXECUTABLE_DIR
_COMMANDS = {
- 'unzip': 'org.chromium.android.commands.unzip.Unzip',
+ 'unzip': 'org.chromium.android.commands.unzip.Unzip',
}
-_SHELL_COMMAND_FORMAT = (
-"""#!/system/bin/sh
+_SHELL_COMMAND_FORMAT = ("""#!/system/bin/sh
base=%s
export CLASSPATH=$base/framework/chromium_commands.jar
exec app_process $base/bin %s $@
@@ -39,19 +38,17 @@ def InstallCommands(device):
chromium_commands_jar_path = devil_env.config.FetchPath('chromium_commands')
if not os.path.exists(chromium_commands_jar_path):
raise device_errors.CommandFailedError(
- '%s not found. Please build chromium_commands.'
- % chromium_commands_jar_path)
+ '%s not found. Please build chromium_commands.' %
+ chromium_commands_jar_path)
- device.RunShellCommand(
- ['mkdir', '-p', BIN_DIR, _FRAMEWORK_DIR], check_return=True)
+ device.RunShellCommand(['mkdir', '-p', BIN_DIR, _FRAMEWORK_DIR],
+ check_return=True)
for command, main_class in _COMMANDS.iteritems():
- shell_command = _SHELL_COMMAND_FORMAT % (
- file_system.TEST_EXECUTABLE_DIR, main_class)
+ shell_command = _SHELL_COMMAND_FORMAT % (file_system.TEST_EXECUTABLE_DIR,
+ main_class)
shell_file = '%s/%s' % (BIN_DIR, command)
device.WriteFile(shell_file, shell_command)
- device.RunShellCommand(
- ['chmod', '755', shell_file], check_return=True)
+ device.RunShellCommand(['chmod', '755', shell_file], check_return=True)
- device.adb.Push(
- chromium_commands_jar_path,
- '%s/chromium_commands.jar' % _FRAMEWORK_DIR)
+ device.adb.Push(chromium_commands_jar_path,
+ '%s/chromium_commands.jar' % _FRAMEWORK_DIR)
diff --git a/catapult/devil/devil/android/logcat_monitor.py b/catapult/devil/devil/android/logcat_monitor.py
index b5f796b7..bec74440 100644
--- a/catapult/devil/devil/android/logcat_monitor.py
+++ b/catapult/devil/devil/android/logcat_monitor.py
@@ -23,15 +23,20 @@ logger = logging.getLogger(__name__)
class LogcatMonitor(object):
- _RECORD_ITER_TIMEOUT = 0.2
+ _RECORD_ITER_TIMEOUT = 0.01
_RECORD_THREAD_JOIN_WAIT = 5.0
_WAIT_TIME = 0.2
THREADTIME_RE_FORMAT = (
r'(?P<date>\S*) +(?P<time>\S*) +(?P<proc_id>%s) +(?P<thread_id>%s) +'
r'(?P<log_level>%s) +(?P<component>%s) *: +(?P<message>%s)$')
- def __init__(self, adb, clear=True, filter_specs=None, output_file=None,
- transform_func=None, check_error=True):
+ def __init__(self,
+ adb,
+ clear=True,
+ filter_specs=None,
+ output_file=None,
+ transform_func=None,
+ check_error=True):
"""Create a LogcatMonitor instance.
Args:
@@ -63,7 +68,10 @@ class LogcatMonitor(object):
return self._output_file
@decorators.WithTimeoutAndRetriesDefaults(10, 0)
- def WaitFor(self, success_regex, failure_regex=None, timeout=None,
+ def WaitFor(self,
+ success_regex,
+ failure_regex=None,
+ timeout=None,
retries=None):
"""Wait for a matching logcat line or until a timeout occurs.
@@ -118,7 +126,11 @@ class LogcatMonitor(object):
else:
time.sleep(self._WAIT_TIME)
- def FindAll(self, message_regex, proc_id=None, thread_id=None, log_level=None,
+ def FindAll(self,
+ message_regex,
+ proc_id=None,
+ thread_id=None,
+ log_level=None,
component=None):
"""Finds all lines in the logcat that match the provided constraints.
@@ -153,8 +165,8 @@ class LogcatMonitor(object):
component = r'[^\s:]+'
# pylint: disable=protected-access
threadtime_re = re.compile(
- type(self).THREADTIME_RE_FORMAT % (
- proc_id, thread_id, log_level, component, message_regex))
+ type(self).THREADTIME_RE_FORMAT % (proc_id, thread_id, log_level,
+ component, message_regex))
with open(self._record_file.name, 'r') as f:
for line in f:
@@ -168,6 +180,7 @@ class LogcatMonitor(object):
Function spawns a thread that records logcat to file and will not die
until |StopRecording| is called.
"""
+
def record_to_file():
# Write the log with line buffering so the consumer sees each individual
# line.
@@ -259,8 +272,7 @@ class LogcatMonitor(object):
"""Closes logcat recording file in case |Close| was never called."""
with self._record_file_lock:
if self._record_file:
- logger.warning(
- 'Need to call |Close| on the logcat monitor when done!')
+ logger.warning('Need to call |Close| on the logcat monitor when done!')
self._record_file.close()
@property
diff --git a/catapult/devil/devil/android/logcat_monitor_test.py b/catapult/devil/devil/android/logcat_monitor_test.py
index 8fb4d74b..7f2f10a6 100755
--- a/catapult/devil/devil/android/logcat_monitor_test.py
+++ b/catapult/devil/devil/android/logcat_monitor_test.py
@@ -28,19 +28,19 @@ class LogcatMonitorTest(unittest.TestCase):
_TEST_THREADTIME_LOGCAT_DATA = [
'01-01 01:02:03.456 7890 0987 V LogcatMonitorTest: '
- 'verbose logcat monitor test message 1',
+ 'verbose logcat monitor test message 1',
'01-01 01:02:03.457 8901 1098 D LogcatMonitorTest: '
- 'debug logcat monitor test message 2',
+ 'debug logcat monitor test message 2',
'01-01 01:02:03.458 9012 2109 I LogcatMonitorTest: '
- 'info logcat monitor test message 3',
+ 'info logcat monitor test message 3',
'01-01 01:02:03.459 0123 3210 W LogcatMonitorTest: '
- 'warning logcat monitor test message 4',
+ 'warning logcat monitor test message 4',
'01-01 01:02:03.460 1234 4321 E LogcatMonitorTest: '
- 'error logcat monitor test message 5',
+ 'error logcat monitor test message 5',
'01-01 01:02:03.461 2345 5432 F LogcatMonitorTest: '
- 'fatal logcat monitor test message 6',
+ 'fatal logcat monitor test message 6',
'01-01 01:02:03.462 3456 6543 D LogcatMonitorTest: '
- 'last line'
+ 'last line'
]
def assertIterEqual(self, expected_iter, actual_iter):
@@ -71,8 +71,7 @@ class LogcatMonitorTest(unittest.TestCase):
self.assertTrue(actual_match)
self.assertEqual(
'01-01 01:02:03.460 1234 4321 E LogcatMonitorTest: '
- 'error logcat monitor test message 5',
- actual_match.group(0))
+ 'error logcat monitor test message 5', actual_match.group(0))
self.assertEqual('error', actual_match.group(1))
test_log.Stop()
test_log.Close()
@@ -82,8 +81,8 @@ class LogcatMonitorTest(unittest.TestCase):
test_log = _CreateTestLog(
raw_logcat=type(self)._TEST_THREADTIME_LOGCAT_DATA)
test_log.Start()
- actual_match = test_log.WaitFor(
- r'.*My Success Regex.*', r'.*(fatal|error) logcat monitor.*')
+ actual_match = test_log.WaitFor(r'.*My Success Regex.*',
+ r'.*(fatal|error) logcat monitor.*')
self.assertIsNone(actual_match)
test_log.Stop()
test_log.Close()
@@ -120,19 +119,18 @@ class LogcatMonitorTest(unittest.TestCase):
test_log.Start()
test_log.WaitFor(r'.*last line.*', None)
test_log.Stop()
- expected_results = [
- ('7890', '0987', 'V', 'LogcatMonitorTest',
- 'verbose logcat monitor test message 1'),
- ('8901', '1098', 'D', 'LogcatMonitorTest',
- 'debug logcat monitor test message 2'),
- ('9012', '2109', 'I', 'LogcatMonitorTest',
- 'info logcat monitor test message 3'),
- ('0123', '3210', 'W', 'LogcatMonitorTest',
- 'warning logcat monitor test message 4'),
- ('1234', '4321', 'E', 'LogcatMonitorTest',
- 'error logcat monitor test message 5'),
- ('2345', '5432', 'F', 'LogcatMonitorTest',
- 'fatal logcat monitor test message 6')]
+ expected_results = [('7890', '0987', 'V', 'LogcatMonitorTest',
+ 'verbose logcat monitor test message 1'),
+ ('8901', '1098', 'D', 'LogcatMonitorTest',
+ 'debug logcat monitor test message 2'),
+ ('9012', '2109', 'I', 'LogcatMonitorTest',
+ 'info logcat monitor test message 3'),
+ ('0123', '3210', 'W', 'LogcatMonitorTest',
+ 'warning logcat monitor test message 4'),
+ ('1234', '4321', 'E', 'LogcatMonitorTest',
+ 'error logcat monitor test message 5'),
+ ('2345', '5432', 'F', 'LogcatMonitorTest',
+ 'fatal logcat monitor test message 6')]
actual_results = test_log.FindAll(r'\S* logcat monitor test message \d')
self.assertIterEqual(iter(expected_results), actual_results)
test_log.Close()
@@ -158,9 +156,8 @@ class LogcatMonitorTest(unittest.TestCase):
test_log.Stop()
actual_results = test_log.FindAll(
r'\S* logcat monitor test message \d', proc_id=1234)
- expected_results = [
- ('1234', '4321', 'E', 'LogcatMonitorTest',
- 'error logcat monitor test message 5')]
+ expected_results = [('1234', '4321', 'E', 'LogcatMonitorTest',
+ 'error logcat monitor test message 5')]
self.assertIterEqual(iter(expected_results), actual_results)
test_log.Close()
@@ -173,9 +170,8 @@ class LogcatMonitorTest(unittest.TestCase):
test_log.Stop()
actual_results = test_log.FindAll(
r'\S* logcat monitor test message \d', thread_id=2109)
- expected_results = [
- ('9012', '2109', 'I', 'LogcatMonitorTest',
- 'info logcat monitor test message 3')]
+ expected_results = [('9012', '2109', 'I', 'LogcatMonitorTest',
+ 'info logcat monitor test message 3')]
self.assertIterEqual(iter(expected_results), actual_results)
test_log.Close()
@@ -188,12 +184,10 @@ class LogcatMonitorTest(unittest.TestCase):
test_log.Stop()
actual_results = test_log.FindAll(
r'\S* logcat monitor test message \d', log_level=r'[DW]')
- expected_results = [
- ('8901', '1098', 'D', 'LogcatMonitorTest',
- 'debug logcat monitor test message 2'),
- ('0123', '3210', 'W', 'LogcatMonitorTest',
- 'warning logcat monitor test message 4')
- ]
+ expected_results = [('8901', '1098', 'D', 'LogcatMonitorTest',
+ 'debug logcat monitor test message 2'),
+ ('0123', '3210', 'W', 'LogcatMonitorTest',
+ 'warning logcat monitor test message 4')]
self.assertIterEqual(iter(expected_results), actual_results)
test_log.Close()
@@ -205,26 +199,22 @@ class LogcatMonitorTest(unittest.TestCase):
test_log.WaitFor(r'.*last line.*', None)
test_log.Stop()
actual_results = test_log.FindAll(r'.*', component='LogcatMonitorTest')
- expected_results = [
- ('7890', '0987', 'V', 'LogcatMonitorTest',
- 'verbose logcat monitor test message 1'),
- ('8901', '1098', 'D', 'LogcatMonitorTest',
- 'debug logcat monitor test message 2'),
- ('9012', '2109', 'I', 'LogcatMonitorTest',
- 'info logcat monitor test message 3'),
- ('0123', '3210', 'W', 'LogcatMonitorTest',
- 'warning logcat monitor test message 4'),
- ('1234', '4321', 'E', 'LogcatMonitorTest',
- 'error logcat monitor test message 5'),
- ('2345', '5432', 'F', 'LogcatMonitorTest',
- 'fatal logcat monitor test message 6'),
- ('3456', '6543', 'D', 'LogcatMonitorTest',
- 'last line')
- ]
+ expected_results = [('7890', '0987', 'V', 'LogcatMonitorTest',
+ 'verbose logcat monitor test message 1'),
+ ('8901', '1098', 'D', 'LogcatMonitorTest',
+ 'debug logcat monitor test message 2'),
+ ('9012', '2109', 'I', 'LogcatMonitorTest',
+ 'info logcat monitor test message 3'),
+ ('0123', '3210', 'W', 'LogcatMonitorTest',
+ 'warning logcat monitor test message 4'),
+ ('1234', '4321', 'E', 'LogcatMonitorTest',
+ 'error logcat monitor test message 5'),
+ ('2345', '5432', 'F', 'LogcatMonitorTest',
+ 'fatal logcat monitor test message 6'),
+ ('3456', '6543', 'D', 'LogcatMonitorTest', 'last line')]
self.assertIterEqual(iter(expected_results), actual_results)
test_log.Close()
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/catapult/devil/devil/android/md5sum.py b/catapult/devil/devil/android/md5sum.py
index f5b6f3cf..8adf4ef7 100644
--- a/catapult/devil/devil/android/md5sum.py
+++ b/catapult/devil/devil/android/md5sum.py
@@ -2,9 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import base64
+import gzip
import os
-import posixpath
import re
+import StringIO
from devil import devil_env
from devil.android import device_errors
@@ -13,7 +15,16 @@ from devil.utils import cmd_helper
MD5SUM_DEVICE_LIB_PATH = '/data/local/tmp/md5sum'
MD5SUM_DEVICE_BIN_PATH = MD5SUM_DEVICE_LIB_PATH + '/md5sum_bin'
-_STARTS_WITH_CHECKSUM_RE = re.compile(r'^\s*[0-9a-fA-F]{32}\s+')
+_STARTS_WITH_CHECKSUM_RE = re.compile(r'^[0-9a-fA-F]{16}$')
+
+# We need to cap how many paths we send to the md5_sum binaries at once because
+# the ARG_MAX on Android devices is relatively small, typically 131072 bytes.
+# However, the more paths we use per invocation, the lower the overhead of
+# starting processes, so we want to maximize this number, but we can't compute
+# it exactly as we don't know how well our paths will compress.
+# 5000 is experimentally determined to be reasonable. 10000 fails, and 7500
+# works with existing usage, so 5000 seems like a pretty safe compromise.
+_MAX_PATHS_PER_INVOCATION = 5000
def CalculateHostMd5Sums(paths):
@@ -29,14 +40,22 @@ def CalculateHostMd5Sums(paths):
"""
if isinstance(paths, basestring):
paths = [paths]
+ paths = list(paths)
md5sum_bin_host_path = devil_env.config.FetchPath('md5sum_host')
if not os.path.exists(md5sum_bin_host_path):
raise IOError('File not built: %s' % md5sum_bin_host_path)
- out = cmd_helper.GetCmdOutput(
- [md5sum_bin_host_path] + [os.path.realpath(p) for p in paths])
+ out = ""
+ for i in range(0, len(paths), _MAX_PATHS_PER_INVOCATION):
+ mem_file = StringIO.StringIO()
+ compressed = gzip.GzipFile(fileobj=mem_file, mode="wb")
+ compressed.write(";".join(
+ [os.path.realpath(p) for p in paths[i:i+_MAX_PATHS_PER_INVOCATION]]))
+ compressed.close()
+ compressed_paths = base64.b64encode(mem_file.getvalue())
+ out += cmd_helper.GetCmdOutput([md5sum_bin_host_path, "-gz", compressed_paths])
- return _ParseMd5SumOutput(out.splitlines())
+ return dict(zip(paths, out.splitlines()))
def CalculateDeviceMd5Sums(paths, device):
@@ -55,7 +74,6 @@ def CalculateDeviceMd5Sums(paths, device):
if isinstance(paths, basestring):
paths = [paths]
- # Allow generators
paths = list(paths)
md5sum_dist_path = devil_env.config.FetchPath('md5sum_device', device=device)
@@ -78,16 +96,13 @@ def CalculateDeviceMd5Sums(paths, device):
md5sum_script += '! [[ $(ls -l $a) = *%d* ]]&&exit 2;' % md5sum_file_size
# Make sure it can find libbase.so
md5sum_script += 'export LD_LIBRARY_PATH=%s;' % MD5SUM_DEVICE_LIB_PATH
- if len(paths) > 1:
- prefix = posixpath.commonprefix(paths)
- if len(prefix) > 4:
- md5sum_script += 'p="%s";' % prefix
- paths = ['$p"%s"' % p[len(prefix):] for p in paths]
-
- md5sum_script += ';'.join('$a %s' % p for p in paths)
- # Don't fail the script if the last md5sum fails (due to file not found)
- # Note: ":" is equivalent to "true".
- md5sum_script += ';:'
+ for i in range(0, len(paths), _MAX_PATHS_PER_INVOCATION):
+ mem_file = StringIO.StringIO()
+ compressed = gzip.GzipFile(fileobj=mem_file, mode="wb")
+ compressed.write(";".join(paths[i:i+_MAX_PATHS_PER_INVOCATION]))
+ compressed.close()
+ compressed_paths = base64.b64encode(mem_file.getvalue())
+ md5sum_script += '$a -gz %s;' % compressed_paths
try:
out = device.RunShellCommand(
md5sum_script, shell=True, check_return=True, large_output=True)
@@ -99,7 +114,8 @@ def CalculateDeviceMd5Sums(paths, device):
# to re-push as non-root causes the push command to report success, but
# actually fail. So, wipe the directory first.
device.RunShellCommand(['rm', '-rf', MD5SUM_DEVICE_LIB_PATH],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
if os.path.isdir(md5sum_dist_path):
device.adb.Push(md5sum_dist_path, MD5SUM_DEVICE_LIB_PATH)
else:
@@ -112,11 +128,4 @@ def CalculateDeviceMd5Sums(paths, device):
else:
raise
- return _ParseMd5SumOutput(out)
-
-
-def _ParseMd5SumOutput(out):
- hash_and_path = (l.split(None, 1) for l in out
- if l and _STARTS_WITH_CHECKSUM_RE.match(l))
- return dict((p, h) for h, p in hash_and_path)
-
+ return dict(zip(paths, [l for l in out if _STARTS_WITH_CHECKSUM_RE.match(l)]))
diff --git a/catapult/devil/devil/android/md5sum_test.py b/catapult/devil/devil/android/md5sum_test.py
index c9b49545..548b2d02 100755
--- a/catapult/devil/devil/android/md5sum_test.py
+++ b/catapult/devil/devil/android/md5sum_test.py
@@ -19,17 +19,16 @@ MD5_DIST = os.path.join(TEST_OUT_DIR, 'md5sum_dist')
class Md5SumTest(unittest.TestCase):
-
def setUp(self):
mocked_attrs = {
- 'md5sum_host': HOST_MD5_EXECUTABLE,
- 'md5sum_device': MD5_DIST,
+ 'md5sum_host': HOST_MD5_EXECUTABLE,
+ 'md5sum_device': MD5_DIST,
}
self._patchers = [
- mock.patch('devil.devil_env._Environment.FetchPath',
- mock.Mock(side_effect=lambda a, device=None: mocked_attrs[a])),
- mock.patch('os.path.exists',
- new=mock.Mock(return_value=True)),
+ mock.patch(
+ 'devil.devil_env._Environment.FetchPath',
+ mock.Mock(side_effect=lambda a, device=None: mocked_attrs[a])),
+ mock.patch('os.path.exists', new=mock.Mock(return_value=True)),
]
for p in self._patchers:
p.start()
@@ -41,53 +40,30 @@ class Md5SumTest(unittest.TestCase):
def testCalculateHostMd5Sums_singlePath(self):
test_path = '/test/host/file.dat'
mock_get_cmd_output = mock.Mock(
- return_value='0123456789abcdeffedcba9876543210 /test/host/file.dat')
- with mock.patch('devil.utils.cmd_helper.GetCmdOutput',
- new=mock_get_cmd_output):
+ return_value='0123456789abcdef')
+ with mock.patch(
+ 'devil.utils.cmd_helper.GetCmdOutput', new=mock_get_cmd_output):
out = md5sum.CalculateHostMd5Sums(test_path)
self.assertEquals(1, len(out))
self.assertTrue('/test/host/file.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
- out['/test/host/file.dat'])
+ self.assertEquals('0123456789abcdef', out['/test/host/file.dat'])
mock_get_cmd_output.assert_called_once_with(
- [HOST_MD5_EXECUTABLE, '/test/host/file.dat'])
+ [HOST_MD5_EXECUTABLE, "-gz", mock.ANY])
def testCalculateHostMd5Sums_list(self):
test_paths = ['/test/host/file0.dat', '/test/host/file1.dat']
mock_get_cmd_output = mock.Mock(
- return_value='0123456789abcdeffedcba9876543210 /test/host/file0.dat\n'
- '123456789abcdef00fedcba987654321 /test/host/file1.dat\n')
- with mock.patch('devil.utils.cmd_helper.GetCmdOutput',
- new=mock_get_cmd_output):
+ return_value='0123456789abcdef\n123456789abcdef0\n')
+ with mock.patch(
+ 'devil.utils.cmd_helper.GetCmdOutput', new=mock_get_cmd_output):
out = md5sum.CalculateHostMd5Sums(test_paths)
self.assertEquals(2, len(out))
self.assertTrue('/test/host/file0.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
- out['/test/host/file0.dat'])
+ self.assertEquals('0123456789abcdef', out['/test/host/file0.dat'])
self.assertTrue('/test/host/file1.dat' in out)
- self.assertEquals('123456789abcdef00fedcba987654321',
- out['/test/host/file1.dat'])
+ self.assertEquals('123456789abcdef0', out['/test/host/file1.dat'])
mock_get_cmd_output.assert_called_once_with(
- [HOST_MD5_EXECUTABLE, '/test/host/file0.dat',
- '/test/host/file1.dat'])
-
- def testCalculateHostMd5Sums_generator(self):
- test_paths = ('/test/host/' + p for p in ['file0.dat', 'file1.dat'])
- mock_get_cmd_output = mock.Mock(
- return_value='0123456789abcdeffedcba9876543210 /test/host/file0.dat\n'
- '123456789abcdef00fedcba987654321 /test/host/file1.dat\n')
- with mock.patch('devil.utils.cmd_helper.GetCmdOutput',
- new=mock_get_cmd_output):
- out = md5sum.CalculateHostMd5Sums(test_paths)
- self.assertEquals(2, len(out))
- self.assertTrue('/test/host/file0.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
- out['/test/host/file0.dat'])
- self.assertTrue('/test/host/file1.dat' in out)
- self.assertEquals('123456789abcdef00fedcba987654321',
- out['/test/host/file1.dat'])
- mock_get_cmd_output.assert_called_once_with(
- [HOST_MD5_EXECUTABLE, '/test/host/file0.dat', '/test/host/file1.dat'])
+ [HOST_MD5_EXECUTABLE, "-gz", mock.ANY])
def testCalculateDeviceMd5Sums_noPaths(self):
device = mock.NonCallableMock()
@@ -100,29 +76,26 @@ class Md5SumTest(unittest.TestCase):
test_path = '/storage/emulated/legacy/test/file.dat'
device = mock.NonCallableMock()
- device_md5sum_output = [
- '0123456789abcdeffedcba9876543210 '
- '/storage/emulated/legacy/test/file.dat',
- ]
+ device_md5sum_output = ['0123456789abcdef',]
device.RunShellCommand = mock.Mock(return_value=device_md5sum_output)
with mock.patch('os.path.getsize', return_value=1337):
out = md5sum.CalculateDeviceMd5Sums(test_path, device)
self.assertEquals(1, len(out))
self.assertTrue('/storage/emulated/legacy/test/file.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
+ self.assertEquals('0123456789abcdef',
out['/storage/emulated/legacy/test/file.dat'])
self.assertEquals(1, len(device.RunShellCommand.call_args_list))
def testCalculateDeviceMd5Sums_list(self):
- test_path = ['/storage/emulated/legacy/test/file0.dat',
- '/storage/emulated/legacy/test/file1.dat']
+ test_path = [
+ '/storage/emulated/legacy/test/file0.dat',
+ '/storage/emulated/legacy/test/file1.dat'
+ ]
device = mock.NonCallableMock()
device_md5sum_output = [
- '0123456789abcdeffedcba9876543210 '
- '/storage/emulated/legacy/test/file0.dat',
- '123456789abcdef00fedcba987654321 '
- '/storage/emulated/legacy/test/file1.dat',
+ '0123456789abcdef',
+ '123456789abcdef0',
]
device.RunShellCommand = mock.Mock(return_value=device_md5sum_output)
@@ -130,10 +103,10 @@ class Md5SumTest(unittest.TestCase):
out = md5sum.CalculateDeviceMd5Sums(test_path, device)
self.assertEquals(2, len(out))
self.assertTrue('/storage/emulated/legacy/test/file0.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
+ self.assertEquals('0123456789abcdef',
out['/storage/emulated/legacy/test/file0.dat'])
self.assertTrue('/storage/emulated/legacy/test/file1.dat' in out)
- self.assertEquals('123456789abcdef00fedcba987654321',
+ self.assertEquals('123456789abcdef0',
out['/storage/emulated/legacy/test/file1.dat'])
self.assertEquals(1, len(device.RunShellCommand.call_args_list))
@@ -143,10 +116,8 @@ class Md5SumTest(unittest.TestCase):
device = mock.NonCallableMock()
device_md5sum_output = [
- '0123456789abcdeffedcba9876543210 '
- '/storage/emulated/legacy/test/file0.dat',
- '123456789abcdef00fedcba987654321 '
- '/storage/emulated/legacy/test/file1.dat',
+ '0123456789abcdef',
+ '123456789abcdef0',
]
device.RunShellCommand = mock.Mock(return_value=device_md5sum_output)
@@ -154,10 +125,10 @@ class Md5SumTest(unittest.TestCase):
out = md5sum.CalculateDeviceMd5Sums(test_path, device)
self.assertEquals(2, len(out))
self.assertTrue('/storage/emulated/legacy/test/file0.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
+ self.assertEquals('0123456789abcdef',
out['/storage/emulated/legacy/test/file0.dat'])
self.assertTrue('/storage/emulated/legacy/test/file1.dat' in out)
- self.assertEquals('123456789abcdef00fedcba987654321',
+ self.assertEquals('123456789abcdef0',
out['/storage/emulated/legacy/test/file1.dat'])
self.assertEquals(1, len(device.RunShellCommand.call_args_list))
@@ -168,10 +139,9 @@ class Md5SumTest(unittest.TestCase):
device = mock.NonCallableMock()
device_md5sum_output = [
'WARNING: linker: /data/local/tmp/md5sum/md5sum_bin: '
- 'unused DT entry: type 0x1d arg 0x15db',
+ 'unused DT entry: type 0x1d arg 0x15db',
'THIS_IS_NOT_A_VALID_CHECKSUM_ZZZ some random text',
- '0123456789abcdeffedcba9876543210 '
- '/storage/emulated/legacy/test/file.dat',
+ '0123456789abcdef',
]
device.RunShellCommand = mock.Mock(return_value=device_md5sum_output)
@@ -179,17 +149,18 @@ class Md5SumTest(unittest.TestCase):
out = md5sum.CalculateDeviceMd5Sums(test_path, device)
self.assertEquals(1, len(out))
self.assertTrue('/storage/emulated/legacy/test/file.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
+ self.assertEquals('0123456789abcdef',
out['/storage/emulated/legacy/test/file.dat'])
self.assertEquals(1, len(device.RunShellCommand.call_args_list))
def testCalculateDeviceMd5Sums_list_fileMissing(self):
- test_path = ['/storage/emulated/legacy/test/file0.dat',
- '/storage/emulated/legacy/test/file1.dat']
+ test_path = [
+ '/storage/emulated/legacy/test/file0.dat',
+ '/storage/emulated/legacy/test/file1.dat'
+ ]
device = mock.NonCallableMock()
device_md5sum_output = [
- '0123456789abcdeffedcba9876543210 '
- '/storage/emulated/legacy/test/file0.dat',
+ '0123456789abcdef',
'[0819/203513:ERROR:md5sum.cc(25)] Could not open file asdf',
'',
]
@@ -199,7 +170,7 @@ class Md5SumTest(unittest.TestCase):
out = md5sum.CalculateDeviceMd5Sums(test_path, device)
self.assertEquals(1, len(out))
self.assertTrue('/storage/emulated/legacy/test/file0.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
+ self.assertEquals('0123456789abcdef',
out['/storage/emulated/legacy/test/file0.dat'])
self.assertEquals(1, len(device.RunShellCommand.call_args_list))
@@ -211,27 +182,26 @@ class Md5SumTest(unittest.TestCase):
device.adb.Push = mock.Mock()
device_md5sum_output = [
'WARNING: linker: /data/local/tmp/md5sum/md5sum_bin: '
- 'unused DT entry: type 0x1d arg 0x15db',
+ 'unused DT entry: type 0x1d arg 0x15db',
'THIS_IS_NOT_A_VALID_CHECKSUM_ZZZ some random text',
- '0123456789abcdeffedcba9876543210 '
- '/storage/emulated/legacy/test/file.dat',
+ '0123456789abcdef',
]
error = device_errors.AdbShellCommandFailedError('cmd', 'out', 2)
device.RunShellCommand = mock.Mock(
side_effect=(error, '', device_md5sum_output))
- with mock.patch('os.path.isdir', return_value=True), (
- mock.patch('os.path.getsize', return_value=1337)):
+ with mock.patch(
+ 'os.path.isdir', return_value=True), (mock.patch(
+ 'os.path.getsize', return_value=1337)):
out = md5sum.CalculateDeviceMd5Sums(test_path, device)
self.assertEquals(1, len(out))
self.assertTrue('/storage/emulated/legacy/test/file.dat' in out)
- self.assertEquals('0123456789abcdeffedcba9876543210',
+ self.assertEquals('0123456789abcdef',
out['/storage/emulated/legacy/test/file.dat'])
self.assertEquals(3, len(device.RunShellCommand.call_args_list))
- device.adb.Push.assert_called_once_with(
- 'test/out/directory/md5sum_dist', '/data/local/tmp/md5sum')
+ device.adb.Push.assert_called_once_with('test/out/directory/md5sum_dist',
+ '/data/local/tmp/md5sum')
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/catapult/devil/devil/android/ndk/abis.py b/catapult/devil/devil/android/ndk/abis.py
index dd32f7cb..e92ef2d1 100644
--- a/catapult/devil/devil/android/ndk/abis.py
+++ b/catapult/devil/devil/android/ndk/abis.py
@@ -1,7 +1,6 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Android NDK ABIs.
https://developer.android.com/ndk/guides/abis
diff --git a/catapult/devil/devil/android/perf/perf_control.py b/catapult/devil/devil/android/perf/perf_control.py
index 1226be80..59485e0e 100644
--- a/catapult/devil/devil/android/perf/perf_control.py
+++ b/catapult/devil/devil/android/perf/perf_control.py
@@ -11,7 +11,6 @@ from devil.android import device_errors
logger = logging.getLogger(__name__)
_atexit_messages = set()
-
# Defines how to switch between the default performance configuration
# ('default_mode') and the mode for use when benchmarking ('high_perf_mode').
# For devices not in the list the defaults are to set up the scaling governor to
@@ -24,93 +23,138 @@ _atexit_messages = set()
# TODO(crbug.com/383566): Add definitions for all devices used in the perf
# waterfall.
_PERFORMANCE_MODE_DEFINITIONS = {
- # Fire TV Edition - 4K
- 'AFTKMST12': {
- 'default_mode_governor': 'interactive',
- },
- # Pixel 3
- 'blueline': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- # The SoC is Arm big.LITTLE. The cores 0..3 are LITTLE, the 4..7 are big.
- 'cpu_max_freq': {'0..3': 1228800, '4..7': 1536000},
- 'gpu_max_freq': 520000000,
- },
- 'default_mode': {
- 'cpu_max_freq': {'0..3': 1766400, '4..7': 2649600},
- 'gpu_max_freq': 710000000,
- },
- 'default_mode_governor': 'schedutil',
- },
- 'GT-I9300': {
- 'default_mode_governor': 'pegasusq',
- },
- 'Galaxy Nexus': {
- 'default_mode_governor': 'interactive',
- },
- # Pixel
- 'msm8996': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1209600,
- 'gpu_max_freq': 315000000,
- },
- 'default_mode': {
- # The SoC is Arm big.LITTLE. The cores 0..1 are LITTLE, the 2..3 are big.
- 'cpu_max_freq': {'0..1': 1593600, '2..3': 2150400},
- 'gpu_max_freq': 624000000,
- },
- 'default_mode_governor': 'sched',
- },
- 'Nexus 7': {
- 'default_mode_governor': 'interactive',
- },
- 'Nexus 10': {
- 'default_mode_governor': 'interactive',
- },
- 'Nexus 4': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
+ # Fire TV Edition - 4K
+ 'AFTKMST12': {
+ 'default_mode_governor': 'interactive',
+ },
+ # Pixel 3
+ 'blueline': {
+ 'high_perf_mode': {
+ 'bring_cpu_cores_online': True,
+ # The SoC is Arm big.LITTLE. The cores 0..3 are LITTLE,
+ # the 4..7 are big.
+ 'cpu_max_freq': {
+ '0..3': 1228800,
+ '4..7': 1536000
+ },
+ 'gpu_max_freq': 520000000,
+ },
+ 'default_mode': {
+ 'cpu_max_freq': {
+ '0..3': 1766400,
+ '4..7': 2649600
+ },
+ 'gpu_max_freq': 710000000,
+ },
+ 'big_cores': ['4', '5', '6', '7'],
+ 'default_mode_governor': 'schedutil',
+ },
+ 'Pixel 2': {
+ 'high_perf_mode': {
+ 'bring_cpu_cores_online': True,
+ # These are set to roughly 7/8 of the max frequency. The purpose of
+ # this is to ensure that thermal throttling doesn't kick in midway
+ # through a test and cause flaky results. It should also improve the
+ # longevity of the devices by keeping them cooler.
+ 'cpu_max_freq': {
+ '0..3': 1670400,
+ '4..7': 2208000,
+ },
+ 'gpu_max_freq': 670000000,
+ },
+ 'default_mode': {
+ # These are the maximum frequencies available for these CPUs and
+ # GPUs.
+ 'cpu_max_freq': {
+ '0..3': 1900800,
+ '4..7': 2457600,
+ },
+ 'gpu_max_freq': 710000000,
+ },
+ 'big_cores': ['4', '5', '6', '7'],
+ 'default_mode_governor': 'schedutil',
+ },
+ 'GT-I9300': {
+ 'default_mode_governor': 'pegasusq',
+ },
+ 'Galaxy Nexus': {
+ 'default_mode_governor': 'interactive',
+ },
+ # Pixel
+ 'msm8996': {
+ 'high_perf_mode': {
+ 'bring_cpu_cores_online': True,
+ 'cpu_max_freq': 1209600,
+ 'gpu_max_freq': 315000000,
+ },
+ 'default_mode': {
+ # The SoC is Arm big.LITTLE. The cores 0..1 are LITTLE,
+ # the 2..3 are big.
+ 'cpu_max_freq': {
+ '0..1': 1593600,
+ '2..3': 2150400
+ },
+ 'gpu_max_freq': 624000000,
+ },
+ 'big_cores': ['2', '3'],
+ 'default_mode_governor': 'sched',
},
- 'default_mode_governor': 'ondemand',
- },
- 'Nexus 5': {
- # The list of possible GPU frequency values can be found in:
- # /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies.
- # For CPU cores the possible frequency values are at:
- # /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1190400,
- 'gpu_max_freq': 200000000,
+ 'Nexus 7': {
+ 'default_mode_governor': 'interactive',
},
- 'default_mode': {
- 'cpu_max_freq': 2265600,
- 'gpu_max_freq': 450000000,
+ 'Nexus 10': {
+ 'default_mode_governor': 'interactive',
},
- 'default_mode_governor': 'ondemand',
- },
- 'Nexus 5X': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1248000,
- 'gpu_max_freq': 300000000,
+ 'Nexus 4': {
+ 'high_perf_mode': {
+ 'bring_cpu_cores_online': True,
+ },
+ 'default_mode_governor': 'ondemand',
},
- 'default_mode': {
- 'governor': 'ondemand',
- # The SoC is ARM big.LITTLE. The cores 4..5 are big, the 0..3 are LITTLE.
- 'cpu_max_freq': {'0..3': 1440000, '4..5': 1824000},
- 'gpu_max_freq': 600000000,
+ 'Nexus 5': {
+ # The list of possible GPU frequency values can be found in:
+ # /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies.
+ # For CPU cores the possible frequency values are at:
+ # /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
+ 'high_perf_mode': {
+ 'bring_cpu_cores_online': True,
+ 'cpu_max_freq': 1190400,
+ 'gpu_max_freq': 200000000,
+ },
+ 'default_mode': {
+ 'cpu_max_freq': 2265600,
+ 'gpu_max_freq': 450000000,
+ },
+ 'default_mode_governor': 'ondemand',
+ },
+ 'Nexus 5X': {
+ 'high_perf_mode': {
+ 'bring_cpu_cores_online': True,
+ 'cpu_max_freq': 1248000,
+ 'gpu_max_freq': 300000000,
+ },
+ 'default_mode': {
+ 'governor': 'ondemand',
+ # The SoC is ARM big.LITTLE. The cores 4..5 are big,
+ # the 0..3 are LITTLE.
+ 'cpu_max_freq': {
+ '0..3': 1440000,
+ '4..5': 1824000
+ },
+ 'gpu_max_freq': 600000000,
+ },
+ 'big_cores': ['4', '5'],
+ 'default_mode_governor': 'ondemand',
},
- 'default_mode_governor': 'ondemand',
- },
}
+
def _GetPerfModeDefinitions(product_model):
if product_model.startswith('AOSP on '):
product_model = product_model.replace('AOSP on ', '')
return _PERFORMANCE_MODE_DEFINITIONS.get(product_model)
+
def _NoisyWarning(message):
message += ' Results may be NOISY!!'
logger.warning(message)
@@ -144,7 +188,8 @@ class PerfControl(object):
raw = self._ReadEachCpuFile(self._AVAILABLE_GOVERNORS_REL_PATH)
self._available_governors = [
(cpu, raw_governors.strip().split() if not exit_code else None)
- for cpu, raw_governors, exit_code in raw]
+ for cpu, raw_governors, exit_code in raw
+ ]
def _SetMaxFrequenciesFromMode(self, mode):
"""Set maximum frequencies for GPU and CPU cores.
@@ -165,8 +210,9 @@ class PerfControl(object):
range_min, range_max = int(range_min), int(range_max)
else:
range_min = range_max = int(key)
- cpu_files = ['cpu%d' % number
- for number in xrange(range_min, range_max + 1)]
+ cpu_files = [
+ 'cpu%d' % number for number in xrange(range_min, range_max + 1)
+ ]
# Set the |max_frequency| on requested subset of the cores.
self._SetScalingMaxFreqForCpus(max_frequency, ' '.join(cpu_files))
gpu_max_freq = mode.get('gpu_max_freq')
@@ -197,6 +243,25 @@ class PerfControl(object):
self.SetScalingGovernor('performance')
self._SetMaxFrequenciesFromMode(high_perf_mode)
+ def SetLittleOnlyMode(self):
+ """Turns off big CPU cores on the device."""
+ try:
+ self._device.EnableRoot()
+ except device_errors.CommandFailedError:
+ _NoisyWarning('Need root to turn off cores.')
+ return
+ mode_definitions = _GetPerfModeDefinitions(self._device.product_model)
+ if not mode_definitions:
+ _NoisyWarning('Unknown device: %s. Can\'t turn off cores.'
+ % self._device.product_model)
+ return
+ big_cores = mode_definitions.get('big_cores', [])
+ if not big_cores:
+ _NoisyWarning('No mode definition for device: %s.' %
+ self._device.product_model)
+ return
+ self._ForceCpusOffline(cpu_list=big_cores)
+
def SetDefaultPerfMode(self):
"""Sets the performance mode for the device to its default mode."""
if not self._device.HasRoot():
@@ -207,7 +272,7 @@ class PerfControl(object):
else:
default_mode_governor = mode_definitions.get('default_mode_governor')
assert default_mode_governor, ('Default mode governor must be provided '
- 'for all perf mode definitions.')
+ 'for all perf mode definitions.')
self.SetScalingGovernor(default_mode_governor)
default_mode = mode_definitions.get('default_mode')
if default_mode:
@@ -226,9 +291,10 @@ class PerfControl(object):
def GetCpuInfo(self):
online = (output.rstrip() == '1' and status == 0
for (_, output, status) in self._ForEachCpu('cat "$CPU/online"'))
- governor = (output.rstrip() if status == 0 else None
- for (_, output, status)
- in self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"'))
+ governor = (
+ output.rstrip() if status == 0 else None
+ for (_, output,
+ status) in self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"'))
return zip(self._cpu_files, online, governor)
def _ForEachCpu(self, cmd, cpu_list=None):
@@ -249,9 +315,7 @@ class PerfControl(object):
cpu_list = self._cpu_file_list
script = '; '.join([
'for CPU in %s' % cpu_list,
- 'do %s' % cmd,
- 'echo -n "%~%$?%~%"',
- 'done'
+ 'do %s' % cmd, 'echo -n "%~%$?%~%"', 'done'
])
output = self._device.RunShellCommand(
script, cwd=self._CPU_PATH, check_return=True, as_root=True, shell=True)
@@ -273,8 +337,7 @@ class PerfControl(object):
self._ConditionallyWriteCpuFiles(path, value, cpu_files, condition='true')
def _ReadEachCpuFile(self, path):
- return self._ForEachCpu(
- 'cat "$CPU/{path}"'.format(path=path))
+ return self._ForEachCpu('cat "$CPU/{path}"'.format(path=path))
def SetScalingGovernor(self, value):
"""Sets the scaling governor to the given value on all possible CPUs.
@@ -286,10 +349,9 @@ class PerfControl(object):
value: [string] The new governor value.
"""
condition = 'test -e "{path}" && grep -q {value} {path}'.format(
- path=('${CPU}/%s' % self._AVAILABLE_GOVERNORS_REL_PATH),
- value=value)
- self._ConditionallyWriteCpuFiles(
- 'cpufreq/scaling_governor', value, self._cpu_file_list, condition)
+ path=('${CPU}/%s' % self._AVAILABLE_GOVERNORS_REL_PATH), value=value)
+ self._ConditionallyWriteCpuFiles('cpufreq/scaling_governor', value,
+ self._cpu_file_list, condition)
def GetScalingGovernor(self):
"""Gets the currently set governor for each CPU.
@@ -299,9 +361,8 @@ class PerfControl(object):
governor.
"""
raw = self._ReadEachCpuFile('cpufreq/scaling_governor')
- return [
- (cpu, raw_governor.strip() if not exit_code else None)
- for cpu, raw_governor, exit_code in raw]
+ return [(cpu, raw_governor.strip() if not exit_code else None)
+ for cpu, raw_governor, exit_code in raw]
def ListAvailableGovernors(self):
"""Returns the list of available governors for each CPU.
@@ -316,9 +377,8 @@ class PerfControl(object):
self._WriteCpuFiles('cpufreq/scaling_max_freq', '%d' % value, cpu_files)
def _SetMaxGpuClock(self, value):
- self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk',
- str(value),
- as_root=True)
+ self._device.WriteFile(
+ '/sys/class/kgsl/kgsl-3d0/max_gpuclk', str(value), as_root=True)
def _AllCpusAreOnline(self):
results = self._ForEachCpu('cat "$CPU/online"')
@@ -326,8 +386,7 @@ class PerfControl(object):
# is likely because on these devices it is impossible to bring the cpu0
# offline. Assuming the same for all devices until proven otherwise.
return all(output.rstrip() == '1' and status == 0
- for (cpu, output, status) in results
- if cpu != 'cpu0')
+ for (cpu, output, status) in results if cpu != 'cpu0')
def _ForceAllCpusOnline(self, force_online):
"""Enable all CPUs on a device.
@@ -352,3 +411,11 @@ class PerfControl(object):
if force_online:
self._ForEachCpu('echo 1 > "$CPU/online"')
+
+ def _ForceCpusOffline(self, cpu_list):
+ """Disable selected CPUs on a device."""
+ if self._have_mpdecision:
+ cmd = ['stop', 'mpdecision']
+ self._device.RunShellCommand(cmd, check_return=True, as_root=True)
+
+ self._ForEachCpu('echo 0 > "$CPU/online"', cpu_list=cpu_list)
diff --git a/catapult/devil/devil/android/perf/perf_control_devicetest.py b/catapult/devil/devil/android/perf/perf_control_devicetest.py
index b6458030..f73601a0 100644
--- a/catapult/devil/devil/android/perf/perf_control_devicetest.py
+++ b/catapult/devil/devil/android/perf/perf_control_devicetest.py
@@ -15,7 +15,6 @@ from devil.android.perf import perf_control
class TestPerfControl(device_test_case.DeviceTestCase):
-
def setUp(self):
super(TestPerfControl, self).setUp()
if not os.getenv('BUILDTYPE'):
@@ -34,5 +33,6 @@ class TestPerfControl(device_test_case.DeviceTestCase):
finally:
perf.SetDefaultPerfMode()
+
if __name__ == '__main__':
unittest.main()
diff --git a/catapult/devil/devil/android/perf/perf_control_test.py b/catapult/devil/devil/android/perf/perf_control_test.py
index 3832424c..bde54d5a 100644
--- a/catapult/devil/devil/android/perf/perf_control_test.py
+++ b/catapult/devil/devil/android/perf/perf_control_test.py
@@ -14,9 +14,18 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
# pylint: disable=unused-argument
-def _ShellCommandHandler(cmd, shell=False, check_return=False,
- cwd=None, env=None, run_as=None, as_root=False, single_line=False,
- large_output=False, raw_output=False, timeout=None, retries=None):
+def _ShellCommandHandler(cmd,
+ shell=False,
+ check_return=False,
+ cwd=None,
+ env=None,
+ run_as=None,
+ as_root=False,
+ single_line=False,
+ large_output=False,
+ raw_output=False,
+ timeout=None,
+ retries=None):
if cmd.startswith('for CPU in '):
if 'scaling_available_governors' in cmd:
contents = 'interactive ondemand userspace powersave performance'
diff --git a/catapult/devil/devil/android/perf/surface_stats_collector.py b/catapult/devil/devil/android/perf/surface_stats_collector.py
index f1140c12..6240624f 100644
--- a/catapult/devil/devil/android/perf/surface_stats_collector.py
+++ b/catapult/devil/devil/android/perf/surface_stats_collector.py
@@ -7,7 +7,6 @@ import Queue
import re
import threading
-
# Log marker containing SurfaceTexture timestamps.
_SURFACE_TEXTURE_TIMESTAMPS_MESSAGE = 'SurfaceTexture update timestamps'
_SURFACE_TEXTURE_TIMESTAMP_RE = r'\d+'
@@ -75,8 +74,10 @@ class SurfaceStatsCollector(object):
break
raise Exception('Unable to get surface flinger latency data')
- timestamps += [timestamp for timestamp in new_timestamps
- if timestamp > last_timestamp]
+ timestamps += [
+ timestamp for timestamp in new_timestamps
+ if timestamp > last_timestamp
+ ]
if len(timestamps):
last_timestamp = timestamps[-1]
diff --git a/catapult/devil/devil/android/perf/surface_stats_collector_test.py b/catapult/devil/devil/android/perf/surface_stats_collector_test.py
index 13b345ce..dda88ae0 100644
--- a/catapult/devil/devil/android/perf/surface_stats_collector_test.py
+++ b/catapult/devil/devil/android/perf/surface_stats_collector_test.py
@@ -14,7 +14,8 @@ class SurfaceStatsCollectorTests(unittest.TestCase):
'7657467895508 7657482691352 7657493499756',
'7657484466553 7657499645964 7657511077881',
'7657500793457 7657516600576 7657527404785',
- ], parse_timestamps=True)
+ ],
+ parse_timestamps=True)
self.assertEqual(
actual, (16.954612, [7657482.691352, 7657499.645964, 7657516.600576]))
@@ -24,9 +25,9 @@ class SurfaceStatsCollectorTests(unittest.TestCase):
'7657467895508 7657482691352 7657493499756',
'7657484466553 7657499645964 7657511077881',
'7657500793457 7657516600576 7657527404785',
- ], parse_timestamps=False)
- self.assertEqual(
- actual, (16.954612, []))
+ ],
+ parse_timestamps=False)
+ self.assertEqual(actual, (16.954612, []))
def testParseFrameData_withWarning(self):
actual = surface_stats_collector.ParseFrameData([
@@ -35,6 +36,7 @@ class SurfaceStatsCollectorTests(unittest.TestCase):
'7657467895508 7657482691352 7657493499756',
'7657484466553 7657499645964 7657511077881',
'7657500793457 7657516600576 7657527404785',
- ], parse_timestamps=True)
+ ],
+ parse_timestamps=True)
self.assertEqual(
actual, (16.954612, [7657482.691352, 7657499.645964, 7657516.600576]))
diff --git a/catapult/devil/devil/android/perf/thermal_throttle.py b/catapult/devil/devil/android/perf/thermal_throttle.py
index fd6b08f3..9b8dc07b 100644
--- a/catapult/devil/devil/android/perf/thermal_throttle.py
+++ b/catapult/devil/devil/android/perf/thermal_throttle.py
@@ -39,6 +39,7 @@ class OmapThrottlingDetector(object):
class ExynosThrottlingDetector(object):
"""Class to detect and track thermal throttling on an Exynos 5."""
+
@staticmethod
def IsSupported(device):
return device.FileExists('/sys/bus/exynos5-core')
@@ -98,8 +99,9 @@ class ThermalThrottle(object):
return False
has_been_throttled = False
serial_number = str(self._device)
- log = self._device.RunShellCommand(
- ['dmesg', '-c'], large_output=True, check_return=True)
+ log = self._device.RunShellCommand(['dmesg', '-c'],
+ large_output=True,
+ check_return=True)
degree_symbol = unichr(0x00B0)
for line in log:
if self._detector.BecameThrottled(line):
@@ -114,19 +116,19 @@ class ThermalThrottle(object):
has_been_throttled = True
temperature = self._detector.GetThrottlingTemperature(line)
if temperature is not None:
- logger.info(u'Device %s thermally throttled at %3.1f%sC',
- serial_number, temperature, degree_symbol)
+ logger.info(u'Device %s thermally throttled at %3.1f%sC', serial_number,
+ temperature, degree_symbol)
if logger.isEnabledFor(logging.DEBUG):
# Print current temperature of CPU SoC.
temperature = self._detector.GetCurrentTemperature()
if temperature is not None:
- logger.debug(u'Current SoC temperature of %s = %3.1f%sC',
- serial_number, temperature, degree_symbol)
+ logger.debug(u'Current SoC temperature of %s = %3.1f%sC', serial_number,
+ temperature, degree_symbol)
# Print temperature of battery, to give a system temperature
- dumpsys_log = self._device.RunShellCommand(
- ['dumpsys', 'battery'], check_return=True)
+ dumpsys_log = self._device.RunShellCommand(['dumpsys', 'battery'],
+ check_return=True)
for line in dumpsys_log:
if 'temperature' in line:
btemp = float([s for s in line.split() if s.isdigit()][0]) / 10.0
diff --git a/catapult/devil/devil/android/ports.py b/catapult/devil/devil/android/ports.py
index 4547a627..f25c8bfd 100644
--- a/catapult/devil/devil/android/ports.py
+++ b/catapult/devil/devil/android/ports.py
@@ -1,7 +1,6 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Functions that deal with local and device ports."""
import contextlib
@@ -63,8 +62,7 @@ def AllocateTestServerPort():
while not IsHostPortAvailable(port):
port += 1
ports_tried.append(port)
- if (port > _TEST_SERVER_PORT_LAST or
- port < _TEST_SERVER_PORT_FIRST):
+ if port > _TEST_SERVER_PORT_LAST or port < _TEST_SERVER_PORT_FIRST:
port = 0
else:
fp.seek(0, os.SEEK_SET)
@@ -78,8 +76,9 @@ def AllocateTestServerPort():
if port:
logger.info('Allocate port %d for test server.', port)
else:
- logger.error('Could not allocate port for test server. '
- 'List of ports tried: %s', str(ports_tried))
+ logger.error(
+ 'Could not allocate port for test server. '
+ 'List of ports tried: %s', str(ports_tried))
return port
@@ -115,8 +114,9 @@ def IsDevicePortUsed(device, device_port, state=''):
True if the port on device is already used, otherwise returns False.
"""
base_urls = ('127.0.0.1:%d' % device_port, 'localhost:%d' % device_port)
- netstat_results = device.RunShellCommand(
- ['netstat', '-an'], check_return=True, large_output=True)
+ netstat_results = device.RunShellCommand(['netstat', '-an'],
+ check_return=True,
+ large_output=True)
for single_connect in netstat_results:
# Column 3 is the local address which we want to check with.
connect_results = single_connect.split()
@@ -131,8 +131,13 @@ def IsDevicePortUsed(device, device_port, state=''):
return False
-def IsHttpServerConnectable(host, port, tries=3, command='GET', path='/',
- expected_read='', timeout=2):
+def IsHttpServerConnectable(host,
+ port,
+ tries=3,
+ command='GET',
+ path='/',
+ expected_read='',
+ timeout=2):
"""Checks whether the specified http server is ready to serve request or not.
Args:
@@ -157,8 +162,8 @@ def IsHttpServerConnectable(host, port, tries=3, command='GET', path='/',
for i in xrange(0, tries):
client_error = None
try:
- with contextlib.closing(httplib.HTTPConnection(
- host, port, timeout=timeout)) as http:
+ with contextlib.closing(
+ httplib.HTTPConnection(host, port, timeout=timeout)) as http:
# Output some debug information when we have tried more than 2 times.
http.set_debuglevel(i >= 2)
http.request(command, path)
@@ -167,8 +172,8 @@ def IsHttpServerConnectable(host, port, tries=3, command='GET', path='/',
if r.status == 200 and r.reason == 'OK' and content == expected_read:
return (True, '')
client_error = ('Bad response: %s %s version %s\n ' %
- (r.status, r.reason, r.version) +
- '\n '.join([': '.join(h) for h in r.getheaders()]))
+ (r.status, r.reason, r.version) + '\n '.join(
+ [': '.join(h) for h in r.getheaders()]))
except (httplib.HTTPException, socket.error) as e:
# Probably too quick connecting: try again.
exception_error_msgs = traceback.format_exception_only(type(e), e)
diff --git a/catapult/devil/devil/android/sdk/aapt.py b/catapult/devil/devil/android/sdk/aapt.py
index 7ae3a938..76c7ef68 100644
--- a/catapult/devil/devil/android/sdk/aapt.py
+++ b/catapult/devil/devil/android/sdk/aapt.py
@@ -1,14 +1,12 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""This module wraps the Android Asset Packaging Tool."""
from devil.android.sdk import build_tools
from devil.utils import cmd_helper
from devil.utils import lazy
-
_aapt_path = lazy.WeakConstant(lambda: build_tools.GetPath('aapt'))
diff --git a/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py b/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py
index cbe2a1b6..d2c2d4f5 100644
--- a/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py
+++ b/catapult/devil/devil/android/sdk/adb_compatibility_devicetest.py
@@ -11,8 +11,8 @@ import signal
import sys
import unittest
-_CATAPULT_BASE_DIR = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..', '..', '..'))
+_CATAPULT_BASE_DIR = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
sys.path.append(os.path.join(_CATAPULT_BASE_DIR, 'devil'))
from devil import devil_env
@@ -22,9 +22,8 @@ from devil.android.sdk import adb_wrapper
from devil.utils import cmd_helper
from devil.utils import timeout_retry
-
-_TEST_DATA_DIR = os.path.abspath(os.path.join(
- os.path.dirname(__file__), 'test', 'data'))
+_TEST_DATA_DIR = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), 'test', 'data'))
def _hostAdbPids():
@@ -34,26 +33,24 @@ def _hostAdbPids():
return []
pids_and_names = (line.split() for line in ps_output.splitlines())
- return [int(pid) for pid, name in pids_and_names
- if name == 'adb']
+ return [int(pid) for pid, name in pids_and_names if name == 'adb']
class AdbCompatibilityTest(device_test_case.DeviceTestCase):
-
@classmethod
def setUpClass(cls):
custom_adb_path = os.environ.get('ADB_PATH')
custom_deps = {
- 'config_type': 'BaseConfig',
- 'dependencies': {},
+ 'config_type': 'BaseConfig',
+ 'dependencies': {},
}
if custom_adb_path:
custom_deps['dependencies']['adb'] = {
- 'file_info': {
- devil_env.GetPlatform(): {
- 'local_paths': [custom_adb_path],
+ 'file_info': {
+ devil_env.GetPlatform(): {
+ 'local_paths': [custom_adb_path],
+ },
},
- },
}
devil_env.config.Initialize(configs=[custom_deps])
@@ -124,7 +121,7 @@ class AdbCompatibilityTest(device_test_case.DeviceTestCase):
if not external_storage:
self.skipTest('External storage not available.')
while True:
- random_hex = hex(random.randint(0, 2 ** 52))[2:]
+ random_hex = hex(random.randint(0, 2**52))[2:]
name = 'tmp_push_test%s' % random_hex
path = posixpath.join(external_storage, name)
try:
@@ -156,9 +153,8 @@ class AdbCompatibilityTest(device_test_case.DeviceTestCase):
with self.assertRaises(device_errors.AdbShellCommandFailedError):
under_test.Shell('ls %s' % resulting_file)
under_test.Push(src, dest)
- self.assertEquals(
- resulting_file,
- under_test.Shell('ls %s' % resulting_file).strip())
+ self.assertEquals(resulting_file,
+ under_test.Shell('ls %s' % resulting_file).strip())
def testPush_directoryToDirectory(self):
under_test = self.getTestInstance()
diff --git a/catapult/devil/devil/android/sdk/adb_wrapper.py b/catapult/devil/devil/android/sdk/adb_wrapper.py
index 13c0f520..8cd73136 100644
--- a/catapult/devil/devil/android/sdk/adb_wrapper.py
+++ b/catapult/devil/devil/android/sdk/adb_wrapper.py
@@ -1,7 +1,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""This module wraps Android's adb tool.
This is a thin wrapper around the adb interface. Any additional complexity
@@ -32,7 +31,6 @@ with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH):
logger = logging.getLogger(__name__)
-
ADB_KEYS_FILE = '/data/misc/adb/adb_keys'
ADB_HOST_KEYS_DIR = os.path.join(os.path.expanduser('~'), '.android')
@@ -76,8 +74,8 @@ def _FindAdb():
pass
try:
- return os.path.join(devil_env.config.LocalPath('android_sdk'),
- 'platform-tools', 'adb')
+ return os.path.join(
+ devil_env.config.LocalPath('android_sdk'), 'platform-tools', 'adb')
except dependency_manager.NoPathFoundError:
pass
@@ -101,8 +99,8 @@ def _ShouldRetryAdbCmd(exc):
# Errors are potentially transient and should be retried, with the exception
# of NoAdbError. Exceptions [e.g. generated from SIGTERM handler] should be
# raised.
- return (isinstance(exc, base_error.BaseError) and
- not isinstance(exc, device_errors.NoAdbError))
+ return (isinstance(exc, base_error.BaseError)
+ and not isinstance(exc, device_errors.NoAdbError))
DeviceStat = collections.namedtuple('DeviceStat',
@@ -157,6 +155,7 @@ class AdbWrapper(object):
pshell.RunCommand('which ls')
pshell.RunCommand('echo TEST', close=True)
'''
+
def __init__(self, serial):
"""Initialization function:
@@ -179,11 +178,12 @@ class AdbWrapper(object):
if self._process is not None:
raise RuntimeError('Persistent shell already running.')
# pylint: disable=protected-access
- self._process = subprocess.Popen(self._cmd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- shell=False,
- env=AdbWrapper._ADB_ENV)
+ self._process = subprocess.Popen(
+ self._cmd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ shell=False,
+ env=AdbWrapper._ADB_ENV)
def WaitForReady(self):
"""Wait for the shell to be ready after starting.
@@ -210,6 +210,7 @@ class AdbWrapper(object):
"""
if close:
+
def run_cmd(cmd):
send_cmd = '( %s ); echo $?; exit;\n' % cmd.rstrip()
(output, _) = self._process.communicate(send_cmd)
@@ -218,6 +219,7 @@ class AdbWrapper(object):
yield x
else:
+
def run_cmd(cmd):
send_cmd = '( %s ); echo DONE:$?;\n' % cmd.rstrip()
self._process.stdin.write(send_cmd)
@@ -228,8 +230,10 @@ class AdbWrapper(object):
break
yield output_line
- result = [line for line in run_cmd(command)
- if not _IsExtraneousLine(line, command)]
+ result = [
+ line for line in run_cmd(command)
+ if not _IsExtraneousLine(line, command)
+ ]
return (result[:-1], int(result[-1]))
@@ -262,8 +266,14 @@ class AdbWrapper(object):
# pylint: disable=unused-argument
@classmethod
@decorators.WithTimeoutAndConditionalRetries(_ShouldRetryAdbCmd)
- def _RunAdbCmd(cls, args, timeout=None, retries=None, device_serial=None,
- check_error=True, cpu_affinity=None, additional_env=None):
+ def _RunAdbCmd(cls,
+ args,
+ timeout=None,
+ retries=None,
+ device_serial=None,
+ check_error=True,
+ cpu_affinity=None,
+ additional_env=None):
if timeout:
remaining = timeout_retry.CurrentTimeoutThreadGroup().GetRemainingTime()
if remaining:
@@ -276,14 +286,18 @@ class AdbWrapper(object):
if additional_env:
env.update(additional_env)
try:
- status, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
- cls._BuildAdbCmd(args, device_serial, cpu_affinity=cpu_affinity),
- timeout, env=env)
+ adb_cmd = cls._BuildAdbCmd(args, device_serial, cpu_affinity=cpu_affinity)
+ status, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(adb_cmd,
+ timeout,
+ env=env)
except OSError as e:
if e.errno in (errno.ENOENT, errno.ENOEXEC):
raise device_errors.NoAdbError(msg=str(e))
else:
raise
+ except cmd_helper.TimeoutError:
+ logger.error('Timeout on adb command: %r', adb_cmd)
+ raise
# Best effort to catch errors from adb; unfortunately adb is very
# inconsistent with error reporting so many command failures present
@@ -292,14 +306,15 @@ class AdbWrapper(object):
not_found_m = _DEVICE_NOT_FOUND_RE.match(output)
device_waiting_m = _WAITING_FOR_DEVICE_RE.match(output)
if (device_waiting_m is not None
- or (not_found_m is not None and
- not_found_m.group('serial') == device_serial)):
+ or (not_found_m is not None
+ and not_found_m.group('serial') == device_serial)):
raise device_errors.DeviceUnreachableError(device_serial)
else:
- raise device_errors.AdbCommandFailedError(
- args, output, status, device_serial)
+ raise device_errors.AdbCommandFailedError(args, output, status,
+ device_serial)
return output
+
# pylint: enable=unused-argument
def _RunDeviceAdbCmd(self, args, timeout, retries, check_error=True):
@@ -316,12 +331,14 @@ class AdbWrapper(object):
Returns:
The output of the command.
"""
- return self._RunAdbCmd(args, timeout=timeout, retries=retries,
- device_serial=self._device_serial,
- check_error=check_error)
+ return self._RunAdbCmd(
+ args,
+ timeout=timeout,
+ retries=retries,
+ device_serial=self._device_serial,
+ check_error=check_error)
- def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout,
- check_error=True):
+ def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout, check_error=True):
"""Runs an adb command and returns an iterator over its output lines.
Args:
@@ -371,6 +388,7 @@ class AdbWrapper(object):
output = [int(x) for x in output.split()]
logger.info('PIDs for adb found: %r', output)
return status == 0
+
# pylint: enable=unused-argument
@classmethod
@@ -378,7 +396,9 @@ class AdbWrapper(object):
cls._RunAdbCmd(['kill-server'], timeout=timeout, retries=retries)
@classmethod
- def StartServer(cls, keys=None, timeout=DEFAULT_TIMEOUT,
+ def StartServer(cls,
+ keys=None,
+ timeout=DEFAULT_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Starts the ADB server.
@@ -391,8 +411,11 @@ class AdbWrapper(object):
if keys:
additional_env['ADB_VENDOR_KEYS'] = ':'.join(keys)
# CPU affinity is used to reduce adb instability http://crbug.com/268450
- cls._RunAdbCmd(['start-server'], timeout=timeout, retries=retries,
- cpu_affinity=0, additional_env=additional_env)
+ cls._RunAdbCmd(['start-server'],
+ timeout=timeout,
+ retries=retries,
+ cpu_affinity=0,
+ additional_env=additional_env)
@classmethod
def GetDevices(cls, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
@@ -401,8 +424,11 @@ class AdbWrapper(object):
return cls.Devices(timeout=timeout, retries=retries)
@classmethod
- def Devices(cls, desired_state=_READY_STATE, long_list=False,
- timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
+ def Devices(cls,
+ desired_state=_READY_STATE,
+ long_list=False,
+ timeout=DEFAULT_TIMEOUT,
+ retries=DEFAULT_RETRIES):
"""Get the list of active attached devices.
Args:
@@ -415,23 +441,22 @@ class AdbWrapper(object):
Yields:
AdbWrapper instances.
"""
- lines = cls._RawDevices(long_list=long_list, timeout=timeout,
- retries=retries)
+ lines = cls._RawDevices(
+ long_list=long_list, timeout=timeout, retries=retries)
if long_list:
- return [
- [AdbWrapper(line[0])] + line[1:]
- for line in lines
- if (len(line) >= 2 and (not desired_state or line[1] == desired_state))
- ]
+ return [[AdbWrapper(line[0])] + line[1:] for line in lines if (
+ len(line) >= 2 and (not desired_state or line[1] == desired_state))]
else:
return [
- AdbWrapper(line[0])
- for line in lines
- if (len(line) == 2 and (not desired_state or line[1] == desired_state))
+ AdbWrapper(line[0]) for line in lines
+ if (len(line) == 2 and (not desired_state or line[1] == desired_state)
+ )
]
@classmethod
- def _RawDevices(cls, long_list=False, timeout=DEFAULT_TIMEOUT,
+ def _RawDevices(cls,
+ long_list=False,
+ timeout=DEFAULT_TIMEOUT,
retries=DEFAULT_RETRIES):
cmd = ['devices']
if long_list:
@@ -447,14 +472,24 @@ class AdbWrapper(object):
"""
return self._device_serial
- def Push(self, local, remote, timeout=60 * 5, retries=DEFAULT_RETRIES):
+ def Push(self,
+ local,
+ remote,
+ sync=False,
+ timeout=60 * 5,
+ retries=DEFAULT_RETRIES):
"""Pushes a file from the host to the device.
Args:
local: Path on the host filesystem.
remote: Path on the device filesystem.
+ sync: (optional) Whether to only push files that are newer on the host.
+ Not supported when using adb prior to 1.0.39.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
+
+ Raises:
+ AdbVersionError if sync=True with versions of adb prior to 1.0.39.
"""
VerifyLocalFileExists(local)
@@ -495,7 +530,24 @@ class AdbWrapper(object):
# without modification.
pass
- self._RunDeviceAdbCmd(['push', local, remote], timeout, retries)
+ push_cmd = ['push']
+
+ if sync:
+ push_cmd += ['--sync']
+ if (du_version.LooseVersion(self.Version()) <
+ du_version.LooseVersion('1.0.39')):
+ # The --sync flag for `adb push` is a relatively recent addition.
+ # We're not sure exactly which release first contained it, but it
+ # exists at least as far back as 1.0.39.
+ raise device_errors.AdbVersionError(
+ push_cmd,
+ desc='--sync not supported',
+ actual_version=self.Version(),
+ min_version='1.0.39')
+
+ push_cmd += [local, remote]
+
+ self._RunDeviceAdbCmd(push_cmd, timeout, retries)
def Pull(self, remote, local, timeout=60 * 5, retries=DEFAULT_RETRIES):
"""Pulls a file from the device to the host.
@@ -529,7 +581,10 @@ class AdbWrapper(object):
return cmd_helper.StartCmd(
self._BuildAdbCmd(['shell'] + cmd, self._device_serial))
- def Shell(self, command, expect_status=0, timeout=DEFAULT_TIMEOUT,
+ def Shell(self,
+ command,
+ expect_status=0,
+ timeout=DEFAULT_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Runs a shell command on the device.
@@ -582,8 +637,9 @@ class AdbWrapper(object):
"""
args = ['shell', command]
return cmd_helper.IterCmdOutputLines(
- self._BuildAdbCmd(args, self._device_serial), timeout=timeout,
- env=self._ADB_ENV)
+ self._BuildAdbCmd(args, self._device_serial),
+ timeout=timeout,
+ env=self._ADB_ENV)
def Ls(self, path, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
"""List the contents of a directory on the device.
@@ -602,12 +658,16 @@ class AdbWrapper(object):
directory in the device, or the output of "adb ls" command is less
than four columns
"""
+
def ParseLine(line, cmd):
cols = line.split(None, 3)
if len(cols) < 4:
raise device_errors.AdbCommandFailedError(
- cmd, line, "the output should be 4 columns, but is only %d columns"
- % len(cols), device_serial=self._device_serial)
+ cmd,
+ line,
+ "the output should be 4 columns, but is only %d columns" %
+ len(cols),
+ device_serial=self._device_serial)
filename = cols.pop()
stat = DeviceStat(*[int(num, base=16) for num in cols])
return (filename, stat)
@@ -619,12 +679,20 @@ class AdbWrapper(object):
return [ParseLine(line, cmd) for line in lines]
else:
raise device_errors.AdbCommandFailedError(
- cmd, 'path does not specify an accessible directory in the device',
+ cmd,
+ 'path does not specify an accessible directory in the device',
device_serial=self._device_serial)
- def Logcat(self, clear=False, dump=False, filter_specs=None,
- logcat_format=None, ring_buffer=None, iter_timeout=None,
- check_error=True, timeout=None, retries=DEFAULT_RETRIES):
+ def Logcat(self,
+ clear=False,
+ dump=False,
+ filter_specs=None,
+ logcat_format=None,
+ ring_buffer=None,
+ iter_timeout=None,
+ check_error=True,
+ timeout=None,
+ retries=DEFAULT_RETRIES):
"""Get an iterable over the logcat output.
Args:
@@ -674,8 +742,12 @@ class AdbWrapper(object):
cmd, timeout, retries, check_error=check_error)
return output.splitlines()
- def Forward(self, local, remote, allow_rebind=False,
- timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
+ def Forward(self,
+ local,
+ remote,
+ allow_rebind=False,
+ timeout=DEFAULT_TIMEOUT,
+ retries=DEFAULT_RETRIES):
"""Forward socket connections from the local socket to the remote socket.
Sockets are specified by one of:
@@ -703,7 +775,9 @@ class AdbWrapper(object):
if output:
logger.warning('Unexpected output from "adb forward": %s', output)
- def ForwardRemove(self, local, timeout=DEFAULT_TIMEOUT,
+ def ForwardRemove(self,
+ local,
+ timeout=DEFAULT_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Remove a forward socket connection.
@@ -712,8 +786,7 @@ class AdbWrapper(object):
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
"""
- self._RunDeviceAdbCmd(['forward', '--remove', str(local)], timeout,
- retries)
+ self._RunDeviceAdbCmd(['forward', '--remove', str(local)], timeout, retries)
def ForwardList(self, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
"""List all currently forwarded socket connections.
@@ -747,11 +820,19 @@ class AdbWrapper(object):
Returns:
A list of PIDs as strings.
"""
- return [a.strip() for a in
- self._RunDeviceAdbCmd(['jdwp'], timeout, retries).split('\n')]
-
- def Install(self, apk_path, forward_lock=False, allow_downgrade=False,
- reinstall=False, sd_card=False, timeout=60 * 2,
+ return [
+ a.strip()
+ for a in self._RunDeviceAdbCmd(['jdwp'], timeout, retries).split('\n')
+ ]
+
+ def Install(self,
+ apk_path,
+ forward_lock=False,
+ allow_downgrade=False,
+ reinstall=False,
+ sd_card=False,
+ streaming=None,
+ timeout=60 * 2,
retries=DEFAULT_RETRIES):
"""Install an apk on the device.
@@ -761,6 +842,10 @@ class AdbWrapper(object):
allow_downgrade: (optional) If set, allows for downgrades.
reinstall: (optional) If set reinstalls the app, keeping its data.
sd_card: (optional) If set installs on the SD card.
+ streaming: (optional) If not set, use default way to install.
+ If True, performs streaming install.
+ If False, app is pushed to device and be installed from there.
+ Note this option is not supported prior to adb version 1.0.40
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
"""
@@ -774,15 +859,32 @@ class AdbWrapper(object):
cmd.append('-s')
if allow_downgrade:
cmd.append('-d')
+ if streaming in (True, False):
+ if (du_version.LooseVersion(self.Version()) <
+ du_version.LooseVersion('1.0.40')):
+ logging.warning(
+ 'adb: streaming options not supported prior to version 1.0.40 '
+ '(current: %s)', self.Version())
+ elif streaming:
+ cmd.append('--streaming')
+ else:
+ cmd.append('--no-streaming')
cmd.append(apk_path)
output = self._RunDeviceAdbCmd(cmd, timeout, retries)
if 'Success' not in output:
raise device_errors.AdbCommandFailedError(
cmd, output, device_serial=self._device_serial)
- def InstallMultiple(self, apk_paths, forward_lock=False, reinstall=False,
- sd_card=False, allow_downgrade=False, partial=False,
- timeout=60 * 2, retries=DEFAULT_RETRIES):
+ def InstallMultiple(self,
+ apk_paths,
+ forward_lock=False,
+ reinstall=False,
+ sd_card=False,
+ allow_downgrade=False,
+ partial=False,
+ streaming=None,
+ timeout=60 * 2,
+ retries=DEFAULT_RETRIES):
"""Install an apk with splits on the device.
Args:
@@ -792,6 +894,10 @@ class AdbWrapper(object):
sd_card: (optional) If set installs on the SD card.
allow_downgrade: (optional) Allow versionCode downgrade.
partial: (optional) Package ID if apk_paths doesn't include all .apks.
+ streaming: (optional) If not set, use default way to install.
+ If True, performs streaming install.
+ If False, app is pushed to device and be installed from there.
+ Note this option is not supported prior to adb version 1.0.40
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
"""
@@ -806,6 +912,16 @@ class AdbWrapper(object):
cmd.append('-s')
if allow_downgrade:
cmd.append('-d')
+ if streaming in (True, False):
+ if (du_version.LooseVersion(self.Version()) <
+ du_version.LooseVersion('1.0.40')):
+ logging.warning(
+ 'adb: streaming options not supported prior to version 1.0.40 '
+ '(current: %s)', self.Version())
+ elif streaming:
+ cmd.append('--streaming')
+ else:
+ cmd.append('--no-streaming')
if partial:
cmd.extend(('-p', partial))
cmd.extend(apk_paths)
@@ -814,7 +930,10 @@ class AdbWrapper(object):
raise device_errors.AdbCommandFailedError(
cmd, output, device_serial=self._device_serial)
- def Uninstall(self, package, keep_data=False, timeout=DEFAULT_TIMEOUT,
+ def Uninstall(self,
+ package,
+ keep_data=False,
+ timeout=DEFAULT_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Remove the app |package| from the device.
@@ -833,8 +952,14 @@ class AdbWrapper(object):
raise device_errors.AdbCommandFailedError(
cmd, output, device_serial=self._device_serial)
- def Backup(self, path, packages=None, apk=False, shared=False,
- nosystem=True, include_all=False, timeout=DEFAULT_TIMEOUT,
+ def Backup(self,
+ path,
+ packages=None,
+ apk=False,
+ shared=False,
+ nosystem=True,
+ include_all=False,
+ timeout=DEFAULT_TIMEOUT,
retries=DEFAULT_RETRIES):
"""Write an archive of the device's data to |path|.
@@ -949,8 +1074,7 @@ class AdbWrapper(object):
raise device_errors.AdbCommandFailedError(
['root'], output, device_serial=self._device_serial)
- def Emu(self, cmd, timeout=DEFAULT_TIMEOUT,
- retries=DEFAULT_RETRIES):
+ def Emu(self, cmd, timeout=DEFAULT_TIMEOUT, retries=DEFAULT_RETRIES):
"""Runs an emulator console command.
See http://developer.android.com/tools/devices/emulator.html#console
diff --git a/catapult/devil/devil/android/sdk/adb_wrapper_devicetest.py b/catapult/devil/devil/android/sdk/adb_wrapper_devicetest.py
index b0ccb24a..f8b010b8 100755
--- a/catapult/devil/devil/android/sdk/adb_wrapper_devicetest.py
+++ b/catapult/devil/devil/android/sdk/adb_wrapper_devicetest.py
@@ -3,7 +3,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Tests for the AdbWrapper class."""
import os
@@ -17,7 +16,6 @@ from devil.android.sdk import adb_wrapper
class TestAdbWrapper(device_test_case.DeviceTestCase):
-
def setUp(self):
super(TestAdbWrapper, self).setUp()
self._adb = adb_wrapper.AdbWrapper(self.serial)
@@ -54,7 +52,7 @@ class TestAdbWrapper(device_test_case.DeviceTestCase):
def testPersistentShell(self):
# We need to access the device serial number here in order
# to create the persistent shell.
- serial = self._adb.GetDeviceSerial() # pylint: disable=protected-access
+ serial = self._adb.GetDeviceSerial() # pylint: disable=protected-access
with self._adb.PersistentShell(serial) as pshell:
(res1, code1) = pshell.RunCommand('echo TEST')
(res2, code2) = pshell.RunCommand('echo TEST2')
diff --git a/catapult/devil/devil/android/sdk/adb_wrapper_test.py b/catapult/devil/devil/android/sdk/adb_wrapper_test.py
index 07f784d0..f30ab152 100755
--- a/catapult/devil/devil/android/sdk/adb_wrapper_test.py
+++ b/catapult/devil/devil/android/sdk/adb_wrapper_test.py
@@ -2,7 +2,6 @@
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for some APIs with conditional logic in adb_wrapper.py
"""
@@ -24,8 +23,7 @@ class AdbWrapperTest(unittest.TestCase):
def _MockRunDeviceAdbCmd(self, return_value):
return mock.patch.object(
- self.adb,
- '_RunDeviceAdbCmd',
+ self.adb, '_RunDeviceAdbCmd',
mock.Mock(side_effect=None, return_value=return_value))
def testDisableVerityWhenDisabled(self):
@@ -50,23 +48,23 @@ class AdbWrapperTest(unittest.TestCase):
def testFailEnableVerity(self):
with self._MockRunDeviceAdbCmd('error: closed'):
- self.assertRaises(
- device_errors.AdbCommandFailedError, self.adb.EnableVerity)
+ self.assertRaises(device_errors.AdbCommandFailedError,
+ self.adb.EnableVerity)
def testFailDisableVerity(self):
with self._MockRunDeviceAdbCmd('error: closed'):
- self.assertRaises(
- device_errors.AdbCommandFailedError, self.adb.DisableVerity)
+ self.assertRaises(device_errors.AdbCommandFailedError,
+ self.adb.DisableVerity)
@mock.patch('devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout')
def testDeviceUnreachable(self, get_cmd_mock):
get_cmd_mock.return_value = (
1, "error: device '%s' not found" % self.device_serial)
- self.assertRaises(
- device_errors.DeviceUnreachableError, self.adb.Shell, '/bin/true')
+ self.assertRaises(device_errors.DeviceUnreachableError, self.adb.Shell,
+ '/bin/true')
@mock.patch('devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout')
def testWaitingForDevice(self, get_cmd_mock):
get_cmd_mock.return_value = (1, '- waiting for device - ')
- self.assertRaises(
- device_errors.DeviceUnreachableError, self.adb.Shell, '/bin/true')
+ self.assertRaises(device_errors.DeviceUnreachableError, self.adb.Shell,
+ '/bin/true')
diff --git a/catapult/devil/devil/android/sdk/build_tools.py b/catapult/devil/devil/android/sdk/build_tools.py
index 99083d99..64cc96d1 100644
--- a/catapult/devil/devil/android/sdk/build_tools.py
+++ b/catapult/devil/devil/android/sdk/build_tools.py
@@ -27,8 +27,16 @@ def GetPath(build_tool):
def _PathInLocalSdk(build_tool):
build_tools_path = _build_tools_path.read()
- return (os.path.join(build_tools_path, build_tool) if build_tools_path
- else None)
+ if not build_tools_path:
+ raise dependency_manager.NoPathFoundError(build_tool,
+ devil_env.GetPlatform())
+
+ candidate_path = os.path.join(build_tools_path, build_tool)
+ if not os.path.exists(candidate_path):
+ raise dependency_manager.NoPathFoundError(build_tool,
+ devil_env.GetPlatform())
+
+ return candidate_path
def _FindBuildTools():
diff --git a/catapult/devil/devil/android/sdk/bundletool.py b/catapult/devil/devil/android/sdk/bundletool.py
new file mode 100644
index 00000000..5c181c56
--- /dev/null
+++ b/catapult/devil/devil/android/sdk/bundletool.py
@@ -0,0 +1,62 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""This module wraps bundletool."""
+
+import json
+
+from devil import base_error
+from devil import devil_env
+from devil.utils import cmd_helper
+from devil.utils import lazy
+from py_utils import tempfile_ext
+
+_bundletool_path = lazy.WeakConstant(lambda: devil_env.config.FetchPath(
+ 'bundletool'))
+
+
+def ExtractApks(output_dir,
+ apks_path,
+ abis,
+ locales,
+ features,
+ pixel_density,
+ sdk_version,
+ modules=None):
+ """Extracts splits from APKS archive.
+
+ Args:
+ output_dir: Directory to extract splits into.
+ apks_path: Path to APKS archive.
+ abis: ABIs to support.
+ locales: Locales to support.
+ features: Device features to support.
+ pixel_density: Pixel density to support.
+ sdk_version: Target SDK version.
+ modules: Extra modules to install.
+ """
+ device_spec = {
+ 'supportedAbis': abis,
+ 'supportedLocales': ['%s-%s' % l for l in locales],
+ 'deviceFeatures': features,
+ 'screenDensity': pixel_density,
+ 'sdkVersion': sdk_version,
+ }
+ with tempfile_ext.TemporaryFileName(suffix='.json') as device_spec_path:
+ with open(device_spec_path, 'w') as f:
+ json.dump(device_spec, f)
+ cmd = [
+ 'java',
+ '-jar',
+ _bundletool_path.read(),
+ 'extract-apks',
+ '--apks=%s' % apks_path,
+ '--device-spec=%s' % device_spec_path,
+ '--output-dir=%s' % output_dir,
+ ]
+ if modules:
+ cmd += ['--modules=%s' % ','.join(modules)]
+ status, stdout, stderr = cmd_helper.GetCmdStatusOutputAndError(cmd)
+ if status != 0:
+ raise base_error.BaseError('Failed running {} with output\n{}\n{}'.format(
+ ' '.join(cmd), stdout, stderr))
diff --git a/catapult/devil/devil/android/sdk/dexdump.py b/catapult/devil/devil/android/sdk/dexdump.py
index 992366e8..2a59e9bf 100644
--- a/catapult/devil/devil/android/sdk/dexdump.py
+++ b/catapult/devil/devil/android/sdk/dexdump.py
@@ -6,7 +6,6 @@ from devil.android.sdk import build_tools
from devil.utils import cmd_helper
from devil.utils import lazy
-
_dexdump_path = lazy.WeakConstant(lambda: build_tools.GetPath('dexdump'))
@@ -28,4 +27,3 @@ def DexDump(dexfiles, file_summary=False):
args.append('-f')
return cmd_helper.IterCmdOutputLines(args)
-
diff --git a/catapult/devil/devil/android/sdk/fastboot.py b/catapult/devil/devil/android/sdk/fastboot.py
index 47f4167f..d0f85667 100644
--- a/catapult/devil/devil/android/sdk/fastboot.py
+++ b/catapult/devil/devil/android/sdk/fastboot.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""This module wraps Android's fastboot tool.
This is a thin wrapper around the fastboot interface. Any additional complexity
@@ -22,10 +21,12 @@ _FLASH_TIMEOUT = _DEFAULT_TIMEOUT * 10
class Fastboot(object):
- _fastboot_path = lazy.WeakConstant(
- lambda: devil_env.config.FetchPath('fastboot'))
+ _fastboot_path = lazy.WeakConstant(lambda: devil_env.config.FetchPath(
+ 'fastboot'))
- def __init__(self, device_serial, default_timeout=_DEFAULT_TIMEOUT,
+ def __init__(self,
+ device_serial,
+ default_timeout=_DEFAULT_TIMEOUT,
default_retries=_DEFAULT_RETRIES):
"""Initializes the FastbootWrapper.
@@ -57,9 +58,10 @@ class Fastboot(object):
if isinstance(cmd, list):
cmd = [cls._fastboot_path.read()] + cmd
else:
- raise TypeError(
- 'Command for _RunDeviceFastbootCommand must be a list.')
- status, output = cmd_helper.GetCmdStatusAndOutput(cmd)
+ raise TypeError('Command for _RunDeviceFastbootCommand must be a list.')
+ # fastboot can't be trusted to keep non-error output out of stderr, so
+ # capture stderr as part of stdout.
+ status, output = cmd_helper.GetCmdStatusAndOutput(cmd, merge_stderr=True)
if int(status) != 0:
raise device_errors.FastbootCommandFailedError(cmd, output, status)
return output
@@ -80,6 +82,19 @@ class Fastboot(object):
cmd = ['-s', self._device_serial] + cmd
return self._RunFastbootCommand(cmd)
+ @decorators.WithTimeoutAndRetriesDefaults(_DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
+ def GetVar(self, variable, timeout=None, retries=None):
+ args = ['getvar', variable]
+ output = self._RunDeviceFastbootCommand(args)
+ # getvar returns timing information on the last line of output, so only
+ # parse the first line.
+ output = output.splitlines()[0]
+ # And the first line should match the format '$(var): $(value)'.
+ if variable + ': ' not in output:
+ raise device_errors.FastbootCommandFailedError(
+ args, output, message="Unknown 'getvar' output format.")
+ return output.split('%s: ' % variable)[1].strip()
+
@decorators.WithTimeoutAndRetriesDefaults(_FLASH_TIMEOUT, 0)
def Flash(self, partition, image, timeout=None, retries=None):
"""Flash partition with img.
@@ -118,5 +133,4 @@ class Fastboot(object):
Args:
value: boolean value to set off-mode-charging on or off.
"""
- self._RunDeviceFastbootCommand(
- ['oem', 'off-mode-charge', str(int(value))])
+ self._RunDeviceFastbootCommand(['oem', 'off-mode-charge', str(int(value))])
diff --git a/catapult/devil/devil/android/sdk/gce_adb_wrapper.py b/catapult/devil/devil/android/sdk/gce_adb_wrapper.py
index 71600f40..c6061973 100644
--- a/catapult/devil/devil/android/sdk/gce_adb_wrapper.py
+++ b/catapult/devil/devil/android/sdk/gce_adb_wrapper.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Provides a work around for various adb commands on android gce instances.
Some adb commands don't work well when the device is a cloud vm, namely
@@ -22,14 +21,14 @@ logger = logging.getLogger(__name__)
class GceAdbWrapper(adb_wrapper.AdbWrapper):
-
def __init__(self, device_serial):
super(GceAdbWrapper, self).__init__(device_serial)
self._Connect()
self.Root()
self._instance_ip = self.Shell('getprop net.gce.ip').strip()
- def _Connect(self, timeout=adb_wrapper.DEFAULT_TIMEOUT,
+ def _Connect(self,
+ timeout=adb_wrapper.DEFAULT_TIMEOUT,
retries=adb_wrapper.DEFAULT_RETRIES):
"""Connects ADB to the android gce instance."""
cmd = ['connect', self._device_serial]
@@ -59,8 +58,8 @@ class GceAdbWrapper(adb_wrapper.AdbWrapper):
# with the destination dir. So if local is a dir, just scp its contents.
for f in os.listdir(local):
self._PushObject(os.path.join(local, f), os.path.join(remote, f))
- self.Shell('chmod 777 %s' %
- cmd_helper.SingleQuote(os.path.join(remote, f)))
+ self.Shell(
+ 'chmod 777 %s' % cmd_helper.SingleQuote(os.path.join(remote, f)))
else:
parent_dir = remote[0:remote.rfind('/')]
if parent_dir:
@@ -76,17 +75,15 @@ class GceAdbWrapper(adb_wrapper.AdbWrapper):
remote: Path on the instance filesystem.
"""
cmd = [
- 'scp',
- '-r',
- '-o', 'UserKnownHostsFile=/dev/null',
- '-o', 'StrictHostKeyChecking=no',
- local,
+ 'scp', '-r', '-o', 'UserKnownHostsFile=/dev/null', '-o',
+ 'StrictHostKeyChecking=no', local,
'root@%s:%s' % (self._instance_ip, remote)
]
status, _ = cmd_helper.GetCmdStatusAndOutput(cmd)
if status:
raise device_errors.AdbCommandFailedError(
- cmd, 'File not reachable on host: %s' % local,
+ cmd,
+ 'File not reachable on host: %s' % local,
device_serial=str(self))
# override
@@ -101,15 +98,18 @@ class GceAdbWrapper(adb_wrapper.AdbWrapper):
'scp',
'-p',
'-r',
- '-o', 'UserKnownHostsFile=/dev/null',
- '-o', 'StrictHostKeyChecking=no',
+ '-o',
+ 'UserKnownHostsFile=/dev/null',
+ '-o',
+ 'StrictHostKeyChecking=no',
'root@%s:%s' % (self._instance_ip, remote),
local,
]
status, _ = cmd_helper.GetCmdStatusAndOutput(cmd)
if status:
raise device_errors.AdbCommandFailedError(
- cmd, 'File not reachable on host: %s' % local,
+ cmd,
+ 'File not reachable on host: %s' % local,
device_serial=str(self))
try:
@@ -117,12 +117,17 @@ class GceAdbWrapper(adb_wrapper.AdbWrapper):
except (subprocess.CalledProcessError, IOError):
logger.exception('Error when pulling files from android instance.')
raise device_errors.AdbCommandFailedError(
- cmd, 'File not reachable on host: %s' % local,
+ cmd,
+ 'File not reachable on host: %s' % local,
device_serial=str(self))
# override
- def Install(self, apk_path, forward_lock=False, reinstall=False,
- sd_card=False, **kwargs):
+ def Install(self,
+ apk_path,
+ forward_lock=False,
+ reinstall=False,
+ sd_card=False,
+ **kwargs):
"""Installs an apk on the gce instance
Args:
diff --git a/catapult/devil/devil/android/sdk/intent.py b/catapult/devil/devil/android/sdk/intent.py
index cdefb463..69c7d900 100644
--- a/catapult/devil/devil/android/sdk/intent.py
+++ b/catapult/devil/devil/android/sdk/intent.py
@@ -1,7 +1,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Manages intents and associated information.
This is generally intended to be used with functions that calls Android's
@@ -25,10 +24,15 @@ def _bitwise_or(flags):
class Intent(object):
-
- def __init__(self, action='android.intent.action.VIEW', activity=None,
- category=None, component=None, data=None, extras=None,
- flags=None, package=None):
+ def __init__(self,
+ action='android.intent.action.VIEW',
+ activity=None,
+ category=None,
+ component=None,
+ data=None,
+ extras=None,
+ flags=None,
+ package=None):
"""Creates an Intent.
Args:
diff --git a/catapult/devil/devil/android/sdk/keyevent.py b/catapult/devil/devil/android/sdk/keyevent.py
index 657dc963..61d61c84 100644
--- a/catapult/devil/devil/android/sdk/keyevent.py
+++ b/catapult/devil/devil/android/sdk/keyevent.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Android KeyEvent constants.
http://developer.android.com/reference/android/view/KeyEvent.html
diff --git a/catapult/devil/devil/android/sdk/shared_prefs.py b/catapult/devil/devil/android/sdk/shared_prefs.py
index c8c82b4e..7b12bf54 100644
--- a/catapult/devil/devil/android/sdk/shared_prefs.py
+++ b/catapult/devil/devil/android/sdk/shared_prefs.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Helper object to read and modify Shared Preferences from Android apps.
See e.g.:
@@ -17,7 +16,6 @@ from devil.android.sdk import version_codes
logger = logging.getLogger(__name__)
-
_XML_DECLARATION = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
@@ -161,12 +159,14 @@ class StringSetPref(StringPref):
ElementTree.SubElement(self._elem, 'string').text = str(item)
-_PREF_TYPES = {c.tag_name: c for c in [BooleanPref, FloatPref, IntPref,
- LongPref, StringPref, StringSetPref]}
+_PREF_TYPES = {
+ c.tag_name: c
+ for c in
+ [BooleanPref, FloatPref, IntPref, LongPref, StringPref, StringSetPref]
+}
class SharedPrefs(object):
-
def __init__(self, device, package, filename, use_encrypted_path=False):
"""Helper object to read and update "Shared Prefs" of Android apps.
@@ -224,8 +224,10 @@ class SharedPrefs(object):
def __repr__(self):
"""Get a useful printable representation of the object."""
return '<{cls} file {filename} for {package} on {device}>'.format(
- cls=type(self).__name__, filename=self.filename, package=self.package,
- device=str(self._device))
+ cls=type(self).__name__,
+ filename=self.filename,
+ package=self.package,
+ device=str(self._device))
def __str__(self):
"""Get the underlying xml document as a string."""
@@ -292,15 +294,16 @@ class SharedPrefs(object):
return
self._device.RunShellCommand(
['mkdir', '-p', posixpath.dirname(self.path)],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
self._device.WriteFile(self.path, str(self), as_root=True)
# Creating the directory/file can cause issues with SELinux if they did
# not already exist. As a workaround, apply the package's security context
# to the shared_prefs directory, which mimics the behavior of a file
# created by the app itself
if self._device.build_version_sdk >= version_codes.MARSHMALLOW:
- security_context = self._device.GetSecurityContextForPackage(self.package,
- encrypted=self._encrypted)
+ security_context = self._device.GetSecurityContextForPackage(
+ self.package, encrypted=self._encrypted)
if security_context is None:
raise device_errors.CommandFailedError(
'Failed to get security context for %s' % self.package)
@@ -310,11 +313,11 @@ class SharedPrefs(object):
# Ensure that there isn't both an encrypted and unencrypted version of the
# file on the device at the same time.
if self._device.build_version_sdk >= version_codes.NOUGAT:
- remove_path = (self._unencrypted_path if self._encrypted
- else self._encrypted_path)
+ remove_path = (self._unencrypted_path
+ if self._encrypted else self._encrypted_path)
if self._device.PathExists(remove_path, as_root=True):
logging.warning('Found an equivalent shared prefs file at %s, removing',
- remove_path)
+ remove_path)
self._device.RemovePath(remove_path, as_root=True)
self._device.KillAll(self.package, exact=True, as_root=True, quiet=True)
@@ -431,8 +434,8 @@ class SharedPrefs(object):
pref = pref_cls(self._GetChild(key))
old_value = pref.get()
except KeyError:
- pref = pref_cls(ElementTree.SubElement(
- self.xml, pref_cls.tag_name, {'name': key}))
+ pref = pref_cls(
+ ElementTree.SubElement(self.xml, pref_cls.tag_name, {'name': key}))
old_value = None
if old_value != value:
pref.set(value)
diff --git a/catapult/devil/devil/android/sdk/shared_prefs_test.py b/catapult/devil/devil/android/sdk/shared_prefs_test.py
index 08bbb467..7374c892 100755
--- a/catapult/devil/devil/android/sdk/shared_prefs_test.py
+++ b/catapult/devil/devil/android/sdk/shared_prefs_test.py
@@ -2,7 +2,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for the contents of shared_prefs.py (mostly SharedPrefs).
"""
@@ -18,7 +17,6 @@ from devil.android.sdk import version_codes
with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock # pylint: disable=import-error
-
INITIAL_XML = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
'<map>\n'
' <int name="databaseVersion" value="107" />\n'
@@ -48,17 +46,19 @@ def MockDeviceWithFiles(files=None):
class SharedPrefsTest(unittest.TestCase):
-
def setUp(self):
self.device = MockDeviceWithFiles({
- '/data/data/com.some.package/shared_prefs/prefs.xml': INITIAL_XML})
- self.expected_data = {'databaseVersion': 107,
- 'featureEnabled': False,
- 'someHashValue': '249b3e5af13d4db2'}
+ '/data/data/com.some.package/shared_prefs/prefs.xml': INITIAL_XML
+ })
+ self.expected_data = {
+ 'databaseVersion': 107,
+ 'featureEnabled': False,
+ 'someHashValue': '249b3e5af13d4db2'
+ }
def testPropertyLifetime(self):
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml')
+ prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml')
self.assertEquals(len(prefs), 0) # collection is empty before loading
prefs.SetInt('myValue', 444)
self.assertEquals(len(prefs), 1)
@@ -71,8 +71,8 @@ class SharedPrefsTest(unittest.TestCase):
prefs.GetInt('myValue')
def testPropertyType(self):
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml')
+ prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml')
prefs.SetInt('myValue', 444)
self.assertEquals(prefs.PropertyType('myValue'), 'int')
with self.assertRaises(TypeError):
@@ -81,8 +81,8 @@ class SharedPrefsTest(unittest.TestCase):
prefs.SetString('myValue', 'hello')
def testLoad(self):
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml')
+ prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml')
self.assertEquals(len(prefs), 0) # collection is empty before loading
prefs.Load()
self.assertEquals(len(prefs), len(self.expected_data))
@@ -90,8 +90,8 @@ class SharedPrefsTest(unittest.TestCase):
self.assertFalse(prefs.changed)
def testClear(self):
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml')
+ prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml')
prefs.Load()
self.assertEquals(prefs.AsDict(), self.expected_data)
self.assertFalse(prefs.changed)
@@ -102,8 +102,8 @@ class SharedPrefsTest(unittest.TestCase):
def testCommit(self):
type(self.device).build_version_sdk = mock.PropertyMock(
return_value=version_codes.LOLLIPOP_MR1)
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'other_prefs.xml')
+ prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'other_prefs.xml')
self.assertFalse(self.device.FileExists(prefs.path)) # file does not exist
prefs.Load()
self.assertEquals(len(prefs), 0) # file did not exist, collection is empty
@@ -115,54 +115,58 @@ class SharedPrefsTest(unittest.TestCase):
self.assertTrue(prefs.changed)
prefs.Commit()
self.assertTrue(self.device.FileExists(prefs.path)) # should exist now
- self.device.KillAll.assert_called_once_with(prefs.package, exact=True,
- as_root=True, quiet=True)
+ self.device.KillAll.assert_called_once_with(
+ prefs.package, exact=True, as_root=True, quiet=True)
self.assertFalse(prefs.changed)
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'other_prefs.xml')
+ prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'other_prefs.xml')
self.assertEquals(len(prefs), 0) # collection is empty before loading
prefs.Load()
- self.assertEquals(prefs.AsDict(), {
- 'magicNumber': 42,
- 'myMetric': 3.14,
- 'bigNumner': 6000000000,
- 'apps': ['gmail', 'chrome', 'music']}) # data survived roundtrip
+ self.assertEquals(
+ prefs.AsDict(), {
+ 'magicNumber': 42,
+ 'myMetric': 3.14,
+ 'bigNumner': 6000000000,
+ 'apps': ['gmail', 'chrome', 'music']
+ }) # data survived roundtrip
def testForceCommit(self):
- prefs = shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml')
+ prefs = shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml')
prefs.Load()
new_xml = 'Not valid XML'
self.device.WriteFile('/data/data/com.some.package/shared_prefs/prefs.xml',
- new_xml)
+ new_xml)
prefs.Commit()
# Since we didn't change anything, Commit() should be a no-op.
- self.assertEquals(self.device.ReadFile(
- '/data/data/com.some.package/shared_prefs/prefs.xml'), new_xml)
+ self.assertEquals(
+ self.device.ReadFile(
+ '/data/data/com.some.package/shared_prefs/prefs.xml'), new_xml)
prefs.Commit(force_commit=True)
# Forcing the commit should restore the originally read XML.
- self.assertEquals(self.device.ReadFile(
- '/data/data/com.some.package/shared_prefs/prefs.xml'), INITIAL_XML)
+ self.assertEquals(
+ self.device.ReadFile(
+ '/data/data/com.some.package/shared_prefs/prefs.xml'), INITIAL_XML)
def testAsContextManager_onlyReads(self):
- with shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml') as prefs:
+ with shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml') as prefs:
self.assertEquals(prefs.AsDict(), self.expected_data) # loaded and ready
self.assertEquals(self.device.WriteFile.call_args_list, []) # did not write
def testAsContextManager_readAndWrite(self):
type(self.device).build_version_sdk = mock.PropertyMock(
return_value=version_codes.LOLLIPOP_MR1)
- with shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml') as prefs:
+ with shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml') as prefs:
prefs.SetBoolean('featureEnabled', True)
prefs.Remove('someHashValue')
prefs.SetString('newString', 'hello')
self.assertTrue(self.device.WriteFile.called) # did write
- with shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml') as prefs:
+ with shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml') as prefs:
# changes persisted
self.assertTrue(prefs.GetBoolean('featureEnabled'))
self.assertFalse(prefs.HasProperty('someHashValue'))
@@ -171,32 +175,35 @@ class SharedPrefsTest(unittest.TestCase):
def testAsContextManager_commitAborted(self):
with self.assertRaises(TypeError):
- with shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml') as prefs:
+ with shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml') as prefs:
prefs.SetBoolean('featureEnabled', True)
prefs.Remove('someHashValue')
prefs.SetString('newString', 'hello')
prefs.SetInt('newString', 123) # oops!
self.assertEquals(self.device.WriteFile.call_args_list, []) # did not write
- with shared_prefs.SharedPrefs(
- self.device, 'com.some.package', 'prefs.xml') as prefs:
+ with shared_prefs.SharedPrefs(self.device, 'com.some.package',
+ 'prefs.xml') as prefs:
# contents were not modified
self.assertEquals(prefs.AsDict(), self.expected_data)
def testEncryptedPath(self):
type(self.device).build_version_sdk = mock.PropertyMock(
return_value=version_codes.MARSHMALLOW)
- with shared_prefs.SharedPrefs(self.device, 'com.some.package',
- 'prefs.xml', use_encrypted_path=True) as prefs:
+ with shared_prefs.SharedPrefs(
+ self.device, 'com.some.package', 'prefs.xml',
+ use_encrypted_path=True) as prefs:
self.assertTrue(prefs.path.startswith('/data/data'))
type(self.device).build_version_sdk = mock.PropertyMock(
return_value=version_codes.NOUGAT)
- with shared_prefs.SharedPrefs(self.device, 'com.some.package',
- 'prefs.xml', use_encrypted_path=True) as prefs:
+ with shared_prefs.SharedPrefs(
+ self.device, 'com.some.package', 'prefs.xml',
+ use_encrypted_path=True) as prefs:
self.assertTrue(prefs.path.startswith('/data/user_de/0'))
+
if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
unittest.main(verbosity=2)
diff --git a/catapult/devil/devil/android/sdk/split_select.py b/catapult/devil/devil/android/sdk/split_select.py
index 6c3d231a..6b46e576 100644
--- a/catapult/devil/devil/android/sdk/split_select.py
+++ b/catapult/devil/devil/android/sdk/split_select.py
@@ -1,16 +1,14 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""This module wraps Android's split-select tool."""
from devil.android.sdk import build_tools
from devil.utils import cmd_helper
from devil.utils import lazy
-
-_split_select_path = lazy.WeakConstant(
- lambda: build_tools.GetPath('split-select'))
+_split_select_path = lazy.WeakConstant(lambda: build_tools.GetPath(
+ 'split-select'))
def _RunSplitSelectCmd(args):
@@ -37,11 +35,9 @@ def _SplitConfig(device, allow_cached_props=False):
device: A DeviceUtils object.
allow_cached_props: Whether to use cached values for device properties.
"""
- return ('%s-r%s-%s:%s' %
- (device.GetLanguage(cache=allow_cached_props),
- device.GetCountry(cache=allow_cached_props),
- device.screen_density,
- device.product_cpu_abi))
+ return ('%s-r%s-%s:%s' % (device.GetLanguage(cache=allow_cached_props),
+ device.GetCountry(cache=allow_cached_props),
+ device.screen_density, device.product_cpu_abi))
def SelectSplits(device, base_apk, split_apks, allow_cached_props=False):
diff --git a/catapult/devil/devil/android/sdk/version_codes.py b/catapult/devil/devil/android/sdk/version_codes.py
index 29c7285e..943b9d3f 100644
--- a/catapult/devil/devil/android/sdk/version_codes.py
+++ b/catapult/devil/devil/android/sdk/version_codes.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Android SDK version codes.
http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
@@ -20,3 +19,4 @@ NOUGAT_MR1 = 25
OREO = 26
OREO_MR1 = 27
PIE = 28
+Q = 29
diff --git a/catapult/devil/devil/android/settings.py b/catapult/devil/devil/android/settings.py
index 1713be46..3cb1002d 100644
--- a/catapult/devil/devil/android/settings.py
+++ b/catapult/devil/devil/android/settings.py
@@ -12,111 +12,110 @@ _ALTERNATE_LOCK_SCREEN_SETTINGS_PATH = (
PASSWORD_QUALITY_UNSPECIFIED = '0'
_COMPATIBLE_BUILD_TYPES = ['userdebug', 'eng']
-
ENABLE_LOCATION_SETTINGS = [
- # Note that setting these in this order is required in order for all of
- # them to take and stick through a reboot.
- ('com.google.settings/partner', [
- ('use_location_for_services', 1),
- ]),
- ('settings/secure', [
- # Ensure Geolocation is enabled and allowed for tests.
- ('location_providers_allowed', 'gps,network'),
- ]),
- ('com.google.settings/partner', [
- ('network_location_opt_in', 1),
- ])
+ # Note that setting these in this order is required in order for all of
+ # them to take and stick through a reboot.
+ ('com.google.settings/partner', [
+ ('use_location_for_services', 1),
+ ]),
+ (
+ 'settings/secure',
+ [
+ # Ensure Geolocation is enabled and allowed for tests.
+ ('location_providers_allowed', 'gps,network'),
+ ]),
+ ('com.google.settings/partner', [
+ ('network_location_opt_in', 1),
+ ])
]
DISABLE_LOCATION_SETTINGS = [
- ('com.google.settings/partner', [
- ('use_location_for_services', 0),
- ]),
- ('settings/secure', [
- # Ensure Geolocation is disabled.
- ('location_providers_allowed', ''),
- ]),
+ ('com.google.settings/partner', [
+ ('use_location_for_services', 0),
+ ]),
+ (
+ 'settings/secure',
+ [
+ # Ensure Geolocation is disabled.
+ ('location_providers_allowed', ''),
+ ]),
]
ENABLE_MOCK_LOCATION_SETTINGS = [
- ('settings/secure', [
- ('mock_location', 1),
- ]),
+ ('settings/secure', [
+ ('mock_location', 1),
+ ]),
]
DISABLE_MOCK_LOCATION_SETTINGS = [
- ('settings/secure', [
- ('mock_location', 0),
- ]),
+ ('settings/secure', [
+ ('mock_location', 0),
+ ]),
]
DETERMINISTIC_DEVICE_SETTINGS = [
- ('settings/global', [
- ('assisted_gps_enabled', 0),
-
- # Disable "auto time" and "auto time zone" to avoid network-provided time
- # to overwrite the device's datetime and timezone synchronized from host
- # when running tests later. See b/6569849.
- ('auto_time', 0),
- ('auto_time_zone', 0),
-
- ('development_settings_enabled', 1),
-
- # Flag for allowing ActivityManagerService to send ACTION_APP_ERROR intents
- # on application crashes and ANRs. If this is disabled, the crash/ANR dialog
- # will never display the "Report" button.
- # Type: int ( 0 = disallow, 1 = allow )
- ('send_action_app_error', 0),
-
- ('stay_on_while_plugged_in', 3),
-
- ('verifier_verify_adb_installs', 0),
-
- ('window_animation_scale', 0),
- ]),
- ('settings/secure', [
- ('allowed_geolocation_origins',
- 'http://www.google.co.uk http://www.google.com'),
-
- # Ensure that we never get random dialogs like "Unfortunately the process
- # android.process.acore has stopped", which steal the focus, and make our
- # automation fail (because the dialog steals the focus then mistakenly
- # receives the injected user input events).
- ('anr_show_background', 0),
-
- ('lockscreen.disabled', 1),
-
- ('screensaver_enabled', 0),
-
- ('skip_first_use_hints', 1),
- ]),
- ('settings/system', [
- # Don't want devices to accidentally rotate the screen as that could
- # affect performance measurements.
- ('accelerometer_rotation', 0),
-
- ('lockscreen.disabled', 1),
-
- # Turn down brightness and disable auto-adjust so that devices run cooler.
- ('screen_brightness', 5),
- ('screen_brightness_mode', 0),
-
- ('user_rotation', 0),
-
- ('window_animation_scale', 0),
- ]),
+ (
+ 'settings/global',
+ [
+ ('assisted_gps_enabled', 0),
+
+ # Disable "auto time" and "auto time zone" to avoid network-provided
+ # time to overwrite the device's datetime and timezone synchronized
+ # from host when running tests later. See b/6569849.
+ ('auto_time', 0),
+ ('auto_time_zone', 0),
+ ('development_settings_enabled', 1),
+
+ # Flag for allowing ActivityManagerService to send ACTION_APP_ERROR
+ # intens on application crashes and ANRs. If this is disabled, the
+ # crash/ANR dialog will never display the "Report" button.
+ # Type: int ( 0 = disallow, 1 = allow )
+ ('send_action_app_error', 0),
+ ('stay_on_while_plugged_in', 3),
+ ('verifier_verify_adb_installs', 0),
+ ('window_animation_scale', 0),
+ ]),
+ (
+ 'settings/secure',
+ [
+ ('allowed_geolocation_origins',
+ 'http://www.google.co.uk http://www.google.com'),
+
+ # Ensure that we never get random dialogs like "Unfortunately the
+ # process android.process.acore has stopped", which steal the focus,
+ # and make our automation fail (because the dialog steals the focus
+ # then mistakenly receives the injected user input events).
+ ('anr_show_background', 0),
+ ('lockscreen.disabled', 1),
+ ('screensaver_enabled', 0),
+ ('skip_first_use_hints', 1),
+ ]),
+ (
+ 'settings/system',
+ [
+ # Don't want devices to accidentally rotate the screen as that could
+ # affect performance measurements.
+ ('accelerometer_rotation', 0),
+ ('lockscreen.disabled', 1),
+
+ # Turn down brightness and disable auto-adjust so that devices run
+ # cooler.
+ ('screen_brightness', 5),
+ ('screen_brightness_mode', 0),
+ ('user_rotation', 0),
+ ('window_animation_scale', 0),
+ ]),
]
NETWORK_DISABLED_SETTINGS = [
- ('settings/global', [
- ('airplane_mode_on', 1),
- ('wifi_on', 0),
- ]),
+ ('settings/global', [
+ ('airplane_mode_on', 1),
+ ('wifi_on', 0),
+ ]),
]
class ContentSettings(dict):
-
"""A dict interface to interact with device content settings.
System properties are key/value pairs as exposed by adb shell content.
@@ -143,18 +142,24 @@ class ContentSettings(dict):
def iteritems(self):
for row in self._device.RunShellCommand(
- ['content', 'query', '--uri', 'content://%s' % self._table],
- check_return=True, as_root=True):
+ ['content', 'query', '--uri',
+ 'content://%s' % self._table],
+ check_return=True,
+ as_root=True):
key, value = _ParseContentRow(row)
if not key:
continue
yield key, value
def __getitem__(self, key):
- query_row = self._device.RunShellCommand(
- ['content', 'query', '--uri', 'content://%s' % self._table,
- '--where', "name='%s'" % key],
- check_return=True, as_root=True, single_line=True)
+ query_row = self._device.RunShellCommand([
+ 'content', 'query', '--uri',
+ 'content://%s' % self._table, '--where',
+ "name='%s'" % key
+ ],
+ check_return=True,
+ as_root=True,
+ single_line=True)
parsed_key, parsed_value = _ParseContentRow(query_row)
if parsed_key is None:
raise KeyError('key=%s not found' % key)
@@ -164,23 +169,32 @@ class ContentSettings(dict):
def __setitem__(self, key, value):
if key in self:
- self._device.RunShellCommand(
- ['content', 'update', '--uri', 'content://%s' % self._table,
- '--bind', 'value:%s:%s' % (self._GetTypeBinding(value), value),
- '--where', "name='%s'" % key],
- check_return=True, as_root=True)
+ self._device.RunShellCommand([
+ 'content', 'update', '--uri',
+ 'content://%s' % self._table, '--bind',
+ 'value:%s:%s' % (self._GetTypeBinding(value), value), '--where',
+ "name='%s'" % key
+ ],
+ check_return=True,
+ as_root=True)
else:
- self._device.RunShellCommand(
- ['content', 'insert', '--uri', 'content://%s' % self._table,
- '--bind', 'name:%s:%s' % (self._GetTypeBinding(key), key),
- '--bind', 'value:%s:%s' % (self._GetTypeBinding(value), value)],
- check_return=True, as_root=True)
+ self._device.RunShellCommand([
+ 'content', 'insert', '--uri',
+ 'content://%s' % self._table, '--bind',
+ 'name:%s:%s' % (self._GetTypeBinding(key), key), '--bind',
+ 'value:%s:%s' % (self._GetTypeBinding(value), value)
+ ],
+ check_return=True,
+ as_root=True)
def __delitem__(self, key):
- self._device.RunShellCommand(
- ['content', 'delete', '--uri', 'content://%s' % self._table,
- '--bind', 'name:%s:%s' % (self._GetTypeBinding(key), key)],
- check_return=True, as_root=True)
+ self._device.RunShellCommand([
+ 'content', 'delete', '--uri',
+ 'content://%s' % self._table, '--bind',
+ 'name:%s:%s' % (self._GetTypeBinding(key), key)
+ ],
+ check_return=True,
+ as_root=True)
def ConfigureContentSettings(device, desired_settings):
@@ -259,14 +273,15 @@ def SetLockScreenSettings(device):
delete from '%(table)s' where %(primary_key)s='%(primary_value)s';
insert into '%(table)s' (%(columns)s) values (%(values)s);
commit transaction;""" % {
- 'table': table,
- 'primary_key': columns[0],
- 'primary_value': values[0],
- 'columns': ', '.join(columns),
- 'values': ', '.join(["'%s'" % value for value in values])
+ 'table': table,
+ 'primary_key': columns[0],
+ 'primary_value': values[0],
+ 'columns': ', '.join(columns),
+ 'values': ', '.join(["'%s'" % value for value in values])
}
- output_msg = device.RunShellCommand(
- ['sqlite3', db, cmd], check_return=True, as_root=True)
+ output_msg = device.RunShellCommand(['sqlite3', db, cmd],
+ check_return=True,
+ as_root=True)
if output_msg:
logger.info(' '.join(output_msg))
diff --git a/catapult/devil/devil/android/tools/adb_run_shell_cmd.py b/catapult/devil/devil/android/tools/adb_run_shell_cmd.py
index 6edd5606..e6b05aa2 100755
--- a/catapult/devil/devil/android/tools/adb_run_shell_cmd.py
+++ b/catapult/devil/devil/android/tools/adb_run_shell_cmd.py
@@ -10,8 +10,8 @@ import sys
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import device_utils
from devil.android.tools import script_common
@@ -26,17 +26,16 @@ def main():
script_common.AddDeviceArguments(parser)
script_common.AddEnvironmentArguments(parser)
parser.add_argument('--as-root', action='store_true', help='Run as root.')
- parser.add_argument('--json-output',
- help='File to dump json output to.')
+ parser.add_argument('--json-output', help='File to dump json output to.')
args = parser.parse_args()
logging_common.InitializeLogging(args)
script_common.InitializeEnvironment(args)
- devices = script_common.GetDevices(args.devices, args.blacklist_file)
+ devices = script_common.GetDevices(args.devices, args.denylist_file)
p_out = (device_utils.DeviceUtils.parallel(devices).RunShellCommand(
- args.cmd, large_output=True, as_root=args.as_root, check_return=True)
- .pGet(None))
+ args.cmd, large_output=True, as_root=args.as_root,
+ check_return=True).pGet(None))
data = {}
for device, output in zip(devices, p_out):
diff --git a/catapult/devil/devil/android/tools/cpufreq.py b/catapult/devil/devil/android/tools/cpufreq.py
index 6ce0affd..f33542bf 100755
--- a/catapult/devil/devil/android/tools/cpufreq.py
+++ b/catapult/devil/devil/android/tools/cpufreq.py
@@ -2,7 +2,6 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A script to manipulate device CPU frequency."""
import argparse
@@ -12,8 +11,8 @@ import sys
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import device_utils
from devil.android.perf import perf_control
@@ -43,15 +42,16 @@ def main(raw_args):
logging_common.AddLoggingArguments(parser)
script_common.AddEnvironmentArguments(parser)
parser.add_argument(
- '--device', dest='devices', action='append', default=[],
+ '--device',
+ dest='devices',
+ action='append',
+ default=[],
help='Devices for which the governor should be set. Defaults to all.')
subparsers = parser.add_subparsers()
set_governor = subparsers.add_parser('set-governor')
- set_governor.add_argument(
- 'governor',
- help='Desired CPU governor.')
+ set_governor.add_argument('governor', help='Desired CPU governor.')
set_governor.set_defaults(func=SetScalingGovernor)
get_governor = subparsers.add_parser('get-governor')
diff --git a/catapult/devil/devil/android/tools/device_monitor.py b/catapult/devil/devil/android/tools/device_monitor.py
index 565f8658..6128dae8 100755
--- a/catapult/devil/devil/android/tools/device_monitor.py
+++ b/catapult/devil/devil/android/tools/device_monitor.py
@@ -2,7 +2,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Launches a daemon to monitor android device temperatures & status.
This script will repeatedly poll the given devices for their temperatures and
@@ -22,24 +21,23 @@ import time
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import battery_utils
-from devil.android import device_blacklist
+from devil.android import device_denylist
from devil.android import device_errors
from devil.android import device_utils
from devil.android.tools import script_common
-
# Various names of sensors used to measure cpu temp
CPU_TEMP_SENSORS = [
- # most nexus devices
- 'tsens_tz_sensor0',
- # android one
- 'mtktscpu',
- # nexus 9
- 'CPU-therm',
+ # most nexus devices
+ 'tsens_tz_sensor0',
+ # android one
+ 'mtktscpu',
+ # nexus 9
+ 'CPU-therm',
]
DEVICE_FILE_VERSION = 1
@@ -47,7 +45,8 @@ DEVICE_FILE = os.path.join(
os.path.expanduser('~'), '.android',
'%s__android_device_status.json' % socket.gethostname().split('.')[0])
-MEM_INFO_REGEX = re.compile(r'.*?\:\s*(\d+)\s*kB') # ex: 'MemTotal: 185735 kB'
+MEM_INFO_REGEX = re.compile(
+ r'.*?\:\s*(\d+)\s*kB') # ex: 'MemTotal: 185735 kB'
def get_device_status_unsafe(device):
@@ -129,14 +128,16 @@ def get_device_status_unsafe(device):
files = []
try:
files = device.RunShellCommand(
- 'grep -lE "%s" /sys/class/thermal/thermal_zone*/type' % '|'.join(
- CPU_TEMP_SENSORS), shell=True, check_return=True)
+ 'grep -lE "%s" /sys/class/thermal/thermal_zone*/type' %
+ '|'.join(CPU_TEMP_SENSORS),
+ shell=True,
+ check_return=True)
except device_errors.AdbShellCommandFailedError:
logging.exception('Unable to list thermal sensors.')
for f in files:
try:
sensor_name = device.ReadFile(f).strip()
- temp = float(device.ReadFile(f[:-4] + 'temp').strip()) # s/type^/temp
+ temp = float(device.ReadFile(f[:-4] + 'temp').strip()) # s/type^/temp
status['temp'][sensor_name] = temp
except (device_errors.AdbShellCommandFailedError, ValueError):
logging.exception('Unable to read thermal sensor %s', f)
@@ -144,7 +145,7 @@ def get_device_status_unsafe(device):
# Uptime
try:
uptimes = device.ReadFile('/proc/uptime').split()
- status['uptime'] = float(uptimes[0]) # Take the first field (actual uptime)
+ status['uptime'] = float(uptimes[0]) # Take the first field (actual uptime)
except (device_errors.AdbShellCommandFailedError, ValueError):
logging.exception('Unable to read /proc/uptime')
@@ -167,24 +168,26 @@ def get_device_status(device):
return status
-def get_all_status(blacklist):
+def get_all_status(denylist):
status_dict = {
'version': DEVICE_FILE_VERSION,
'devices': {},
}
- healthy_devices = device_utils.DeviceUtils.HealthyDevices(blacklist)
+ healthy_devices = device_utils.DeviceUtils.HealthyDevices(denylist)
parallel_devices = device_utils.DeviceUtils.parallel(healthy_devices)
results = parallel_devices.pMap(get_device_status).pGet(None)
status_dict['devices'] = {
- device.serial: result for device, result in zip(healthy_devices, results)
+ device.serial: result
+ for device, result in zip(healthy_devices, results)
}
- if blacklist:
- for device, reason in blacklist.Read().iteritems():
+ if denylist:
+ for device, reason in denylist.Read().iteritems():
status_dict['devices'][device] = {
- 'state': reason.get('reason', 'blacklisted')}
+ 'state': reason.get('reason', 'denylisted')
+ }
status_dict['timestamp'] = time.time()
return status_dict
@@ -194,33 +197,37 @@ def main(argv):
"""Launches the device monitor.
Polls the devices for their battery and cpu temperatures and scans the
- blacklist file every 60 seconds and dumps the data to DEVICE_FILE.
+ denylist file every 60 seconds and dumps the data to DEVICE_FILE.
"""
- parser = argparse.ArgumentParser(
- description='Launches the device monitor.')
+ parser = argparse.ArgumentParser(description='Launches the device monitor.')
script_common.AddEnvironmentArguments(parser)
- parser.add_argument('--blacklist-file', help='Path to device blacklist file.')
+ parser.add_argument('--denylist-file', help='Path to device denylist file.')
+ # TODO(crbug.com/1097306): Remove this once chromium_android/api.py stops
+ # using it.
+ parser.add_argument('--blacklist-file',
+ dest='denylist_file',
+ help=argparse.SUPPRESS)
args = parser.parse_args(argv)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
'/tmp/device_monitor.log', maxBytes=10 * 1024 * 1024, backupCount=5)
- fmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s',
- datefmt='%y%m%d %H:%M:%S')
+ fmt = logging.Formatter(
+ '%(asctime)s %(levelname)s %(message)s', datefmt='%y%m%d %H:%M:%S')
handler.setFormatter(fmt)
logger.addHandler(handler)
script_common.InitializeEnvironment(args)
- blacklist = (device_blacklist.Blacklist(args.blacklist_file)
- if args.blacklist_file else None)
+ denylist = (device_denylist.Denylist(args.denylist_file)
+ if args.denylist_file else None)
- logging.info('Device monitor running with pid %d, adb: %s, blacklist: %s',
- os.getpid(), args.adb_path, args.blacklist_file)
+ logging.info('Device monitor running with pid %d, adb: %s, denylist: %s',
+ os.getpid(), args.adb_path, args.denylist_file)
while True:
start = time.time()
- status_dict = get_all_status(blacklist)
+ status_dict = get_all_status(denylist)
with open(DEVICE_FILE, 'wb') as f:
json.dump(status_dict, f, indent=2, sort_keys=True)
logging.info('Got status of all devices in %.2fs.', time.time() - start)
diff --git a/catapult/devil/devil/android/tools/device_monitor_test.py b/catapult/devil/devil/android/tools/device_monitor_test.py
index 2cb0dd28..8082d26e 100755
--- a/catapult/devil/devil/android/tools/device_monitor_test.py
+++ b/catapult/devil/devil/android/tools/device_monitor_test.py
@@ -9,8 +9,8 @@ import unittest
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil import devil_env
from devil.android import device_errors
@@ -22,20 +22,26 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
class DeviceMonitorTest(unittest.TestCase):
-
def setUp(self):
- self.device = mock.Mock(spec=device_utils.DeviceUtils,
- serial='device_cereal', build_id='abc123', build_product='clownfish',
+ self.device = mock.Mock(
+ spec=device_utils.DeviceUtils,
+ serial='device_cereal',
+ build_id='abc123',
+ build_product='clownfish',
GetIMEI=lambda: '123456789')
self.file_contents = {
- '/proc/meminfo': """
+ '/proc/meminfo':
+ """
MemTotal: 1234567 kB
MemFree: 1000000 kB
MemUsed: 234567 kB
""",
- '/sys/class/thermal/thermal_zone0/type': 'CPU-therm',
- '/sys/class/thermal/thermal_zone0/temp': '30',
- '/proc/uptime': '12345 99999',
+ '/sys/class/thermal/thermal_zone0/type':
+ 'CPU-therm',
+ '/sys/class/thermal/thermal_zone0/temp':
+ '30',
+ '/proc/uptime':
+ '12345 99999',
}
self.device.ReadFile = mock.MagicMock(
side_effect=lambda file_name: self.file_contents[file_name])
@@ -56,31 +62,33 @@ class DeviceMonitorTest(unittest.TestCase):
self.device.RunShellCommand = mock.MagicMock(side_effect=mock_run_shell)
self.battery = mock.Mock()
- self.battery.GetBatteryInfo = mock.MagicMock(
- return_value={'level': '80', 'temperature': '123'})
+ self.battery.GetBatteryInfo = mock.MagicMock(return_value={
+ 'level': '80',
+ 'temperature': '123'
+ })
self.expected_status = {
- 'device_cereal': {
- 'processes': 5,
- 'temp': {
- 'CPU-therm': 30.0
- },
- 'battery': {
- 'temperature': 123,
- 'level': 80
- },
- 'uptime': 12345.0,
- 'mem': {
- 'total': 1234567,
- 'free': 1000000
- },
- 'build': {
- 'build.id': 'abc123',
- 'product.device': 'clownfish',
- },
- 'imei': '123456789',
- 'state': 'available',
- }
+ 'device_cereal': {
+ 'processes': 5,
+ 'temp': {
+ 'CPU-therm': 30.0
+ },
+ 'battery': {
+ 'temperature': 123,
+ 'level': 80
+ },
+ 'uptime': 12345.0,
+ 'mem': {
+ 'total': 1234567,
+ 'free': 1000000
+ },
+ 'build': {
+ 'build.id': 'abc123',
+ 'product.device': 'clownfish',
+ },
+ 'imei': '123456789',
+ 'state': 'available',
+ }
}
@mock.patch('devil.android.battery_utils.BatteryUtils')
@@ -98,8 +106,10 @@ class DeviceMonitorTest(unittest.TestCase):
get_devices.return_value = [self.device]
get_battery.return_value = self.battery
broken_battery_info = mock.Mock()
- broken_battery_info.GetBatteryInfo = mock.MagicMock(
- return_value={'level': '-1', 'temperature': 'not_a_number'})
+ broken_battery_info.GetBatteryInfo = mock.MagicMock(return_value={
+ 'level': '-1',
+ 'temperature': 'not_a_number'
+ })
get_battery.return_value = broken_battery_info
# Should be same status dict but without battery stats.
@@ -141,18 +151,20 @@ class DeviceMonitorTest(unittest.TestCase):
@mock.patch('devil.android.battery_utils.BatteryUtils')
@mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices')
- def test_getStatsWithBlacklist(self, get_devices, get_battery):
+ def test_getStatsWithDenylist(self, get_devices, get_battery):
get_devices.return_value = [self.device]
get_battery.return_value = self.battery
- blacklist = mock.Mock()
- blacklist.Read = mock.MagicMock(
- return_value={'bad_device': {'reason': 'offline'}})
+ denylist = mock.Mock()
+ denylist.Read = mock.MagicMock(
+ return_value={'bad_device': {
+ 'reason': 'offline'
+ }})
- # Should be same status dict but with extra blacklisted device.
+ # Should be same status dict but with extra denylisted device.
expected_status = self.expected_status.copy()
expected_status['bad_device'] = {'state': 'offline'}
- status = device_monitor.get_all_status(blacklist)
+ status = device_monitor.get_all_status(denylist)
self.assertEquals(expected_status, status['devices'])
@mock.patch('devil.android.battery_utils.BatteryUtils')
diff --git a/catapult/devil/devil/android/tools/device_recovery.py b/catapult/devil/devil/android/tools/device_recovery.py
index 8050e6fe..f25c5a25 100755
--- a/catapult/devil/devil/android/tools/device_recovery.py
+++ b/catapult/devil/devil/android/tools/device_recovery.py
@@ -2,7 +2,6 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A script to recover devices in a known bad state."""
import argparse
@@ -16,9 +15,9 @@ import psutil
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
-from devil.android import device_blacklist
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
+from devil.android import device_denylist
from devil.android import device_errors
from devil.android import device_utils
from devil.android.sdk import adb_wrapper
@@ -33,7 +32,6 @@ logger = logging.getLogger(__name__)
from py_utils import modules_util
-
# Script depends on features from psutil version 2.0 or higher.
modules_util.RequireVersion(psutil, '2.0')
@@ -76,16 +74,16 @@ def TryAuth(device):
"""
possible_keys = glob.glob(os.path.join(adb_wrapper.ADB_HOST_KEYS_DIR, '*key'))
if len(possible_keys) <= 1:
- logger.warning(
- 'Only %d ADB keys available. Not forcing auth.', len(possible_keys))
+ logger.warning('Only %d ADB keys available. Not forcing auth.',
+ len(possible_keys))
return False
KillAllAdb()
adb_wrapper.AdbWrapper.StartServer(keys=possible_keys)
new_state = device.adb.GetState()
if new_state != 'device':
- logger.error(
- 'Auth failed. Device %s still stuck in %s.', str(device), new_state)
+ logger.error('Auth failed. Device %s still stuck in %s.', str(device),
+ new_state)
return False
# It worked! Now register the host's default ADB key on the device so we don't
@@ -99,18 +97,16 @@ def TryAuth(device):
pub_key_contents = f.read()
try:
device.WriteFile(adb_wrapper.ADB_KEYS_FILE, pub_key_contents, as_root=True)
- except (device_errors.CommandTimeoutError,
- device_errors.CommandFailedError,
+ except (device_errors.CommandTimeoutError, device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
logger.exception('Unable to write default ADB key to %s.', str(device))
return False
return True
-def RecoverDevice(device, blacklist, should_reboot=lambda device: True):
- if device_status.IsBlacklisted(device.adb.GetDeviceSerial(),
- blacklist):
- logger.debug('%s is blacklisted, skipping recovery.', str(device))
+def RecoverDevice(device, denylist, should_reboot=lambda device: True):
+ if device_status.IsDenylisted(device.adb.GetDeviceSerial(), denylist):
+ logger.debug('%s is denylisted, skipping recovery.', str(device))
return
if device.adb.GetState() == 'unauthorized' and TryAuth(device):
@@ -120,17 +116,18 @@ def RecoverDevice(device, blacklist, should_reboot=lambda device: True):
if should_reboot(device):
try:
device.WaitUntilFullyBooted(retries=0)
- except (device_errors.CommandTimeoutError,
- device_errors.CommandFailedError,
+ except (device_errors.CommandTimeoutError, device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
- logger.exception('Failure while waiting for %s. '
- 'Attempting to recover.', str(device))
+ logger.exception(
+ 'Failure while waiting for %s. '
+ 'Attempting to recover.', str(device))
try:
try:
device.Reboot(block=False, timeout=5, retries=0)
except device_errors.CommandTimeoutError:
- logger.warning('Timed out while attempting to reboot %s normally.'
- 'Attempting alternative reboot.', str(device))
+ logger.warning(
+ 'Timed out while attempting to reboot %s normally.'
+ 'Attempting alternative reboot.', str(device))
# The device drops offline before we can grab the exit code, so
# we don't check for status.
try:
@@ -139,19 +136,20 @@ def RecoverDevice(device, blacklist, should_reboot=lambda device: True):
# We are already in a failure mode, attempt to reboot regardless of
# what device.adb.Root() returns. If the sysrq reboot fails an
# exception willbe thrown at that level.
- device.adb.Shell('echo b > /proc/sysrq-trigger', expect_status=None,
- timeout=5, retries=0)
+ device.adb.Shell(
+ 'echo b > /proc/sysrq-trigger',
+ expect_status=None,
+ timeout=5,
+ retries=0)
except (device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
logger.exception('Failed to reboot %s.', str(device))
- if blacklist:
- blacklist.Extend([device.adb.GetDeviceSerial()],
- reason='reboot_failure')
+ if denylist:
+ denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_failure')
except device_errors.CommandTimeoutError:
logger.exception('Timed out while rebooting %s.', str(device))
- if blacklist:
- blacklist.Extend([device.adb.GetDeviceSerial()],
- reason='reboot_timeout')
+ if denylist:
+ denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_timeout')
try:
device.WaitUntilFullyBooted(
@@ -159,37 +157,34 @@ def RecoverDevice(device, blacklist, should_reboot=lambda device: True):
except (device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
logger.exception('Failure while waiting for %s.', str(device))
- if blacklist:
- blacklist.Extend([device.adb.GetDeviceSerial()],
- reason='reboot_failure')
+ if denylist:
+ denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_failure')
except device_errors.CommandTimeoutError:
logger.exception('Timed out while waiting for %s.', str(device))
- if blacklist:
- blacklist.Extend([device.adb.GetDeviceSerial()],
- reason='reboot_timeout')
+ if denylist:
+ denylist.Extend([device.adb.GetDeviceSerial()], reason='reboot_timeout')
-def RecoverDevices(devices, blacklist, enable_usb_reset=False):
+def RecoverDevices(devices, denylist, enable_usb_reset=False):
"""Attempts to recover any inoperable devices in the provided list.
Args:
devices: The list of devices to attempt to recover.
- blacklist: The current device blacklist, which will be used then
+ denylist: The current device denylist, which will be used then
reset.
"""
- statuses = device_status.DeviceStatus(devices, blacklist)
+ statuses = device_status.DeviceStatus(devices, denylist)
should_restart_usb = set(
status['serial'] for status in statuses
- if (not status['usb_status']
- or status['adb_status'] in ('offline', 'missing')))
- should_restart_adb = should_restart_usb.union(set(
- status['serial'] for status in statuses
- if status['adb_status'] == 'unauthorized'))
- should_reboot_device = should_restart_usb.union(set(
- status['serial'] for status in statuses
- if status['blacklisted']))
+ if (not status['usb_status'] or status['adb_status'] in ('offline',
+ 'missing')))
+ should_restart_adb = should_restart_usb.union(
+ set(status['serial'] for status in statuses
+ if status['adb_status'] == 'unauthorized'))
+ should_reboot_device = should_restart_usb.union(
+ set(status['serial'] for status in statuses if status['denylisted']))
logger.debug('Should restart USB for:')
for d in should_restart_usb:
@@ -201,8 +196,8 @@ def RecoverDevices(devices, blacklist, enable_usb_reset=False):
for d in should_reboot_device:
logger.debug(' %s', d)
- if blacklist:
- blacklist.Reset()
+ if denylist:
+ denylist.Reset()
if should_restart_adb:
KillAllAdb()
@@ -215,19 +210,19 @@ def RecoverDevices(devices, blacklist, enable_usb_reset=False):
if enable_usb_reset:
reset_usb.reset_android_usb(serial)
else:
- logger.warning('USB reset disabled for %s (crbug.com/642914)',
- serial)
+ logger.warning('USB reset disabled for %s (crbug.com/642914)', serial)
except IOError:
logger.exception('Unable to reset USB for %s.', serial)
- if blacklist:
- blacklist.Extend([serial], reason='USB failure')
+ if denylist:
+ denylist.Extend([serial], reason='USB failure')
except device_errors.DeviceUnreachableError:
logger.exception('Unable to reset USB for %s.', serial)
- if blacklist:
- blacklist.Extend([serial], reason='offline')
+ if denylist:
+ denylist.Extend([serial], reason='offline')
device_utils.DeviceUtils.parallel(devices).pMap(
- RecoverDevice, blacklist,
+ RecoverDevice,
+ denylist,
should_reboot=lambda device: device.serial in should_reboot_device)
@@ -235,27 +230,35 @@ def main():
parser = argparse.ArgumentParser()
logging_common.AddLoggingArguments(parser)
script_common.AddEnvironmentArguments(parser)
- parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
- parser.add_argument('--known-devices-file', action='append', default=[],
- dest='known_devices_files',
- help='Path to known device lists.')
- parser.add_argument('--enable-usb-reset', action='store_true',
- help='Reset USB if necessary.')
+ # TODO(crbug.com/1097306): Remove this once callers switch to --denylist-file.
+ parser.add_argument('--blacklist-file', help=argparse.SUPPRESS)
+ parser.add_argument('--denylist-file', help='Device denylist JSON file.')
+ parser.add_argument(
+ '--known-devices-file',
+ action='append',
+ default=[],
+ dest='known_devices_files',
+ help='Path to known device lists.')
+ parser.add_argument(
+ '--enable-usb-reset', action='store_true', help='Reset USB if necessary.')
args = parser.parse_args()
logging_common.InitializeLogging(args)
script_common.InitializeEnvironment(args)
- blacklist = (device_blacklist.Blacklist(args.blacklist_file)
- if args.blacklist_file
- else None)
+ denylist = (device_denylist.Denylist(args.denylist_file)
+ if args.denylist_file else None)
+ # TODO(crbug.com/1097306): Remove this once callers switch to --denylist-file.
+ if not denylist and args.blacklist_file:
+ denylist = device_denylist.Denylist(args.blacklist_file)
expected_devices = device_status.GetExpectedDevices(args.known_devices_files)
usb_devices = set(lsusb.get_android_devices())
- devices = [device_utils.DeviceUtils(s)
- for s in expected_devices.union(usb_devices)]
+ devices = [
+ device_utils.DeviceUtils(s) for s in expected_devices.union(usb_devices)
+ ]
- RecoverDevices(devices, blacklist, enable_usb_reset=args.enable_usb_reset)
+ RecoverDevices(devices, denylist, enable_usb_reset=args.enable_usb_reset)
if __name__ == '__main__':
diff --git a/catapult/devil/devil/android/tools/device_status.py b/catapult/devil/devil/android/tools/device_status.py
index dbbf2908..d0eb7df1 100755
--- a/catapult/devil/devil/android/tools/device_status.py
+++ b/catapult/devil/devil/android/tools/device_status.py
@@ -2,7 +2,6 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A script to keep track of devices across builds and report state."""
import argparse
@@ -14,10 +13,10 @@ import sys
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import battery_utils
-from devil.android import device_blacklist
+from devil.android import device_denylist
from devil.android import device_errors
from devil.android import device_list
from devil.android import device_utils
@@ -32,11 +31,11 @@ logger = logging.getLogger(__name__)
_RE_DEVICE_ID = re.compile(r'Device ID = (\d+)')
-def IsBlacklisted(serial, blacklist):
- return blacklist and serial in blacklist.Read()
+def IsDenylisted(serial, denylist):
+ return denylist and serial in denylist.Read()
-def _BatteryStatus(device, blacklist):
+def _BatteryStatus(device, denylist):
battery_info = {}
try:
battery = battery_utils.BatteryUtils(device)
@@ -48,23 +47,22 @@ def _BatteryStatus(device, blacklist):
battery = battery_utils.BatteryUtils(device)
if not battery.GetCharging():
battery.SetCharging(True)
- if blacklist:
- blacklist.Extend([device.adb.GetDeviceSerial()], reason='low_battery')
+ if denylist:
+ denylist.Extend([device.adb.GetDeviceSerial()], reason='low_battery')
except (device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
- logger.exception('Failed to get battery information for %s',
- str(device))
+ logger.exception('Failed to get battery information for %s', str(device))
return battery_info
-def DeviceStatus(devices, blacklist):
+def DeviceStatus(devices, denylist):
"""Generates status information for the given devices.
Args:
devices: The devices to generate status for.
- blacklist: The current device blacklist.
+ denylist: The current device denylist.
Returns:
A dict of the following form:
{
@@ -72,8 +70,8 @@ def DeviceStatus(devices, blacklist):
'serial': '<serial>',
'adb_status': str,
'usb_status': bool,
- 'blacklisted': bool,
- # only if the device is connected and not blacklisted
+ 'denylisted': bool,
+ # only if the device is connected and not denylisted
'type': ro.build.product,
'build': ro.build.id,
'build_detail': ro.build.fingerprint,
@@ -87,25 +85,25 @@ def DeviceStatus(devices, blacklist):
}
"""
adb_devices = {
- a[0].GetDeviceSerial(): a
- for a in adb_wrapper.AdbWrapper.Devices(desired_state=None, long_list=True)
+ a[0].GetDeviceSerial(): a
+ for a in adb_wrapper.AdbWrapper.Devices(
+ desired_state=None, long_list=True)
}
usb_devices = set(lsusb.get_android_devices())
- def blacklisting_device_status(device):
+ def denylisting_device_status(device):
serial = device.adb.GetDeviceSerial()
- adb_status = (
- adb_devices[serial][1] if serial in adb_devices
- else 'missing')
+ adb_status = (adb_devices[serial][1]
+ if serial in adb_devices else 'missing')
usb_status = bool(serial in usb_devices)
device_status = {
- 'serial': serial,
- 'adb_status': adb_status,
- 'usb_status': usb_status,
+ 'serial': serial,
+ 'adb_status': adb_status,
+ 'usb_status': usb_status,
}
- if not IsBlacklisted(serial, blacklist):
+ if not IsDenylisted(serial, denylist):
if adb_status == 'device':
try:
build_product = device.build_product
@@ -113,50 +111,50 @@ def DeviceStatus(devices, blacklist):
build_fingerprint = device.build_fingerprint
build_description = device.build_description
wifi_ip = device.GetProp('dhcp.wlan0.ipaddress')
- battery_info = _BatteryStatus(device, blacklist)
+ battery_info = _BatteryStatus(device, denylist)
try:
imei_slice = device.GetIMEI()
except device_errors.CommandFailedError:
logging.exception('Unable to fetch IMEI for %s.', str(device))
imei_slice = 'unknown'
- if (device.product_name == 'mantaray' and
- battery_info.get('AC powered', None) != 'true'):
+ if (device.product_name == 'mantaray'
+ and battery_info.get('AC powered', None) != 'true'):
logger.error('Mantaray device not connected to AC power.')
device_status.update({
- 'ro.build.product': build_product,
- 'ro.build.id': build_id,
- 'ro.build.fingerprint': build_fingerprint,
- 'ro.build.description': build_description,
- 'battery': battery_info,
- 'imei_slice': imei_slice,
- 'wifi_ip': wifi_ip,
+ 'ro.build.product': build_product,
+ 'ro.build.id': build_id,
+ 'ro.build.fingerprint': build_fingerprint,
+ 'ro.build.description': build_description,
+ 'battery': battery_info,
+ 'imei_slice': imei_slice,
+ 'wifi_ip': wifi_ip,
})
except (device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
logger.exception('Failure while getting device status for %s.',
str(device))
- if blacklist:
- blacklist.Extend([serial], reason='status_check_failure')
+ if denylist:
+ denylist.Extend([serial], reason='status_check_failure')
except device_errors.CommandTimeoutError:
logger.exception('Timeout while getting device status for %s.',
str(device))
- if blacklist:
- blacklist.Extend([serial], reason='status_check_timeout')
+ if denylist:
+ denylist.Extend([serial], reason='status_check_timeout')
- elif blacklist:
- blacklist.Extend([serial],
- reason=adb_status if usb_status else 'offline')
+ elif denylist:
+ denylist.Extend([serial],
+ reason=adb_status if usb_status else 'offline')
- device_status['blacklisted'] = IsBlacklisted(serial, blacklist)
+ device_status['denylisted'] = IsDenylisted(serial, denylist)
return device_status
parallel_devices = device_utils.DeviceUtils.parallel(devices)
- statuses = parallel_devices.pMap(blacklisting_device_status).pGet(None)
+ statuses = parallel_devices.pMap(denylisting_device_status).pGet(None)
return statuses
@@ -165,12 +163,12 @@ def _LogStatuses(statuses):
for status in statuses:
logger.info(status['serial'])
adb_status = status.get('adb_status')
- blacklisted = status.get('blacklisted')
+ denylisted = status.get('denylisted')
logger.info(' USB status: %s',
'online' if status.get('usb_status') else 'offline')
logger.info(' ADB status: %s', adb_status)
- logger.info(' Blacklisted: %s', str(blacklisted))
- if adb_status == 'device' and not blacklisted:
+ logger.info(' Denylisted: %s', str(denylisted))
+ if adb_status == 'device' and not denylisted:
logger.info(' Device type: %s', status.get('ro.build.product'))
logger.info(' OS build: %s', status.get('ro.build.id'))
logger.info(' OS build fingerprint: %s',
@@ -189,25 +187,21 @@ def _WriteBuildbotFile(file_path, statuses):
for status in statuses:
try:
if status['adb_status'] == 'device':
- f.write('{serial} {adb_status} {build_product} {build_id} '
- '{temperature:.1f}C {level}%\n'.format(
- serial=status['serial'],
- adb_status=status['adb_status'],
- build_product=status['type'],
- build_id=status['build'],
- temperature=float(status['battery']['temperature']) / 10,
- level=status['battery']['level']
- ))
+ f.write(
+ '{serial} {adb_status} {build_product} {build_id} '
+ '{temperature:.1f}C {level}%\n'.format(
+ serial=status['serial'],
+ adb_status=status['adb_status'],
+ build_product=status['type'],
+ build_id=status['build'],
+ temperature=float(status['battery']['temperature']) / 10,
+ level=status['battery']['level']))
elif status.get('usb_status', False):
f.write('{serial} {adb_status}\n'.format(
- serial=status['serial'],
- adb_status=status['adb_status']
- ))
+ serial=status['serial'], adb_status=status['adb_status']))
else:
- f.write('{serial} offline\n'.format(
- serial=status['serial']
- ))
- except Exception: # pylint: disable=broad-except
+ f.write('{serial} offline\n'.format(serial=status['serial']))
+ except Exception: # pylint: disable=broad-except
pass
@@ -229,19 +223,32 @@ def GetExpectedDevices(known_devices_files):
def AddArguments(parser):
- parser.add_argument('--json-output',
- help='Output JSON information into a specified file.')
- parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
- parser.add_argument('--known-devices-file', action='append', default=[],
- dest='known_devices_files',
- help='Path to known device lists.')
- parser.add_argument('--buildbot-path', '-b',
- default='/home/chrome-bot/.adb_device_info',
- help='Absolute path to buildbot file location')
- parser.add_argument('-w', '--overwrite-known-devices-files',
- action='store_true',
- help='If set, overwrites known devices files wiht new '
- 'values.')
+ parser.add_argument(
+ '--json-output', help='Output JSON information into a specified file.')
+ parser.add_argument('--denylist-file', help='Device denylist JSON file.')
+ # TODO(crbug.com/1097306): Remove this once //testing/scripts/host_info.py
+ # stops using it.
+ parser.add_argument('--blacklist-file',
+ dest='denylist_file',
+ help=argparse.SUPPRESS)
+ parser.add_argument(
+ '--known-devices-file',
+ action='append',
+ default=[],
+ dest='known_devices_files',
+ help='Path to known device lists.')
+ parser.add_argument(
+ '--buildbot-path',
+ '-b',
+ default='/home/chrome-bot/.adb_device_info',
+ help='Absolute path to buildbot file location')
+ parser.add_argument(
+ '-w',
+ '--overwrite-known-devices-files',
+ action='store_true',
+ help='If set, overwrites known devices files wiht new '
+ 'values.')
+
def main():
parser = argparse.ArgumentParser()
@@ -253,16 +260,16 @@ def main():
logging_common.InitializeLogging(args)
script_common.InitializeEnvironment(args)
- blacklist = (device_blacklist.Blacklist(args.blacklist_file)
- if args.blacklist_file
- else None)
+ denylist = (device_denylist.Denylist(args.denylist_file)
+ if args.denylist_file else None)
expected_devices = GetExpectedDevices(args.known_devices_files)
usb_devices = set(lsusb.get_android_devices())
- devices = [device_utils.DeviceUtils(s)
- for s in expected_devices.union(usb_devices)]
+ devices = [
+ device_utils.DeviceUtils(s) for s in expected_devices.union(usb_devices)
+ ]
- statuses = DeviceStatus(devices, blacklist)
+ statuses = DeviceStatus(devices, denylist)
# Log the state of all devices.
_LogStatuses(statuses)
@@ -279,12 +286,15 @@ def main():
# Dump the device statuses to JSON.
if args.json_output:
with open(args.json_output, 'wb') as f:
- f.write(json.dumps(
- statuses, indent=4, sort_keys=True, separators=(',', ': ')))
-
- live_devices = [status['serial'] for status in statuses
- if (status['adb_status'] == 'device'
- and not IsBlacklisted(status['serial'], blacklist))]
+ f.write(
+ json.dumps(
+ statuses, indent=4, sort_keys=True, separators=(',', ': ')))
+
+ live_devices = [
+ status['serial'] for status in statuses
+ if (status['adb_status'] == 'device'
+ and not IsDenylisted(status['serial'], denylist))
+ ]
# If all devices failed, or if there are no devices, it's an infra error.
if not live_devices:
diff --git a/catapult/devil/devil/android/tools/flash_device.py b/catapult/devil/devil/android/tools/flash_device.py
index 8b51c604..cb06a120 100755
--- a/catapult/devil/devil/android/tools/flash_device.py
+++ b/catapult/devil/devil/android/tools/flash_device.py
@@ -9,14 +9,17 @@ import os
import sys
if __name__ == '__main__':
- sys.path.append(os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..', '..')))
-from devil.android import device_blacklist
-from devil.android import device_utils
+ sys.path.append(
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
+from devil.android import device_denylist
+from devil.android import device_errors
from devil.android import fastboot_utils
+from devil.android.sdk import fastboot
from devil.android.tools import script_common
from devil.constants import exit_codes
from devil.utils import logging_common
+from devil.utils import parallelizer
logger = logging.getLogger(__name__)
@@ -24,18 +27,18 @@ logger = logging.getLogger(__name__)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('build_path', help='Path to android build.')
- parser.add_argument('-w', '--wipe', action='store_true',
- help='If set, wipes user data')
+ parser.add_argument(
+ '-w', '--wipe', action='store_true', help='If set, wipes user data')
logging_common.AddLoggingArguments(parser)
script_common.AddDeviceArguments(parser)
args = parser.parse_args()
logging_common.InitializeLogging(args)
- if args.blacklist_file:
- blacklist = device_blacklist.Blacklist(args.blacklist_file).Read()
- if blacklist:
- logger.critical('Device(s) in blacklist, not flashing devices:')
- for key in blacklist:
+ if args.denylist_file:
+ denylist = device_denylist.Denylist(args.denylist_file).Read()
+ if denylist:
+ logger.critical('Device(s) in denylist, not flashing devices:')
+ for key in denylist:
logger.critical(' %s', key)
return exit_codes.INFRA
@@ -43,16 +46,30 @@ def main():
failed_devices = []
def flash(device):
- fastboot = fastboot_utils.FastbootUtils(device)
try:
- fastboot.FlashDevice(args.build_path, wipe=args.wipe)
+ device.FlashDevice(args.build_path, wipe=args.wipe)
flashed_devices.append(device)
except Exception: # pylint: disable=broad-except
logger.exception('Device %s failed to flash.', str(device))
failed_devices.append(device)
- devices = script_common.GetDevices(args.devices, args.blacklist_file)
- device_utils.DeviceUtils.parallel(devices).pMap(flash)
+ devices = []
+ try:
+ adb_devices = script_common.GetDevices(args.devices, args.denylist_file)
+ devices += [fastboot_utils.FastbootUtils(device=d) for d in adb_devices]
+ except device_errors.NoDevicesError:
+ # Don't bail out if we're not looking for any particular device and there's
+ # at least one sitting in fastboot mode. Note that if we ARE looking for a
+ # particular device, and it's in fastboot mode, this will still fail.
+ fastboot_devices = fastboot.Fastboot.Devices()
+ if args.devices or not fastboot_devices:
+ raise
+ devices += [
+ fastboot_utils.FastbootUtils(fastbooter=d) for d in fastboot_devices
+ ]
+
+ parallel_devices = parallelizer.SyncParallelizer(devices)
+ parallel_devices.pMap(flash)
if flashed_devices:
logger.info('The following devices were flashed:')
@@ -63,5 +80,6 @@ def main():
return exit_codes.INFRA
return 0
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/catapult/devil/devil/android/tools/keyboard.py b/catapult/devil/devil/android/tools/keyboard.py
index c5cb6149..e400bca4 100755
--- a/catapult/devil/devil/android/tools/keyboard.py
+++ b/catapult/devil/devil/android/tools/keyboard.py
@@ -2,7 +2,6 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Use your keyboard as your phone's keyboard. Experimental."""
import argparse
@@ -14,56 +13,55 @@ import tty
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil import base_error
from devil.android.sdk import keyevent
from devil.android.tools import script_common
from devil.utils import logging_common
-
_KEY_MAPPING = {
- '\x08': keyevent.KEYCODE_DEL,
- '\x0a': keyevent.KEYCODE_ENTER,
- ' ': keyevent.KEYCODE_SPACE,
- '.': keyevent.KEYCODE_PERIOD,
- '0': keyevent.KEYCODE_0,
- '1': keyevent.KEYCODE_1,
- '2': keyevent.KEYCODE_2,
- '3': keyevent.KEYCODE_3,
- '4': keyevent.KEYCODE_4,
- '5': keyevent.KEYCODE_5,
- '6': keyevent.KEYCODE_6,
- '7': keyevent.KEYCODE_7,
- '8': keyevent.KEYCODE_8,
- '9': keyevent.KEYCODE_9,
- 'a': keyevent.KEYCODE_A,
- 'b': keyevent.KEYCODE_B,
- 'c': keyevent.KEYCODE_C,
- 'd': keyevent.KEYCODE_D,
- 'e': keyevent.KEYCODE_E,
- 'f': keyevent.KEYCODE_F,
- 'g': keyevent.KEYCODE_G,
- 'h': keyevent.KEYCODE_H,
- 'i': keyevent.KEYCODE_I,
- 'j': keyevent.KEYCODE_J,
- 'k': keyevent.KEYCODE_K,
- 'l': keyevent.KEYCODE_L,
- 'm': keyevent.KEYCODE_M,
- 'n': keyevent.KEYCODE_N,
- 'o': keyevent.KEYCODE_O,
- 'p': keyevent.KEYCODE_P,
- 'q': keyevent.KEYCODE_Q,
- 'r': keyevent.KEYCODE_R,
- 's': keyevent.KEYCODE_S,
- 't': keyevent.KEYCODE_T,
- 'u': keyevent.KEYCODE_U,
- 'v': keyevent.KEYCODE_V,
- 'w': keyevent.KEYCODE_W,
- 'x': keyevent.KEYCODE_X,
- 'y': keyevent.KEYCODE_Y,
- 'z': keyevent.KEYCODE_Z,
- '\x7f': keyevent.KEYCODE_DEL,
+ '\x08': keyevent.KEYCODE_DEL,
+ '\x0a': keyevent.KEYCODE_ENTER,
+ ' ': keyevent.KEYCODE_SPACE,
+ '.': keyevent.KEYCODE_PERIOD,
+ '0': keyevent.KEYCODE_0,
+ '1': keyevent.KEYCODE_1,
+ '2': keyevent.KEYCODE_2,
+ '3': keyevent.KEYCODE_3,
+ '4': keyevent.KEYCODE_4,
+ '5': keyevent.KEYCODE_5,
+ '6': keyevent.KEYCODE_6,
+ '7': keyevent.KEYCODE_7,
+ '8': keyevent.KEYCODE_8,
+ '9': keyevent.KEYCODE_9,
+ 'a': keyevent.KEYCODE_A,
+ 'b': keyevent.KEYCODE_B,
+ 'c': keyevent.KEYCODE_C,
+ 'd': keyevent.KEYCODE_D,
+ 'e': keyevent.KEYCODE_E,
+ 'f': keyevent.KEYCODE_F,
+ 'g': keyevent.KEYCODE_G,
+ 'h': keyevent.KEYCODE_H,
+ 'i': keyevent.KEYCODE_I,
+ 'j': keyevent.KEYCODE_J,
+ 'k': keyevent.KEYCODE_K,
+ 'l': keyevent.KEYCODE_L,
+ 'm': keyevent.KEYCODE_M,
+ 'n': keyevent.KEYCODE_N,
+ 'o': keyevent.KEYCODE_O,
+ 'p': keyevent.KEYCODE_P,
+ 'q': keyevent.KEYCODE_Q,
+ 'r': keyevent.KEYCODE_R,
+ 's': keyevent.KEYCODE_S,
+ 't': keyevent.KEYCODE_T,
+ 'u': keyevent.KEYCODE_U,
+ 'v': keyevent.KEYCODE_V,
+ 'w': keyevent.KEYCODE_W,
+ 'x': keyevent.KEYCODE_X,
+ 'y': keyevent.KEYCODE_Y,
+ 'z': keyevent.KEYCODE_Z,
+ '\x7f': keyevent.KEYCODE_DEL,
}
diff --git a/catapult/devil/devil/android/tools/provision_devices.py b/catapult/devil/devil/android/tools/provision_devices.py
index 47b1dc3f..4b0f44a2 100755
--- a/catapult/devil/devil/android/tools/provision_devices.py
+++ b/catapult/devil/devil/android/tools/provision_devices.py
@@ -3,7 +3,6 @@
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Provisions Android devices with settings required for bots.
Usage:
@@ -27,11 +26,11 @@ import _strptime # pylint: disable=unused-import
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import battery_utils
-from devil.android import device_blacklist
+from devil.android import device_denylist
from devil.android import device_errors
from devil.android import device_temp_file
from devil.android import device_utils
@@ -53,7 +52,7 @@ _SYSTEM_WEBVIEW_NAMES = ['webview', 'WebViewGoogle']
_CHROME_PACKAGE_REGEX = re.compile('.*chrom.*')
_TOMBSTONE_REGEX = re.compile('tombstone.*')
_STANDALONE_VR_DEVICES = [
- 'vega', # Lenovo Mirage Solo
+ 'vega', # Lenovo Mirage Solo
]
@@ -66,53 +65,51 @@ class _DEFAULT_TIMEOUTS(object):
class ProvisionStep(object):
-
def __init__(self, cmd, reboot=False):
self.cmd = cmd
self.reboot = reboot
-def ProvisionDevices(
- devices,
- blacklist_file,
- adb_key_files=None,
- disable_location=False,
- disable_mock_location=False,
- disable_network=False,
- disable_system_chrome=False,
- emulators=False,
- enable_java_debug=False,
- max_battery_temp=None,
- min_battery_level=None,
- output_device_blacklist=None,
- reboot_timeout=None,
- remove_system_webview=False,
- system_app_remove_list=None,
- system_package_remove_list=None,
- wipe=True):
- blacklist = (device_blacklist.Blacklist(blacklist_file)
- if blacklist_file
- else None)
+def ProvisionDevices(devices,
+ denylist_file,
+ adb_key_files=None,
+ disable_location=False,
+ disable_mock_location=False,
+ disable_network=False,
+ disable_system_chrome=False,
+ emulators=False,
+ enable_java_debug=False,
+ max_battery_temp=None,
+ min_battery_level=None,
+ output_device_denylist=None,
+ reboot_timeout=None,
+ remove_system_webview=False,
+ system_app_remove_list=None,
+ system_package_remove_list=None,
+ wipe=True):
+ denylist = (device_denylist.Denylist(denylist_file)
+ if denylist_file else None)
system_app_remove_list = system_app_remove_list or []
system_package_remove_list = system_package_remove_list or []
try:
- devices = script_common.GetDevices(devices, blacklist)
+ devices = script_common.GetDevices(devices, denylist)
except device_errors.NoDevicesError:
logging.error('No available devices to provision.')
- if blacklist:
- logging.error('Local device blacklist: %s', blacklist.Read())
+ if denylist:
+ logging.error('Local device denylist: %s', denylist.Read())
raise
- devices = [d for d in devices
- if not emulators or d.adb.is_emulator]
+ devices = [d for d in devices if not emulators or d.adb.is_emulator]
parallel_devices = device_utils.DeviceUtils.parallel(devices)
steps = []
if wipe:
steps += [ProvisionStep(lambda d: Wipe(d, adb_key_files), reboot=True)]
- steps += [ProvisionStep(
- lambda d: SetProperties(d, enable_java_debug, disable_location,
- disable_mock_location),
- reboot=not emulators)]
+ steps += [
+ ProvisionStep(
+ lambda d: SetProperties(d, enable_java_debug, disable_location,
+ disable_mock_location),
+ reboot=not emulators)
+ ]
if disable_network:
steps.append(ProvisionStep(DisableNetwork))
@@ -121,37 +118,36 @@ def ProvisionDevices(
steps.append(ProvisionStep(DisableSystemChrome))
if max_battery_temp:
- steps.append(ProvisionStep(
- lambda d: WaitForBatteryTemperature(d, max_battery_temp)))
+ steps.append(
+ ProvisionStep(lambda d: WaitForBatteryTemperature(d, max_battery_temp)))
if min_battery_level:
- steps.append(ProvisionStep(
- lambda d: WaitForCharge(d, min_battery_level)))
+ steps.append(ProvisionStep(lambda d: WaitForCharge(d, min_battery_level)))
if remove_system_webview:
system_app_remove_list.extend(_SYSTEM_WEBVIEW_NAMES)
if system_app_remove_list or system_package_remove_list:
- steps.append(ProvisionStep(
- lambda d: RemoveSystemApps(
- d, system_app_remove_list, system_package_remove_list)))
+ steps.append(
+ ProvisionStep(lambda d: RemoveSystemApps(d, system_app_remove_list,
+ system_package_remove_list)))
steps.append(ProvisionStep(SetDate))
steps.append(ProvisionStep(CheckExternalStorage))
steps.append(ProvisionStep(StandaloneVrDeviceSetup))
- parallel_devices.pMap(ProvisionDevice, steps, blacklist, reboot_timeout)
+ parallel_devices.pMap(ProvisionDevice, steps, denylist, reboot_timeout)
- blacklisted_devices = blacklist.Read() if blacklist else []
- if output_device_blacklist:
- with open(output_device_blacklist, 'w') as f:
- json.dump(blacklisted_devices, f)
- if all(d in blacklisted_devices for d in devices):
+ denylisted_devices = denylist.Read() if denylist else []
+ if output_device_denylist:
+ with open(output_device_denylist, 'w') as f:
+ json.dump(denylisted_devices, f)
+ if all(d in denylisted_devices for d in devices):
raise device_errors.NoDevicesError
return 0
-def ProvisionDevice(device, steps, blacklist, reboot_timeout=None):
+def ProvisionDevice(device, steps, denylist, reboot_timeout=None):
try:
if not reboot_timeout:
if device.build_version_sdk >= version_codes.LOLLIPOP:
@@ -171,27 +167,27 @@ def ProvisionDevice(device, steps, blacklist, reboot_timeout=None):
device.adb.WaitForDevice()
except device_errors.CommandTimeoutError:
- logger.exception('Timed out waiting for device %s. Adding to blacklist.',
+ logger.exception('Timed out waiting for device %s. Adding to denylist.',
str(device))
- if blacklist:
- blacklist.Extend([str(device)], reason='provision_timeout')
+ if denylist:
+ denylist.Extend([str(device)], reason='provision_timeout')
except (device_errors.CommandFailedError,
device_errors.DeviceUnreachableError):
- logger.exception('Failed to provision device %s. Adding to blacklist.',
+ logger.exception('Failed to provision device %s. Adding to denylist.',
str(device))
- if blacklist:
- blacklist.Extend([str(device)], reason='provision_failure')
+ if denylist:
+ denylist.Extend([str(device)], reason='provision_failure')
def Wipe(device, adb_key_files=None):
- if (device.IsUserBuild() or
- device.build_version_sdk >= version_codes.MARSHMALLOW):
+ if (device.IsUserBuild()
+ or device.build_version_sdk >= version_codes.MARSHMALLOW):
WipeChromeData(device)
package = 'com.google.android.gms'
- if device.GetApplicationPaths(package):
- version_name = device.GetApplicationVersion(package)
+ version_name = device.GetApplicationVersion(package)
+ if version_name:
logger.info('Version name for %s is %s', package, version_name)
else:
logger.info('Package %s is not installed', package)
@@ -219,10 +215,12 @@ def WipeChromeData(device):
try:
if device.IsUserBuild():
_UninstallIfMatch(device, _CHROME_PACKAGE_REGEX)
- device.RunShellCommand('rm -rf %s/*' % device.GetExternalStoragePath(),
- shell=True, check_return=True)
- device.RunShellCommand('rm -rf /data/local/tmp/*',
- shell=True, check_return=True)
+ device.RunShellCommand(
+ 'rm -rf %s/*' % device.GetExternalStoragePath(),
+ shell=True,
+ check_return=True)
+ device.RunShellCommand(
+ 'rm -rf /data/local/tmp/*', shell=True, check_return=True)
else:
device.EnableRoot()
_UninstallIfMatch(device, _CHROME_PACKAGE_REGEX)
@@ -233,19 +231,23 @@ def WipeChromeData(device):
_WipeFileOrDir(device, '/data/local/chrome-command-line')
_WipeFileOrDir(device, '/data/local/.config/')
_WipeFileOrDir(device, '/data/local/tmp/')
- device.RunShellCommand('rm -rf %s/*' % device.GetExternalStoragePath(),
- shell=True, check_return=True)
+ device.RunShellCommand(
+ 'rm -rf %s/*' % device.GetExternalStoragePath(),
+ shell=True,
+ check_return=True)
except device_errors.CommandFailedError:
logger.exception('Possible failure while wiping the device. '
'Attempting to continue.')
def _UninstallIfMatch(device, pattern):
- installed_packages = device.RunShellCommand(
- ['pm', 'list', 'packages'], check_return=True)
+ installed_packages = device.RunShellCommand(['pm', 'list', 'packages'],
+ check_return=True)
installed_system_packages = [
- pkg.split(':')[1] for pkg in device.RunShellCommand(
- ['pm', 'list', 'packages', '-s'], check_return=True)]
+ pkg.split(':')[1]
+ for pkg in device.RunShellCommand(['pm', 'list', 'packages', '-s'],
+ check_return=True)
+ ]
for package_output in installed_packages:
package = package_output.split(":")[1]
if pattern.match(package) and package not in installed_system_packages:
@@ -279,10 +281,9 @@ def WipeDevice(device, adb_key_files):
device.EnableRoot()
device_authorized = device.FileExists(adb_wrapper.ADB_KEYS_FILE)
if device_authorized:
- adb_keys = device.ReadFile(adb_wrapper.ADB_KEYS_FILE,
- as_root=True).splitlines()
- device.RunShellCommand(['wipe', 'data'],
- as_root=True, check_return=True)
+ adb_keys = device.ReadFile(
+ adb_wrapper.ADB_KEYS_FILE, as_root=True).splitlines()
+ device.RunShellCommand(['wipe', 'data'], as_root=True, check_return=True)
device.adb.WaitForDevice()
if device_authorized:
@@ -303,12 +304,15 @@ def WipeDevice(device, adb_key_files):
def _WriteAdbKeysFile(device, adb_keys_string):
dir_path = posixpath.dirname(adb_wrapper.ADB_KEYS_FILE)
device.RunShellCommand(['mkdir', '-p', dir_path],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
device.RunShellCommand(['restorecon', dir_path],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
device.WriteFile(adb_wrapper.ADB_KEYS_FILE, adb_keys_string, as_root=True)
device.RunShellCommand(['restorecon', adb_wrapper.ADB_KEYS_FILE],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
def SetProperties(device, enable_java_debug, disable_location,
@@ -322,21 +326,20 @@ def SetProperties(device, enable_java_debug, disable_location,
_ConfigureLocalProperties(device, enable_java_debug)
else:
logger.warning('Cannot configure properties in user builds.')
- settings.ConfigureContentSettings(
- device, settings.DETERMINISTIC_DEVICE_SETTINGS)
+ settings.ConfigureContentSettings(device,
+ settings.DETERMINISTIC_DEVICE_SETTINGS)
if disable_location:
- settings.ConfigureContentSettings(
- device, settings.DISABLE_LOCATION_SETTINGS)
+ settings.ConfigureContentSettings(device,
+ settings.DISABLE_LOCATION_SETTINGS)
else:
- settings.ConfigureContentSettings(
- device, settings.ENABLE_LOCATION_SETTINGS)
+ settings.ConfigureContentSettings(device, settings.ENABLE_LOCATION_SETTINGS)
if disable_mock_location:
- settings.ConfigureContentSettings(
- device, settings.DISABLE_MOCK_LOCATION_SETTINGS)
+ settings.ConfigureContentSettings(device,
+ settings.DISABLE_MOCK_LOCATION_SETTINGS)
else:
- settings.ConfigureContentSettings(
- device, settings.ENABLE_MOCK_LOCATION_SETTINGS)
+ settings.ConfigureContentSettings(device,
+ settings.ENABLE_MOCK_LOCATION_SETTINGS)
settings.SetLockScreenSettings(device)
@@ -345,18 +348,19 @@ def SetProperties(device, enable_java_debug, disable_location,
def DisableNetwork(device):
- settings.ConfigureContentSettings(
- device, settings.NETWORK_DISABLED_SETTINGS)
+ settings.ConfigureContentSettings(device, settings.NETWORK_DISABLED_SETTINGS)
if device.build_version_sdk >= version_codes.MARSHMALLOW:
# Ensure that NFC is also switched off.
device.RunShellCommand(['svc', 'nfc', 'disable'],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
def DisableSystemChrome(device):
# The system chrome version on the device interferes with some tests.
device.RunShellCommand(['pm', 'disable', 'com.android.chrome'],
- as_root=True, check_return=True)
+ as_root=True,
+ check_return=True)
def _FindSystemPackagePaths(device, system_package_list):
@@ -376,8 +380,8 @@ def _FindSystemAppPaths(device, system_app_list):
return found_paths
-def RemoveSystemApps(
- device, system_app_remove_list, system_package_remove_list):
+def RemoveSystemApps(device, system_app_remove_list,
+ system_package_remove_list):
"""Attempts to remove the provided system apps from the given device.
Arguments:
@@ -418,19 +422,17 @@ def _ConfigureLocalProperties(device, java_debug=True):
'ro.test_harness=1',
'ro.audio.silent=1',
'ro.setupwizard.mode=DISABLED',
- ]
+ ]
if java_debug:
- local_props.append(
- '%s=all' % device_utils.DeviceUtils.JAVA_ASSERT_PROPERTY)
+ local_props.append('%s=all' % device_utils.DeviceUtils.JAVA_ASSERT_PROPERTY)
local_props.append('debug.checkjni=1')
try:
device.WriteFile(
- device.LOCAL_PROPERTIES_PATH,
- '\n'.join(local_props), as_root=True)
+ device.LOCAL_PROPERTIES_PATH, '\n'.join(local_props), as_root=True)
# Android will not respect the local props file if it is world writable.
- device.RunShellCommand(
- ['chmod', '644', device.LOCAL_PROPERTIES_PATH],
- as_root=True, check_return=True)
+ device.RunShellCommand(['chmod', '644', device.LOCAL_PROPERTIES_PATH],
+ as_root=True,
+ check_return=True)
except device_errors.CommandFailedError:
logger.exception('Failed to configure local properties.')
@@ -478,8 +480,8 @@ def SetDate(device):
get_date_command.append('+"%Y%m%d.%H%M%S"')
device_time = device.RunShellCommand(
- get_date_command, check_return=True,
- as_root=True, single_line=True).replace('"', '')
+ get_date_command, check_return=True, as_root=True,
+ single_line=True).replace('"', '')
device_time = datetime.datetime.strptime(device_time, "%Y%m%d.%H%M%S")
correct_time = datetime.datetime.strptime(strgmtime, date_format)
tdelta = (correct_time - device_time).seconds
@@ -504,8 +506,7 @@ def SetDate(device):
# The following intent can take a bit to complete when ran shortly after
# device boot-up.
device.BroadcastIntent(
- intent.Intent(action='android.intent.action.TIME_SET'),
- timeout=180)
+ intent.Intent(action='android.intent.action.TIME_SET'), timeout=180)
def LogDeviceProperties(device):
@@ -529,7 +530,8 @@ def CheckExternalStorage(device):
except device_errors.CommandFailedError:
logger.info('External storage not writable. Remounting / as RW')
device.RunShellCommand(['mount', '-o', 'remount,rw', '/'],
- check_return=True, as_root=True)
+ check_return=True,
+ as_root=True)
device.EnableRoot()
with device_temp_file.DeviceTempFile(
device.adb, suffix='.sh', dir=device.GetExternalStoragePath()) as f:
@@ -546,8 +548,11 @@ def StandaloneVrDeviceSetup(device):
return
# Modify VrCore's settings so that any first time setup, etc. is skipped.
- shared_pref = shared_prefs.SharedPrefs(device, 'com.google.vr.vrcore',
- 'VrCoreSettings.xml', use_encrypted_path=True)
+ shared_pref = shared_prefs.SharedPrefs(
+ device,
+ 'com.google.vr.vrcore',
+ 'VrCoreSettings.xml',
+ use_encrypted_path=True)
shared_pref.Load()
# Skip first time setup.
shared_pref.SetBoolean('DaydreamSetupComplete', True)
@@ -576,84 +581,107 @@ def main(raw_args):
script_common.AddDeviceArguments(parser)
script_common.AddEnvironmentArguments(parser)
parser.add_argument(
- '--adb-key-files', type=str, nargs='+',
+ '--adb-key-files',
+ type=str,
+ nargs='+',
help='list of adb keys to push to device')
parser.add_argument(
- '--disable-location', action='store_true',
+ '--disable-location',
+ action='store_true',
help='disable Google location services on devices')
parser.add_argument(
- '--disable-mock-location', action='store_true', default=False,
+ '--disable-mock-location',
+ action='store_true',
+ default=False,
help='Set ALLOW_MOCK_LOCATION to false')
parser.add_argument(
- '--disable-network', action='store_true',
+ '--disable-network',
+ action='store_true',
help='disable network access on devices')
parser.add_argument(
- '--disable-java-debug', action='store_false',
- dest='enable_java_debug', default=True,
+ '--disable-java-debug',
+ action='store_false',
+ dest='enable_java_debug',
+ default=True,
help='disable Java property asserts and JNI checking')
parser.add_argument(
- '--disable-system-chrome', action='store_true',
+ '--disable-system-chrome',
+ action='store_true',
help='DEPRECATED: use --remove-system-packages com.android.google '
- 'Disable the system chrome from devices.')
+ 'Disable the system chrome from devices.')
parser.add_argument(
- '--emulators', action='store_true',
+ '--emulators',
+ action='store_true',
help='provision only emulators and ignore usb devices '
- '(this will not wipe emulators)')
+ '(this will not wipe emulators)')
parser.add_argument(
- '--max-battery-temp', type=int, metavar='NUM',
+ '--max-battery-temp',
+ type=int,
+ metavar='NUM',
help='Wait for the battery to have this temp or lower.')
parser.add_argument(
- '--min-battery-level', type=int, metavar='NUM',
+ '--min-battery-level',
+ type=int,
+ metavar='NUM',
help='wait for the device to reach this minimum battery'
- ' level before trying to continue')
- parser.add_argument(
- '--output-device-blacklist',
- help='Json file to output the device blacklist.')
+ ' level before trying to continue')
+ # TODO(crbug.com/1097306): Remove after updating clients.
+ parser.add_argument('--output-device-blacklist',
+ help=argparse.SUPPRESS)
+ parser.add_argument('--output-device-denylist',
+ help='Json file to output the device denylist.')
parser.add_argument(
- '--reboot-timeout', metavar='SECS', type=int,
+ '--reboot-timeout',
+ metavar='SECS',
+ type=int,
help='when wiping the device, max number of seconds to'
- ' wait after each reboot '
- '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
+ ' wait after each reboot '
+ '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
parser.add_argument(
- '--remove-system-apps', nargs='*', dest='system_app_remove_list',
+ '--remove-system-apps',
+ nargs='*',
+ dest='system_app_remove_list',
help='DEPRECATED: use --remove-system-packages instead. '
- 'The names of system apps to remove. ')
+ 'The names of system apps to remove. ')
parser.add_argument(
- '--remove-system-packages', nargs='*', dest='system_package_remove_list',
+ '--remove-system-packages',
+ nargs='*',
+ dest='system_package_remove_list',
help='The names of system packages to remove.')
parser.add_argument(
- '--remove-system-webview', action='store_true',
+ '--remove-system-webview',
+ action='store_true',
help='DEPRECATED: use --remove-system-packages '
- 'com.google.android.webview com.android.webview '
- 'Remove the system webview from devices.')
+ 'com.google.android.webview com.android.webview '
+ 'Remove the system webview from devices.')
parser.add_argument(
- '--skip-wipe', action='store_true', default=False,
+ '--skip-wipe',
+ action='store_true',
+ default=False,
help='do not wipe device data during provisioning')
# No-op arguments for compatibility with build/android/provision_devices.py.
# TODO(jbudorick): Remove these once all callers have stopped using them.
parser.add_argument(
- '--chrome-specific-wipe', action='store_true',
- help=argparse.SUPPRESS)
+ '--chrome-specific-wipe', action='store_true', help=argparse.SUPPRESS)
+ parser.add_argument('--phase', action='append', help=argparse.SUPPRESS)
parser.add_argument(
- '--phase', action='append',
- help=argparse.SUPPRESS)
- parser.add_argument(
- '-r', '--auto-reconnect', action='store_true',
- help=argparse.SUPPRESS)
- parser.add_argument(
- '-t', '--target',
- help=argparse.SUPPRESS)
+ '-r', '--auto-reconnect', action='store_true', help=argparse.SUPPRESS)
+ parser.add_argument('-t', '--target', help=argparse.SUPPRESS)
args = parser.parse_args(raw_args)
logging_common.InitializeLogging(args)
script_common.InitializeEnvironment(args)
+ output_device_denylist = args.output_device_denylist
+ if not output_device_denylist and args.output_device_blacklist:
+ output_device_denylist = args.output_device_blacklist
+
try:
return ProvisionDevices(
args.devices,
- args.blacklist_file,
+ args.denylist_file,
adb_key_files=args.adb_key_files,
disable_location=args.disable_location,
disable_mock_location=args.disable_mock_location,
@@ -663,7 +691,7 @@ def main(raw_args):
enable_java_debug=args.enable_java_debug,
max_battery_temp=args.max_battery_temp,
min_battery_level=args.min_battery_level,
- output_device_blacklist=args.output_device_blacklist,
+ output_device_denylist=output_device_denylist,
reboot_timeout=args.reboot_timeout,
remove_system_webview=args.remove_system_webview,
system_app_remove_list=args.system_app_remove_list,
diff --git a/catapult/devil/devil/android/tools/screenshot.py b/catapult/devil/devil/android/tools/screenshot.py
index 3b3335c2..fe6be04d 100755
--- a/catapult/devil/devil/android/tools/screenshot.py
+++ b/catapult/devil/devil/android/tools/screenshot.py
@@ -2,7 +2,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Takes a screenshot from an Android device."""
import argparse
@@ -11,8 +10,9 @@ import os
import sys
if __name__ == '__main__':
- sys.path.append(os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..', '..')))
+ sys.path.append(
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import device_utils
from devil.android.tools import script_common
from devil.utils import logging_common
@@ -25,17 +25,22 @@ def main():
parser = argparse.ArgumentParser(description=__doc__)
logging_common.AddLoggingArguments(parser)
script_common.AddDeviceArguments(parser)
- parser.add_argument('-f', '--file', metavar='FILE',
- help='Save result to file instead of generating a '
- 'timestamped file name.')
- parser.add_argument('host_file', nargs='?',
- help='File to which the screenshot will be saved.')
+ parser.add_argument(
+ '-f',
+ '--file',
+ metavar='FILE',
+ help='Save result to file instead of generating a '
+ 'timestamped file name.')
+ parser.add_argument(
+ 'host_file',
+ nargs='?',
+ help='File to which the screenshot will be saved.')
args = parser.parse_args()
host_file = args.host_file or args.file
logging_common.InitializeLogging(args)
- devices = script_common.GetDevices(args.devices, args.blacklist_file)
+ devices = script_common.GetDevices(args.devices, args.denylist_file)
def screenshot(device):
f = None
@@ -43,8 +48,8 @@ def main():
root, ext = os.path.splitext(host_file)
f = '%s_%s%s' % (root, str(device), ext)
f = device.TakeScreenshot(f)
- print 'Screenshot for device %s written to %s' % (
- str(device), os.path.abspath(f))
+ print 'Screenshot for device %s written to %s' % (str(device),
+ os.path.abspath(f))
device_utils.DeviceUtils.parallel(devices).pMap(screenshot)
return 0
diff --git a/catapult/devil/devil/android/tools/script_common.py b/catapult/devil/devil/android/tools/script_common.py
index 897659bb..ae2b7a84 100644
--- a/catapult/devil/devil/android/tools/script_common.py
+++ b/catapult/devil/devil/android/tools/script_common.py
@@ -2,10 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import argparse
import os
from devil import devil_env
-from devil.android import device_blacklist
+from devil.android import device_denylist
from devil.android import device_errors
from devil.android import device_utils
@@ -20,8 +21,7 @@ def AddEnvironmentArguments(parser):
parser: an instance of argparse.ArgumentParser
"""
parser.add_argument(
- '--adb-path', type=os.path.realpath,
- help='Path to the adb binary')
+ '--adb-path', type=os.path.realpath, help='Path to the adb binary')
def InitializeEnvironment(args):
@@ -46,32 +46,51 @@ def InitializeEnvironment(args):
devil_dynamic_config = devil_env.EmptyConfig()
if args.adb_path:
devil_dynamic_config['dependencies'].update(
- devil_env.LocalConfigItem(
- 'adb', devil_env.GetPlatform(), args.adb_path))
+ devil_env.LocalConfigItem('adb', devil_env.GetPlatform(),
+ args.adb_path))
devil_env.config.Initialize(configs=[devil_dynamic_config])
def AddDeviceArguments(parser):
- """Adds device and blacklist arguments to the provided parser.
+ """Adds device and denylist arguments to the provided parser.
Args:
parser: an instance of argparse.ArgumentParser
"""
parser.add_argument(
- '-d', '--device', dest='devices', action='append',
+ '-d',
+ '--device',
+ dest='devices',
+ action='append',
+ default=[],
help='Serial number of the Android device to use. (default: use all)')
- parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
+ # TODO(crbug.com/1097306): Simplify this to an ungrouped --denylist-file
+ # once all uses of --blacklist-file / args.blacklist_file have been updated.
+ class DenylistAction(argparse.Action):
-def GetDevices(requested_devices, blacklist_file):
+ #override
+ def __call__(self, parser, namespace, values, option_string=None):
+ setattr(namespace, 'denylist_file', values)
+ setattr(namespace, 'blacklist_file', values)
+
+ denylist_group = parser.add_mutually_exclusive_group()
+ denylist_group.add_argument('--denylist-file',
+ help='Device denylist JSON file.',
+ action=DenylistAction)
+ denylist_group.add_argument('--blacklist-file',
+ help=argparse.SUPPRESS,
+ action=DenylistAction)
+
+
+def GetDevices(requested_devices, denylist_file):
"""Gets a list of healthy devices matching the given parameters."""
- if not isinstance(blacklist_file, device_blacklist.Blacklist):
- blacklist_file = (device_blacklist.Blacklist(blacklist_file)
- if blacklist_file
- else None)
+ if not isinstance(denylist_file, device_denylist.Denylist):
+ denylist_file = (device_denylist.Denylist(denylist_file)
+ if denylist_file else None)
- devices = device_utils.DeviceUtils.HealthyDevices(blacklist_file)
+ devices = device_utils.DeviceUtils.HealthyDevices(denylist_file)
if not devices:
raise device_errors.NoDevicesError()
elif requested_devices:
@@ -80,8 +99,7 @@ def GetDevices(requested_devices, blacklist_file):
missing = requested.difference(available)
if missing:
raise device_errors.DeviceUnreachableError(next(iter(missing)))
- return sorted(device_utils.DeviceUtils(d)
- for d in available.intersection(requested))
+ return sorted(
+ device_utils.DeviceUtils(d) for d in available.intersection(requested))
else:
return devices
-
diff --git a/catapult/devil/devil/android/tools/script_common_test.py b/catapult/devil/devil/android/tools/script_common_test.py
index 30f9aaf9..f8269cc0 100755
--- a/catapult/devil/devil/android/tools/script_common_test.py
+++ b/catapult/devil/devil/android/tools/script_common_test.py
@@ -3,7 +3,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
import argparse
import sys
import tempfile
@@ -23,44 +22,43 @@ with devil_env.SysPath(devil_env.DEPENDENCY_MANAGER_PATH):
class GetDevicesTest(unittest.TestCase):
-
def testNoSpecs(self):
devices = [
device_utils.DeviceUtils('123'),
device_utils.DeviceUtils('456'),
]
- with mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices',
- return_value=devices):
- self.assertEquals(
- devices,
- script_common.GetDevices(None, None))
+ with mock.patch(
+ 'devil.android.device_utils.DeviceUtils.HealthyDevices',
+ return_value=devices):
+ self.assertEquals(devices, script_common.GetDevices(None, None))
def testWithDevices(self):
devices = [
device_utils.DeviceUtils('123'),
device_utils.DeviceUtils('456'),
]
- with mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices',
- return_value=devices):
- self.assertEquals(
- [device_utils.DeviceUtils('456')],
- script_common.GetDevices(['456'], None))
+ with mock.patch(
+ 'devil.android.device_utils.DeviceUtils.HealthyDevices',
+ return_value=devices):
+ self.assertEquals([device_utils.DeviceUtils('456')],
+ script_common.GetDevices(['456'], None))
def testMissingDevice(self):
- with mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices',
- return_value=[device_utils.DeviceUtils('123')]):
+ with mock.patch(
+ 'devil.android.device_utils.DeviceUtils.HealthyDevices',
+ return_value=[device_utils.DeviceUtils('123')]):
with self.assertRaises(device_errors.DeviceUnreachableError):
script_common.GetDevices(['456'], None)
def testNoDevices(self):
- with mock.patch('devil.android.device_utils.DeviceUtils.HealthyDevices',
- return_value=[]):
+ with mock.patch(
+ 'devil.android.device_utils.DeviceUtils.HealthyDevices',
+ return_value=[]):
with self.assertRaises(device_errors.NoDevicesError):
script_common.GetDevices(None, None)
class InitializeEnvironmentTest(unittest.TestCase):
-
def setUp(self):
# pylint: disable=protected-access
self.parser = argparse.ArgumentParser()
@@ -77,9 +75,7 @@ class InitializeEnvironmentTest(unittest.TestCase):
with tempfile.NamedTemporaryFile() as f:
args = self.parser.parse_args(['--adb-path=%s' % f.name])
script_common.InitializeEnvironment(args)
- self.assertEquals(
- f.name,
- devil_env.config.LocalPath('adb'))
+ self.assertEquals(f.name, devil_env.config.LocalPath('adb'))
def testNonExistentAdb(self):
with tempfile.NamedTemporaryFile() as f:
diff --git a/catapult/devil/devil/android/tools/system_app.py b/catapult/devil/devil/android/tools/system_app.py
index 8629ae68..c2f058e0 100755
--- a/catapult/devil/devil/android/tools/system_app.py
+++ b/catapult/devil/devil/android/tools/system_app.py
@@ -2,7 +2,6 @@
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A script to replace a system app while running a command."""
import argparse
@@ -13,14 +12,13 @@ import posixpath
import re
import sys
-
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
-
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import apk_helper
+from devil.android import decorators
from devil.android import device_errors
from devil.android import device_temp_file
from devil.android.sdk import version_codes
@@ -31,20 +29,25 @@ from devil.utils import run_tests_helper
logger = logging.getLogger(__name__)
-
# Some system apps aren't actually installed in the /system/ directory, so
# special case them here with the correct install location.
SPECIAL_SYSTEM_APP_LOCATIONS = {
- # This also gets installed in /data/app when not a system app, so this script
- # will remove either version. This doesn't appear to cause any issues, but
- # will cause a few unnecessary reboots if this is the only package getting
- # removed and it's already not a system app.
- 'com.google.ar.core': '/data/app/',
+ # Older versions of ArCore were installed in /data/app/ regardless of
+ # whether they were system apps or not. Newer versions install in /system/
+ # if they are system apps, and in /data/app/ if they aren't.
+ 'com.google.ar.core': ['/data/app/', '/system/'],
+ # On older versions of VrCore, the system app version is installed in
+ # /system/ like normal. However, at some point, this moved to /data/.
+ # So, we have to handle both cases. Like ArCore, this means we'll end up
+ # removing even non-system versions due to this, but it doesn't cause any
+ # issues.
+ 'com.google.vr.core': ['/data/app/', '/system/'],
}
# Gets app path and package name pm list packages -f output.
_PM_LIST_PACKAGE_PATH_RE = re.compile(r'^\s*package:(\S+)=(\S+)\s*$')
+
def RemoveSystemApps(device, package_names):
"""Removes the given system apps.
@@ -60,7 +63,9 @@ def RemoveSystemApps(device, package_names):
@contextlib.contextmanager
-def ReplaceSystemApp(device, package_name, replacement_apk,
+def ReplaceSystemApp(device,
+ package_name,
+ replacement_apk,
install_timeout=None):
"""A context manager that replaces the given system app while in scope.
@@ -94,8 +99,8 @@ def _FindSystemPackagePaths(device, system_package_list):
# TODO(aluo): Move this into device_utils.py
def _GetApplicationPaths(device, package):
paths = []
- lines = device.RunShellCommand(['pm', 'list', 'packages', '-f', '-u',
- package], check_return=True)
+ lines = device.RunShellCommand(
+ ['pm', 'list', 'packages', '-f', '-u', package], check_return=True)
for line in lines:
match = re.match(_PM_LIST_PACKAGE_PATH_RE, line)
if match:
@@ -108,24 +113,34 @@ def _GetApplicationPaths(device, package):
def _GetSystemPath(package, paths):
for p in paths:
- if p.startswith(SPECIAL_SYSTEM_APP_LOCATIONS.get(package, '/system/')):
- return p
+ app_locations = SPECIAL_SYSTEM_APP_LOCATIONS.get(package,
+ ['/system/', '/product/'])
+ for location in app_locations:
+ if p.startswith(location):
+ return p
return None
+_MODIFICATION_TIMEOUT = 300
+_MODIFICATION_RETRIES = 2
_ENABLE_MODIFICATION_PROP = 'devil.modify_sys_apps'
-@contextlib.contextmanager
-def EnableSystemAppModification(device):
- """A context manager that allows system apps to be modified while in scope.
+def _ShouldRetryModification(exc):
+ return not isinstance(exc, device_errors.CommandTimeoutError)
- Args:
- device: (device_utils.DeviceUtils) the device
- """
- if device.GetProp(_ENABLE_MODIFICATION_PROP) == '1':
- yield
- return
+
+# timeout and retries are both required by the decorator, but neither
+# are used within the body of the function.
+# pylint: disable=unused-argument
+
+
+@decorators.WithTimeoutAndConditionalRetries(_ShouldRetryModification)
+def _SetUpSystemAppModification(device, timeout=None, retries=None):
+ # Ensure that the device is online & available before proceeding to
+ # handle the case where something fails in the middle of set up and
+ # triggers a retry.
+ device.WaitUntilFullyBooted()
# All calls that could potentially need root should run with as_root=True, but
# it looks like some parts of Telemetry work as-is by implicitly assuming that
@@ -151,13 +166,61 @@ def EnableSystemAppModification(device):
device.adb.Remount()
device.RunShellCommand(['stop'], check_return=True)
device.SetProp(_ENABLE_MODIFICATION_PROP, '1')
- yield
- finally:
+ except device_errors.CommandFailedError:
+ if device.adb.is_emulator:
+ # Point the user to documentation, since there's a good chance they can
+ # workaround this on an emulator.
+ docs_url = ('https://chromium.googlesource.com/chromium/src/+/'
+ 'master/docs/android_emulator.md#writable-system-partition')
+ logger.error(
+ 'Did you start the emulator with "-writable-system?"\n'
+ 'See %s\n', docs_url)
+ raise
+
+ return should_restore_root
+
+
+@decorators.WithTimeoutAndConditionalRetries(_ShouldRetryModification)
+def _TearDownSystemAppModification(device,
+ should_restore_root,
+ timeout=None,
+ retries=None):
+ try:
device.SetProp(_ENABLE_MODIFICATION_PROP, '0')
device.Reboot()
device.WaitUntilFullyBooted()
if should_restore_root:
device.EnableRoot()
+ except device_errors.CommandTimeoutError:
+ logger.error('Timed out while tearing down system app modification.')
+ logger.error(' device state: %s', device.adb.GetState())
+ raise
+
+
+# pylint: enable=unused-argument
+
+
+@contextlib.contextmanager
+def EnableSystemAppModification(device):
+ """A context manager that allows system apps to be modified while in scope.
+
+ Args:
+ device: (device_utils.DeviceUtils) the device
+ """
+ if device.GetProp(_ENABLE_MODIFICATION_PROP) == '1':
+ yield
+ return
+
+ should_restore_root = _SetUpSystemAppModification(
+ device, timeout=_MODIFICATION_TIMEOUT, retries=_MODIFICATION_RETRIES)
+ try:
+ yield
+ finally:
+ _TearDownSystemAppModification(
+ device,
+ should_restore_root,
+ timeout=_MODIFICATION_TIMEOUT,
+ retries=_MODIFICATION_RETRIES)
@contextlib.contextmanager
@@ -171,11 +234,9 @@ def _RelocateApp(device, package_name, relocate_to):
for p in system_package_paths
}
relocation_dirs = [
- posixpath.dirname(d)
- for _, d in relocation_map.iteritems()
+ posixpath.dirname(d) for _, d in relocation_map.iteritems()
]
- device.RunShellCommand(['mkdir', '-p'] + relocation_dirs,
- check_return=True)
+ device.RunShellCommand(['mkdir', '-p'] + relocation_dirs, check_return=True)
_MoveApp(device, relocation_map)
else:
logger.info('No system package "%s"', package_name)
@@ -207,10 +268,7 @@ def _MoveApp(device, relocation_map):
device: (device_utils.DeviceUtils)
relocation_map: (dict) A dict that maps src to dest
"""
- movements = [
- 'mv %s %s' % (k, v)
- for k, v in relocation_map.iteritems()
- ]
+ movements = ['mv %s %s' % (k, v) for k, v in relocation_map.iteritems()]
cmd = ' && '.join(movements)
with EnableSystemAppModification(device):
device.RunShellCommand(cmd, as_root=True, check_return=True, shell=True)
@@ -224,7 +282,10 @@ def main(raw_args):
script_common.AddDeviceArguments(p)
script_common.AddEnvironmentArguments(p)
p.add_argument(
- '-v', '--verbose', action='count', default=0,
+ '-v',
+ '--verbose',
+ action='count',
+ default=0,
help='Print more information.')
p.add_argument('command', nargs='*')
@@ -235,7 +296,10 @@ def main(raw_args):
remove_parser = subparsers.add_parser('remove')
remove_parser.add_argument(
- '--package', dest='packages', nargs='*', required=True,
+ '--package',
+ dest='packages',
+ nargs='*',
+ required=True,
help='The system package(s) to remove.')
add_common_arguments(remove_parser)
remove_parser.set_defaults(func=remove_system_app)
@@ -247,10 +311,11 @@ def main(raw_args):
replace_parser = subparsers.add_parser('replace')
replace_parser.add_argument(
- '--package', required=True,
- help='The system package to replace.')
+ '--package', required=True, help='The system package to replace.')
replace_parser.add_argument(
- '--replace-with', metavar='APK', required=True,
+ '--replace-with',
+ metavar='APK',
+ required=True,
help='The APK with which the existing system app should be replaced.')
add_common_arguments(replace_parser)
replace_parser.set_defaults(func=replace_system_app)
@@ -260,7 +325,7 @@ def main(raw_args):
run_tests_helper.SetLogLevel(args.verbose)
script_common.InitializeEnvironment(args)
- devices = script_common.GetDevices(args.devices, args.blacklist_file)
+ devices = script_common.GetDevices(args.devices, args.denylist_file)
parallel_devices = parallelizer.SyncParallelizer(
[args.func(d, args) for d in devices])
with parallel_devices:
diff --git a/catapult/devil/devil/android/tools/system_app_devicetest.py b/catapult/devil/devil/android/tools/system_app_devicetest.py
index 293bad19..65faeea7 100755
--- a/catapult/devil/devil/android/tools/system_app_devicetest.py
+++ b/catapult/devil/devil/android/tools/system_app_devicetest.py
@@ -13,8 +13,8 @@ import unittest
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil import base_error
from devil import devil_env
@@ -55,9 +55,9 @@ class SystemAppDeviceTest(device_test_case.DeviceTestCase):
try:
with device_temp_file.DeviceTempFile(self._device.adb) as tmp:
self._device.adb.Push(cached_apk, tmp.name)
- self._device.RunShellCommand(
- ['mv', tmp.name, install_path],
- as_root=True, check_return=True)
+ self._device.RunShellCommand(['mv', tmp.name, install_path],
+ as_root=True,
+ check_return=True)
except base_error.BaseError:
logger.warning('Failed to reinstall %s',
os.path.basename(cached_apk))
@@ -71,11 +71,11 @@ class SystemAppDeviceTest(device_test_case.DeviceTestCase):
def _check_preconditions(self):
if not self._original_paths:
- self.skipTest('%s is not installed on %s' % (
- self.PACKAGE, str(self._device)))
+ self.skipTest(
+ '%s is not installed on %s' % (self.PACKAGE, str(self._device)))
if not any(p.startswith('/system/') for p in self._original_paths):
- self.skipTest('%s is not installed in a system location on %s' % (
- self.PACKAGE, str(self._device)))
+ self.skipTest('%s is not installed in a system location on %s' %
+ (self.PACKAGE, str(self._device)))
def testReplace(self):
self._check_preconditions()
diff --git a/catapult/devil/devil/android/tools/system_app_test.py b/catapult/devil/devil/android/tools/system_app_test.py
index 44df7ead..1f1cb36e 100644
--- a/catapult/devil/devil/android/tools/system_app_test.py
+++ b/catapult/devil/devil/android/tools/system_app_test.py
@@ -8,8 +8,9 @@ import sys
import unittest
if __name__ == '__main__':
- sys.path.append(os.path.abspath(
- os.path.join(os.path.dirname(__file__), '..', '..', '..')))
+ sys.path.append(
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil import devil_env
from devil.android import device_utils
@@ -20,19 +21,21 @@ from devil.android.tools import system_app
with devil_env.SysPath(devil_env.PYMOCK_PATH):
import mock
-
_PACKAGE_NAME = 'com.android'
_PACKAGE_PATH = '/path/to/com.android.apk'
-_PM_LIST_PACKAGES_COMMAND = ['pm', 'list', 'packages', '-f', '-u',
- _PACKAGE_NAME]
-_PM_LIST_PACKAGES_OUTPUT_WITH_PATH = ['package:/path/to/other=' + _PACKAGE_NAME
- + '.other', 'package:' + _PACKAGE_PATH +
- '=' + _PACKAGE_NAME]
-_PM_LIST_PACKAGES_OUTPUT_WITHOUT_PATH = ['package:/path/to/other=' +
- _PACKAGE_NAME + '.other']
+_PM_LIST_PACKAGES_COMMAND = [
+ 'pm', 'list', 'packages', '-f', '-u', _PACKAGE_NAME
+]
+_PM_LIST_PACKAGES_OUTPUT_WITH_PATH = [
+ 'package:/path/to/other=' + _PACKAGE_NAME + '.other',
+ 'package:' + _PACKAGE_PATH + '=' + _PACKAGE_NAME
+]
+_PM_LIST_PACKAGES_OUTPUT_WITHOUT_PATH = [
+ 'package:/path/to/other=' + _PACKAGE_NAME + '.other'
+]
-class SystemAppTest(unittest.TestCase):
+class SystemAppTest(unittest.TestCase):
def testDoubleEnableModification(self):
"""Ensures that system app modification logic isn't repeated.
@@ -79,8 +82,7 @@ class SystemAppTest(unittest.TestCase):
# pylint: disable=protected-access
mock_device = mock.Mock(spec=device_utils.DeviceUtils)
mock_device.RunShellCommand.configure_mock(
- return_value=_PM_LIST_PACKAGES_OUTPUT_WITH_PATH
- )
+ return_value=_PM_LIST_PACKAGES_OUTPUT_WITH_PATH)
paths = system_app._GetApplicationPaths(mock_device, _PACKAGE_NAME)
@@ -93,8 +95,7 @@ class SystemAppTest(unittest.TestCase):
# pylint: disable=protected-access
mock_device = mock.Mock(spec=device_utils.DeviceUtils)
mock_device.RunShellCommand.configure_mock(
- return_value=_PM_LIST_PACKAGES_OUTPUT_WITHOUT_PATH
- )
+ return_value=_PM_LIST_PACKAGES_OUTPUT_WITHOUT_PATH)
paths = system_app._GetApplicationPaths(mock_device, _PACKAGE_NAME)
@@ -106,9 +107,7 @@ class SystemAppTest(unittest.TestCase):
"""Nothing containing text of package name found in output."""
# pylint: disable=protected-access
mock_device = mock.Mock(spec=device_utils.DeviceUtils)
- mock_device.RunShellCommand.configure_mock(
- return_value=[]
- )
+ mock_device.RunShellCommand.configure_mock(return_value=[])
paths = system_app._GetApplicationPaths(mock_device, _PACKAGE_NAME)
@@ -121,8 +120,7 @@ class SystemAppTest(unittest.TestCase):
# pylint: disable=protected-access
mock_device = mock.Mock(spec=device_utils.DeviceUtils)
mock_device.RunShellCommand.configure_mock(
- return_value=_PM_LIST_PACKAGES_OUTPUT_WITH_PATH
- )
+ return_value=_PM_LIST_PACKAGES_OUTPUT_WITH_PATH)
paths = system_app._GetApplicationPaths(mock_device, '')
diff --git a/catapult/devil/devil/android/tools/unlock_bootloader.py b/catapult/devil/devil/android/tools/unlock_bootloader.py
index b38f6690..96870581 100644
--- a/catapult/devil/devil/android/tools/unlock_bootloader.py
+++ b/catapult/devil/devil/android/tools/unlock_bootloader.py
@@ -2,7 +2,6 @@
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A script to open the unlock bootloader on-screen prompt on all devices."""
import argparse
@@ -14,8 +13,8 @@ import time
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil import devil_env
from devil.android import device_errors
@@ -71,11 +70,9 @@ def unlock_bootloader(d):
cmd_old = [d._fastboot_path.read(), '-s', str(d), 'oem', 'unlock']
cmd_new = [d._fastboot_path.read(), '-s', str(d), 'flashing', 'unlock']
unlocking_processes.append(
- subprocess.Popen(
- cmd_old, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
+ subprocess.Popen(cmd_old, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
unlocking_processes.append(
- subprocess.Popen(
- cmd_new, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
+ subprocess.Popen(cmd_new, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
# Give the unlocking command time to finish and/or open the on-screen prompt.
logging.info('Sleeping for 5 seconds...')
@@ -95,8 +92,8 @@ def unlock_bootloader(d):
logging.info('Device %s is waiting for confirmation.', d)
else:
logging.error(
- 'Device %s is hanging, but not waiting for confirmation: %s',
- d, out)
+ 'Device %s is hanging, but not waiting for confirmation: %s', d,
+ out)
leftover_pids.append(p.pid)
else:
if 'unknown command' in out:
@@ -123,21 +120,22 @@ def main():
parser = argparse.ArgumentParser()
script_common.AddDeviceArguments(parser)
- parser.add_argument('--adb-path',
- help='Absolute path to the adb binary to use.')
+ parser.add_argument(
+ '--adb-path', help='Absolute path to the adb binary to use.')
args = parser.parse_args()
devil_dynamic_config = devil_env.EmptyConfig()
if args.adb_path:
devil_dynamic_config['dependencies'].update(
- devil_env.LocalConfigItem(
- 'adb', devil_env.GetPlatform(), args.adb_path))
+ devil_env.LocalConfigItem('adb', devil_env.GetPlatform(),
+ args.adb_path))
devil_env.config.Initialize(configs=[devil_dynamic_config])
reboot_into_bootloader(args.devices)
devices = [
- d for d in fastboot.Fastboot.Devices() if not args.devices or
- str(d) in args.devices]
+ d for d in fastboot.Fastboot.Devices()
+ if not args.devices or str(d) in args.devices
+ ]
parallel_devices = parallelizer.Parallelizer(devices)
parallel_devices.pMap(unlock_bootloader).pGet(None)
return 0
diff --git a/catapult/devil/devil/android/tools/video_recorder.py b/catapult/devil/devil/android/tools/video_recorder.py
index 08432640..984931f3 100755
--- a/catapult/devil/devil/android/tools/video_recorder.py
+++ b/catapult/devil/devil/android/tools/video_recorder.py
@@ -2,7 +2,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Captures a video from an Android device."""
import argparse
@@ -13,8 +12,9 @@ import time
import sys
if __name__ == '__main__':
- sys.path.append(os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..', '..')))
+ sys.path.append(
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import device_signal
from devil.android import device_utils
from devil.android.tools import script_common
@@ -28,8 +28,7 @@ logger = logging.getLogger(__name__)
class VideoRecorder(object):
"""Records a screen capture video from an Android Device (KitKat or newer)."""
- def __init__(self, device, megabits_per_second=4, size=None,
- rotate=False):
+ def __init__(self, device, megabits_per_second=4, size=None, rotate=False):
"""Creates a VideoRecorder instance.
Args:
@@ -44,7 +43,7 @@ class VideoRecorder(object):
self._bit_rate = megabits_per_second * 1000 * 1000
self._device = device
self._device_file = (
- '%s/screen-recording.mp4' % device.GetExternalStoragePath())
+ '%s/screen-recording.mp4' % device.GetAppWritablePath())
self._recorder_thread = None
self._rotate = rotate
self._size = size
@@ -55,6 +54,7 @@ class VideoRecorder(object):
def Start(self, timeout=None):
"""Start recording video."""
+
def screenrecord_started():
return bool(self._device.GetPids('screenrecord'))
@@ -85,8 +85,8 @@ class VideoRecorder(object):
def Stop(self):
"""Stop recording video."""
- if not self._device.KillAll('screenrecord', signum=device_signal.SIGINT,
- quiet=True):
+ if not self._device.KillAll(
+ 'screenrecord', signum=device_signal.SIGINT, quiet=True):
logger.warning('Nothing to kill: screenrecord was not running')
self._recorder_thread.join()
@@ -100,11 +100,8 @@ class VideoRecorder(object):
"""
# TODO(jbudorick): Merge filename generation with the logic for doing so in
# DeviceUtils.
- host_file_name = (
- host_file
- or 'screen-recording-%s-%s.mp4' % (
- str(self._device),
- time.strftime('%Y%m%dT%H%M%S', time.localtime())))
+ host_file_name = (host_file or 'screen-recording-%s-%s.mp4' % (str(
+ self._device), time.strftime('%Y%m%dT%H%M%S', time.localtime())))
host_file_name = os.path.abspath(host_file_name)
self._device.PullFile(self._device_file, host_file_name)
self._device.RemovePath(self._device_file, force=True)
@@ -114,24 +111,34 @@ class VideoRecorder(object):
def main():
# Parse options.
parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('-d', '--device', dest='devices', action='append',
- help='Serial number of Android device to use.')
- parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
- parser.add_argument('-f', '--file', metavar='FILE',
- help='Save result to file instead of generating a '
- 'timestamped file name.')
- parser.add_argument('-v', '--verbose', action='store_true',
- help='Verbose logging.')
- parser.add_argument('-b', '--bitrate', default=4, type=float,
- help='Bitrate in megabits/s, from 0.1 to 100 mbps, '
- '%(default)d mbps by default.')
- parser.add_argument('-r', '--rotate', action='store_true',
- help='Rotate video by 90 degrees.')
- parser.add_argument('-s', '--size', metavar='WIDTHxHEIGHT',
- help='Frame size to use instead of the device '
- 'screen size.')
- parser.add_argument('host_file', nargs='?',
- help='File to which the video capture will be written.')
+ script_common.AddDeviceArguments(parser)
+ parser.add_argument(
+ '-f',
+ '--file',
+ metavar='FILE',
+ help='Save result to file instead of generating a '
+ 'timestamped file name.')
+ parser.add_argument(
+ '-v', '--verbose', action='store_true', help='Verbose logging.')
+ parser.add_argument(
+ '-b',
+ '--bitrate',
+ default=4,
+ type=float,
+ help='Bitrate in megabits/s, from 0.1 to 100 mbps, '
+ '%(default)d mbps by default.')
+ parser.add_argument(
+ '-r', '--rotate', action='store_true', help='Rotate video by 90 degrees.')
+ parser.add_argument(
+ '-s',
+ '--size',
+ metavar='WIDTHxHEIGHT',
+ help='Frame size to use instead of the device '
+ 'screen size.')
+ parser.add_argument(
+ 'host_file',
+ nargs='?',
+ help='File to which the video capture will be written.')
args = parser.parse_args()
@@ -140,9 +147,7 @@ def main():
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
- size = (tuple(int(i) for i in args.size.split('x'))
- if args.size
- else None)
+ size = (tuple(int(i) for i in args.size.split('x')) if args.size else None)
def record_video(device, stop_recording):
recorder = VideoRecorder(
@@ -157,9 +162,9 @@ def main():
f = recorder.Pull(f)
print 'Video written to %s' % os.path.abspath(f)
- parallel_devices = device_utils.DeviceUtils.parallel(
- script_common.GetDevices(args.devices, args.blacklist_file),
- async=True)
+ parallel_devices = device_utils.DeviceUtils.parallel(script_common.GetDevices(
+ args.devices, args.denylist_file),
+ async=True)
stop_recording = threading.Event()
running_recording = parallel_devices.pMap(record_video, stop_recording)
print 'Recording. Press Enter to stop.',
diff --git a/catapult/devil/devil/android/tools/wait_for_devices.py b/catapult/devil/devil/android/tools/wait_for_devices.py
index bc733355..062f788f 100755
--- a/catapult/devil/devil/android/tools/wait_for_devices.py
+++ b/catapult/devil/devil/android/tools/wait_for_devices.py
@@ -2,7 +2,6 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Waits for the given devices to be available."""
import argparse
@@ -11,8 +10,8 @@ import sys
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
from devil.android import device_utils
from devil.android.tools import script_common
@@ -22,11 +21,18 @@ from devil.utils import run_tests_helper
def main(raw_args):
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='count', help='Log more.')
- parser.add_argument('-t', '--timeout', default=30, type=int,
- help='Seconds to wait for the devices.')
+ parser.add_argument(
+ '-t',
+ '--timeout',
+ default=30,
+ type=int,
+ help='Seconds to wait for the devices.')
parser.add_argument('--adb-path', help='ADB binary to use.')
- parser.add_argument('device_serials', nargs='*', metavar='SERIAL',
- help='Serials of the devices to wait for.')
+ parser.add_argument(
+ 'device_serials',
+ nargs='*',
+ metavar='SERIAL',
+ help='Serials of the devices to wait for.')
args = parser.parse_args(raw_args)
diff --git a/catapult/devil/devil/android/tools/webview_app.py b/catapult/devil/devil/android/tools/webview_app.py
index 36b70391..406de1cb 100755
--- a/catapult/devil/devil/android/tools/webview_app.py
+++ b/catapult/devil/devil/android/tools/webview_app.py
@@ -2,7 +2,6 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A script to use a package as the WebView provider while running a command."""
import argparse
@@ -12,14 +11,15 @@ import os
import re
import sys
-
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..')))
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..', '..', '..', 'common', 'py_utils')))
-
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..')))
+ sys.path.append(
+ os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__), '..', '..', '..', '..', 'common',
+ 'py_utils')))
from devil.android import apk_helper
from devil.android import device_errors
@@ -33,9 +33,10 @@ from py_utils import tempfile_ext
logger = logging.getLogger(__name__)
-_SYSTEM_PATH_RE = re.compile(r'^\s*\/system\/')
+_SYSTEM_PATH_RE = re.compile(r'^\s*\/(system|product)\/')
_WEBVIEW_INSTALL_TIMEOUT = 300
+
@contextlib.contextmanager
def UseWebViewProvider(device, apk, expected_package=''):
"""A context manager that uses the apk as the webview provider while in scope.
@@ -55,8 +56,9 @@ def UseWebViewProvider(device, apk, expected_package=''):
'WebView Provider package %s does not match expected %s' %
(package_name, expected_package), str(device))
- if (device.build_version_sdk in
- [version_codes.NOUGAT, version_codes.NOUGAT_MR1]):
+ if (device.build_version_sdk in [
+ version_codes.NOUGAT, version_codes.NOUGAT_MR1
+ ]):
logger.warning('Due to webviewupdate bug in Nougat, WebView Fallback Logic '
'will be disabled and WebView provider may be changed after '
'exit of UseWebViewProvider context manager scope.')
@@ -84,9 +86,7 @@ def UseWebViewProvider(device, apk, expected_package=''):
elif system_paths:
# app is system app, use ReplaceSystemApp to install
with system_app.ReplaceSystemApp(
- device,
- package_name,
- apk,
+ device, package_name, apk,
install_timeout=_WEBVIEW_INSTALL_TIMEOUT):
_SetWebViewProvider(device, package_name)
yield
@@ -138,15 +138,15 @@ def _UninstallNonSystemApp(device, package_name):
for user_path in user_paths:
host_path = _RebasePath(temp_dir, user_path)
# PullFile takes care of host_path creation if needed.
- device.PullFile(user_path, host_path)
+ device.PullFile(user_path, host_path, timeout=_WEBVIEW_INSTALL_TIMEOUT)
host_paths.append(host_path)
device.Uninstall(package_name)
try:
yield
finally:
for host_path in reversed(host_paths):
- device.Install(host_path, reinstall=True,
- timeout=_WEBVIEW_INSTALL_TIMEOUT)
+ device.Install(
+ host_path, reinstall=True, timeout=_WEBVIEW_INSTALL_TIMEOUT)
else:
yield
@@ -169,7 +169,10 @@ def main(raw_args):
script_common.AddDeviceArguments(p)
script_common.AddEnvironmentArguments(p)
p.add_argument(
- '-v', '--verbose', action='count', default=0,
+ '-v',
+ '--verbose',
+ action='count',
+ default=0,
help='Print more information.')
p.add_argument('command', nargs='*')
@@ -179,10 +182,10 @@ def main(raw_args):
yield
parser.add_argument(
- '--apk', required=True,
- help='The apk to use as the provider.')
+ '--apk', required=True, help='The apk to use as the provider.')
parser.add_argument(
- '--expected-package', default='',
+ '--expected-package',
+ default='',
help="Verify apk's package name matches value, disabled by default.")
add_common_arguments(parser)
parser.set_defaults(func=use_webview_provider)
@@ -192,7 +195,7 @@ def main(raw_args):
run_tests_helper.SetLogLevel(args.verbose)
script_common.InitializeEnvironment(args)
- devices = script_common.GetDevices(args.devices, args.blacklist_file)
+ devices = script_common.GetDevices(args.devices, args.denylist_file)
parallel_devices = parallelizer.SyncParallelizer(
[args.func(d, args) for d in devices])
with parallel_devices:
diff --git a/catapult/devil/devil/android/valgrind_tools/base_tool.py b/catapult/devil/devil/android/valgrind_tools/base_tool.py
index 2e6e9af3..b8445fcc 100644
--- a/catapult/devil/devil/android/valgrind_tools/base_tool.py
+++ b/catapult/devil/devil/android/valgrind_tools/base_tool.py
@@ -5,6 +5,7 @@
class BaseTool(object):
"""A tool that does nothing."""
+
# pylint: disable=R0201
def __init__(self):
diff --git a/catapult/devil/devil/base_error.py b/catapult/devil/devil/base_error.py
index 4b896613..b7b33ccf 100644
--- a/catapult/devil/devil/base_error.py
+++ b/catapult/devil/devil/base_error.py
@@ -21,4 +21,3 @@ class BaseError(Exception):
def is_infra_error(self):
"""Property to indicate if error was caused by an infrastructure issue."""
return self._is_infra_error
-
diff --git a/catapult/devil/devil/constants/exit_codes.py b/catapult/devil/devil/constants/exit_codes.py
index aaeca4a8..a2deac69 100644
--- a/catapult/devil/devil/constants/exit_codes.py
+++ b/catapult/devil/devil/constants/exit_codes.py
@@ -1,7 +1,6 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Common exit codes used by devil."""
ERROR = 1
diff --git a/catapult/devil/devil/devil_dependencies.json b/catapult/devil/devil/devil_dependencies.json
index 8b397882..8e15e357 100644
--- a/catapult/devil/devil/devil_dependencies.json
+++ b/catapult/devil/devil/devil_dependencies.json
@@ -31,12 +31,22 @@
}
}
},
+ "bundletool": {
+ "cloud_storage_base_folder": "binary_dependencies",
+ "cloud_storage_bucket": "chromium-telemetry",
+ "file_info": {
+ "default": {
+ "cloud_storage_hash": "721525531f3c3ec66b39afd74c2a05e6d3973eef",
+ "download_path": "../bin/deps/default/lib.java/bundletool-all-0.10.3.jar"
+ }
+ }
+ },
"chromium_commands": {
"cloud_storage_base_folder": "binary_dependencies",
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "4e22f641e4757309510e8d9f933f5aa504574ab6",
+ "cloud_storage_hash": "6efb1501c7c49e6ae84cc467648930eaf9f6a9e5",
"download_path": "../bin/deps/linux2/x86_64/lib.java/chromium_commands.dex.jar"
}
}
@@ -70,7 +80,7 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "db9728166f182800eb9d09e9f036d56e105e8235",
+ "cloud_storage_hash": "fe84a6714192f6596bf09215caeea34ed937a42b",
"download_path": "../bin/deps/linux2/x86_64/bin/fastboot"
}
}
@@ -80,12 +90,20 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"android_arm64-v8a": {
- "cloud_storage_hash": "f222268d8442979240d1b18de00911a49e548daa",
+ "cloud_storage_hash": "b30e8bfb234d02899b05a7957c6a84dc6e16ec4b",
"download_path": "../bin/deps/android/arm64-v8a/bin/forwarder_device"
},
"android_armeabi-v7a": {
- "cloud_storage_hash": "c15267bf01c26eb0aea4f61c780bbba460c5c981",
+ "cloud_storage_hash": "efc7f092f5394a7b88d77a2544adfe33c5c4fd3c",
"download_path": "../bin/deps/android/armeabi-v7a/bin/forwarder_device"
+ },
+ "android_x86": {
+ "cloud_storage_hash": "ec841e99335f8c9bbcf2e3da40dd8a7ebefe2b6e",
+ "download_path": "../bin/deps/android/x86/bin/forwarder_device"
+ },
+ "android_x86_64": {
+ "cloud_storage_hash": "b9921146385bb7c3fc7246084c37a175e7bb97bd",
+ "download_path": "../bin/deps/android/x86_64/bin/forwarder_device"
}
}
},
@@ -94,7 +112,7 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "8fe69994b670f028484eed475dbffc838c8a57f7",
+ "cloud_storage_hash": "334c172dd90bae568cbff9465130f1245a35cade",
"download_path": "../bin/deps/linux2/x86_64/forwarder_host"
}
}
@@ -104,16 +122,20 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"android_arm64-v8a": {
- "cloud_storage_hash": "4e7d2dedd9c6321fdc152b06869e09a3c5817904",
+ "cloud_storage_hash": "e9e1a4e2f1c24180a5cac496c3c50213384b51eb",
"download_path": "../bin/deps/android/arm64-v8a/bin/md5sum_device"
},
"android_armeabi-v7a": {
- "cloud_storage_hash": "39fd90af0f8828202b687f7128393759181c5e2e",
+ "cloud_storage_hash": "f0cf40aec71df24d50a2644bed199ffd7588082d",
"download_path": "../bin/deps/android/armeabi-v7a/bin/md5sum_device"
},
"android_x86": {
- "cloud_storage_hash": "d5cf42ab5986a69c31c0177b0df499d6bf708df6",
+ "cloud_storage_hash": "7530812af4b3cd2f20d9ce27e3615828cb4e4fc2",
"download_path": "../bin/deps/android/x86/bin/md5sum_device"
+ },
+ "android_x86_64": {
+ "cloud_storage_hash": "4cd36953c44119578568a723854d43b8ed124d7e",
+ "download_path": "../bin/deps/android/x86_64/bin/md5sum_device"
}
}
},
@@ -122,7 +144,7 @@
"cloud_storage_bucket": "chromium-telemetry",
"file_info": {
"linux2_x86_64": {
- "cloud_storage_hash": "4db5bd5e9bea8880d8bf2caa59d0efb0acc19f74",
+ "cloud_storage_hash": "f2428a52730c6593267678c75f95cdc7dffb58df",
"download_path": "../bin/deps/linux2/x86_64/bin/md5sum_host"
}
}
diff --git a/catapult/devil/devil/devil_env.py b/catapult/devil/devil/devil_env.py
index aa4fe1ee..b39e01c8 100644
--- a/catapult/devil/devil/devil_env.py
+++ b/catapult/devil/devil/devil_env.py
@@ -11,12 +11,10 @@ import sys
import tempfile
import threading
-CATAPULT_ROOT_PATH = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..'))
-DEPENDENCY_MANAGER_PATH = os.path.join(
- CATAPULT_ROOT_PATH, 'dependency_manager')
-PYMOCK_PATH = os.path.join(
- CATAPULT_ROOT_PATH, 'third_party', 'mock')
+CATAPULT_ROOT_PATH = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
+DEPENDENCY_MANAGER_PATH = os.path.join(CATAPULT_ROOT_PATH, 'dependency_manager')
+PYMOCK_PATH = os.path.join(CATAPULT_ROOT_PATH, 'third_party', 'mock')
@contextlib.contextmanager
@@ -28,52 +26,49 @@ def SysPath(path):
else:
sys.path.pop()
+
with SysPath(DEPENDENCY_MANAGER_PATH):
import dependency_manager # pylint: disable=import-error
_ANDROID_BUILD_TOOLS = {'aapt', 'dexdump', 'split-select'}
-_DEVIL_DEFAULT_CONFIG = os.path.abspath(os.path.join(
- os.path.dirname(__file__), 'devil_dependencies.json'))
+_DEVIL_DEFAULT_CONFIG = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), 'devil_dependencies.json'))
_LEGACY_ENVIRONMENT_VARIABLES = {
- 'ADB_PATH': {
- 'dependency_name': 'adb',
- 'platform': 'linux2_x86_64',
- },
- 'ANDROID_SDK_ROOT': {
- 'dependency_name': 'android_sdk',
- 'platform': 'linux2_x86_64',
- },
+ 'ADB_PATH': {
+ 'dependency_name': 'adb',
+ 'platform': 'linux2_x86_64',
+ },
+ 'ANDROID_SDK_ROOT': {
+ 'dependency_name': 'android_sdk',
+ 'platform': 'linux2_x86_64',
+ },
}
def EmptyConfig():
- return {
- 'config_type': 'BaseConfig',
- 'dependencies': {}
- }
+ return {'config_type': 'BaseConfig', 'dependencies': {}}
def LocalConfigItem(dependency_name, dependency_platform, dependency_path):
if isinstance(dependency_path, basestring):
dependency_path = [dependency_path]
return {
- dependency_name: {
- 'file_info': {
- dependency_platform: {
- 'local_paths': dependency_path
- },
+ dependency_name: {
+ 'file_info': {
+ dependency_platform: {
+ 'local_paths': dependency_path
+ },
+ },
},
- },
}
def _GetEnvironmentVariableConfig():
env_config = EmptyConfig()
- path_config = (
- (os.environ.get(k), v)
- for k, v in _LEGACY_ENVIRONMENT_VARIABLES.iteritems())
+ path_config = ((os.environ.get(k), v)
+ for k, v in _LEGACY_ENVIRONMENT_VARIABLES.iteritems())
path_config = ((p, c) for p, c in path_config if p)
for p, c in path_config:
env_config['dependencies'].update(
@@ -82,7 +77,6 @@ def _GetEnvironmentVariableConfig():
class _Environment(object):
-
def __init__(self):
self._dm_init_lock = threading.Lock()
self._dm = None
@@ -111,9 +105,7 @@ class _Environment(object):
env_config = _GetEnvironmentVariableConfig()
if env_config:
configs.insert(0, env_config)
- self._InitializeRecursive(
- configs=configs,
- config_files=config_files)
+ self._InitializeRecursive(configs=configs, config_files=config_files)
assert self._dm is not None, 'Failed to create dependency manager.'
def _InitializeRecursive(self, configs=None, config_files=None):
@@ -191,4 +183,3 @@ def GetPlatform(arch=None, device=None):
config = _Environment()
-
diff --git a/catapult/devil/devil/devil_env_test.py b/catapult/devil/devil/devil_env_test.py
index e78221a0..ee7cd8fd 100755
--- a/catapult/devil/devil/devil_env_test.py
+++ b/catapult/devil/devil/devil_env_test.py
@@ -19,12 +19,10 @@ _sys_path_after = list(sys.path)
class DevilEnvTest(unittest.TestCase):
-
def testSysPath(self):
self.assertEquals(_sys_path_before, _sys_path_after)
- self.assertEquals(
- _sys_path_before + [devil_env.PYMOCK_PATH],
- _sys_path_with_pymock)
+ self.assertEquals(_sys_path_before + [devil_env.PYMOCK_PATH],
+ _sys_path_with_pymock)
def testGetEnvironmentVariableConfig_configType(self):
with mock.patch('os.environ.get',
@@ -42,20 +40,17 @@ class DevilEnvTest(unittest.TestCase):
def mock_environment(env_var):
return '/my/fake/adb/path' if env_var == 'ADB_PATH' else None
- with mock.patch('os.environ.get',
- mock.Mock(side_effect=mock_environment)):
+ with mock.patch('os.environ.get', mock.Mock(side_effect=mock_environment)):
env_config = devil_env._GetEnvironmentVariableConfig()
- self.assertEquals(
- {
- 'adb': {
+ self.assertEquals({
+ 'adb': {
'file_info': {
- 'linux2_x86_64': {
- 'local_paths': ['/my/fake/adb/path'],
- },
+ 'linux2_x86_64': {
+ 'local_paths': ['/my/fake/adb/path'],
+ },
},
- },
},
- env_config.get('dependencies'))
+ }, env_config.get('dependencies'))
if __name__ == '__main__':
diff --git a/catapult/devil/devil/utils/cmd_helper.py b/catapult/devil/devil/utils/cmd_helper.py
index 3c4a06ed..634c9716 100644
--- a/catapult/devil/devil/utils/cmd_helper.py
+++ b/catapult/devil/devil/utils/cmd_helper.py
@@ -1,7 +1,6 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A wrapper for subprocess to make calling shell commands easier."""
import codecs
@@ -86,6 +85,7 @@ def ShrinkToSnippet(cmd_parts, var_name, var_value):
Returns:
A shell snippet that does not include setting the variable.
"""
+
def shrink(value):
parts = (x and SingleQuote(x) for x in value.split(var_value))
with_substitutions = ('"$%s"' % var_name).join(parts)
@@ -94,23 +94,36 @@ def ShrinkToSnippet(cmd_parts, var_name, var_value):
return ' '.join(shrink(part) for part in cmd_parts)
-def Popen(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
+def Popen(args,
+ stdin=None,
+ stdout=None,
+ stderr=None,
+ shell=None,
+ cwd=None,
+ env=None):
# preexec_fn isn't supported on windows.
if sys.platform == 'win32':
- close_fds = (stdout is None and stderr is None)
+ close_fds = (stdin is None and stdout is None and stderr is None)
preexec_fn = None
else:
close_fds = True
preexec_fn = lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)
return subprocess.Popen(
- args=args, cwd=cwd, stdout=stdout, stderr=stderr,
- shell=shell, close_fds=close_fds, env=env, preexec_fn=preexec_fn)
+ args=args,
+ cwd=cwd,
+ stdin=stdin,
+ stdout=stdout,
+ stderr=stderr,
+ shell=shell,
+ close_fds=close_fds,
+ env=env,
+ preexec_fn=preexec_fn)
def Call(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
- pipe = Popen(args, stdout=stdout, stderr=stderr, shell=shell, cwd=cwd,
- env=env)
+ pipe = Popen(
+ args, stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, env=env)
pipe.communicate()
return pipe.wait()
@@ -127,7 +140,7 @@ def RunCmd(args, cwd=None):
Returns:
Return code from the command execution.
"""
- logger.info(str(args) + ' ' + (cwd or ''))
+ logger.debug(str(args) + ' ' + (cwd or ''))
return Call(args, cwd=cwd)
@@ -167,7 +180,11 @@ def _ValidateAndLogCommand(args, cwd, shell):
return args
-def GetCmdStatusAndOutput(args, cwd=None, shell=False, env=None):
+def GetCmdStatusAndOutput(args,
+ cwd=None,
+ shell=False,
+ env=None,
+ merge_stderr=False):
"""Executes a subprocess and returns its exit code and output.
Args:
@@ -179,12 +196,13 @@ def GetCmdStatusAndOutput(args, cwd=None, shell=False, env=None):
is a string and False if args is a sequence.
env: If not None, a mapping that defines environment variables for the
subprocess.
+ merge_stderr: If True, captures stderr as part of stdout.
Returns:
The 2-tuple (exit code, stdout).
"""
status, stdout, stderr = GetCmdStatusOutputAndError(
- args, cwd=cwd, shell=shell, env=env)
+ args, cwd=cwd, shell=shell, env=env, merge_stderr=merge_stderr)
if stderr:
logger.critical('STDERR: %s', stderr)
@@ -210,11 +228,20 @@ def StartCmd(args, cwd=None, shell=False, env=None):
A process handle from subprocess.Popen.
"""
_ValidateAndLogCommand(args, cwd, shell)
- return Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- shell=shell, cwd=cwd, env=env)
-
-
-def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
+ return Popen(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=shell,
+ cwd=cwd,
+ env=env)
+
+
+def GetCmdStatusOutputAndError(args,
+ cwd=None,
+ shell=False,
+ env=None,
+ merge_stderr=False):
"""Executes a subprocess and returns its exit code, output, and errors.
Args:
@@ -226,13 +253,20 @@ def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
is a string and False if args is a sequence.
env: If not None, a mapping that defines environment variables for the
subprocess.
+ merge_stderr: If True, captures stderr as part of stdout.
Returns:
The 3-tuple (exit code, stdout, stderr).
"""
_ValidateAndLogCommand(args, cwd, shell)
- pipe = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- shell=shell, cwd=cwd, env=env)
+ stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
+ pipe = Popen(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=stderr,
+ shell=shell,
+ cwd=cwd,
+ env=env)
stdout, stderr = pipe.communicate()
return (pipe.returncode, stdout, stderr)
@@ -249,9 +283,11 @@ class TimeoutError(base_error.BaseError):
return self._output
-def _IterProcessStdoutFcntl(
- process, iter_timeout=None, timeout=None, buffer_size=4096,
- poll_interval=1):
+def _IterProcessStdoutFcntl(process,
+ iter_timeout=None,
+ timeout=None,
+ buffer_size=4096,
+ poll_interval=1):
"""An fcntl-based implementation of _IterProcessStdout."""
# pylint: disable=too-many-nested-blocks
import fcntl
@@ -272,14 +308,13 @@ def _IterProcessStdoutFcntl(
iter_end_time = time.time() + iter_timeout
if iter_end_time:
- iter_aware_poll_interval = min(
- poll_interval,
- max(0, iter_end_time - time.time()))
+ iter_aware_poll_interval = min(poll_interval,
+ max(0, iter_end_time - time.time()))
else:
iter_aware_poll_interval = poll_interval
- read_fds, _, _ = select.select(
- [child_fd], [], [], iter_aware_poll_interval)
+ read_fds, _, _ = select.select([child_fd], [], [],
+ iter_aware_poll_interval)
if child_fd in read_fds:
data = os.read(child_fd, buffer_size)
if not data:
@@ -290,8 +325,8 @@ def _IterProcessStdoutFcntl(
# If process is closed, keep checking for output data (because of timing
# issues).
while True:
- read_fds, _, _ = select.select(
- [child_fd], [], [], iter_aware_poll_interval)
+ read_fds, _, _ = select.select([child_fd], [], [],
+ iter_aware_poll_interval)
if child_fd in read_fds:
data = os.read(child_fd, buffer_size)
if data:
@@ -310,9 +345,11 @@ def _IterProcessStdoutFcntl(
process.wait()
-def _IterProcessStdoutQueue(
- process, iter_timeout=None, timeout=None, buffer_size=4096,
- poll_interval=1):
+def _IterProcessStdoutQueue(process,
+ iter_timeout=None,
+ timeout=None,
+ buffer_size=4096,
+ poll_interval=1):
"""A Queue.Queue-based implementation of _IterProcessStdout.
TODO(jbudorick): Evaluate whether this is a suitable replacement for
@@ -363,10 +400,8 @@ def _IterProcessStdoutQueue(
reader_thread.join()
-_IterProcessStdout = (
- _IterProcessStdoutQueue
- if sys.platform == 'win32'
- else _IterProcessStdoutFcntl)
+_IterProcessStdout = (_IterProcessStdoutQueue
+ if sys.platform == 'win32' else _IterProcessStdoutFcntl)
"""Iterate over a process's stdout.
This is intentionally not public.
@@ -390,8 +425,12 @@ Yields:
"""
-def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
- logfile=None, env=None):
+def GetCmdStatusAndOutputWithTimeout(args,
+ timeout,
+ cwd=None,
+ shell=False,
+ logfile=None,
+ env=None):
"""Executes a subprocess with a timeout.
Args:
@@ -414,8 +453,13 @@ def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
"""
_ValidateAndLogCommand(args, cwd, shell)
output = StringIO.StringIO()
- process = Popen(args, cwd=cwd, shell=shell, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT, env=env)
+ process = Popen(
+ args,
+ cwd=cwd,
+ shell=shell,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ env=env)
try:
for data in _IterProcessStdout(process, timeout=timeout):
if logfile:
@@ -430,8 +474,13 @@ def GetCmdStatusAndOutputWithTimeout(args, timeout, cwd=None, shell=False,
return process.returncode, str_output
-def IterCmdOutputLines(args, iter_timeout=None, timeout=None, cwd=None,
- shell=False, env=None, check_status=True):
+def IterCmdOutputLines(args,
+ iter_timeout=None,
+ timeout=None,
+ cwd=None,
+ shell=False,
+ env=None,
+ check_status=True):
"""Executes a subprocess and continuously yields lines from its output.
Args:
@@ -455,13 +504,25 @@ def IterCmdOutputLines(args, iter_timeout=None, timeout=None, cwd=None,
non-zero exit status.
"""
cmd = _ValidateAndLogCommand(args, cwd, shell)
- process = Popen(args, cwd=cwd, shell=shell, env=env,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ process = Popen(
+ args,
+ cwd=cwd,
+ shell=shell,
+ env=env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
return _IterCmdOutputLines(
- process, cmd, iter_timeout=iter_timeout, timeout=timeout,
+ process,
+ cmd,
+ iter_timeout=iter_timeout,
+ timeout=timeout,
check_status=check_status)
-def _IterCmdOutputLines(process, cmd, iter_timeout=None, timeout=None,
+
+def _IterCmdOutputLines(process,
+ cmd,
+ iter_timeout=None,
+ timeout=None,
check_status=True):
buffer_output = ''
@@ -471,8 +532,8 @@ def _IterCmdOutputLines(process, cmd, iter_timeout=None, timeout=None,
iter_end = time.time() + iter_timeout
cur_iter_timeout = iter_timeout
- for data in _IterProcessStdout(process, iter_timeout=cur_iter_timeout,
- timeout=timeout):
+ for data in _IterProcessStdout(
+ process, iter_timeout=cur_iter_timeout, timeout=timeout):
if iter_timeout:
# Check whether the current iteration has timed out.
cur_iter_timeout = iter_end - time.time()
diff --git a/catapult/devil/devil/utils/cmd_helper_test.py b/catapult/devil/devil/utils/cmd_helper_test.py
index 6a8e8813..57abceb4 100755
--- a/catapult/devil/devil/utils/cmd_helper_test.py
+++ b/catapult/devil/devil/utils/cmd_helper_test.py
@@ -2,7 +2,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Tests for the cmd_helper module."""
import unittest
@@ -18,14 +17,11 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
class CmdHelperSingleQuoteTest(unittest.TestCase):
-
def testSingleQuote_basic(self):
- self.assertEquals('hello',
- cmd_helper.SingleQuote('hello'))
+ self.assertEquals('hello', cmd_helper.SingleQuote('hello'))
def testSingleQuote_withSpaces(self):
- self.assertEquals("'hello world'",
- cmd_helper.SingleQuote('hello world'))
+ self.assertEquals("'hello world'", cmd_helper.SingleQuote('hello world'))
def testSingleQuote_withUnsafeChars(self):
self.assertEquals("""'hello'"'"'; rm -rf /'""",
@@ -39,14 +35,11 @@ class CmdHelperSingleQuoteTest(unittest.TestCase):
class CmdHelperDoubleQuoteTest(unittest.TestCase):
-
def testDoubleQuote_basic(self):
- self.assertEquals('hello',
- cmd_helper.DoubleQuote('hello'))
+ self.assertEquals('hello', cmd_helper.DoubleQuote('hello'))
def testDoubleQuote_withSpaces(self):
- self.assertEquals('"hello world"',
- cmd_helper.DoubleQuote('hello world'))
+ self.assertEquals('"hello world"', cmd_helper.DoubleQuote('hello world'))
def testDoubleQuote_withUnsafeChars(self):
self.assertEquals('''"hello\\"; rm -rf /"''',
@@ -60,33 +53,34 @@ class CmdHelperDoubleQuoteTest(unittest.TestCase):
class CmdHelperShinkToSnippetTest(unittest.TestCase):
-
def testShrinkToSnippet_noArgs(self):
- self.assertEquals('foo',
- cmd_helper.ShrinkToSnippet(['foo'], 'a', 'bar'))
+ self.assertEquals('foo', cmd_helper.ShrinkToSnippet(['foo'], 'a', 'bar'))
self.assertEquals("'foo foo'",
- cmd_helper.ShrinkToSnippet(['foo foo'], 'a', 'bar'))
+ cmd_helper.ShrinkToSnippet(['foo foo'], 'a', 'bar'))
self.assertEquals('"$a"\' bar\'',
- cmd_helper.ShrinkToSnippet(['foo bar'], 'a', 'foo'))
+ cmd_helper.ShrinkToSnippet(['foo bar'], 'a', 'foo'))
self.assertEquals('\'foo \'"$a"',
- cmd_helper.ShrinkToSnippet(['foo bar'], 'a', 'bar'))
+ cmd_helper.ShrinkToSnippet(['foo bar'], 'a', 'bar'))
self.assertEquals('foo"$a"',
- cmd_helper.ShrinkToSnippet(['foobar'], 'a', 'bar'))
+ cmd_helper.ShrinkToSnippet(['foobar'], 'a', 'bar'))
def testShrinkToSnippet_singleArg(self):
self.assertEquals("foo ''",
- cmd_helper.ShrinkToSnippet(['foo', ''], 'a', 'bar'))
+ cmd_helper.ShrinkToSnippet(['foo', ''], 'a', 'bar'))
self.assertEquals("foo foo",
- cmd_helper.ShrinkToSnippet(['foo', 'foo'], 'a', 'bar'))
+ cmd_helper.ShrinkToSnippet(['foo', 'foo'], 'a', 'bar'))
self.assertEquals('"$a" "$a"',
- cmd_helper.ShrinkToSnippet(['foo', 'foo'], 'a', 'foo'))
+ cmd_helper.ShrinkToSnippet(['foo', 'foo'], 'a', 'foo'))
self.assertEquals('foo "$a""$a"',
- cmd_helper.ShrinkToSnippet(['foo', 'barbar'], 'a', 'bar'))
- self.assertEquals('foo "$a"\' \'"$a"',
+ cmd_helper.ShrinkToSnippet(['foo', 'barbar'], 'a', 'bar'))
+ self.assertEquals(
+ 'foo "$a"\' \'"$a"',
cmd_helper.ShrinkToSnippet(['foo', 'bar bar'], 'a', 'bar'))
- self.assertEquals('foo "$a""$a"\' \'',
+ self.assertEquals(
+ 'foo "$a""$a"\' \'',
cmd_helper.ShrinkToSnippet(['foo', 'barbar '], 'a', 'bar'))
- self.assertEquals('foo \' \'"$a""$a"\' \'',
+ self.assertEquals(
+ 'foo \' \'"$a""$a"\' \'',
cmd_helper.ShrinkToSnippet(['foo', ' barbar '], 'a', 'bar'))
@@ -94,7 +88,6 @@ _DEFAULT = 'DEFAULT'
class _ProcessOutputEvent(object):
-
def __init__(self, select_fds=_DEFAULT, read_contents=None, ts=_DEFAULT):
self.select_fds = select_fds
self.read_contents = read_contents
@@ -102,7 +95,6 @@ class _ProcessOutputEvent(object):
class _MockProcess(object):
-
def __init__(self, output_sequence=None, return_value=0):
# Arbitrary.
@@ -130,8 +122,7 @@ class _MockProcess(object):
# Use an leading element to make the iteration logic work.
initial_seq_element = _ProcessOutputEvent(
- _DEFAULT, '',
- output_sequence[0].ts if output_sequence else _DEFAULT)
+ _DEFAULT, '', output_sequence[0].ts if output_sequence else _DEFAULT)
output_sequence.insert(0, initial_seq_element)
for o in output_sequence:
@@ -159,10 +150,10 @@ class _MockProcess(object):
else:
self._output_seq_index += 1
if self._output_seq_index < len(self._output_sequence):
- return (self._output_sequence[self._output_seq_index].select_fds,
- None, None)
+ return (self._output_sequence[self._output_seq_index].select_fds, None,
+ None)
else:
- return([], None, None)
+ return ([], None, None)
def time_side_effect(*_args, **_kwargs):
return self._output_sequence[self._output_seq_index].ts
@@ -179,9 +170,9 @@ class _MockProcess(object):
# Set up but *do not start* the mocks.
self._mocks = [
- mock.patch('os.read', new=mock_read),
- mock.patch('select.select', new=mock_select),
- mock.patch('time.time', new=mock_time),
+ mock.patch('os.read', new=mock_read),
+ mock.patch('select.select', new=mock_select),
+ mock.patch('time.time', new=mock_time),
]
if sys.platform != 'win32':
self._mocks.append(mock.patch('fcntl.fcntl'))
@@ -204,7 +195,7 @@ class CmdHelperIterCmdOutputLinesTest(unittest.TestCase):
# pylint: disable=protected-access
_SIMPLE_OUTPUT_SEQUENCE = [
- _ProcessOutputEvent(read_contents='1\n2\n'),
+ _ProcessOutputEvent(read_contents='1\n2\n'),
]
def testIterCmdOutputLines_success(self):
@@ -216,25 +207,27 @@ class CmdHelperIterCmdOutputLinesTest(unittest.TestCase):
def testIterCmdOutputLines_exitStatusFail(self):
with self.assertRaises(subprocess.CalledProcessError):
- with _MockProcess(output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
- return_value=1) as mock_proc:
+ with _MockProcess(
+ output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
+ return_value=1) as mock_proc:
for num, line in enumerate(
cmd_helper._IterCmdOutputLines(mock_proc, 'mock_proc'), 1):
self.assertEquals(num, int(line))
# after reading all the output we get an exit status of 1
def testIterCmdOutputLines_exitStatusIgnored(self):
- with _MockProcess(output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
- return_value=1) as mock_proc:
+ with _MockProcess(
+ output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
+ return_value=1) as mock_proc:
for num, line in enumerate(
cmd_helper._IterCmdOutputLines(
- mock_proc, 'mock_proc', check_status=False),
- 1):
+ mock_proc, 'mock_proc', check_status=False), 1):
self.assertEquals(num, int(line))
def testIterCmdOutputLines_exitStatusSkipped(self):
- with _MockProcess(output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
- return_value=1) as mock_proc:
+ with _MockProcess(
+ output_sequence=self._SIMPLE_OUTPUT_SEQUENCE,
+ return_value=1) as mock_proc:
for num, line in enumerate(
cmd_helper._IterCmdOutputLines(mock_proc, 'mock_proc'), 1):
self.assertEquals(num, int(line))
@@ -245,14 +238,14 @@ class CmdHelperIterCmdOutputLinesTest(unittest.TestCase):
def testIterCmdOutputLines_delay(self):
output_sequence = [
- _ProcessOutputEvent(read_contents='1\n2\n', ts=1),
- _ProcessOutputEvent(read_contents=None, ts=2),
- _ProcessOutputEvent(read_contents='Awake', ts=10),
+ _ProcessOutputEvent(read_contents='1\n2\n', ts=1),
+ _ProcessOutputEvent(read_contents=None, ts=2),
+ _ProcessOutputEvent(read_contents='Awake', ts=10),
]
with _MockProcess(output_sequence=output_sequence) as mock_proc:
for num, line in enumerate(
- cmd_helper._IterCmdOutputLines(mock_proc, 'mock_proc',
- iter_timeout=5), 1):
+ cmd_helper._IterCmdOutputLines(
+ mock_proc, 'mock_proc', iter_timeout=5), 1):
if num <= 2:
self.assertEquals(num, int(line))
elif num == 3:
diff --git a/catapult/devil/devil/utils/file_utils.py b/catapult/devil/devil/utils/file_utils.py
index dc5a9efc..6bc9e95d 100644
--- a/catapult/devil/devil/utils/file_utils.py
+++ b/catapult/devil/devil/utils/file_utils.py
@@ -27,5 +27,3 @@ def MergeFiles(dest_file, source_files):
except OSError:
pass
raise e
-
-
diff --git a/catapult/devil/devil/utils/find_usb_devices.py b/catapult/devil/devil/utils/find_usb_devices.py
index b6dcc702..eb45a6ce 100755
--- a/catapult/devil/devil/utils/find_usb_devices.py
+++ b/catapult/devil/devil/utils/find_usb_devices.py
@@ -11,8 +11,7 @@ import sys
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..')))
+ os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
from devil.utils import cmd_helper
from devil.utils import usb_hubs
@@ -267,13 +266,13 @@ def GetBusNumberToDeviceTreeMap(fast=True):
for line in lsusb.raw_lsusb().splitlines():
match = _LSUSB_BUS_DEVICE_RE.match(line)
if match:
- info_map[(int(match.group(1)), int(match.group(2)))] = (
- {'desc':match.group(3)})
+ info_map[(int(match.group(1)), int(match.group(2)))] = ({
+ 'desc': match.group(3)
+ })
else:
info_map = {((int(line['bus']), int(line['device']))): line
for line in _GetParsedLSUSBOutput()}
-
tree = {}
bus_num = -1
for line in _GetUSBDevicesOutput().splitlines():
@@ -290,10 +289,10 @@ def GetBusNumberToDeviceTreeMap(fast=True):
tree[bus_num] = USBBusNode(bus_num=bus_num)
# create the new device
- new_device = USBDeviceNode(bus_num=bus_num,
- device_num=device_num,
- info=info_map.get((bus_num, device_num),
- {'desc': 'NOT AVAILABLE'}))
+ new_device = USBDeviceNode(
+ bus_num=bus_num,
+ device_num=device_num,
+ info=info_map.get((bus_num, device_num), {'desc': 'NOT AVAILABLE'}))
# add device to bus
if parent_num != 0:
@@ -351,8 +350,10 @@ def GetPhysicalPortToBusDeviceMap(hub, hub_type):
Dict of {physical port: (bus number, device number)}
"""
port_device = hub_type.GetPhysicalPortToNodeTuples(hub)
- return {port: (device.bus_num, device.device_num)
- for (port, device) in port_device}
+ return {
+ port: (device.bus_num, device.device_num)
+ for (port, device) in port_device
+ }
def GetPhysicalPortToSerialMap(hub, hub_type):
@@ -366,9 +367,10 @@ def GetPhysicalPortToSerialMap(hub, hub_type):
Dict of {physical port: serial number)}
"""
port_device = hub_type.GetPhysicalPortToNodeTuples(hub)
- return {port: device.serial
- for (port, device) in port_device
- if device.serial}
+ return {
+ port: device.serial
+ for (port, device) in port_device if device.serial
+ }
def GetPhysicalPortToTTYMap(device, hub_type):
@@ -382,9 +384,11 @@ def GetPhysicalPortToTTYMap(device, hub_type):
"""
port_device = hub_type.GetPhysicalPortToNodeTuples(device)
bus_device_to_tty = GetBusDeviceToTTYMap()
- return {port: bus_device_to_tty[(device.bus_num, device.device_num)]
- for (port, device) in port_device
- if (device.bus_num, device.device_num) in bus_device_to_tty}
+ return {
+ port: bus_device_to_tty[(device.bus_num, device.device_num)]
+ for (port, device) in port_device
+ if (device.bus_num, device.device_num) in bus_device_to_tty
+ }
def CollectHubMaps(hub_types, map_func, device_tree_map=None, fast=False):
@@ -538,9 +542,11 @@ def parse_options(argv):
parser = argparse.ArgumentParser(usage=USAGE)
return parser.parse_args(argv[1:])
+
def main():
parse_options(sys.argv)
TestUSBTopologyScript()
+
if __name__ == "__main__":
sys.exit(main())
diff --git a/catapult/devil/devil/utils/find_usb_devices_test.py b/catapult/devil/devil/utils/find_usb_devices_test.py
index c22f21b8..a8620b62 100755
--- a/catapult/devil/devil/utils/find_usb_devices_test.py
+++ b/catapult/devil/devil/utils/find_usb_devices_test.py
@@ -4,7 +4,6 @@
# found in the LICENSE file.
# pylint: disable=protected-access
-
"""
Unit tests for the contents of find_usb_devices.py.
@@ -37,32 +36,29 @@ from devil.utils import lsusb
from devil.utils import usb_hubs
with devil_env.SysPath(devil_env.PYMOCK_PATH):
- import mock # pylint: disable=import-error
+ import mock # pylint: disable=import-error
# Output of lsusb.lsusb().
# We just test that the dictionary is working by creating an
# "ID number" equal to (bus_num*1000)+device_num and seeing if
# it is picked up correctly. Also we test the description
-DEVLIST = [(1, 11, 'foo'),
- (1, 12, 'bar'),
- (1, 13, 'baz'),
- (2, 11, 'quux'),
- (2, 20, 'My Test HUB'),
- (2, 21, 'ID 0403:6001 usb_device_p7_h1_t0'),
+DEVLIST = [(1, 11, 'foo'), (1, 12, 'bar'), (1, 13, 'baz'), (2, 11, 'quux'),
+ (2, 20, 'My Test HUB'), (2, 21, 'ID 0403:6001 usb_device_p7_h1_t0'),
(2, 22, 'ID 0403:6001 usb_device_p5_h1_t1'),
(2, 23, 'My Test Internal HUB'),
(2, 24, 'ID 0403:6001 usb_device_p3_h1_t2'),
(2, 25, 'ID 0403:6001 usb_device_p1_h1_t3'),
- (2, 26, 'Not a Battery Monitor'),
- (2, 100, 'My Test HUB'),
+ (2, 26, 'Not a Battery Monitor'), (2, 100, 'My Test HUB'),
(2, 101, 'My Test Internal HUB'),
(2, 102, 'ID 0403:6001 usb_device_p1_h1_t4')]
-LSUSB_OUTPUT = [
- {'bus': b, 'device': d, 'desc': t, 'id': (1000*b)+d}
- for (b, d, t) in DEVLIST]
-
+LSUSB_OUTPUT = [{
+ 'bus': b,
+ 'device': d,
+ 'desc': t,
+ 'id': (1000 * b) + d
+} for (b, d, t) in DEVLIST]
# Note: "Lev", "Cnt", "Spd", and "MxCh" are not used by parser,
# so we just leave them as zeros here. Also note that the port
@@ -170,12 +166,14 @@ ATTRS{devnum}=="0"
'''
UDEVADM_OUTPUT_DICT = {
- 'ttyUSB0': UDEVADM_USBTTY0_OUTPUT,
- 'ttyUSB1': UDEVADM_USBTTY1_OUTPUT,
- 'ttyUSB2': UDEVADM_USBTTY2_OUTPUT,
- 'ttyUSB3': UDEVADM_USBTTY3_OUTPUT,
- 'ttyUSB4': UDEVADM_USBTTY4_OUTPUT,
- 'ttyUSB5': UDEVADM_USBTTY5_OUTPUT}
+ 'ttyUSB0': UDEVADM_USBTTY0_OUTPUT,
+ 'ttyUSB1': UDEVADM_USBTTY1_OUTPUT,
+ 'ttyUSB2': UDEVADM_USBTTY2_OUTPUT,
+ 'ttyUSB3': UDEVADM_USBTTY3_OUTPUT,
+ 'ttyUSB4': UDEVADM_USBTTY4_OUTPUT,
+ 'ttyUSB5': UDEVADM_USBTTY5_OUTPUT
+}
+
# Identification criteria for Plugable 7-Port Hub
def isTestHub(node):
@@ -192,11 +190,19 @@ def isTestHub(node):
return False
return 'Test Internal HUB' in node.PortToDevice(4).desc
-TEST_HUB = usb_hubs.HubType(isTestHub,
- {1:7,
- 2:6,
- 3:5,
- 4:{1:4, 2:3, 3:2, 4:1}})
+
+TEST_HUB = usb_hubs.HubType(isTestHub, {
+ 1: 7,
+ 2: 6,
+ 3: 5,
+ 4: {
+ 1: 4,
+ 2: 3,
+ 3: 2,
+ 4: 1
+ }
+})
+
class USBScriptTest(unittest.TestCase):
def setUp(self):
@@ -206,38 +212,42 @@ class USBScriptTest(unittest.TestCase):
return_value=LSUSB_OUTPUT)
find_usb_devices._GetUSBDevicesOutput = mock.Mock(
return_value=USB_DEVICES_OUTPUT)
- find_usb_devices._GetCommList = mock.Mock(
- return_value=LIST_TTY_OUTPUT)
- lsusb.raw_lsusb = mock.Mock(
- return_value=RAW_LSUSB_OUTPUT)
+ find_usb_devices._GetCommList = mock.Mock(return_value=LIST_TTY_OUTPUT)
+ lsusb.raw_lsusb = mock.Mock(return_value=RAW_LSUSB_OUTPUT)
def testGetTTYDevices(self):
pp = find_usb_devices.GetAllPhysicalPortToTTYMaps([TEST_HUB])
result = list(pp)
- self.assertEquals(result[0], {7:'ttyUSB0',
- 5:'ttyUSB1',
- 3:'ttyUSB2',
- 2:'ttyUSB5',
- 1:'ttyUSB3'})
- self.assertEquals(result[1], {1:'ttyUSB4'})
+ self.assertEquals(result[0], {
+ 7: 'ttyUSB0',
+ 5: 'ttyUSB1',
+ 3: 'ttyUSB2',
+ 2: 'ttyUSB5',
+ 1: 'ttyUSB3'
+ })
+ self.assertEquals(result[1], {1: 'ttyUSB4'})
def testGetPortDeviceMapping(self):
pp = find_usb_devices.GetAllPhysicalPortToBusDeviceMaps([TEST_HUB])
result = list(pp)
- self.assertEquals(result[0], {7:(2, 21),
- 5:(2, 22),
- 3:(2, 24),
- 2:(2, 26),
- 1:(2, 25)})
- self.assertEquals(result[1], {1:(2, 102)})
+ self.assertEquals(result[0], {
+ 7: (2, 21),
+ 5: (2, 22),
+ 3: (2, 24),
+ 2: (2, 26),
+ 1: (2, 25)
+ })
+ self.assertEquals(result[1], {1: (2, 102)})
def testGetSerialMapping(self):
pp = find_usb_devices.GetAllPhysicalPortToSerialMaps([TEST_HUB])
result = list(pp)
- self.assertEquals(result[0], {7:'UsbDevice0',
- 5:'UsbDevice1',
- 3:'UsbDevice2',
- 1:'UsbDevice3'})
+ self.assertEquals(result[0], {
+ 7: 'UsbDevice0',
+ 5: 'UsbDevice1',
+ 3: 'UsbDevice2',
+ 1: 'UsbDevice3'
+ })
self.assertEquals(result[1], {})
def testFastDeviceDescriptions(self):
@@ -248,7 +258,7 @@ class USBScriptTest(unittest.TestCase):
self.assertEquals(dev_foo.desc, 'FAST foo')
self.assertEquals(dev_bar.desc, 'FAST bar')
self.assertEquals(dev_usb_device_p7_h1_t0.desc,
- 'ID 0403:6001 usb_device_p7_h1_t0')
+ 'ID 0403:6001 usb_device_p7_h1_t0')
def testDeviceDescriptions(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
@@ -258,7 +268,7 @@ class USBScriptTest(unittest.TestCase):
self.assertEquals(dev_foo.desc, 'foo')
self.assertEquals(dev_bar.desc, 'bar')
self.assertEquals(dev_usb_device_p7_h1_t0.desc,
- 'ID 0403:6001 usb_device_p7_h1_t0')
+ 'ID 0403:6001 usb_device_p7_h1_t0')
def testDeviceInformation(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
diff --git a/catapult/devil/devil/utils/geometry.py b/catapult/devil/devil/utils/geometry.py
index da21770b..6da6d034 100644
--- a/catapult/devil/devil/utils/geometry.py
+++ b/catapult/devil/devil/utils/geometry.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Objects for convenient manipulation of points and other surface areas."""
import collections
diff --git a/catapult/devil/devil/utils/geometry_test.py b/catapult/devil/devil/utils/geometry_test.py
index af694429..e2024828 100644
--- a/catapult/devil/devil/utils/geometry_test.py
+++ b/catapult/devil/devil/utils/geometry_test.py
@@ -1,7 +1,6 @@
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Tests for the geometry module."""
import unittest
@@ -10,7 +9,6 @@ from devil.utils import geometry as g
class PointTest(unittest.TestCase):
-
def testStr(self):
p = g.Point(1, 2)
self.assertEquals(str(p), '(1, 2)')
@@ -45,7 +43,6 @@ class PointTest(unittest.TestCase):
class RectangleTest(unittest.TestCase):
-
def testStr(self):
r = g.Rectangle(g.Point(0, 1), g.Point(2, 3))
self.assertEquals(str(r), '[(0, 1), (2, 3)]')
diff --git a/catapult/devil/devil/utils/host_utils.py b/catapult/devil/devil/utils/host_utils.py
index 6c337cf7..2918e06d 100644
--- a/catapult/devil/devil/utils/host_utils.py
+++ b/catapult/devil/devil/utils/host_utils.py
@@ -8,6 +8,7 @@ import os
def GetRecursiveDiskUsage(path):
"""Returns the disk usage in bytes of |path|. Similar to `du -sb |path|`."""
+
def get_size(filepath):
try:
return os.path.getsize(filepath)
@@ -18,6 +19,6 @@ def GetRecursiveDiskUsage(path):
running_size = get_size(path)
if os.path.isdir(path):
for root, dirs, files in os.walk(path):
- running_size += sum([get_size(os.path.join(root, f))
- for f in files + dirs])
+ running_size += sum(
+ [get_size(os.path.join(root, f)) for f in files + dirs])
return running_size
diff --git a/catapult/devil/devil/utils/lazy/weak_constant.py b/catapult/devil/devil/utils/lazy/weak_constant.py
index 24ad9406..41101937 100644
--- a/catapult/devil/devil/utils/lazy/weak_constant.py
+++ b/catapult/devil/devil/utils/lazy/weak_constant.py
@@ -30,11 +30,11 @@ class WeakConstant(object):
# We initialize the value on a separate thread to protect
# from holding self._lock indefinitely in the event that
# self._initializer hangs.
- initializer_thread = reraiser_thread.ReraiserThread(
- self._initializer)
+ initializer_thread = reraiser_thread.ReraiserThread(self._initializer)
initializer_thread.start()
timeout_retry.WaitFor(
- lambda: initializer_thread.join(1) or not initializer_thread.isAlive(),
+ lambda: initializer_thread.join(1) or not initializer_thread.
+ isAlive(),
wait_period=0)
self._val = initializer_thread.GetReturnValue()
self._initialized.set()
diff --git a/catapult/devil/devil/utils/lazy/weak_constant_test.py b/catapult/devil/devil/utils/lazy/weak_constant_test.py
index 643351d8..583fd07a 100644
--- a/catapult/devil/devil/utils/lazy/weak_constant_test.py
+++ b/catapult/devil/devil/utils/lazy/weak_constant_test.py
@@ -30,14 +30,11 @@ class DynamicSideEffect(object):
class WeakConstantTest(unittest.TestCase):
-
def testUninitialized(self):
"""Ensure that the first read calls the initializer."""
initializer = mock.Mock(return_value='initializer called')
test_constant = lazy.WeakConstant(initializer)
- self.assertEquals(
- 'initializer called',
- test_constant.read())
+ self.assertEquals('initializer called', test_constant.read())
initializer.assert_called_once_with()
def testInitialized(self):
@@ -46,23 +43,18 @@ class WeakConstantTest(unittest.TestCase):
test_constant = lazy.WeakConstant(initializer)
test_constant._initialized.set()
test_constant._val = 'initializer not called'
- self.assertEquals(
- 'initializer not called',
- test_constant.read())
+ self.assertEquals('initializer not called', test_constant.read())
self.assertFalse(initializer.mock_calls) # assert not called
def testFirstCallHangs(self):
"""Ensure that reading works even if the first initializer call hangs."""
- dyn = DynamicSideEffect([
- lambda: time.sleep(10),
- lambda: 'second try worked!'
- ])
+ dyn = DynamicSideEffect(
+ [lambda: time.sleep(10), lambda: 'second try worked!'])
initializer = mock.Mock(side_effect=dyn)
test_constant = lazy.WeakConstant(initializer)
- self.assertEquals(
- 'second try worked!',
- timeout_retry.Run(test_constant.read, 1, 1))
+ self.assertEquals('second try worked!',
+ timeout_retry.Run(test_constant.read, 1, 1))
initializer.assert_has_calls([mock.call(), mock.call()])
diff --git a/catapult/devil/devil/utils/logging_common.py b/catapult/devil/devil/utils/logging_common.py
index ab364a20..3df7aab6 100644
--- a/catapult/devil/devil/utils/logging_common.py
+++ b/catapult/devil/devil/utils/logging_common.py
@@ -15,10 +15,16 @@ def AddLoggingArguments(parser):
"""
group = parser.add_mutually_exclusive_group()
group.add_argument(
- '-v', '--verbose', action='count', default=0,
+ '-v',
+ '--verbose',
+ action='count',
+ default=0,
help='Log more. Use multiple times for even more logging.')
group.add_argument(
- '-q', '--quiet', action='count', default=0,
+ '-q',
+ '--quiet',
+ action='count',
+ default=0,
help=('Log less (suppress output). Use multiple times for even less '
'output.'))
@@ -66,4 +72,3 @@ class CustomFormatter(logging.Formatter):
msg = msg.replace('MainThread', 'Main', 1)
timediff = time.time() - self._creation_time
return '%s %8.3fs %s' % (record.levelname[0], timediff, msg)
-
diff --git a/catapult/devil/devil/utils/lsusb.py b/catapult/devil/devil/utils/lsusb.py
index 6cbf2567..dd5d471f 100644
--- a/catapult/devil/devil/utils/lsusb.py
+++ b/catapult/devil/devil/utils/lsusb.py
@@ -85,11 +85,9 @@ def _lsusbv_on_device(bus_id, dev_id):
m = _LSUSB_BUS_DEVICE_RE.match(line)
if m:
if m.group(1) != bus_id:
- logger.warning(
- 'Expected bus_id value: %r, seen %r', bus_id, m.group(1))
+ logger.warning('Expected bus_id value: %r, seen %r', bus_id, m.group(1))
if m.group(2) != dev_id:
- logger.warning(
- 'Expected dev_id value: %r, seen %r', dev_id, m.group(2))
+ logger.warning('Expected dev_id value: %r, seen %r', dev_id, m.group(2))
device['desc'] = m.group(3)
continue
@@ -102,8 +100,7 @@ def _lsusbv_on_device(bus_id, dev_id):
# Determine the indentation depth.
depth = 1 + len(indent_match.group(1)) / 2
if depth > len(depth_stack):
- logger.error(
- 'lsusb parsing error: unexpected indentation: "%s"', line)
+ logger.error('lsusb parsing error: unexpected indentation: "%s"', line)
continue
# Pop everything off the depth stack that isn't a parent of
@@ -123,8 +120,8 @@ def _lsusbv_on_device(bus_id, dev_id):
m = _LSUSB_ENTRY_RE.match(line)
if m:
new_entry = {
- '_value': m.group(2),
- '_desc': m.group(3),
+ '_value': m.group(2),
+ '_desc': m.group(3),
}
cur[m.group(1)] = new_entry
depth_stack.append(new_entry)
@@ -134,10 +131,11 @@ def _lsusbv_on_device(bus_id, dev_id):
return device
+
def lsusb():
"""Call lsusb and return the parsed output."""
- _, lsusb_list_output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
- ['lsusb'], timeout=10)
+ _, lsusb_list_output = cmd_helper.GetCmdStatusAndOutputWithTimeout(['lsusb'],
+ timeout=10)
devices = []
for line in lsusb_list_output.splitlines():
m = _LSUSB_BUS_DEVICE_RE.match(line)
@@ -147,19 +145,22 @@ def lsusb():
try:
devices.append(_lsusbv_on_device(bus_num, dev_num))
except cmd_helper.TimeoutError:
- # Will be blacklisted if it is in expected device file, but times out.
+ # Will be denylisted if it is in expected device file, but times out.
logger.info('lsusb -v %s:%s timed out.', bus_num, dev_num)
return devices
+
def raw_lsusb():
return cmd_helper.GetCmdOutput(['lsusb'])
+
def get_lsusb_serial(device):
try:
return device['Device Descriptor']['iSerial']['_desc']
except KeyError:
return None
+
def _is_android_device(device):
try:
# Hubs are not android devices.
@@ -169,6 +170,7 @@ def _is_android_device(device):
pass
return get_lsusb_serial(device) is not None
+
def get_android_devices():
android_devices = (d for d in lsusb() if _is_android_device(d))
return [get_lsusb_serial(d) for d in android_devices]
diff --git a/catapult/devil/devil/utils/lsusb_test.py b/catapult/devil/devil/utils/lsusb_test.py
index f381e72f..6a870d1b 100755
--- a/catapult/devil/devil/utils/lsusb_test.py
+++ b/catapult/devil/devil/utils/lsusb_test.py
@@ -2,7 +2,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Tests for the cmd_helper module."""
import unittest
@@ -12,7 +11,7 @@ from devil.utils import lsusb
from devil.utils import mock_calls
with devil_env.SysPath(devil_env.PYMOCK_PATH):
- import mock # pylint: disable=import-error
+ import mock # pylint: disable=import-error
RAW_OUTPUT = """
Bus 003 Device 007: ID 18d1:4ee2 Google Inc. Nexus 4 (debug)
@@ -138,81 +137,210 @@ EXPECTED_RESULT = {
}
},
'Device Descriptor': {
- 'bLength': {'_value': '18', '_desc': None},
- 'bcdDevice': {'_value': '2.28', '_desc': None},
- 'bDeviceSubClass': {'_value': '0', '_desc': None},
- 'idVendor': {'_value': '0x18d1', '_desc': 'Google Inc.'},
- 'bcdUSB': {'_value': '2.00', '_desc': None},
- 'bDeviceProtocol': {'_value': '0', '_desc': None},
- 'bDescriptorType': {'_value': '1', '_desc': None},
+ 'bLength': {
+ '_value': '18',
+ '_desc': None
+ },
+ 'bcdDevice': {
+ '_value': '2.28',
+ '_desc': None
+ },
+ 'bDeviceSubClass': {
+ '_value': '0',
+ '_desc': None
+ },
+ 'idVendor': {
+ '_value': '0x18d1',
+ '_desc': 'Google Inc.'
+ },
+ 'bcdUSB': {
+ '_value': '2.00',
+ '_desc': None
+ },
+ 'bDeviceProtocol': {
+ '_value': '0',
+ '_desc': None
+ },
+ 'bDescriptorType': {
+ '_value': '1',
+ '_desc': None
+ },
'Configuration Descriptor': {
- 'bLength': {'_value': '9', '_desc': None},
- 'wTotalLength': {'_value': '62', '_desc': None},
- 'bConfigurationValue': {'_value': '1', '_desc': None},
+ 'bLength': {
+ '_value': '9',
+ '_desc': None
+ },
+ 'wTotalLength': {
+ '_value': '62',
+ '_desc': None
+ },
+ 'bConfigurationValue': {
+ '_value': '1',
+ '_desc': None
+ },
'Interface Descriptor': {
- 'bLength': {'_value': '9', '_desc': None},
- 'bAlternateSetting': {'_value': '0', '_desc': None},
- 'bInterfaceNumber': {'_value': '1', '_desc': None},
- 'bNumEndpoints': {'_value': '2', '_desc': None},
- 'bDescriptorType': {'_value': '4', '_desc': None},
- 'bInterfaceSubClass': {'_value': '66', '_desc': None},
+ 'bLength': {
+ '_value': '9',
+ '_desc': None
+ },
+ 'bAlternateSetting': {
+ '_value': '0',
+ '_desc': None
+ },
+ 'bInterfaceNumber': {
+ '_value': '1',
+ '_desc': None
+ },
+ 'bNumEndpoints': {
+ '_value': '2',
+ '_desc': None
+ },
+ 'bDescriptorType': {
+ '_value': '4',
+ '_desc': None
+ },
+ 'bInterfaceSubClass': {
+ '_value': '66',
+ '_desc': None
+ },
'bInterfaceClass': {
'_value': '255',
'_desc': 'Vendor Specific Class'
},
- 'bInterfaceProtocol': {'_value': '1', '_desc': None},
+ 'bInterfaceProtocol': {
+ '_value': '1',
+ '_desc': None
+ },
'Endpoint Descriptor': {
- 'bLength': {'_value': '7', '_desc': None},
- 'bEndpointAddress': {'_value': '0x02', '_desc': 'EP 2 OUT'},
- 'bInterval': {'_value': '0', '_desc': None},
- 'bDescriptorType': {'_value': '5', '_desc': None},
+ 'bLength': {
+ '_value': '7',
+ '_desc': None
+ },
+ 'bEndpointAddress': {
+ '_value': '0x02',
+ '_desc': 'EP 2 OUT'
+ },
+ 'bInterval': {
+ '_value': '0',
+ '_desc': None
+ },
+ 'bDescriptorType': {
+ '_value': '5',
+ '_desc': None
+ },
'bmAttributes': {
'_value': '2',
- 'Transfer': {'_value': 'Type', '_desc': 'Bulk'},
- 'Usage': {'_value': 'Type', '_desc': 'Data'},
+ 'Transfer': {
+ '_value': 'Type',
+ '_desc': 'Bulk'
+ },
+ 'Usage': {
+ '_value': 'Type',
+ '_desc': 'Data'
+ },
'_desc': None,
- 'Synch': {'_value': 'Type', '_desc': 'None'}
+ 'Synch': {
+ '_value': 'Type',
+ '_desc': 'None'
+ }
},
'wMaxPacketSize': {
'_value': '0x0040',
'_desc': '1x 64 bytes'
}
},
- 'iInterface': {'_value': '0', '_desc': None}
+ 'iInterface': {
+ '_value': '0',
+ '_desc': None
+ }
+ },
+ 'bDescriptorType': {
+ '_value': '2',
+ '_desc': None
+ },
+ 'iConfiguration': {
+ '_value': '0',
+ '_desc': None
},
- 'bDescriptorType': {'_value': '2', '_desc': None},
- 'iConfiguration': {'_value': '0', '_desc': None},
'bmAttributes': {
'_value': '0x80',
'_desc': None,
- '(Bus': {'_value': 'Powered)', '_desc': None}
+ '(Bus': {
+ '_value': 'Powered)',
+ '_desc': None
+ }
+ },
+ 'bNumInterfaces': {
+ '_value': '2',
+ '_desc': None
},
- 'bNumInterfaces': {'_value': '2', '_desc': None},
- 'MaxPower': {'_value': '500mA', '_desc': None}
+ 'MaxPower': {
+ '_value': '500mA',
+ '_desc': None
+ }
+ },
+ 'iSerial': {
+ '_value': '3',
+ '_desc': '01d2450ea194a93b'
+ },
+ 'idProduct': {
+ '_value': '0x4ee2',
+ '_desc': 'Nexus 4 (debug)'
+ },
+ 'iManufacturer': {
+ '_value': '1',
+ '_desc': 'LGE'
},
- 'iSerial': {'_value': '3', '_desc': '01d2450ea194a93b'},
- 'idProduct': {'_value': '0x4ee2', '_desc': 'Nexus 4 (debug)'},
- 'iManufacturer': {'_value': '1', '_desc': 'LGE'},
'bDeviceClass': {
'_value': '0',
'_desc': '(Defined at Interface level)'
},
- 'iProduct': {'_value': '2', '_desc': 'Nexus 4'},
- 'bMaxPacketSize0': {'_value': '64', '_desc': None},
- 'bNumConfigurations': {'_value': '1', '_desc': None}
+ 'iProduct': {
+ '_value': '2',
+ '_desc': 'Nexus 4'
+ },
+ 'bMaxPacketSize0': {
+ '_value': '64',
+ '_desc': None
+ },
+ 'bNumConfigurations': {
+ '_value': '1',
+ '_desc': None
+ }
},
'Device Qualifier (for other device speed)': {
- 'bLength': {'_value': '10', '_desc': None},
- 'bNumConfigurations': {'_value': '1', '_desc': None},
- 'bDeviceSubClass': {'_value': '0', '_desc': None},
- 'bcdUSB': {'_value': '2.00', '_desc': None},
- 'bDeviceProtocol': {'_value': '0', '_desc': None},
- 'bDescriptorType': {'_value': '6', '_desc': None},
+ 'bLength': {
+ '_value': '10',
+ '_desc': None
+ },
+ 'bNumConfigurations': {
+ '_value': '1',
+ '_desc': None
+ },
+ 'bDeviceSubClass': {
+ '_value': '0',
+ '_desc': None
+ },
+ 'bcdUSB': {
+ '_value': '2.00',
+ '_desc': None
+ },
+ 'bDeviceProtocol': {
+ '_value': '0',
+ '_desc': None
+ },
+ 'bDescriptorType': {
+ '_value': '6',
+ '_desc': None
+ },
'bDeviceClass': {
'_value': '0',
'_desc': '(Defined at Interface level)'
},
- 'bMaxPacketSize0': {'_value': '64', '_desc': None}
+ 'bMaxPacketSize0': {
+ '_value': '64',
+ '_desc': None
+ }
}
}
@@ -225,7 +353,7 @@ class LsusbTest(mock_calls.TestCase):
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
['lsusb'], timeout=10), (None, DEVICE_LIST)),
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
- ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
+ ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
self.assertDictEqual(lsusb.lsusb().pop(), EXPECTED_RESULT)
def testGetSerial(self):
@@ -233,7 +361,7 @@ class LsusbTest(mock_calls.TestCase):
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
['lsusb'], timeout=10), (None, DEVICE_LIST)),
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
- ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
+ ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
self.assertEqual(lsusb.get_android_devices(), ['01d2450ea194a93b'])
def testGetLsusbSerial(self):
@@ -241,7 +369,7 @@ class LsusbTest(mock_calls.TestCase):
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
['lsusb'], timeout=10), (None, DEVICE_LIST)),
(mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
- ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
+ ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
out = lsusb.lsusb().pop()
self.assertEqual(lsusb.get_lsusb_serial(out), '01d2450ea194a93b')
diff --git a/catapult/devil/devil/utils/markdown.py b/catapult/devil/devil/utils/markdown.py
index ba666643..12d2eb3c 100755
--- a/catapult/devil/devil/utils/markdown.py
+++ b/catapult/devil/devil/utils/markdown.py
@@ -19,8 +19,8 @@ _CODE_BLOCK_FORMAT = '''```{language}
```
'''
-_DEVIL_ROOT = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', '..'))
+_DEVIL_ROOT = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
def md_bold(raw_text):
@@ -31,14 +31,15 @@ def md_bold(raw_text):
def md_code(raw_text, language):
"""Returns a markdown-formatted code block in the given language."""
return _CODE_BLOCK_FORMAT.format(
- language=language or '',
- code=md_escape(raw_text, characters='`'))
+ language=language or '', code=md_escape(raw_text, characters='`'))
def md_escape(raw_text, characters='*_'):
"""Escapes * and _."""
+
def escape_char(m):
return '\\%s' % m.group(0)
+
pattern = '[%s]' % re.escape(characters)
return re.sub(pattern, escape_char, raw_text)
@@ -46,8 +47,8 @@ def md_escape(raw_text, characters='*_'):
def md_heading(raw_text, level):
"""Returns markdown-formatted heading."""
adjusted_level = min(max(level, 0), 6)
- return '%s%s%s' % (
- '#' * adjusted_level, ' ' if adjusted_level > 0 else '', raw_text)
+ return '%s%s%s' % ('#' * adjusted_level, ' ' if adjusted_level > 0 else '',
+ raw_text)
def md_inline_code(raw_text):
@@ -62,9 +63,8 @@ def md_italic(raw_text):
def md_link(link_text, link_target):
"""returns a markdown-formatted link."""
- return '[%s](%s)' % (
- md_escape(link_text, characters=']'),
- md_escape(link_target, characters=')'))
+ return '[%s](%s)' % (md_escape(link_text, characters=']'),
+ md_escape(link_target, characters=')'))
class MarkdownHelpFormatter(argparse.HelpFormatter):
@@ -112,8 +112,10 @@ class MarkdownHelpFormatter(argparse.HelpFormatter):
class MarkdownHelpAction(argparse.Action):
- def __init__(self, option_strings,
- dest=argparse.SUPPRESS, default=argparse.SUPPRESS,
+ def __init__(self,
+ option_strings,
+ dest=argparse.SUPPRESS,
+ default=argparse.SUPPRESS,
**kwargs):
super(MarkdownHelpAction, self).__init__(
option_strings=option_strings,
@@ -137,8 +139,10 @@ def add_md_help_argument(parser):
Args:
parser: The ArgumentParser to which --md-help should be added.
"""
- parser.add_argument('--md-help', action=MarkdownHelpAction,
- help='print Markdown-formatted help text and exit.')
+ parser.add_argument(
+ '--md-help',
+ action=MarkdownHelpAction,
+ help='print Markdown-formatted help text and exit.')
def load_module_from_path(module_path):
@@ -172,24 +176,25 @@ def load_module_from_path(module_path):
return module
-def md_module(module_obj, module_path=None, module_link=None):
- """Write markdown documentation for a class.
+def md_module(module_obj, module_link=None):
+ """Write markdown documentation for a module.
Documents public classes and functions.
Args:
- class_obj: a types.TypeType object for the class that should be
- documented.
+ module_obj: a module object that should be documented.
Returns:
A list of markdown-formatted lines.
"""
+
def should_doc(name):
return (not isinstance(module_obj.__dict__[name], types.ModuleType)
and not name.startswith('_'))
- stuff_to_doc = sorted(
- obj for name, obj in module_obj.__dict__.iteritems()
- if should_doc(name))
+ stuff_to_doc = [
+ obj for name, obj in sorted(module_obj.__dict__.iteritems())
+ if should_doc(name)
+ ]
classes_to_doc = []
functions_to_doc = []
@@ -200,12 +205,6 @@ def md_module(module_obj, module_path=None, module_link=None):
elif isinstance(s, types.FunctionType):
functions_to_doc.append(s)
- command = ['devil/utils/markdown.py']
- if module_link:
- command.extend(['--module-link', module_link])
- if module_path:
- command.append(os.path.relpath(module_path, _DEVIL_ROOT))
-
heading_text = module_obj.__name__
if module_link:
heading_text = md_link(heading_text, module_link)
@@ -213,8 +212,8 @@ def md_module(module_obj, module_path=None, module_link=None):
content = [
md_heading(heading_text, level=1),
'',
- md_italic('This page was autogenerated by %s'
- % md_inline_code(' '.join(command))),
+ md_italic('This page was autogenerated. '
+ 'Run `devil/bin/generate_md_docs` to update'),
'',
]
@@ -248,9 +247,10 @@ def md_class(class_obj):
return (isinstance(obj, types.FunctionType)
and (name.startswith('__') or not name.startswith('_')))
- methods_to_doc = sorted(
- obj for name, obj in class_obj.__dict__.iteritems()
- if should_doc(name, obj))
+ methods_to_doc = [
+ obj for name, obj in sorted(class_obj.__dict__.iteritems())
+ if should_doc(name, obj)
+ ]
for m in methods_to_doc:
content.extend(md_function(m, class_obj=class_obj))
@@ -313,10 +313,8 @@ def main(raw_args):
args = parser.parse_args(raw_args)
return md_module(
- load_module_from_path(args.module_path),
- module_link=args.module_link)
+ load_module_from_path(args.module_path), module_link=args.module_link)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
-
diff --git a/catapult/devil/devil/utils/markdown_test.py b/catapult/devil/devil/utils/markdown_test.py
index 323776ca..621d56ba 100755
--- a/catapult/devil/devil/utils/markdown_test.py
+++ b/catapult/devil/devil/utils/markdown_test.py
@@ -16,7 +16,6 @@ from devil.utils import markdown
class MarkdownTest(unittest.TestCase):
-
def testBold(self):
raw = 'foo'
self.assertEquals('**foo**', markdown.md_bold(raw))
@@ -85,13 +84,13 @@ class MarkdownTest(unittest.TestCase):
def testInlineCode(self):
raw = 'devil.utils.markdown_test'
- self.assertEquals(
- '`devil.utils.markdown_test`', markdown.md_inline_code(raw))
+ self.assertEquals('`devil.utils.markdown_test`',
+ markdown.md_inline_code(raw))
def testInlineCodeContainsTicks(self):
raw = 'this contains `backticks`'
- self.assertEquals(
- '`this contains \\`backticks\\``', markdown.md_inline_code(raw))
+ self.assertEquals('`this contains \\`backticks\\``',
+ markdown.md_inline_code(raw))
def testItalic(self):
raw = 'bar'
diff --git a/catapult/devil/devil/utils/mock_calls.py b/catapult/devil/devil/utils/mock_calls.py
index 5ae951e3..2b359385 100644
--- a/catapult/devil/devil/utils/mock_calls.py
+++ b/catapult/devil/devil/utils/mock_calls.py
@@ -1,7 +1,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
A test facility to assert call sequences while mocking their behavior.
"""
@@ -16,8 +15,8 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
class TestCase(unittest.TestCase):
"""Adds assertCalls to TestCase objects."""
- class _AssertCalls(object):
+ class _AssertCalls(object):
def __init__(self, test_case, expected_calls, watched):
def call_action(pair):
if isinstance(pair, type(mock.call)):
@@ -36,21 +35,24 @@ class TestCase(unittest.TestCase):
received_call == expected_call,
msg=('Expected call mismatch:\n'
' expected: %s\n'
- ' received: %s\n'
- % (str(expected_call), str(received_call))))
+ ' received: %s\n' % (str(expected_call),
+ str(received_call))))
if callable(action):
return action(*args, **kwargs)
else:
return action
+
return side_effect
self._test_case = test_case
self._expected_calls = [call_action(pair) for pair in expected_calls]
watched = watched.copy() # do not pollute the caller's dict
- watched.update((call.parent.name, call.parent)
- for call, _ in self._expected_calls)
- self._patched = [test_case.patch_call(call, side_effect=do_check(call))
- for call in watched.itervalues()]
+ watched.update(
+ (call.parent.name, call.parent) for call, _ in self._expected_calls)
+ self._patched = [
+ test_case.patch_call(call, side_effect=do_check(call))
+ for call in watched.itervalues()
+ ]
def __enter__(self):
for patch in self._patched:
@@ -61,11 +63,10 @@ class TestCase(unittest.TestCase):
for patch in self._patched:
patch.__exit__(exc_type, exc_val, exc_tb)
if exc_type is None:
- missing = ''.join(' expected: %s\n' % str(call)
- for call, _ in self._expected_calls)
+ missing = ''.join(
+ ' expected: %s\n' % str(call) for call, _ in self._expected_calls)
self._test_case.assertFalse(
- missing,
- msg='Expected calls not found:\n' + missing)
+ missing, msg='Expected calls not found:\n' + missing)
def __init__(self, *args, **kwargs):
super(TestCase, self).__init__(*args, **kwargs)
@@ -135,9 +136,9 @@ class TestCase(unittest.TestCase):
target = self.call_target(call)
if ignore is None:
ignore = []
- self.watchCalls(getattr(call, method)
- for method in dir(target.__class__)
- if not method.startswith('_') and not method in ignore)
+ self.watchCalls(
+ getattr(call, method) for method in dir(target.__class__)
+ if not method.startswith('_') and not method in ignore)
def clearWatched(self):
"""Clear the set of watched calls."""
@@ -177,4 +178,3 @@ class TestCase(unittest.TestCase):
def assertCall(self, call, action=None):
return self.assertCalls((call, action))
-
diff --git a/catapult/devil/devil/utils/mock_calls_test.py b/catapult/devil/devil/utils/mock_calls_test.py
index 8eb4fc9d..f1e71780 100755
--- a/catapult/devil/devil/utils/mock_calls_test.py
+++ b/catapult/devil/devil/utils/mock_calls_test.py
@@ -2,7 +2,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""
Unit tests for the contents of mock_calls.py.
"""
@@ -20,7 +19,6 @@ with devil_env.SysPath(devil_env.PYMOCK_PATH):
class _DummyAdb(object):
-
def __str__(self):
return '0123456789abcdef'
@@ -45,13 +43,13 @@ class _DummyAdb(object):
class TestCaseWithAssertCallsTest(mock_calls.TestCase):
-
def setUp(self):
self.adb = _DummyAdb()
def ShellError(self):
def action(cmd):
raise ValueError('(device %s) command %r is not nice' % (self.adb, cmd))
+
return action
def get_answer(self):
@@ -63,8 +61,7 @@ class TestCaseWithAssertCallsTest(mock_calls.TestCase):
return thing
def testCallTarget_succeds(self):
- self.assertEquals(self.adb.Shell,
- self.call_target(self.call.adb.Shell))
+ self.assertEquals(self.adb.Shell, self.call_target(self.call.adb.Shell))
def testCallTarget_failsExternal(self):
with self.assertRaises(ValueError):
@@ -100,8 +97,7 @@ class TestCaseWithAssertCallsTest(mock_calls.TestCase):
def testPatchCall_property(self):
self.assertEquals(version_codes.LOLLIPOP, self.adb.build_version_sdk)
with self.patch_call(
- self.call.adb.build_version_sdk,
- return_value=version_codes.KITKAT):
+ self.call.adb.build_version_sdk, return_value=version_codes.KITKAT):
self.assertEquals(version_codes.KITKAT, self.adb.build_version_sdk)
self.assertEquals(version_codes.LOLLIPOP, self.adb.build_version_sdk)
@@ -114,10 +110,9 @@ class TestCaseWithAssertCallsTest(mock_calls.TestCase):
def testAssertCalls_succeeds_multiple(self):
with self.assertCalls(
(mock.call.os.getcwd(), '/some/path'),
- (self.call.echo('hello'), 'hello'),
- (self.call.get_answer(), 11),
- self.call.adb.Push('this_file', 'that_file'),
- (self.call.get_answer(), 12)):
+ (self.call.echo('hello'), 'hello'), (self.call.get_answer(), 11),
+ self.call.adb.Push('this_file',
+ 'that_file'), (self.call.get_answer(), 12)):
self.assertEquals(os.getcwd(), '/some/path')
self.assertEquals('hello', self.echo('hello'))
self.assertEquals(11, self.get_answer())
@@ -125,8 +120,7 @@ class TestCaseWithAssertCallsTest(mock_calls.TestCase):
self.assertEquals(12, self.get_answer())
def testAsserCalls_succeeds_withAction(self):
- with self.assertCall(
- self.call.adb.Shell('echo hello'), self.ShellError()):
+ with self.assertCall(self.call.adb.Shell('echo hello'), self.ShellError()):
with self.assertRaises(ValueError):
self.adb.Shell('echo hello')
@@ -170,4 +164,3 @@ class TestCaseWithAssertCallsTest(mock_calls.TestCase):
if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
unittest.main(verbosity=2)
-
diff --git a/catapult/devil/devil/utils/parallelizer.py b/catapult/devil/devil/utils/parallelizer.py
index 678066c7..930d01f9 100644
--- a/catapult/devil/devil/utils/parallelizer.py
+++ b/catapult/devil/devil/utils/parallelizer.py
@@ -1,7 +1,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
""" Wrapper that allows method execution in parallel.
This class wraps a list of objects of the same type, emulates their
@@ -113,11 +112,11 @@ class Parallelizer(object):
raise AttributeError("'%s' is not callable" % o.__name__)
r = type(self)(self._orig_objs)
- r._objs = reraiser_thread.ReraiserThreadGroup(
- [reraiser_thread.ReraiserThread(
- o, args=args, kwargs=kwargs,
- name='%s.%s' % (str(d), o.__name__))
- for d, o in zip(self._orig_objs, self._objs)])
+ r._objs = reraiser_thread.ReraiserThreadGroup([
+ reraiser_thread.ReraiserThread(
+ o, args=args, kwargs=kwargs, name='%s.%s' % (str(d), o.__name__))
+ for d, o in zip(self._orig_objs, self._objs)
+ ])
r._objs.StartAll()
return r
@@ -169,11 +168,14 @@ class Parallelizer(object):
"""
self._assertNoShadow('pMap')
r = type(self)(self._orig_objs)
- r._objs = reraiser_thread.ReraiserThreadGroup(
- [reraiser_thread.ReraiserThread(
- f, args=tuple([o] + list(args)), kwargs=kwargs,
+ r._objs = reraiser_thread.ReraiserThreadGroup([
+ reraiser_thread.ReraiserThread(
+ f,
+ args=tuple([o] + list(args)),
+ kwargs=kwargs,
name='%s(%s)' % (f.__name__, d))
- for d, o in zip(self._orig_objs, self._objs)])
+ for d, o in zip(self._orig_objs, self._objs)
+ ])
r._objs.StartAll()
return r
@@ -262,4 +264,3 @@ class SyncParallelizer(Parallelizer):
r = super(SyncParallelizer, self).pMap(f, *args, **kwargs)
r.pFinish(None)
return r
-
diff --git a/catapult/devil/devil/utils/parallelizer_test.py b/catapult/devil/devil/utils/parallelizer_test.py
index acbb986e..7c86148c 100644
--- a/catapult/devil/devil/utils/parallelizer_test.py
+++ b/catapult/devil/devil/utils/parallelizer_test.py
@@ -2,7 +2,6 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Unit tests for the contents of parallelizer.py."""
# pylint: disable=protected-access
@@ -16,8 +15,8 @@ import sys
import unittest
if __name__ == '__main__':
- sys.path.append(os.path.abspath(
- os.path.join(os.path.dirname(__file__), '..', '..')))
+ sys.path.append(
+ os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
from devil.utils import parallelizer
@@ -69,7 +68,6 @@ class ParallelizerTestObject(object):
class ParallelizerTestObjectHelper(object):
-
def __init__(self, thing):
self._thing = thing
@@ -78,7 +76,6 @@ class ParallelizerTestObjectHelper(object):
class ParallelizerTest(unittest.TestCase):
-
def testInitEmptyList(self):
r = parallelizer.Parallelizer([]).replace('a', 'b').pGet(0.1)
self.assertEquals([], r)
@@ -97,15 +94,17 @@ class ParallelizerTest(unittest.TestCase):
def testAllReturn(self):
devices = [ParallelizerTestObject(True) for _ in xrange(0, 10)]
- results = ParallelizerTestObject.parallel(
- devices).doReturnTheThing().pGet(1)
+ results = ParallelizerTestObject.parallel(devices).doReturnTheThing().pGet(
+ 1)
self.assertTrue(isinstance(results, list))
self.assertEquals(10, len(results))
self.assertTrue(all(results))
def testAllRaise(self):
- devices = [ParallelizerTestObject(Exception('thing %d' % i))
- for i in xrange(0, 10)]
+ devices = [
+ ParallelizerTestObject(Exception('thing %d' % i))
+ for i in xrange(0, 10)
+ ]
p = ParallelizerTestObject.parallel(devices).doRaiseTheThing()
with self.assertRaises(Exception):
p.pGet(1)
@@ -116,13 +115,16 @@ class ParallelizerTest(unittest.TestCase):
exception_msg = 'thing %d' % exception_index
try:
- completion_files = [tempfile.NamedTemporaryFile(delete=False)
- for _ in xrange(0, parallel_device_count)]
+ completion_files = [
+ tempfile.NamedTemporaryFile(delete=False)
+ for _ in xrange(0, parallel_device_count)
+ ]
devices = [
ParallelizerTestObject(
i if i != exception_index else Exception(exception_msg),
completion_files[i].name)
- for i in xrange(0, parallel_device_count)]
+ for i in xrange(0, parallel_device_count)
+ ]
for f in completion_files:
f.close()
p = ParallelizerTestObject.parallel(devices)
@@ -151,8 +153,8 @@ class ParallelizerTest(unittest.TestCase):
def testContained(self):
devices = [ParallelizerTestObject(i) for i in xrange(0, 10)]
- results = (ParallelizerTestObject.parallel(devices).helper
- .doReturnStringThing().pGet(1))
+ results = (ParallelizerTestObject.parallel(devices).helper.
+ doReturnStringThing().pGet(1))
self.assertTrue(isinstance(results, list))
self.assertEquals(10, len(results))
for i in xrange(0, 10):
@@ -165,7 +167,6 @@ class ParallelizerTest(unittest.TestCase):
class SyncParallelizerTest(unittest.TestCase):
-
def testContextManager(self):
in_context = [False for i in xrange(10)]
@@ -187,4 +188,3 @@ class SyncParallelizerTest(unittest.TestCase):
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/catapult/devil/devil/utils/reraiser_thread.py b/catapult/devil/devil/utils/reraiser_thread.py
index 6e6c810b..e48dda92 100644
--- a/catapult/devil/devil/utils/reraiser_thread.py
+++ b/catapult/devil/devil/utils/reraiser_thread.py
@@ -1,7 +1,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Thread and ThreadGroup that reraise exceptions on the main thread."""
# pylint: disable=W0212
@@ -17,6 +16,7 @@ from devil.utils import watchdog_timer
class TimeoutError(base_error.BaseError):
"""Module-specific timeout exception."""
+
def __init__(self, message):
super(TimeoutError, self).__init__(message)
@@ -69,13 +69,14 @@ class ReraiserThread(threading.Thread):
self._exc_info = None
self._thread_group = None
- if sys.version_info < (3,):
+ if sys.version_info < (3, ):
# pylint: disable=exec-used
- exec('''def ReraiseIfException(self):
+ exec ('''def ReraiseIfException(self):
"""Reraise exception if an exception was raised in the thread."""
if self._exc_info:
raise self._exc_info[0], self._exc_info[1], self._exc_info[2]''')
else:
+
def ReraiseIfException(self):
"""Reraise exception if an exception was raised in the thread."""
if self._exc_info:
diff --git a/catapult/devil/devil/utils/reraiser_thread_unittest.py b/catapult/devil/devil/utils/reraiser_thread_unittest.py
index e3c4e6be..eb37456c 100644
--- a/catapult/devil/devil/utils/reraiser_thread_unittest.py
+++ b/catapult/devil/devil/utils/reraiser_thread_unittest.py
@@ -1,7 +1,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Unittests for reraiser_thread.py."""
import threading
@@ -52,7 +51,7 @@ class TestReraiserThreadGroup(unittest.TestCase):
ran[i] = True
group = reraiser_thread.ReraiserThreadGroup(
- [reraiser_thread.ReraiserThread(f, args=[i]) for i in range(5)])
+ [reraiser_thread.ReraiserThread(f, args=[i]) for i in range(5)])
group.StartAll()
group.JoinAll()
for v in ran:
@@ -75,8 +74,9 @@ class TestReraiserThreadGroup(unittest.TestCase):
def testJoinRaise(self):
def f():
raise TestException
+
group = reraiser_thread.ReraiserThreadGroup(
- [reraiser_thread.ReraiserThread(f) for _ in xrange(5)])
+ [reraiser_thread.ReraiserThread(f) for _ in xrange(5)])
group.StartAll()
with self.assertRaises(TestException):
group.JoinAll()
@@ -84,10 +84,12 @@ class TestReraiserThreadGroup(unittest.TestCase):
def testJoinTimeout(self):
def f():
pass
+
event = threading.Event()
def g():
event.wait()
+
group = reraiser_thread.ReraiserThreadGroup(
[reraiser_thread.ReraiserThread(g),
reraiser_thread.ReraiserThread(f)])
@@ -113,5 +115,6 @@ class TestRunAsync(unittest.TestCase):
self.assertEqual(1, a)
self.assertEqual(2, b)
+
if __name__ == '__main__':
unittest.main()
diff --git a/catapult/devil/devil/utils/reset_usb.py b/catapult/devil/devil/utils/reset_usb.py
index 404a44c6..d2f80b07 100755
--- a/catapult/devil/devil/utils/reset_usb.py
+++ b/catapult/devil/devil/utils/reset_usb.py
@@ -15,8 +15,7 @@ import re
if __name__ == '__main__':
sys.path.append(
- os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', '..')))
+ os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
from devil.android import device_errors
from devil.utils import lsusb
@@ -56,8 +55,8 @@ def reset_android_usb(serial):
reset_usb(bus, device)
else:
raise device_errors.DeviceUnreachableError(
- 'Unable to determine bus(%s) or device(%s) for device %s'
- % (bus, device, serial))
+ 'Unable to determine bus(%s) or device(%s) for device %s' %
+ (bus, device, serial))
def reset_all_android_devices():
@@ -75,17 +74,13 @@ def _reset_all_matching(condition):
reset_usb(bus, device)
serial = lsusb.get_lsusb_serial(device_info)
if serial:
- logger.info(
- 'Reset USB device (bus: %03d, device: %03d, serial: %s)',
- bus, device, serial)
+ logger.info('Reset USB device (bus: %03d, device: %03d, serial: %s)',
+ bus, device, serial)
else:
- logger.info(
- 'Reset USB device (bus: %03d, device: %03d)',
- bus, device)
+ logger.info('Reset USB device (bus: %03d, device: %03d)', bus, device)
except IOError:
- logger.error(
- 'Failed to reset USB device (bus: %03d, device: %03d)',
- bus, device)
+ logger.error('Failed to reset USB device (bus: %03d, device: %03d)',
+ bus, device)
def main():
@@ -111,4 +106,3 @@ def main():
if __name__ == '__main__':
sys.exit(main())
-
diff --git a/catapult/devil/devil/utils/run_tests_helper.py b/catapult/devil/devil/utils/run_tests_helper.py
index 0b9dd47f..b161fd74 100644
--- a/catapult/devil/devil/utils/run_tests_helper.py
+++ b/catapult/devil/devil/utils/run_tests_helper.py
@@ -1,7 +1,6 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Helper functions common to native, java and host-driven test runners."""
import collections
@@ -9,12 +8,10 @@ import logging
from devil.utils import logging_common
-
CustomFormatter = logging_common.CustomFormatter
-
-_WrappedLoggingArgs = collections.namedtuple(
- '_WrappedLoggingArgs', ['verbose', 'quiet'])
+_WrappedLoggingArgs = collections.namedtuple('_WrappedLoggingArgs',
+ ['verbose', 'quiet'])
def SetLogLevel(verbose_count, add_handler=True):
diff --git a/catapult/devil/devil/utils/timeout_retry.py b/catapult/devil/devil/utils/timeout_retry.py
index d662c1d2..8b26ac70 100644
--- a/catapult/devil/devil/utils/timeout_retry.py
+++ b/catapult/devil/devil/utils/timeout_retry.py
@@ -1,7 +1,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""A utility to run functions with timeouts and retries."""
# pylint: disable=W0702
@@ -16,7 +15,6 @@ logger = logging.getLogger(__name__)
class TimeoutRetryThreadGroup(reraiser_thread.ReraiserThreadGroup):
-
def __init__(self, timeout, threads=None):
super(TimeoutRetryThreadGroup, self).__init__(threads)
self._watcher = watchdog_timer.WatchdogTimer(timeout)
@@ -107,8 +105,8 @@ def WaitFor(condition, wait_period=5, max_tries=None):
return result
if timeout_thread_group:
# pylint: disable=no-member
- timeout_thread_group.GetRemainingTime(wait_period,
- suffix=' waiting for condition %r' % condition_name)
+ timeout_thread_group.GetRemainingTime(
+ wait_period, suffix=' waiting for condition %r' % condition_name)
if wait_period:
time.sleep(wait_period)
return None
@@ -118,8 +116,14 @@ def AlwaysRetry(_exception):
return True
-def Run(func, timeout, retries, args=None, kwargs=None, desc=None,
- error_log_func=logging.critical, retry_if_func=AlwaysRetry):
+def Run(func,
+ timeout,
+ retries,
+ args=None,
+ kwargs=None,
+ desc=None,
+ error_log_func=logging.critical,
+ retry_if_func=AlwaysRetry):
"""Runs the passed function in a separate thread with timeouts and retries.
Args:
@@ -148,14 +152,16 @@ def Run(func, timeout, retries, args=None, kwargs=None, desc=None,
while True:
thread_name = 'TimeoutThread-%d-for-%s' % (num_try,
threading.current_thread().name)
- child_thread = reraiser_thread.ReraiserThread(lambda: func(*args, **kwargs),
- name=thread_name)
+ child_thread = reraiser_thread.ReraiserThread(
+ lambda: func(*args, **kwargs), name=thread_name)
try:
thread_group = TimeoutRetryThreadGroup(timeout, threads=[child_thread])
thread_group.StartAll(will_block=True)
while True:
- thread_group.JoinAll(watcher=thread_group.GetWatcher(), timeout=60,
- error_log_func=error_log_func)
+ thread_group.JoinAll(
+ watcher=thread_group.GetWatcher(),
+ timeout=60,
+ error_log_func=error_log_func)
if thread_group.IsAlive():
logger.info('Still working on %s', desc)
else:
@@ -168,7 +174,6 @@ def Run(func, timeout, retries, args=None, kwargs=None, desc=None,
except Exception as e: # pylint: disable=broad-except
if num_try > retries or not retry_if_func(e):
raise
- error_log_func(
- '(%s) Exception on %s, attempt %d of %d: %r',
- thread_name, desc, num_try, retries + 1, e)
+ error_log_func('(%s) Exception on %s, attempt %d of %d: %r', thread_name,
+ desc, num_try, retries + 1, e)
num_try += 1
diff --git a/catapult/devil/devil/utils/timeout_retry_unittest.py b/catapult/devil/devil/utils/timeout_retry_unittest.py
index 0eeb31a4..cf97bb9b 100755
--- a/catapult/devil/devil/utils/timeout_retry_unittest.py
+++ b/catapult/devil/devil/utils/timeout_retry_unittest.py
@@ -2,7 +2,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""Unittests for timeout_and_retry.py."""
import logging
@@ -12,7 +11,6 @@ import unittest
from devil.utils import reraiser_thread
from devil.utils import timeout_retry
-
_DEFAULT_TIMEOUT = .1
@@ -29,8 +27,7 @@ class TestRun(unittest.TestCase):
"""Tests for timeout_retry.Run."""
def testRun(self):
- self.assertTrue(timeout_retry.Run(
- lambda x: x, 30, 3, [True], {}))
+ self.assertTrue(timeout_retry.Run(lambda x: x, 30, 3, [True], {}))
def testTimeout(self):
tries = [0]
@@ -40,22 +37,34 @@ class TestRun(unittest.TestCase):
time.sleep(1)
self.assertRaises(
- reraiser_thread.TimeoutError, timeout_retry.Run, _sleep, .01, 1,
+ reraiser_thread.TimeoutError,
+ timeout_retry.Run,
+ _sleep,
+ .01,
+ 1,
error_log_func=logging.debug)
self.assertEqual(tries[0], 2)
def testRetries(self):
tries = [0]
self.assertRaises(
- TestException, timeout_retry.Run, lambda: _CountTries(tries),
- _DEFAULT_TIMEOUT, 3, error_log_func=logging.debug)
+ TestException,
+ timeout_retry.Run,
+ lambda: _CountTries(tries),
+ _DEFAULT_TIMEOUT,
+ 3,
+ error_log_func=logging.debug)
self.assertEqual(tries[0], 4)
def testNoRetries(self):
tries = [0]
self.assertRaises(
- TestException, timeout_retry.Run, lambda: _CountTries(tries),
- _DEFAULT_TIMEOUT, 0, error_log_func=logging.debug)
+ TestException,
+ timeout_retry.Run,
+ lambda: _CountTries(tries),
+ _DEFAULT_TIMEOUT,
+ 0,
+ error_log_func=logging.debug)
self.assertEqual(tries[0], 1)
def testReturnValue(self):
@@ -70,7 +79,8 @@ class TestRun(unittest.TestCase):
self.assertEqual(current_thread_group,
timeout_retry.CurrentTimeoutThreadGroup())
return True
- return reraiser_thread.RunAsync((InnerInnerFunc,))[0]
+
+ return reraiser_thread.RunAsync((InnerInnerFunc, ))[0]
self.assertTrue(timeout_retry.Run(InnerFunc, _DEFAULT_TIMEOUT, 3))
diff --git a/catapult/devil/devil/utils/update_dependencies.py b/catapult/devil/devil/utils/update_dependencies.py
new file mode 100755
index 00000000..df1f88b2
--- /dev/null
+++ b/catapult/devil/devil/utils/update_dependencies.py
@@ -0,0 +1,218 @@
+#! /usr/bin/env python
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Updates the chromium binaries used by devil.
+
+This currently must be called from the top-level chromium src directory.
+"""
+
+import argparse
+import collections
+import json
+import logging
+import os
+import sys
+
+_DEVIL_ROOT_DIR = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
+
+sys.path.append(_DEVIL_ROOT_DIR)
+from devil import base_error
+from devil import devil_env
+from devil.utils import cmd_helper
+
+_DEVICE_ARCHS = [
+ {
+ 'cpu': 'arm',
+ 'platform': 'android_armeabi-v7a',
+ },
+ {
+ 'cpu': 'arm64',
+ 'platform': 'android_arm64-v8a',
+ },
+ {
+ 'cpu': 'x86',
+ 'platform': 'android_x86',
+ },
+ {
+ 'cpu': 'x64',
+ 'platform': 'android_x86_64',
+ },
+]
+_HOST_ARCH = [{
+ # Host binaries use x86_64, not arm, but they build with the
+ # host toolchain within a target_cpu="arm" build.
+ 'cpu': 'arm',
+ 'platform': 'linux2_x86_64',
+}]
+
+_CHROMIUM_DEPS = {
+ 'chromium_commands': {
+ 'archs': _HOST_ARCH,
+ 'build_path': 'lib.java/chromium_commands.dex.jar',
+ 'target_name': 'chromium_commands_java',
+ },
+ 'forwarder_device': {
+ 'archs': _DEVICE_ARCHS,
+ 'build_path': 'device_forwarder',
+ 'target_name': 'forwarder2',
+ },
+ 'forwarder_host': {
+ 'archs': _HOST_ARCH,
+ 'build_path': 'clang_x64/host_forwarder',
+ 'target_name': 'forwarder2',
+ },
+ 'md5sum_device': {
+ 'archs': _DEVICE_ARCHS,
+ 'build_path': 'md5sum_bin',
+ 'target_name': 'md5sum',
+ },
+ 'md5sum_host': {
+ 'archs': _HOST_ARCH,
+ 'build_path': 'clang_x64/md5sum_bin',
+ 'target_name': 'md5sum',
+ },
+}
+
+
+def BuildTargetsForCpu(targets, cpu, output_dir):
+ logging.info('Building %s', cpu)
+
+ gn_args = [
+ 'ffmpeg_branding="Chrome"',
+ 'is_component_build=false',
+ 'is_debug=false',
+ 'proprietary_codecs=true',
+ 'symbol_level=1',
+ 'target_cpu="%s"' % cpu,
+ 'target_os="android"',
+ 'use_goma=true',
+ ]
+
+ cmd = ['gn', 'gen', '--args=%s' % (' '.join(gn_args)), output_dir]
+ ec = cmd_helper.RunCmd(cmd)
+ if ec:
+ raise base_error.BaseError('%s failed with %d' % (cmd, ec))
+
+ ec = cmd_helper.RunCmd(['autoninja', '-C', output_dir] + targets)
+ if ec:
+ raise base_error.BaseError('building %s failed with %d' % (cpu, ec))
+
+
+def UpdateDependency(dependency_name, dependency_info, local_path, platform):
+ bucket = dependency_info['cloud_storage_bucket']
+ folder = dependency_info['cloud_storage_base_folder']
+
+ # determine the hash
+ ec, sha1sum_output = cmd_helper.GetCmdStatusAndOutput(['sha1sum', local_path])
+ if ec:
+ raise base_error.BaseError(
+ 'Failed to determine SHA1 for %s: %s' % (local_path, sha1sum_output))
+
+ dependency_sha1 = sha1sum_output.split()[0]
+
+ # upload
+ remote_path = '%s_%s' % (dependency_name, dependency_sha1)
+ gs_dest = 'gs://%s/%s/%s' % (bucket, folder, remote_path)
+ ec, gsutil_output = cmd_helper.GetCmdStatusAndOutput(
+ ['gsutil', 'cp', local_path, gs_dest])
+ if ec:
+ raise base_error.BaseError(
+ 'Failed to upload %s to %s: %s' % (remote_path, gs_dest, gsutil_output))
+
+ # update entry in json
+ file_info = dependency_info['file_info']
+ if platform not in file_info:
+ file_info[platform] = {
+ 'cloud_storage_hash': '',
+ # the user will need to manually update the download path after
+ # uploading a previously unknown dependency.
+ 'download_path': 'FIXME',
+ }
+ file_info[platform]['cloud_storage_hash'] = dependency_sha1
+
+
+def UpdateChromiumDependencies(dependencies, args):
+ deps_by_platform = collections.defaultdict(list)
+ for dep_name, dep_info in _CHROMIUM_DEPS.iteritems():
+ archs = dep_info.get('archs', [])
+ for a in archs:
+ deps_by_platform[(a.get('cpu'), a.get('platform'))].append(
+ (dep_name, dep_info.get('build_path'), dep_info.get('target_name')))
+
+ for arch, arch_deps in deps_by_platform.iteritems():
+ targets = [target_name for _n, _b, target_name in arch_deps]
+ cpu, platform = arch
+ output_dir = os.path.join(args.chromium_src_dir, 'out-devil-deps', platform)
+ BuildTargetsForCpu(targets, cpu, output_dir)
+
+ for dep_name, build_path, _ in arch_deps:
+ local_path = os.path.abspath(os.path.join(output_dir, build_path))
+ UpdateDependency(dep_name,
+ dependencies.get('dependencies', {}).get(dep_name, {}),
+ local_path, platform)
+
+ return dependencies
+
+
+def UpdateGivenDependency(dependencies, args):
+ dep_name = args.name or os.path.basename(args.path)
+ if not dep_name in dependencies.get('dependencies', {}):
+ raise base_error.BaseError('Could not find dependency "%s" in %s' %
+ (dep_name, args.dependencies_json))
+
+ UpdateDependency(dep_name,
+ dependencies.get('dependencies', {}).get(dep_name, {}),
+ args.path, args.platform)
+
+ return dependencies
+
+
+def main(raw_args):
+ parser = argparse.ArgumentParser(description=__doc__)
+
+ # pylint: disable=protected-access
+ parser.add_argument(
+ '--dependencies-json',
+ type=os.path.abspath,
+ default=devil_env._DEVIL_DEFAULT_CONFIG,
+ help='Binary dependency configuration file to update.')
+ # pylint: enable=protected-access
+
+ subparsers = parser.add_subparsers()
+ chromium_parser = subparsers.add_parser('chromium')
+ chromium_parser.add_argument(
+ '--chromium-src-dir',
+ type=os.path.realpath,
+ default=os.getcwd(),
+ help='Path to chromium/src checkout root.')
+ chromium_parser.set_defaults(update_dependencies=UpdateChromiumDependencies)
+
+ dependency_parser = subparsers.add_parser('dependency')
+ dependency_parser.add_argument('--name', help='Name of dependency to update.')
+ dependency_parser.add_argument(
+ '--path',
+ type=os.path.abspath,
+ help='Path to file to upload as new version of dependency.')
+ dependency_parser.add_argument(
+ '--platform', help='Platform of dependency to update.')
+ dependency_parser.set_defaults(update_dependencies=UpdateGivenDependency)
+
+ args = parser.parse_args(raw_args)
+
+ logging.getLogger().setLevel(logging.INFO)
+
+ with open(args.dependencies_json) as f:
+ dependencies = json.load(f)
+
+ dependencies = args.update_dependencies(dependencies, args)
+
+ with open(args.dependencies_json, 'w') as f:
+ json.dump(dependencies, f, indent=2, separators=(',', ': '), sort_keys=True)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/catapult/devil/devil/utils/usb_hubs.py b/catapult/devil/devil/utils/usb_hubs.py
index bd984c7b..313cf3f6 100644
--- a/catapult/devil/devil/utils/usb_hubs.py
+++ b/catapult/devil/devil/utils/usb_hubs.py
@@ -2,25 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-PLUGABLE_7PORT_LAYOUT = {1:7,
- 2:6,
- 3:5,
- 4:{1:4, 2:3, 3:2, 4:1}}
-
-PLUGABLE_7PORT_USB3_LAYOUT = {1:{1:1, 2:2, 3:3, 4:4},
- 2:5,
- 3:6,
- 4:7}
-
-KEEDOX_LAYOUT = {1:1,
- 2:2,
- 3:3,
- 4:{1:4, 2:5, 3:6, 4:7}}
-
-VIA_LAYOUT = {1:1,
- 2:2,
- 3:3,
- 4:{1:4, 2:5, 3:6, 4:7}}
+PLUGABLE_7PORT_LAYOUT = {1: 7, 2: 6, 3: 5, 4: {1: 4, 2: 3, 3: 2, 4: 1}}
+
+PLUGABLE_7PORT_USB3_LAYOUT = {1: {1: 1, 2: 2, 3: 3, 4: 4}, 2: 5, 3: 6, 4: 7}
+
+KEEDOX_LAYOUT = {1: 1, 2: 2, 3: 3, 4: {1: 4, 2: 5, 3: 6, 4: 7}}
+
+VIA_LAYOUT = {1: 1, 2: 2, 3: 3, 4: {1: 4, 2: 5, 3: 6, 4: 7}}
+
class HubType(object):
def __init__(self, id_func, port_mapping):
@@ -92,6 +81,7 @@ class HubType(object):
else:
yield (physical, node.PortToDevice(virtual))
+
def _is_plugable_7port_hub(node):
"""Check if a node is a Plugable 7-Port Hub
(Model USB2-HUB7BC)
@@ -104,11 +94,13 @@ def _is_plugable_7port_hub(node):
return False
return '1a40:0101' in node.PortToDevice(4).desc
+
# Plugable 7-Port USB-3 Hubs show up twice in the USB devices list; they have
# two different "branches", one which has USB2 devices and one which has
# USB3 devices. The "part2" is the "USB-2" branch of the hub, the
# "part3" is the "USB-3" branch of the hub.
+
def _is_plugable_7port_usb3_part2_hub(node):
"""Check if a node is the "USB2 branch" of
a Plugable 7-Port USB-3 Hub (Model USB3-HUB7BC)
@@ -121,6 +113,7 @@ def _is_plugable_7port_usb3_part2_hub(node):
return False
return '2109:2811' in node.PortToDevice(1).desc
+
def _is_plugable_7port_usb3_part3_hub(node):
"""Check if a node is the "USB3 branch" of
a Plugable 7-Port USB-3 Hub (Model USB3-HUB7BC)
@@ -133,6 +126,7 @@ def _is_plugable_7port_usb3_part3_hub(node):
return False
return '2109:8110' in node.PortToDevice(1).desc
+
def _is_keedox_hub(node):
"""Check if a node is a Keedox hub.
The topology of this device is a 4-port hub,
@@ -144,6 +138,7 @@ def _is_keedox_hub(node):
return False
return '0bda:5411' in node.PortToDevice(4).desc
+
def _is_via_hub(node):
"""Check if a node is a Via Labs hub.
The topology of this device is a 4-port hub,
@@ -153,8 +148,8 @@ def _is_via_hub(node):
return False
if not node.HasPort(4):
return False
- return ('2109:2812' in node.PortToDevice(4).desc or
- '2109:0812' in node.PortToDevice(4).desc)
+ return ('2109:2812' in node.PortToDevice(4).desc
+ or '2109:0812' in node.PortToDevice(4).desc)
PLUGABLE_7PORT = HubType(_is_plugable_7port_hub, PLUGABLE_7PORT_LAYOUT)
@@ -165,11 +160,11 @@ PLUGABLE_7PORT_USB3_PART3 = HubType(_is_plugable_7port_usb3_part3_hub,
KEEDOX = HubType(_is_keedox_hub, KEEDOX_LAYOUT)
VIA = HubType(_is_via_hub, VIA_LAYOUT)
-ALL_HUBS = [PLUGABLE_7PORT,
- PLUGABLE_7PORT_USB3_PART2,
- PLUGABLE_7PORT_USB3_PART3,
- KEEDOX,
- VIA]
+ALL_HUBS = [
+ PLUGABLE_7PORT, PLUGABLE_7PORT_USB3_PART2, PLUGABLE_7PORT_USB3_PART3,
+ KEEDOX, VIA
+]
+
def GetHubType(type_name):
if type_name == 'plugable_7port':
diff --git a/catapult/devil/devil/utils/watchdog_timer.py b/catapult/devil/devil/utils/watchdog_timer.py
index bff1f8cc..2c8a2250 100644
--- a/catapult/devil/devil/utils/watchdog_timer.py
+++ b/catapult/devil/devil/utils/watchdog_timer.py
@@ -1,7 +1,6 @@
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
"""WatchdogTimer timeout objects."""
import time
diff --git a/catapult/devil/devil/utils/zip_utils.py b/catapult/devil/devil/utils/zip_utils.py
index e1f812b7..35c59056 100644
--- a/catapult/devil/devil/utils/zip_utils.py
+++ b/catapult/devil/devil/utils/zip_utils.py
@@ -9,18 +9,19 @@ import os
import sys
import zipfile
+_DEVIL_ROOT_DIR = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
if __name__ == '__main__':
- _DEVIL_ROOT_DIR = os.path.abspath(
- os.path.join(os.path.dirname(__file__), '..', '..'))
- _PY_UTILS_ROOT_DIR = os.path.abspath(
- os.path.join(_DEVIL_ROOT_DIR, '..', 'common', 'py_utils'))
- sys.path.extend((_DEVIL_ROOT_DIR, _PY_UTILS_ROOT_DIR))
+ sys.path.append(_DEVIL_ROOT_DIR)
+
+_PY_UTILS_ROOT_DIR = os.path.abspath(
+ os.path.join(_DEVIL_ROOT_DIR, '..', 'common', 'py_utils'))
+sys.path.append(_PY_UTILS_ROOT_DIR)
from devil import base_error
from devil.utils import cmd_helper
from py_utils import tempfile_ext
-
logger = logging.getLogger(__name__)
@@ -72,16 +73,16 @@ def WriteZipFile(zip_path, zip_contents):
ZipFailedError on failure.
"""
zip_spec = {
- 'zip_path': zip_path,
- 'zip_contents': zip_contents,
+ 'zip_path': zip_path,
+ 'zip_contents': zip_contents,
}
with tempfile_ext.NamedTemporaryDirectory() as tmpdir:
json_path = os.path.join(tmpdir, 'zip_spec.json')
with open(json_path, 'w') as json_file:
json.dump(zip_spec, json_file)
- ret, output, error = cmd_helper.GetCmdStatusOutputAndError([
- sys.executable, os.path.abspath(__file__),
- '--zip-spec', json_path])
+ ret, output, error = cmd_helper.GetCmdStatusOutputAndError(
+ [sys.executable,
+ os.path.abspath(__file__), '--zip-spec', json_path])
if ret != 0:
exc_msg = ['Failed to create %s' % zip_path]
diff --git a/catapult/devil/devil/utils/zip_utils_test.py b/catapult/devil/devil/utils/zip_utils_test.py
index 4564e3f1..96a55f55 100644
--- a/catapult/devil/devil/utils/zip_utils_test.py
+++ b/catapult/devil/devil/utils/zip_utils_test.py
@@ -11,7 +11,6 @@ from py_utils import tempfile_ext
class WriteZipFileTest(unittest.TestCase):
-
def testSimple(self):
with tempfile_ext.NamedTemporaryDirectory() as working_dir:
file1 = os.path.join(working_dir, 'file1.txt')
@@ -23,8 +22,8 @@ class WriteZipFileTest(unittest.TestCase):
f2.write('file2')
zip_tuples = [
- (file1, 'foo/file1.txt'),
- (file2, 'bar/file2.txt'),
+ (file1, 'foo/file1.txt'),
+ (file2, 'bar/file2.txt'),
]
zip_path = os.path.join(working_dir, 'out.zip')
@@ -34,10 +33,8 @@ class WriteZipFileTest(unittest.TestCase):
actual = zipfile.ZipFile(zip_path)
expected_files = [
- 'foo/file1.txt',
- 'bar/file2.txt',
+ 'foo/file1.txt',
+ 'bar/file2.txt',
]
- self.assertEquals(
- sorted(expected_files),
- sorted(actual.namelist()))
+ self.assertEquals(sorted(expected_files), sorted(actual.namelist()))
diff --git a/catapult/devil/docs/adb_wrapper.md b/catapult/devil/docs/adb_wrapper.md
index a8dc3b05..3ca8aa68 100644
--- a/catapult/devil/docs/adb_wrapper.md
+++ b/catapult/devil/docs/adb_wrapper.md
@@ -1,132 +1,58 @@
# [devil.android.sdk.adb_wrapper](https://github.com/catapult-project/catapult/blob/master/devil/devil/android/sdk/adb_wrapper.py)
-*This page was autogenerated by `devil/utils/markdown.py --module-link https://github.com/catapult-project/catapult/blob/master/devil/devil/android/sdk/adb_wrapper.py`*
+*This page was autogenerated. Run `devil/bin/generate_md_docs` to update*
-## DeviceStat
-
-DeviceStat(st\_mode, st\_size, st\_time)
-### DeviceStat.\_\_repr\_\_
-
-Return a nicely formatted representation string
-### DeviceStat.\_\_getnewargs\_\_
-
-Return self as a plain tuple. Used by copy and pickle.
-### DeviceStat.\_\_getstate\_\_
-
-Exclude the OrderedDict from pickling
## AdbWrapper
A wrapper around a local Android Debug Bridge executable.
-### AdbWrapper.GetDeviceSerial
-
-Gets the device serial number associated with this object.
-```
- Returns:
- Device serial number as a string.
-```
-
-
-### AdbWrapper.Push
-
-Pushes a file from the host to the device.
-```
- Args:
- local: Path on the host filesystem.
- remote: Path on the device filesystem.
- timeout: (optional) Timeout per try in seconds.
- retries: (optional) Number of retries to attempt.
-```
-
-
-### AdbWrapper.Pull
+### AdbWrapper.Backup
-Pulls a file from the device to the host.
+Write an archive of the device's data to |path|.
```
Args:
- remote: Path on the device filesystem.
- local: Path on the host filesystem.
+ path: Local path to store the backup file.
+ packages: List of to packages to be backed up.
+ apk: (optional) If set include the .apk files in the archive.
+ shared: (optional) If set buckup the device's SD card.
+ nosystem: (optional) If set exclude system applications.
+ include_all: (optional) If set back up all installed applications and
+ |packages| is optional.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
```
-### AdbWrapper.Shell
+### AdbWrapper.DisableVerity
-Runs a shell command on the device.
+Disable Marshmallow's Verity security feature.
```
- Args:
- command: A string with the shell command to run.
- expect_status: (optional) Check that the command's exit status matches
- this value. Default is 0. If set to None the test is skipped.
- timeout: (optional) Timeout per try in seconds.
- retries: (optional) Number of retries to attempt.
-
Returns:
- The output of the shell command as a string.
-
- Raises:
- device_errors.AdbCommandFailedError: If the exit status doesn't match
- |expect_status|.
+ The output of the disable-verity command as a string.
```
-### AdbWrapper.IterShell
-
-Runs a shell command and returns an iterator over its output lines.
-```
- Args:
- command: A string with the shell command to run.
- timeout: Timeout in seconds.
+### AdbWrapper.Emu
- Yields:
- The output of the command line by line.
+Runs an emulator console command.
```
+ See http://developer.android.com/tools/devices/emulator.html#console
-
-### AdbWrapper.Ls
-
-List the contents of a directory on the device.
-```
Args:
- path: Path on the device filesystem.
+ cmd: The command to run on the emulator console.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
Returns:
- A list of pairs (filename, stat) for each file found in the directory,
- where the stat object has the properties: st_mode, st_size, and st_time.
-
- Raises:
- AdbCommandFailedError if |path| does not specify a valid and accessible
- directory in the device, or the output of "adb ls" command is less
- than four columns
+ The output of the emulator console command.
```
-### AdbWrapper.Logcat
+### AdbWrapper.EnableVerity
-Get an iterable over the logcat output.
+Enable Marshmallow's Verity security feature.
```
- Args:
- clear: If true, clear the logcat.
- dump: If true, dump the current logcat contents.
- filter_specs: If set, a list of specs to filter the logcat.
- logcat_format: If set, the format in which the logcat should be output.
- Options include "brief", "process", "tag", "thread", "raw", "time",
- "threadtime", and "long"
- ring_buffer: If set, a list of alternate ring buffers to request.
- Options include "main", "system", "radio", "events", "crash" or "all".
- The default is equivalent to ["main", "system", "crash"].
- iter_timeout: If set and neither clear nor dump is set, the number of
- seconds to wait between iterations. If no line is found before the
- given number of seconds elapses, the iterable will yield None.
- timeout: (optional) If set, timeout per try in seconds. If clear or dump
- is set, defaults to DEFAULT_TIMEOUT.
- retries: (optional) If clear or dump is set, the number of retries to
- attempt. Otherwise, does nothing.
-
- Yields:
- logcat output line by line.
+ Returns:
+ The output of the enable-verity command as a string.
```
@@ -153,6 +79,18 @@ Forward socket connections from the local socket to the remote socket.
```
+### AdbWrapper.ForwardList
+
+List all currently forwarded socket connections.
+```
+ Args:
+ timeout: (optional) Timeout per try in seconds.
+ retries: (optional) Number of retries to attempt.
+ Returns:
+ The output of adb forward --list as a string.
+```
+
+
### AdbWrapper.ForwardRemove
Remove a forward socket connection.
@@ -164,28 +102,38 @@ Remove a forward socket connection.
```
-### AdbWrapper.ForwardList
+### AdbWrapper.GetDevPath
-List all currently forwarded socket connections.
+Gets the device path.
```
Args:
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
+
Returns:
- The output of adb forward --list as a string.
+ The device path (e.g. usb:3-4)
```
-### AdbWrapper.JDWP
+### AdbWrapper.GetDeviceSerial
-List of PIDs of processes hosting a JDWP transport.
+Gets the device serial number associated with this object.
+```
+ Returns:
+ Device serial number as a string.
+```
+
+
+### AdbWrapper.GetState
+
+Get device state.
```
Args:
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
Returns:
- A list of PIDs as strings.
+ One of 'offline', 'bootloader', or 'device'.
```
@@ -199,6 +147,10 @@ Install an apk on the device.
allow_downgrade: (optional) If set, allows for downgrades.
reinstall: (optional) If set reinstalls the app, keeping its data.
sd_card: (optional) If set installs on the SD card.
+ streaming: (optional) If not set, use default way to install.
+ If True, performs streaming install.
+ If False, app is pushed to device and be installed from there.
+ Note this option is not supported prior to adb version 1.0.40
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
```
@@ -215,96 +167,138 @@ Install an apk with splits on the device.
sd_card: (optional) If set installs on the SD card.
allow_downgrade: (optional) Allow versionCode downgrade.
partial: (optional) Package ID if apk_paths doesn't include all .apks.
+ streaming: (optional) If not set, use default way to install.
+ If True, performs streaming install.
+ If False, app is pushed to device and be installed from there.
+ Note this option is not supported prior to adb version 1.0.40
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
```
-### AdbWrapper.Uninstall
+### AdbWrapper.IterShell
-Remove the app |package| from the device.
+Runs a shell command and returns an iterator over its output lines.
```
Args:
- package: The package to uninstall.
- keep_data: (optional) If set keep the data and cache directories.
- timeout: (optional) Timeout per try in seconds.
- retries: (optional) Number of retries to attempt.
+ command: A string with the shell command to run.
+ timeout: Timeout in seconds.
+
+ Yields:
+ The output of the command line by line.
```
-### AdbWrapper.Backup
+### AdbWrapper.JDWP
-Write an archive of the device's data to |path|.
+List of PIDs of processes hosting a JDWP transport.
```
Args:
- path: Local path to store the backup file.
- packages: List of to packages to be backed up.
- apk: (optional) If set include the .apk files in the archive.
- shared: (optional) If set buckup the device's SD card.
- nosystem: (optional) If set exclude system applications.
- include_all: (optional) If set back up all installed applications and
- |packages| is optional.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
+
+ Returns:
+ A list of PIDs as strings.
```
-### AdbWrapper.Restore
+### AdbWrapper.Logcat
-Restore device contents from the backup archive.
+Get an iterable over the logcat output.
```
Args:
- path: Host path to the backup archive.
+ clear: If true, clear the logcat.
+ dump: If true, dump the current logcat contents.
+ filter_specs: If set, a list of specs to filter the logcat.
+ logcat_format: If set, the format in which the logcat should be output.
+ Options include "brief", "process", "tag", "thread", "raw", "time",
+ "threadtime", and "long"
+ ring_buffer: If set, a list of alternate ring buffers to request.
+ Options include "main", "system", "radio", "events", "crash" or "all".
+ The default is equivalent to ["main", "system", "crash"].
+ iter_timeout: If set and neither clear nor dump is set, the number of
+ seconds to wait between iterations. If no line is found before the
+ given number of seconds elapses, the iterable will yield None.
+ check_error: Whether to check the exit status of the logcat command.
+ timeout: (optional) If set, timeout per try in seconds. If clear or dump
+ is set, defaults to DEFAULT_TIMEOUT.
+ retries: (optional) If clear or dump is set, the number of retries to
+ attempt. Otherwise, does nothing.
+
+ Yields:
+ logcat output line by line.
+```
+
+
+### AdbWrapper.Ls
+
+List the contents of a directory on the device.
+```
+ Args:
+ path: Path on the device filesystem.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
+
+ Returns:
+ A list of pairs (filename, stat) for each file found in the directory,
+ where the stat object has the properties: st_mode, st_size, and st_time.
+
+ Raises:
+ AdbCommandFailedError if |path| does not specify a valid and accessible
+ directory in the device, or the output of "adb ls" command is less
+ than four columns
```
-### AdbWrapper.WaitForDevice
+### AdbWrapper.Pull
-Block until the device is online.
+Pulls a file from the device to the host.
```
Args:
+ remote: Path on the device filesystem.
+ local: Path on the host filesystem.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
```
-### AdbWrapper.GetState
+### AdbWrapper.Push
-Get device state.
+Pushes a file from the host to the device.
```
Args:
+ local: Path on the host filesystem.
+ remote: Path on the device filesystem.
+ sync: (optional) Whether to only push files that are newer on the host.
+ Not supported when using adb prior to 1.0.39.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
- Returns:
- One of 'offline', 'bootloader', or 'device'.
+ Raises:
+ AdbVersionError if sync=True with versions of adb prior to 1.0.39.
```
-### AdbWrapper.GetDevPath
+### AdbWrapper.Reboot
-Gets the device path.
+Reboots the device.
```
Args:
+ to_bootloader: (optional) If set reboots to the bootloader.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
-
- Returns:
- The device path (e.g. usb:3-4)
```
### AdbWrapper.Remount
Remounts the /system partition on the device read-write.
-### AdbWrapper.Reboot
+### AdbWrapper.Restore
-Reboots the device.
+Restore device contents from the backup archive.
```
Args:
- to_bootloader: (optional) If set reboots to the bootloader.
+ path: Host path to the backup archive.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
```
@@ -320,34 +314,58 @@ Restarts the adbd daemon with root permissions, if possible.
```
-### AdbWrapper.Emu
+### AdbWrapper.Shell
-Runs an emulator console command.
+Runs a shell command on the device.
```
- See http://developer.android.com/tools/devices/emulator.html#console
-
Args:
- cmd: The command to run on the emulator console.
+ command: A string with the shell command to run.
+ expect_status: (optional) Check that the command's exit status matches
+ this value. Default is 0. If set to None the test is skipped.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
Returns:
- The output of the emulator console command.
+ The output of the shell command as a string.
+
+ Raises:
+ device_errors.AdbCommandFailedError: If the exit status doesn't match
+ |expect_status|.
```
-### AdbWrapper.DisableVerity
+### AdbWrapper.StartShell
-Disable Marshmallow's Verity security feature
-### AdbWrapper.EnableVerity
+Starts a subprocess on the device and returns a handle to the process.
+```
+ Args:
+ args: A sequence of program arguments. The executable to run is the first
+ item in the sequence.
-Enable Marshmallow's Verity security feature
-### AdbWrapper.\_\_init\_\_
+ Returns:
+ An instance of subprocess.Popen associated with the live process.
+```
-Initializes the AdbWrapper.
+
+### AdbWrapper.Uninstall
+
+Remove the app |package| from the device.
```
Args:
- device_serial: The device serial number as a string.
+ package: The package to uninstall.
+ keep_data: (optional) If set keep the data and cache directories.
+ timeout: (optional) Timeout per try in seconds.
+ retries: (optional) Number of retries to attempt.
+```
+
+
+### AdbWrapper.WaitForDevice
+
+Block until the device is online.
+```
+ Args:
+ timeout: (optional) Timeout per try in seconds.
+ retries: (optional) Number of retries to attempt.
```
@@ -363,6 +381,17 @@ Consider instances equal if they refer to the same device.
```
+### AdbWrapper.\_\_init\_\_
+
+Initializes the AdbWrapper.
+```
+ Args:
+ device_serial: The device serial number as a string.
+```
+
+
+### AdbWrapper.\_\_repr\_\_
+
### AdbWrapper.\_\_str\_\_
The string representation of an instance.
@@ -372,8 +401,18 @@ The string representation of an instance.
```
-### AdbWrapper.\_\_repr\_\_
+## DeviceStat
+DeviceStat(st\_mode, st\_size, st\_time)
+### DeviceStat.\_\_getnewargs\_\_
+
+Return self as a plain tuple. Used by copy and pickle.
+### DeviceStat.\_\_getstate\_\_
+
+Exclude the OrderedDict from pickling
+### DeviceStat.\_\_repr\_\_
+
+Return a nicely formatted representation string
### VerifyLocalFileExists
Verifies a local file exists.
diff --git a/catapult/devil/docs/device_blacklist.md b/catapult/devil/docs/device_denylist.md
index c6eed514..0d654cdc 100644
--- a/catapult/devil/docs/device_blacklist.md
+++ b/catapult/devil/docs/device_denylist.md
@@ -3,11 +3,11 @@
found in the LICENSE file.
-->
-# Devil: Device Blacklist
+# Devil: Device Denylist
## What is it?
-The device blacklist is a per-run list of devices detected to be in a known bad
+The device denylist is a per-run list of devices detected to be in a known bad
state along with the reason they are suspected of being in a bad state (offline,
not responding, etc). It is stored as a json file. This gets reset every run
during the device recovery step (currently part of `bb_device_status_check`).
@@ -15,13 +15,13 @@ during the device recovery step (currently part of `bb_device_status_check`).
## Bots
On bots, this is normally found at `//out/bad_devices.json`. If you are having
-problems with blacklisted devices locally even though a device is in a good
+problems with denylisted devices locally even though a device is in a good
state, you can safely delete this file.
-# Tools for interacting with device black list.
+# Tools for interacting with device deny list.
-You can interact with the device blacklist via [devil.android.device\_blacklist](https://cs.chromium.org/chromium/src/third_party/catapult/devil/devil/android/device_blacklist.py).
-This allows for any interaction you would need with a device blacklist:
+You can interact with the device denylist via [devil.android.device\_denylist](https://cs.chromium.org/chromium/src/third_party/catapult/devil/devil/android/device_denylist.py).
+This allows for any interaction you would need with a device denylist:
- Reading
- Writing
@@ -30,28 +30,28 @@ This allows for any interaction you would need with a device blacklist:
An example usecase of this is:
```python
-from devil.android import device_blacklist
-
-blacklist = device_blacklist.Blacklist(blacklist_path)
-blacklisted_devices = blacklist.Read()
-for device in blacklisted_devices:
- print 'Device %s is blacklisted' % device
-blacklist.Reset()
-new_blacklist = {'device_id1': {'timestamp': ts, 'reason': reason}}
-blacklist.Write(new_blacklist)
-blacklist.Extend([device_2, device_3], reason='Reason for blacklisting')
+from devil.android import device_denylist
+
+denylist = device_denylist.Denylist(denylist_path)
+denylisted_devices = denylist.Read()
+for device in denylisted_devices:
+ print 'Device %s is denylisted' % device
+denylist.Reset()
+new_denylist = {'device_id1': {'timestamp': ts, 'reason': reason}}
+denylist.Write(new_denylist)
+denylist.Extend([device_2, device_3], reason='Reason for denylisting')
```
## Where it is used.
-The blacklist file path is passed directly to the following scripts in chromium:
+The denylist file path is passed directly to the following scripts in chromium:
- [test\_runner.py](https://cs.chromium.org/chromium/src/build/android/test_runner.py)
- [provision\_devices.py](https://cs.chromium.org/chromium/src/build/android/provision_devices.py)
- [bb\_device\_status\_check.py](https://cs.chromium.org/chromium/src/build/android/buildbot/bb_device_status_check.py)
-The blacklist is also used in the following scripts:
+The denylist is also used in the following scripts:
- [device\_status.py](https://cs.chromium.org/chromium/src/third_party/catapult/devil/devil/android/tools/device_status.py)
- [device\_recovery.py](https://cs.chromium.org/chromium/src/third_party/catapult/devil/devil/android/tools/device_recovery.py)
diff --git a/catapult/devil/docs/device_utils.md b/catapult/devil/docs/device_utils.md
index a6e89a70..960ca894 100644
--- a/catapult/devil/docs/device_utils.md
+++ b/catapult/devil/docs/device_utils.md
@@ -1,133 +1,297 @@
# [devil.android.device_utils](https://github.com/catapult-project/catapult/blob/master/devil/devil/android/device_utils.py)
-*This page was autogenerated by `devil/utils/markdown.py --module-link https://github.com/catapult-project/catapult/blob/master/devil/devil/android/device_utils.py`*
+*This page was autogenerated. Run `devil/bin/generate_md_docs` to update*
## DeviceUtils
-### DeviceUtils.\_\_init\_\_
+### DeviceUtils.BroadcastIntent
-DeviceUtils constructor.
+Send a broadcast intent.
```
Args:
- device: Either a device serial, an existing AdbWrapper instance, or an
- an existing AndroidCommands instance.
- enable_device_files_cache: For PushChangedFiles(), cache checksums of
- pushed files rather than recomputing them on a subsequent call.
- default_timeout: An integer containing the default number of seconds to
- wait for an operation to complete if no explicit value is provided.
- default_retries: An integer containing the default number or times an
- operation should be retried on failure if no explicit value is provided.
+ intent: An Intent to broadcast.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Raises:
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.\_\_eq\_\_
+### DeviceUtils.ChangeOwner
-Checks whether |other| refers to the same device as |self|.
+Changes file system ownership for permissions.
```
Args:
- other: The object to compare to. This can be a basestring, an instance
- of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
- Returns:
- Whether |other| refers to the same device as |self|.
+ owner_group: New owner and group to assign. Note that this should be a
+ string in the form user[.group] where the group is option.
+ paths: Paths to change ownership of.
+
+ Note that the -R recursive option is not supported by all Android
+ versions.
```
-### DeviceUtils.\_\_lt\_\_
+### DeviceUtils.ChangeSecurityContext
-Compares two instances of DeviceUtils.
+Changes the SELinux security context for files.
```
- This merely compares their serial numbers.
+ Args:
+ security_context: The new security context as a string
+ paths: Paths to change the security context of.
+
+ Note that the -R recursive option is not supported by all Android
+ versions.
+```
+
+### DeviceUtils.ClearApplicationState
+
+Clear all state for the given package.
+```
Args:
- other: The instance of DeviceUtils to compare to.
+ package: A string containing the name of the package to stop.
+ permissions: List of permissions to set after clearing data.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Raises:
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.ClearCache
+
+Clears all caches.
+### DeviceUtils.DismissCrashDialogIfNeeded
+
+Dismiss the error/ANR dialog if present.
+```
+ Returns: Name of the crashed package if a dialog is focused,
+ None otherwise.
+```
+
+
+### DeviceUtils.DumpCacheData
+
+Dumps the current cache state to a string.
+```
+ Args:
+ timeout: timeout in seconds
+ retries: number of retries
+
Returns:
- Whether |self| is less than |other|.
+ A serialized cache as a string.
```
-### DeviceUtils.\_\_str\_\_
+### DeviceUtils.EnableRoot
-Returns the device serial.
-### DeviceUtils.NeedsSU
+Restarts adbd with root privileges.
+```
+ Args:
+ timeout: timeout in seconds
+ retries: number of retries
-Checks whether 'su' is needed to access protected resources.
+ Raises:
+ CommandFailedError if root could not be enabled.
+ CommandTimeoutError on timeout.
+```
+
+
+### DeviceUtils.FileExists
+
+Checks whether the given file exists on the device.
+```
+ Arguments are the same as PathExists.
+```
+
+
+### DeviceUtils.FileSize
+
+Get the size of a file on the device.
```
+ Note: This is implemented by parsing the output of the 'ls' command on
+ the device. On some Android versions, when passing a directory or special
+ file, the size is *not* reported and this function will throw an exception.
+
Args:
+ device_path: A string containing the path of a file on the device.
+ as_root: A boolean indicating whether the to use root privileges to
+ access the file information.
timeout: timeout in seconds
retries: number of retries
Returns:
- True if 'su' is available on the device and is needed to to access
- protected resources; False otherwise if either 'su' is not available
- (e.g. because the device has a user build), or not needed (because adbd
- already has root privileges).
+ The size of the file in bytes.
Raises:
+ CommandFailedError if device_path cannot be found on the device, or
+ its size cannot be determited for some reason.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.IsOnline
+### DeviceUtils.ForceStop
-Checks whether the device is online.
+Close the application.
+```
+ Args:
+ package: A string containing the name of the package to stop.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Raises:
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GetABI
+
+Gets the device main ABI.
```
Args:
timeout: timeout in seconds
retries: number of retries
Returns:
- True if the device is online, False otherwise.
+ The device's main ABI name. For supported ABIs, the return value will be
+ one of the values defined in devil.android.ndk.abis.
Raises:
CommandTimeoutError on timeout.
```
-### DeviceUtils.HasRoot
+### DeviceUtils.GetAppWritablePath
-Checks whether or not adbd has root privileges.
+Get a path that on the device's SD card that apps can write.
```
Args:
timeout: timeout in seconds
retries: number of retries
Returns:
- True if adbd has root privileges, False otherwise.
+ A app-writeable path on the device's SD card.
Raises:
+ CommandFailedError if the external storage path could not be determined.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.EnableRoot
+### DeviceUtils.GetApplicationDataDirectory
-Restarts adbd with root privileges.
+Get the data directory on the device for the given package.
+```
+ Args:
+ package: Name of the package.
+
+ Returns:
+ The package's data directory.
+ Raises:
+ CommandFailedError if the package's data directory can't be found,
+ whether because it's not installed or otherwise.
+```
+
+
+### DeviceUtils.GetApplicationPaths
+
+Get the paths of the installed apks on the device for the given package.
+```
+ Args:
+ package: Name of the package.
+
+ Returns:
+ List of paths to the apks on the device for the given package.
+```
+
+
+### DeviceUtils.GetApplicationPids
+
+Returns the PID or PIDs of a given process name.
```
+ Note that the |process_name|, often the package name, must match exactly.
+
Args:
+ process_name: A string containing the process name to get the PIDs for.
+ at_most_one: A boolean indicating that at most one PID is expected to
+ be found.
timeout: timeout in seconds
retries: number of retries
+ Returns:
+ A list of the PIDs for the named process. If at_most_one=True returns
+ the single PID found or None otherwise.
+
Raises:
- CommandFailedError if root could not be enabled.
+ CommandFailedError if at_most_one=True and more than one PID is found
+ for the named process.
CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.IsUserBuild
+### DeviceUtils.GetApplicationTargetSdk
-Checks whether or not the device is running a user build.
+Get the targetSdkVersion of a package installed on the device.
+```
+ Args:
+ package: Name of the package.
+
+ Returns:
+ A string with the targetSdkVersion or None if the package is not found on
+ the device. Note: this cannot always be cast to an integer. If this
+ application targets a pre-release SDK, this returns the version codename
+ instead (ex. "R").
+```
+
+
+### DeviceUtils.GetApplicationVersion
+
+Get the version name of a package installed on the device.
+```
+ Args:
+ package: Name of the package.
+
+ Returns:
+ A string with the version name or None if the package is not found
+ on the device.
+```
+
+
+### DeviceUtils.GetClientCache
+
+Returns client cache.
+### DeviceUtils.GetCountry
+
+Returns the country setting on the device.
+```
+ DEPRECATED: Prefer GetLocale() instead.
+
+ Args:
+ cache: Whether to use cached properties when available.
+```
+
+
+### DeviceUtils.GetEnforce
+
+Get the current mode of SELinux.
```
Args:
timeout: timeout in seconds
retries: number of retries
Returns:
- True if the device is running a user build, False otherwise (i.e. if
- it's running a userdebug build).
+ True (enforcing), False (permissive), or None (disabled).
Raises:
+ CommandFailedError on failure.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
@@ -137,6 +301,9 @@ Checks whether or not the device is running a user build.
Get the device's path to its SD card.
```
+ Note: this path is read-only by apps in R+. Use GetAppWritablePath() to
+ obtain a path writable by apps.
+
Args:
timeout: timeout in seconds
retries: number of retries
@@ -151,6 +318,9 @@ Get the device's path to its SD card.
```
+### DeviceUtils.GetFeatures
+
+Returns the features supported on the device.
### DeviceUtils.GetIMEI
Get the device's IMEI.
@@ -167,93 +337,190 @@ Get the device's IMEI.
```
-### DeviceUtils.GetApplicationPaths
+### DeviceUtils.GetLanguage
-Get the paths of the installed apks on the device for the given package.
+Returns the language setting on the device.
+```
+ DEPRECATED: Prefer GetLocale() instead.
+
+ Args:
+ cache: Whether to use cached properties when available.
+```
+
+
+### DeviceUtils.GetLocale
+
+Returns the locale setting on the device.
+```
+ Args:
+ cache: Whether to use cached properties when available.
+ Returns:
+ A pair (language, country).
+```
+
+
+### DeviceUtils.GetLogcatMonitor
+
+Returns a new LogcatMonitor associated with this device.
+```
+ Parameters passed to this function are passed directly to
+ |logcat_monitor.LogcatMonitor| and are documented there.
+```
+
+
+### DeviceUtils.GetPackageArchitecture
+
+Get the architecture of a package installed on the device.
```
Args:
package: Name of the package.
Returns:
- List of paths to the apks on the device for the given package.
+ A string with the architecture, or None if the package is missing.
```
-### DeviceUtils.TakeBugReport
+### DeviceUtils.GetPids
-Takes a bug report and dumps it to the specified path.
+Returns the PIDs of processes containing the given name as substring.
```
- This doesn't use adb's bugreport option since its behavior is dependent on
- both adb version and device OS version. To make it simpler, this directly
- runs the bugreport command on the device itself and dumps the stdout to a
- file.
+ DEPRECATED
+
+ Note that the |process_name| is often the package name.
Args:
- path: Path on the host to drop the bug report.
- timeout: (optional) Timeout per try in seconds.
- retries: (optional) Number of retries to attempt.
+ process_name: A string containing the process name to get the PIDs for.
+ If missing returns PIDs for all processes.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ A dict mapping process name to a list of PIDs for each process that
+ contained the provided |process_name|.
+
+ Raises:
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.GetApplicationVersion
+### DeviceUtils.GetProp
-Get the version name of a package installed on the device.
+Gets a property from the device.
```
Args:
- package: Name of the package.
+ property_name: A string containing the name of the property to get from
+ the device.
+ cache: Whether to use cached properties when available.
+ timeout: timeout in seconds
+ retries: number of retries
Returns:
- A string with the version name or None if the package is not found
- on the device.
+ The value of the device's |property_name| property.
+
+ Raises:
+ CommandTimeoutError on timeout.
```
-### DeviceUtils.GetApplicationDataDirectory
+### DeviceUtils.GetSecurityContextForPackage
-Get the data directory on the device for the given package.
+Gets the SELinux security context for the given package.
```
Args:
package: Name of the package.
+ encrypted: Whether to check in the encrypted data directory
+ (/data/user_de/0/) or the unencrypted data directory (/data/data/).
Returns:
- The package's data directory.
+ The package's security context as a string, or None if not found.
+```
+
+
+### DeviceUtils.GetTracingPath
+
+Gets tracing path from the device.
+```
+ Args:
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ /sys/kernel/debug/tracing for device with debugfs mount support;
+ /sys/kernel/tracing for device with tracefs support;
+ /sys/kernel/debug/tracing if support can't be determined.
+
Raises:
- CommandFailedError if the package's data directory can't be found,
- whether because it's not installed or otherwise.
+ CommandTimeoutError on timeout.
```
-### DeviceUtils.WaitUntilFullyBooted
+### DeviceUtils.GetWebViewUpdateServiceDump
-Wait for the device to fully boot.
+Get the WebView update command sysdump on the device.
```
- This means waiting for the device to boot, the package manager to be
- available, and the SD card to be ready. It can optionally mean waiting
- for wifi to come up, too.
+ Returns:
+ A dictionary with these possible entries:
+ FallbackLogicEnabled: True|False
+ CurrentWebViewPackage: "package name" or None
+ MinimumWebViewVersionCode: int
+ WebViewPackages: Dict of installed WebView providers, mapping "package
+ name" to "reason it's valid/invalid."
+
+ It may return an empty dictionary if device does not
+ support the "dumpsys webviewupdate" command.
+
+ Raises:
+ CommandFailedError on failure.
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
+```
+
+
+### DeviceUtils.GoHome
+
+Return to the home screen and obtain launcher focus.
+```
+ This command launches the home screen and attempts to obtain
+ launcher focus until the timeout is reached.
Args:
- wifi: A boolean indicating if we should wait for wifi to come up or not.
timeout: timeout in seconds
retries: number of retries
Raises:
- CommandFailedError on failure.
- CommandTimeoutError if one of the component waits times out.
- DeviceUnreachableError if the device becomes unresponsive.
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.Reboot
+### DeviceUtils.GrantPermissions
-Reboot the device.
+### DeviceUtils.HasRoot
+
+Checks whether or not adbd has root privileges.
```
+ A device is considered to have root if all commands are implicitly run
+ with elevated privileges, i.e. without having to use "su" to run them.
+
+ Note that some devices do not allow this implicit privilige elevation,
+ but _can_ run commands as root just fine when done explicitly with "su".
+ To check if your device can run commands with elevated privileges at all
+ use:
+
+ device.HasRoot() or device.NeedsSU()
+
+ Luckily, for the most part you don't need to worry about this and using
+ RunShellCommand(cmd, as_root=True) will figure out for you the right
+ command incantation to run with elevated privileges.
+
Args:
- block: A boolean indicating if we should wait for the reboot to complete.
- wifi: A boolean indicating if we should wait for wifi to be enabled after
- the reboot. The option has no effect unless |block| is also True.
timeout: timeout in seconds
retries: number of retries
+ Returns:
+ True if adbd has root privileges, False otherwise.
+
Raises:
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
@@ -262,18 +529,30 @@ Reboot the device.
### DeviceUtils.Install
-Install an APK.
+Install an APK or app bundle.
```
- Noop if an identical APK is already installed.
+ Noop if an identical APK is already installed. If installing a bundle, the
+ bundletools helper script (bin/*_bundle) should be used rather than the .aab
+ file.
Args:
- apk: An ApkHelper instance or string containing the path to the APK.
+ apk: An ApkHelper instance or string containing the path to the APK or
+ bundle.
allow_downgrade: A boolean indicating if we should allow downgrades.
reinstall: A boolean indicating if we should keep any existing app data.
+ Ignored if |apk| is a bundle.
permissions: Set of permissions to set. If not set, finds permissions with
apk helper. To set no permissions, pass [].
timeout: timeout in seconds
retries: number of retries
+ modules: An iterable containing specific bundle modules to install.
+ Error if set and |apk| points to an APK instead of a bundle.
+ fake_modules: An iterable containing specific bundle modules that should
+ have their apks copied to |MODULES_SRC_DIRECTORY_PATH| subdirectory
+ rather than installed. Thus the app can emulate SplitCompat while
+ running. This should not have any overlap with |modules|.
+ additional_locales: An iterable with additional locales to install for a
+ bundle.
Raises:
CommandFailedError if the installation fails.
@@ -308,82 +587,69 @@ Install a split APK.
```
-### DeviceUtils.Uninstall
+### DeviceUtils.IsApplicationInstalled
-Remove the app |package\_name| from the device.
+Determines whether a particular package is installed on the device.
```
- This is a no-op if the app is not already installed.
+ Args:
+ package: Name of the package.
+
+ Returns:
+ True if the application is installed, False otherwise.
+```
+
+### DeviceUtils.IsOnline
+
+Checks whether the device is online.
+```
Args:
- package_name: The package to uninstall.
- keep_data: (optional) Whether to keep the data and cache directories.
- timeout: Timeout in seconds.
- retries: Number of retries.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Returns:
+ True if the device is online, False otherwise.
Raises:
- CommandFailedError if the uninstallation fails.
- CommandTimeoutError if the uninstallation times out.
- DeviceUnreachableError on missing device.
+ CommandTimeoutError on timeout.
```
-### DeviceUtils.RunShellCommand
+### DeviceUtils.IsScreenOn
-Run an ADB shell command.
+Determines if screen is on.
```
- The command to run |cmd| should be a sequence of program arguments
- (preferred) or a single string with a shell script to run.
+ Dumpsys input_method exposes screen on/off state. Below is an explination of
+ the states.
- When |cmd| is a sequence, it is assumed to contain the name of the command
- to run followed by its arguments. In this case, arguments are passed to the
- command exactly as given, preventing any further processing by the shell.
- This allows callers to easily pass arguments with spaces or special
- characters without having to worry about quoting rules. Whenever possible,
- it is recomended to pass |cmd| as a sequence.
+ Pre-L:
+ On: mScreenOn=true
+ Off: mScreenOn=false
+ L+:
+ On: mInteractive=true
+ Off: mInteractive=false
- When |cmd| is passed as a single string, |shell| should be set to True.
- The command will be interpreted and run by the shell on the device,
- allowing the use of shell features such as pipes, wildcards, or variables.
- Failing to set shell=True will issue a warning, but this will be changed
- to a hard failure in the future (see: catapult:#3242).
+ Returns:
+ True if screen is on, false if it is off.
+
+ Raises:
+ device_errors.CommandFailedError: If screen state cannot be found.
+```
- This behaviour is consistent with that of command runners in cmd_helper as
- well as Python's own subprocess.Popen.
- TODO(perezju) Change the default of |check_return| to True when callers
- have switched to the new behaviour.
+### DeviceUtils.IsUserBuild
+Checks whether or not the device is running a user build.
+```
Args:
- cmd: A sequence containing the command to run and its arguments, or a
- string with a shell script to run (should also set shell=True).
- shell: A boolean indicating whether shell features may be used in |cmd|.
- check_return: A boolean indicating whether or not the return code should
- be checked.
- cwd: The device directory in which the command should be run.
- env: The environment variables with which the command should be run.
- run_as: A string containing the package as which the command should be
- run.
- as_root: A boolean indicating whether the shell command should be run
- with root privileges.
- single_line: A boolean indicating if only a single line of output is
- expected.
- large_output: Uses a work-around for large shell command output. Without
- this large output will be truncated.
- raw_output: Whether to only return the raw output
- (no splitting into lines).
timeout: timeout in seconds
retries: number of retries
Returns:
- If single_line is False, the output of the command as a list of lines,
- otherwise, a string with the unique line of output emmited by the command
- (with the optional newline at the end stripped).
+ True if the device is running a user build, False otherwise (i.e. if
+ it's running a userdebug build).
Raises:
- AdbCommandFailedError if check_return is True and the exit code of
- the command run on the device is non-zero.
- CommandFailedError if single_line is True but the output contains two or
- more lines.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
@@ -419,108 +685,127 @@ Kill all processes with the given name on the device.
```
-### DeviceUtils.StartActivity
+### DeviceUtils.ListDirectory
-Start package's activity on the device.
+List all files on a device directory.
```
+ Mirroring os.listdir (and most client expectations) the resulting list
+ does not include the special entries '.' and '..' even if they are present
+ in the directory.
+
Args:
- intent_obj: An Intent object to send.
- blocking: A boolean indicating whether we should wait for the activity to
- finish launching.
- trace_file_name: If present, a string that both indicates that we want to
- profile the activity and contains the path to which the
- trace should be saved.
- force_stop: A boolean indicating whether we should stop the activity
- before starting it.
+ device_path: A string containing the path of the directory on the device
+ to list.
+ as_root: A boolean indicating whether the to use root privileges to list
+ the directory contents.
timeout: timeout in seconds
retries: number of retries
+ Returns:
+ A list of filenames for all entries contained in the directory.
+
Raises:
- CommandFailedError if the activity could not be started.
+ AdbCommandFailedError if |device_path| does not specify a valid and
+ accessible directory in the device.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.StartInstrumentation
-
-### DeviceUtils.BroadcastIntent
+### DeviceUtils.ListProcesses
-Send a broadcast intent.
+Returns a list of tuples with info about processes on the device.
```
+ This essentially parses the output of the |ps| command into convenient
+ ProcessInfo tuples.
+
Args:
- intent: An Intent to broadcast.
+ process_name: A string used to filter the returned processes. If given,
+ only processes whose name have this value as a substring
+ will be returned.
timeout: timeout in seconds
retries: number of retries
- Raises:
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
+ Returns:
+ A list of ProcessInfo tuples with |name|, |pid|, and |ppid| fields.
```
-### DeviceUtils.GoHome
+### DeviceUtils.LoadCacheData
-Return to the home screen and obtain launcher focus.
+Initializes the cache from data created using DumpCacheData.
```
- This command launches the home screen and attempts to obtain
- launcher focus until the timeout is reached.
+ The cache is used only if its token matches the one found on the device.
+ This prevents a stale cache from being used (which can happen when sharing
+ devices).
Args:
+ data: A previously serialized cache (string).
timeout: timeout in seconds
retries: number of retries
- Raises:
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
+ Returns:
+ Whether the cache was loaded.
```
-### DeviceUtils.ForceStop
+### DeviceUtils.NeedsSU
-Close the application.
+Checks whether 'su' is needed to access protected resources.
```
Args:
- package: A string containing the name of the package to stop.
timeout: timeout in seconds
retries: number of retries
+ Returns:
+ True if 'su' is available on the device and is needed to to access
+ protected resources; False otherwise if either 'su' is not available
+ (e.g. because the device has a user build), or not needed (because adbd
+ already has root privileges).
+
Raises:
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.ClearApplicationState
+### DeviceUtils.PathExists
-Clear all state for the given package.
+Checks whether the given path(s) exists on the device.
```
Args:
- package: A string containing the name of the package to stop.
- permissions: List of permissions to set after clearing data.
+ device_path: A string containing the absolute path to the file on the
+ device, or an iterable of paths to check.
+ as_root: Whether root permissions should be use to check for the existence
+ of the given path(s).
timeout: timeout in seconds
retries: number of retries
+ Returns:
+ True if the all given paths exist on the device, False otherwise.
+
Raises:
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.SendKeyEvent
+### DeviceUtils.PullFile
-Sends a keycode to the device.
+Pull a file from the device.
```
- See the devil.android.sdk.keyevent module for suitable keycode values.
-
Args:
- keycode: A integer keycode to send to the device.
+ device_path: A string containing the absolute path of the file to pull
+ from the device.
+ host_path: A string containing the absolute path of the destination on
+ the host.
+ as_root: Whether root permissions should be used to pull the file.
timeout: timeout in seconds
retries: number of retries
Raises:
+ CommandFailedError on failure.
CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
```
@@ -538,79 +823,14 @@ Push files to the device, skipping files that don't need updating.
|host_path| is an absolute path of a file or directory on the host
that should be minimially pushed to the device, and |device_path| is
an absolute path of the destination on the device.
- timeout: timeout in seconds
- retries: number of retries
delete_device_stale: option to delete stale files on device
-
- Raises:
- CommandFailedError on failure.
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
-```
-
-
-### DeviceUtils.FileExists
-
-Checks whether the given file exists on the device.
-```
- Arguments are the same as PathExists.
-```
-
-
-### DeviceUtils.PathExists
-
-Checks whether the given path(s) exists on the device.
-```
- Args:
- device_path: A string containing the absolute path to the file on the
- device, or an iterable of paths to check.
- as_root: Whether root permissions should be use to check for the existence
- of the given path(s).
- timeout: timeout in seconds
- retries: number of retries
-
- Returns:
- True if the all given paths exist on the device, False otherwise.
-
- Raises:
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
-```
-
-
-### DeviceUtils.RemovePath
-
-Removes the given path(s) from the device.
-```
- Args:
- device_path: A string containing the absolute path to the file on the
- device, or an iterable of paths to check.
- force: Whether to remove the path(s) with force (-f).
- recursive: Whether to remove any directories in the path(s) recursively.
- as_root: Whether root permissions should be use to remove the given
- path(s).
- rename: Whether to rename the path(s) before removing to help avoid
- filesystem errors. See https://stackoverflow.com/questions/11539657
- timeout: timeout in seconds
- retries: number of retries
-```
-
-
-### DeviceUtils.PullFile
-
-Pull a file from the device.
-```
- Args:
- device_path: A string containing the absolute path of the file to pull
- from the device.
- host_path: A string containing the absolute path of the destination on
- the host.
timeout: timeout in seconds
retries: number of retries
Raises:
CommandFailedError on failure.
CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
@@ -642,156 +862,143 @@ Reads the contents of a file from the device.
```
-### DeviceUtils.WriteFile
+### DeviceUtils.Reboot
-Writes |contents| to a file on the device.
+Reboot the device.
```
Args:
- device_path: A string containing the absolute path to the file to write
- on the device.
- contents: A string containing the data to write to the device.
- as_root: A boolean indicating whether the write should be executed with
- root privileges (if available).
- force_push: A boolean indicating whether to force the operation to be
- performed by pushing a file to the device. The default is, when the
- contents are short, to pass the contents using a shell script instead.
+ block: A boolean indicating if we should wait for the reboot to complete.
+ wifi: A boolean indicating if we should wait for wifi to be enabled after
+ the reboot.
+ The option has no effect unless |block| is also True.
+ decrypt: A boolean indicating if we should wait for full-disk decryption
+ to complete after the reboot.
+ The option has no effect unless |block| is also True.
timeout: timeout in seconds
retries: number of retries
Raises:
- CommandFailedError if the file could not be written on the device.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.ListDirectory
+### DeviceUtils.RemovePath
-List all files on a device directory.
+Removes the given path(s) from the device.
```
- Mirroring os.listdir (and most client expectations) the resulting list
- does not include the special entries '.' and '..' even if they are present
- in the directory.
-
Args:
- device_path: A string containing the path of the directory on the device
- to list.
- as_root: A boolean indicating whether the to use root privileges to list
- the directory contents.
+ device_path: A string containing the absolute path to the file on the
+ device, or an iterable of paths to check.
+ force: Whether to remove the path(s) with force (-f).
+ recursive: Whether to remove any directories in the path(s) recursively.
+ as_root: Whether root permissions should be use to remove the given
+ path(s).
+ rename: Whether to rename the path(s) before removing to help avoid
+ filesystem errors. See https://stackoverflow.com/questions/11539657
timeout: timeout in seconds
retries: number of retries
+```
- Returns:
- A list of filenames for all entries contained in the directory.
- Raises:
- AdbCommandFailedError if |device_path| does not specify a valid and
- accessible directory in the device.
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
+### DeviceUtils.RestartAdbd
+
+### DeviceUtils.RunShellCommand
+
+Run an ADB shell command.
```
+ The command to run |cmd| should be a sequence of program arguments
+ (preferred) or a single string with a shell script to run.
+ When |cmd| is a sequence, it is assumed to contain the name of the command
+ to run followed by its arguments. In this case, arguments are passed to the
+ command exactly as given, preventing any further processing by the shell.
+ This allows callers to easily pass arguments with spaces or special
+ characters without having to worry about quoting rules. Whenever possible,
+ it is recomended to pass |cmd| as a sequence.
-### DeviceUtils.StatDirectory
+ When |cmd| is passed as a single string, |shell| should be set to True.
+ The command will be interpreted and run by the shell on the device,
+ allowing the use of shell features such as pipes, wildcards, or variables.
+ Failing to set shell=True will issue a warning, but this will be changed
+ to a hard failure in the future (see: catapult:#3242).
-List file and stat info for all entries on a device directory.
-```
- Implementation notes: this is currently implemented by parsing the output
- of 'ls -a -l' on the device. Whether possible and convenient, we attempt to
- make parsing strict and return values mirroring those of the standard |os|
- and |stat| Python modules.
+ This behaviour is consistent with that of command runners in cmd_helper as
+ well as Python's own subprocess.Popen.
- Mirroring os.listdir (and most client expectations) the resulting list
- does not include the special entries '.' and '..' even if they are present
- in the directory.
+ TODO(crbug.com/1029769) Change the default of |check_return| to True when
+ callers have switched to the new behaviour.
Args:
- device_path: A string containing the path of the directory on the device
- to list.
- as_root: A boolean indicating whether the to use root privileges to list
- the directory contents.
+ cmd: A sequence containing the command to run and its arguments, or a
+ string with a shell script to run (should also set shell=True).
+ shell: A boolean indicating whether shell features may be used in |cmd|.
+ check_return: A boolean indicating whether or not the return code should
+ be checked.
+ cwd: The device directory in which the command should be run.
+ env: The environment variables with which the command should be run.
+ run_as: A string containing the package as which the command should be
+ run.
+ as_root: A boolean indicating whether the shell command should be run
+ with root privileges.
+ single_line: A boolean indicating if only a single line of output is
+ expected.
+ large_output: Uses a work-around for large shell command output. Without
+ this large output will be truncated.
+ raw_output: Whether to only return the raw output
+ (no splitting into lines).
timeout: timeout in seconds
retries: number of retries
Returns:
- A list of dictionaries, each containing the following keys:
- filename: A string with the file name.
- st_mode: File permissions, use the stat module to interpret these.
- st_nlink: Number of hard links (may be missing).
- st_owner: A string with the user name of the owner.
- st_group: A string with the group name of the owner.
- st_rdev_pair: Device type as (major, minior) (only if inode device).
- st_size: Size of file, in bytes (may be missing for non-regular files).
- st_mtime: Time of most recent modification, in seconds since epoch
- (although resolution is in minutes).
- symbolic_link_to: If entry is a symbolic link, path where it points to;
- missing otherwise.
+ If single_line is False, the output of the command as a list of lines,
+ otherwise, a string with the unique line of output emmited by the command
+ (with the optional newline at the end stripped).
Raises:
- AdbCommandFailedError if |device_path| does not specify a valid and
- accessible directory in the device.
+ AdbCommandFailedError if check_return is True and the exit code of
+ the command run on the device is non-zero.
+ CommandFailedError if single_line is True but the output contains two or
+ more lines.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.StatPath
+### DeviceUtils.SendKeyEvent
-Get the stat attributes of a file or directory on the device.
+Sends a keycode to the device.
```
+ See the devil.android.sdk.keyevent module for suitable keycode values.
+
Args:
- device_path: A string containing the path of a file or directory from
- which to get attributes.
- as_root: A boolean indicating whether the to use root privileges to
- access the file information.
+ keycode: A integer keycode to send to the device.
timeout: timeout in seconds
retries: number of retries
- Returns:
- A dictionary with the stat info collected; see StatDirectory for details.
-
Raises:
- CommandFailedError if device_path cannot be found on the device.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.FileSize
+### DeviceUtils.SetEnforce
-Get the size of a file on the device.
+Modify the mode SELinux is running in.
```
- Note: This is implemented by parsing the output of the 'ls' command on
- the device. On some Android versions, when passing a directory or special
- file, the size is *not* reported and this function will throw an exception.
-
Args:
- device_path: A string containing the path of a file on the device.
- as_root: A boolean indicating whether the to use root privileges to
- access the file information.
+ enabled: a boolean indicating whether to put SELinux in encorcing mode
+ (if True), or permissive mode (otherwise).
timeout: timeout in seconds
retries: number of retries
- Returns:
- The size of the file in bytes.
-
Raises:
- CommandFailedError if device_path cannot be found on the device, or
- its size cannot be determited for some reason.
+ CommandFailedError on failure.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.GetLanguage
-
-Returns the language setting on the device.
-```
- Args:
- cache: Whether to use cached properties when available.
-```
-
-
### DeviceUtils.SetJavaAsserts
Enables or disables Java asserts.
@@ -811,159 +1018,211 @@ Enables or disables Java asserts.
```
-### DeviceUtils.GetCountry
+### DeviceUtils.SetProp
-Returns the country setting on the device.
+Sets a property on the device.
```
Args:
- cache: Whether to use cached properties when available.
+ property_name: A string containing the name of the property to set on
+ the device.
+ value: A string containing the value to set to the property on the
+ device.
+ check: A boolean indicating whether to check that the property was
+ successfully set on the device.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Raises:
+ CommandFailedError if check is true and the property was not correctly
+ set on the device (e.g. because it is not rooted).
+ CommandTimeoutError on timeout.
```
-### DeviceUtils.GetApplicationPids
+### DeviceUtils.SetScreen
-Returns the PID or PIDs of a given process name.
+Turns screen on and off.
```
- Note that the |process_name|, often the package name, must match exactly.
+ Args:
+ on: bool to decide state to switch to. True = on False = off.
+```
+
+
+### DeviceUtils.SetWebViewFallbackLogic
+
+Set whether WebViewUpdateService's "fallback logic" should be enabled.
+```
+ WebViewUpdateService has nonintuitive "fallback logic" for devices where
+ Monochrome (Chrome Stable) is preinstalled as the WebView provider, with a
+ "stub" (little-to-no code) implementation of standalone WebView.
+
+ "Fallback logic" (enabled by default) is designed, in the case where the
+ user has disabled Chrome, to fall back to the stub standalone WebView by
+ enabling the package. The implementation plumbs through the Chrome APK until
+ Play Store installs an update with the full implementation.
+
+ A surprising side-effect of "fallback logic" is that, immediately after
+ sideloading WebView, WebViewUpdateService re-disables the package and
+ uninstalls the update. This can prevent successfully using standalone
+ WebView for development, although "fallback logic" can be disabled on
+ userdebug/eng devices.
+
+ Because this is only relevant for devices with the standalone WebView stub,
+ this command is only relevant on N-P (inclusive).
+
+ You can determine if "fallback logic" is currently enabled by checking
+ FallbackLogicEnabled in the dictionary returned by
+ GetWebViewUpdateServiceDump.
Args:
- process_name: A string containing the process name to get the PIDs for.
- at_most_one: A boolean indicating that at most one PID is expected to
- be found.
+ enabled: bool - True for enabled, False for disabled
timeout: timeout in seconds
retries: number of retries
- Returns:
- A list of the PIDs for the named process. If at_most_one=True returns
- the single PID found or None otherwise.
-
Raises:
- CommandFailedError if at_most_one=True and more than one PID is found
- for the named process.
+ CommandFailedError on failure.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.GetProp
+### DeviceUtils.SetWebViewImplementation
-Gets a property from the device.
+Select the WebView implementation to the specified package.
```
Args:
- property_name: A string containing the name of the property to get from
- the device.
- cache: Whether to use cached properties when available.
+ package_name: The package name of a WebView implementation. The package
+ must be already installed on the device.
timeout: timeout in seconds
retries: number of retries
- Returns:
- The value of the device's |property_name| property.
-
Raises:
+ CommandFailedError on failure.
CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.SetProp
+### DeviceUtils.StartActivity
-Sets a property on the device.
+Start package's activity on the device.
```
Args:
- property_name: A string containing the name of the property to set on
- the device.
- value: A string containing the value to set to the property on the
- device.
- check: A boolean indicating whether to check that the property was
- successfully set on the device.
+ intent_obj: An Intent object to send.
+ blocking: A boolean indicating whether we should wait for the activity to
+ finish launching.
+ trace_file_name: If present, a string that both indicates that we want to
+ profile the activity and contains the path to which the
+ trace should be saved.
+ force_stop: A boolean indicating whether we should stop the activity
+ before starting it.
timeout: timeout in seconds
retries: number of retries
Raises:
- CommandFailedError if check is true and the property was not correctly
- set on the device (e.g. because it is not rooted).
+ CommandFailedError if the activity could not be started.
CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.GetABI
+### DeviceUtils.StartInstrumentation
-Gets the device main ABI.
+### DeviceUtils.StartService
+
+Start a service on the device.
```
Args:
- timeout: timeout in seconds
- retries: number of retries
-
- Returns:
- The device's main ABI name.
+ intent_obj: An Intent object to send describing the service to start.
+ user_id: A specific user to start the service as, defaults to current.
+ timeout: Timeout in seconds.
+ retries: Number of retries
Raises:
+ CommandFailedError if the service could not be started.
CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.GetPids
+### DeviceUtils.StatDirectory
-Returns the PIDs of processes containing the given name as substring.
+List file and stat info for all entries on a device directory.
```
- Note that the |process_name| is often the package name.
+ Implementation notes: this is currently implemented by parsing the output
+ of 'ls -a -l' on the device. Whether possible and convenient, we attempt to
+ make parsing strict and return values mirroring those of the standard |os|
+ and |stat| Python modules.
+
+ Mirroring os.listdir (and most client expectations) the resulting list
+ does not include the special entries '.' and '..' even if they are present
+ in the directory.
Args:
- process_name: A string containing the process name to get the PIDs for.
- If missing returns PIDs for all processes.
+ device_path: A string containing the path of the directory on the device
+ to list.
+ as_root: A boolean indicating whether the to use root privileges to list
+ the directory contents.
timeout: timeout in seconds
retries: number of retries
Returns:
- A dict mapping process name to a list of PIDs for each process that
- contained the provided |process_name|.
+ A list of dictionaries, each containing the following keys:
+ filename: A string with the file name.
+ st_mode: File permissions, use the stat module to interpret these.
+ st_nlink: Number of hard links (may be missing).
+ st_owner: A string with the user name of the owner.
+ st_group: A string with the group name of the owner.
+ st_rdev_pair: Device type as (major, minior) (only if inode device).
+ st_size: Size of file, in bytes (may be missing for non-regular files).
+ st_mtime: Time of most recent modification, in seconds since epoch
+ (although resolution is in minutes).
+ symbolic_link_to: If entry is a symbolic link, path where it points to;
+ missing otherwise.
Raises:
+ AdbCommandFailedError if |device_path| does not specify a valid and
+ accessible directory in the device.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.GetLogcatMonitor
-
-Returns a new LogcatMonitor associated with this device.
-```
- Parameters passed to this function are passed directly to
- |logcat_monitor.LogcatMonitor| and are documented there.
-```
-
-
-### DeviceUtils.GetEnforce
+### DeviceUtils.StatPath
-Get the current mode of SELinux.
+Get the stat attributes of a file or directory on the device.
```
Args:
+ device_path: A string containing the path of a file or directory from
+ which to get attributes.
+ as_root: A boolean indicating whether the to use root privileges to
+ access the file information.
timeout: timeout in seconds
retries: number of retries
Returns:
- True (enforcing), False (permissive), or None (disabled).
+ A dictionary with the stat info collected; see StatDirectory for details.
Raises:
- CommandFailedError on failure.
+ CommandFailedError if device_path cannot be found on the device.
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
```
-### DeviceUtils.SetEnforce
+### DeviceUtils.TakeBugReport
-Modify the mode SELinux is running in.
+Takes a bug report and dumps it to the specified path.
```
- Args:
- enabled: a boolean indicating whether to put SELinux in encorcing mode
- (if True), or permissive mode (otherwise).
- timeout: timeout in seconds
- retries: number of retries
+ This doesn't use adb's bugreport option since its behavior is dependent on
+ both adb version and device OS version. To make it simpler, this directly
+ runs the bugreport command on the device itself and dumps the stdout to a
+ file.
- Raises:
- CommandFailedError on failure.
- CommandTimeoutError on timeout.
- DeviceUnreachableError on missing device.
+ Args:
+ path: Path on the host to drop the bug report.
+ timeout: (optional) Timeout per try in seconds.
+ retries: (optional) Number of retries to attempt.
```
@@ -988,84 +1247,128 @@ Takes a screenshot of the device.
```
-### DeviceUtils.DismissCrashDialogIfNeeded
+### DeviceUtils.Uninstall
-Dismiss the error/ANR dialog if present.
-```
- Returns: Name of the crashed package if a dialog is focused,
- None otherwise.
+Remove the app |package\_name| from the device.
```
+ This is a no-op if the app is not already installed.
+ Args:
+ package_name: The package to uninstall.
+ keep_data: (optional) Whether to keep the data and cache directories.
+ timeout: Timeout in seconds.
+ retries: Number of retries.
-### DeviceUtils.GetClientCache
+ Raises:
+ CommandFailedError if the uninstallation fails.
+ CommandTimeoutError if the uninstallation times out.
+ DeviceUnreachableError on missing device.
+```
-Returns client cache.
-### DeviceUtils.LoadCacheData
-Initializes the cache from data created using DumpCacheData.
+### DeviceUtils.WaitUntilFullyBooted
+
+Wait for the device to fully boot.
```
- The cache is used only if its token matches the one found on the device.
- This prevents a stale cache from being used (which can happen when sharing
- devices).
+ This means waiting for the device to boot, the package manager to be
+ available, and the SD card to be ready.
+ It can optionally wait the following:
+ - Wait for wifi to come up.
+ - Wait for full-disk decryption to complete.
Args:
- data: A previously serialized cache (string).
+ wifi: A boolean indicating if we should wait for wifi to come up or not.
+ decrypt: A boolean indicating if we should wait for full-disk decryption
+ to complete.
timeout: timeout in seconds
retries: number of retries
- Returns:
- Whether the cache was loaded.
+ Raises:
+ CommandFailedError on failure.
+ CommandTimeoutError if one of the component waits times out.
+ DeviceUnreachableError if the device becomes unresponsive.
```
-### DeviceUtils.DumpCacheData
+### DeviceUtils.WriteFile
-Dumps the current cache state to a string.
+Writes |contents| to a file on the device.
```
Args:
+ device_path: A string containing the absolute path to the file to write
+ on the device.
+ contents: A string containing the data to write to the device.
+ as_root: A boolean indicating whether the write should be executed with
+ root privileges (if available).
+ force_push: A boolean indicating whether to force the operation to be
+ performed by pushing a file to the device. The default is, when the
+ contents are short, to pass the contents using a shell script instead.
timeout: timeout in seconds
retries: number of retries
- Returns:
- A serialized cache as a string.
+ Raises:
+ CommandFailedError if the file could not be written on the device.
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
```
-### DeviceUtils.RestartAdbd
-
-### DeviceUtils.GrantPermissions
-
-### DeviceUtils.IsScreenOn
+### DeviceUtils.\_\_eq\_\_
-Determines if screen is on.
+Checks whether |other| refers to the same device as |self|.
+```
+ Args:
+ other: The object to compare to. This can be a basestring, an instance
+ of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
+ Returns:
+ Whether |other| refers to the same device as |self|.
```
- Dumpsys input_method exposes screen on/off state. Below is an explination of
- the states.
- Pre-L:
- On: mScreenOn=true
- Off: mScreenOn=false
- L+:
- On: mInteractive=true
- Off: mInteractive=false
- Returns:
- True if screen is on, false if it is off.
+### DeviceUtils.\_\_init\_\_
- Raises:
- device_errors.CommandFailedError: If screen state cannot be found.
+DeviceUtils constructor.
+```
+ Args:
+ device: Either a device serial, an existing AdbWrapper instance, or an
+ an existing AndroidCommands instance.
+ enable_device_files_cache: For PushChangedFiles(), cache checksums of
+ pushed files rather than recomputing them on a subsequent call.
+ default_timeout: An integer containing the default number of seconds to
+ wait for an operation to complete if no explicit value is provided.
+ default_retries: An integer containing the default number or times an
+ operation should be retried on failure if no explicit value is provided.
```
-### DeviceUtils.SetScreen
+### DeviceUtils.\_\_lt\_\_
-Turns screen on and off.
+Compares two instances of DeviceUtils.
```
+ This merely compares their serial numbers.
+
Args:
- on: bool to decide state to switch to. True = on False = off.
+ other: The instance of DeviceUtils to compare to.
+ Returns:
+ Whether |self| is less than |other|.
```
+### DeviceUtils.\_\_str\_\_
+
+Returns the device serial.
+## ProcessInfo
+
+ProcessInfo(name, pid, ppid)
+### ProcessInfo.\_\_getnewargs\_\_
+
+Return self as a plain tuple. Used by copy and pickle.
+### ProcessInfo.\_\_getstate\_\_
+
+Exclude the OrderedDict from pickling
+### ProcessInfo.\_\_repr\_\_
+
+Return a nicely formatted representation string
### GetAVDs
Returns a list of Android Virtual Devices.
diff --git a/catapult/devil/docs/markdown.md b/catapult/devil/docs/markdown.md
index 957dba7d..b2fec500 100644
--- a/catapult/devil/docs/markdown.md
+++ b/catapult/devil/docs/markdown.md
@@ -1,13 +1,13 @@
# [devil.utils.markdown](https://github.com/catapult-project/catapult/blob/master/devil/devil/utils/markdown.py)
-*This page was autogenerated by `devil/utils/markdown.py --module-link https://github.com/catapult-project/catapult/blob/master/devil/devil/utils/markdown.py`*
+*This page was autogenerated. Run `devil/bin/generate_md_docs` to update*
## MarkdownHelpAction
-### MarkdownHelpAction.\_\_init\_\_
-
### MarkdownHelpAction.\_\_call\_\_
+### MarkdownHelpAction.\_\_init\_\_
+
## MarkdownHelpFormatter
A really bare-bones argparse help formatter that generates valid markdown.
@@ -31,27 +31,6 @@ A really bare-bones argparse help formatter that generates valid markdown.
### MarkdownHelpFormatter.start\_section
-### md\_bold
-
-Returns markdown-formatted bold text.
-### md\_code
-
-Returns a markdown-formatted code block in the given language.
-### md\_escape
-
-Escapes \* and \_.
-### md\_heading
-
-Returns markdown-formatted heading.
-### md\_inline\_code
-
-Returns markdown-formatted inline code.
-### md\_italic
-
-Returns markdown-formatted italic text.
-### md\_link
-
-returns a markdown-formatted link.
### add\_md\_help\_argument
Adds --md-help to the given argparse.ArgumentParser.
@@ -77,20 +56,20 @@ Load a module given only the path name.
```
-### md\_module
+### main
-Write markdown documentation for a class.
+Write markdown documentation for the module at the provided path.
```
- Documents public classes and functions.
-
Args:
- class_obj: a types.TypeType object for the class that should be
- documented.
+ raw_args: the raw command-line args. Usually sys.argv[1:].
Returns:
- A list of markdown-formatted lines.
+ An integer exit code. 0 for success, non-zero for failure.
```
+### md\_bold
+
+Returns markdown-formatted bold text.
### md\_class
Write markdown documentation for a class.
@@ -105,6 +84,9 @@ Write markdown documentation for a class.
```
+### md\_code
+
+Returns a markdown-formatted code block in the given language.
### md\_docstring
Write a markdown-formatted docstring.
@@ -114,6 +96,9 @@ Write a markdown-formatted docstring.
```
+### md\_escape
+
+Escapes \* and \_.
### md\_function
Write markdown documentation for a function.
@@ -126,14 +111,28 @@ Write markdown documentation for a function.
```
-### main
+### md\_heading
-Write markdown documentation for the module at the provided path.
+Returns markdown-formatted heading.
+### md\_inline\_code
+
+Returns markdown-formatted inline code.
+### md\_italic
+
+Returns markdown-formatted italic text.
+### md\_link
+
+returns a markdown-formatted link.
+### md\_module
+
+Write markdown documentation for a module.
```
+ Documents public classes and functions.
+
Args:
- raw_args: the raw command-line args. Usually sys.argv[1:].
+ module_obj: a module object that should be documented.
Returns:
- An integer exit code. 0 for success, non-zero for failure.
+ A list of markdown-formatted lines.
```
diff --git a/catapult/systrace/profile_chrome/chrome_tracing_agent.py b/catapult/systrace/profile_chrome/chrome_tracing_agent.py
index 285d2996..adcbb21f 100644
--- a/catapult/systrace/profile_chrome/chrome_tracing_agent.py
+++ b/catapult/systrace/profile_chrome/chrome_tracing_agent.py
@@ -203,6 +203,9 @@ def _ComputeChromeCategories(config):
if config.trace_gpu:
categories.append('disabled-by-default-gpu.debug*')
if config.trace_flow:
+ categories.append('toplevel.flow')
+ # toplevel.flow was moved out of disabled-by-default, leaving here for
+ # compatibility with older versions of Chrome.
categories.append('disabled-by-default-toplevel.flow')
if config.trace_memory:
categories.append('disabled-by-default-memory')
diff --git a/catapult/systrace/profile_chrome/perf_tracing_agent.py b/catapult/systrace/profile_chrome/perf_tracing_agent.py
index 1414905e..8565c882 100644
--- a/catapult/systrace/profile_chrome/perf_tracing_agent.py
+++ b/catapult/systrace/profile_chrome/perf_tracing_agent.py
@@ -182,7 +182,7 @@ class PerfProfilerAgent(tracing_agents.TracingAgent):
required_libs,
use_symlinks=False)
perfhost_path = binary_manager.FetchPath(
- android_profiling_helper.GetPerfhostName(), 'x86_64', 'linux')
+ android_profiling_helper.GetPerfhostName(), 'linux', 'x86_64')
ui.PrintMessage('\nNote: to view the profile in perf, run:')
ui.PrintMessage(' ' + self._GetInteractivePerfCommand(perfhost_path,
diff --git a/catapult/systrace/systrace/monitor_unittest.py b/catapult/systrace/systrace/monitor_unittest.py
index 65d63e1d..aaeef3ee 100644
--- a/catapult/systrace/systrace/monitor_unittest.py
+++ b/catapult/systrace/systrace/monitor_unittest.py
@@ -36,7 +36,7 @@ class MonitorTest(unittest.TestCase):
@decorators.HostOnlyTest
def test_prefix(self):
- with open(os.path.join(SCRIPT_DIR, 'prefix.html')) as f:
+ with open(os.path.join(SCRIPT_DIR, 'prefix.html.template')) as f:
content = f.read().strip()
self.assertTrue("<html>" in content)
diff --git a/catapult/systrace/systrace/output_generator.py b/catapult/systrace/systrace/output_generator.py
index b1dc2759..40bfe209 100644
--- a/catapult/systrace/systrace/output_generator.py
+++ b/catapult/systrace/systrace/output_generator.py
@@ -28,6 +28,7 @@ _SYSTRACE_TO_TRACE_DATA_NAME_MAPPING = {
'systraceController': trace_data.TELEMETRY_PART,
'traceEvents': trace_data.CHROME_TRACE_PART,
'waltTrace': trace_data.WALT_TRACE_PART,
+ 'cgroupDump': trace_data.CGROUP_TRACE_PART,
}
_SYSTRACE_HEADER = 'Systrace'
@@ -61,7 +62,7 @@ def GenerateHTMLOutput(trace_results, output_file_name):
# Java verison of systrace. Java systrace is expected to be deleted at a later
# date. We should consolidate this logic when that happens.
- if len(trace_results) > 3:
+ if len(trace_results) > 4:
NewGenerateHTMLOutput(trace_results, output_file_name)
return os.path.abspath(output_file_name)
@@ -79,17 +80,28 @@ def GenerateHTMLOutput(trace_results, output_file_name):
# Open the file in binary mode to prevent python from changing the
# line endings, then write the prefix.
systrace_dir = os.path.abspath(os.path.dirname(__file__))
- html_prefix = _ReadAsset(systrace_dir, 'prefix.html')
+ html_prefix = _ReadAsset(systrace_dir, 'prefix.html.template')
html_suffix = _ReadAsset(systrace_dir, 'suffix.html')
trace_viewer_html = _ReadAsset(systrace_dir,
'systrace_trace_viewer.html')
+ catapult_root = os.path.abspath(os.path.dirname(os.path.dirname(
+ os.path.dirname(__file__))))
+ polymer_dir = os.path.join(catapult_root, 'third_party', 'polymer',
+ 'components', 'webcomponentsjs')
+ webcomponent_v0_polyfill = _ReadAsset(polymer_dir, 'webcomponents.min.js')
+
+ # Add the polyfill
+ html_output = html_prefix.replace('{{WEBCOMPONENTS_V0_POLYFILL_JS}}',
+ webcomponent_v0_polyfill)
# Open the file in binary mode to prevent python from changing the
# line endings, then write the prefix.
html_file = open(output_file_name, 'wb')
- html_file.write(html_prefix.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}',
+ html_file.write(html_output.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}',
trace_viewer_html))
+
+
# Write the trace data itself. There is a separate section of the form
# <script class="trace-data" type="application/text"> ... </script>
# for each tracing agent (including the controller tracing agent).
diff --git a/catapult/systrace/systrace/output_generator_unittest.py b/catapult/systrace/systrace/output_generator_unittest.py
index 23473169..87ca3321 100644
--- a/catapult/systrace/systrace/output_generator_unittest.py
+++ b/catapult/systrace/systrace/output_generator_unittest.py
@@ -22,6 +22,7 @@ ATRACE_DATA = os.path.join(TEST_DIR, 'atrace_data')
ATRACE_PROCESS_DUMP_DATA = os.path.join(TEST_DIR, 'atrace_procfs_dump')
COMBINED_PROFILE_CHROME_DATA = os.path.join(
TEST_DIR, 'profile-chrome_systrace_perf_chrome_data')
+CGROUP_DUMP_DATA = os.path.join(TEST_DIR, 'cgroup_dump')
class OutputGeneratorTest(unittest.TestCase):
@@ -86,6 +87,14 @@ class OutputGeneratorTest(unittest.TestCase):
atrace_process_dump_data,
allow_unstructured=True)
+ with open(CGROUP_DUMP_DATA) as fp:
+ cgroup_dump_data = fp.read()
+ trace_results.append(trace_result.TraceResult(
+ 'cgroupDump', cgroup_dump_data))
+ trace_data_builder.AddTraceFor(trace_data_module.CGROUP_TRACE_PART,
+ cgroup_dump_data,
+ allow_unstructured=True)
+
with open(COMBINED_PROFILE_CHROME_DATA) as fp:
chrome_data = json.load(fp)
trace_results.append(
diff --git a/catapult/systrace/systrace/prefix.html b/catapult/systrace/systrace/prefix.html.template
index 828eef03..767a300b 100644
--- a/catapult/systrace/systrace/prefix.html
+++ b/catapult/systrace/systrace/prefix.html.template
@@ -25,6 +25,12 @@
outline: none;
}
</style>
+
+<!-- WebComponent V0 polyfill. See https://crbug.com/1036492 -->
+<script>
+'use strict';
+{{WEBCOMPONENTS_V0_POLYFILL_JS}}
+</script>
{{SYSTRACE_TRACE_VIEWER_HTML}}
</head>
<body>
diff --git a/catapult/systrace/systrace/run_systrace.py b/catapult/systrace/systrace/run_systrace.py
index 15a39a38..c886ddec 100755
--- a/catapult/systrace/systrace/run_systrace.py
+++ b/catapult/systrace/systrace/run_systrace.py
@@ -47,10 +47,11 @@ from systrace.tracing_agents import atrace_from_file_agent
from systrace.tracing_agents import atrace_process_dump
from systrace.tracing_agents import ftrace_agent
from systrace.tracing_agents import walt_agent
+from systrace.tracing_agents import android_cgroup_agent
ALL_MODULES = [atrace_agent, atrace_from_file_agent, atrace_process_dump,
- ftrace_agent, walt_agent]
+ ftrace_agent, walt_agent, android_cgroup_agent]
def parse_options(argv):
diff --git a/catapult/systrace/systrace/systrace_runner.py b/catapult/systrace/systrace/systrace_runner.py
index 514f5bb0..695c0ee6 100644
--- a/catapult/systrace/systrace/systrace_runner.py
+++ b/catapult/systrace/systrace/systrace_runner.py
@@ -11,14 +11,15 @@ as an HTML or JSON file.'''
from systrace import output_generator
from systrace import tracing_controller
from systrace.tracing_agents import android_process_data_agent
+from systrace.tracing_agents import android_cgroup_agent
from systrace.tracing_agents import atrace_agent
from systrace.tracing_agents import atrace_from_file_agent
from systrace.tracing_agents import atrace_process_dump
from systrace.tracing_agents import ftrace_agent
from systrace.tracing_agents import walt_agent
-AGENT_MODULES = [android_process_data_agent, atrace_agent,
- atrace_from_file_agent, atrace_process_dump,
+AGENT_MODULES = [android_process_data_agent, android_cgroup_agent,
+ atrace_agent, atrace_from_file_agent, atrace_process_dump,
ftrace_agent, walt_agent]
class SystraceRunner(object):
diff --git a/catapult/systrace/systrace/systrace_trace_viewer.html b/catapult/systrace/systrace/systrace_trace_viewer.html
index ad9472e1..a472508c 100644
--- a/catapult/systrace/systrace/systrace_trace_viewer.html
+++ b/catapult/systrace/systrace/systrace_trace_viewer.html
@@ -496,7 +496,7 @@
border-right: 1px solid #A3A3A3;
border-top: 1px solid white;
display: flex;
- height: 26px;
+ min-height: 26px;
padding: 0 3px 0 3px;
}
@@ -785,7 +785,40 @@
</style>
<div id="content"></div>
</template>
-</dom-module><dom-module id="tr-ui-e-s-frame-data-side-panel">
+</dom-module><template id="tr-ui-e-img-image-snapshot-view-template">
+ <style>
+ .image-info {
+ margin-bottom: 5px;
+ }
+
+ .image-info .title {
+ font-weight: bold;
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+
+ .image-info .size {
+ margin-right: 5px;
+ }
+
+ .image-container {
+ min-height: 100px;
+ min-width: 200px;
+ overflow: auto;
+ }
+ </style>
+
+ <div class="image-info">
+ <span class="title">Image</span>
+ <span class="size">(unknown)</span>
+ <span class="instructions">
+ [ Drag with mouse to zoom in and out ]
+ </span>
+ </div>
+ <div class="image-container">
+ <img alt="Image snapshot"/>
+ </div>
+</template><dom-module id="tr-ui-e-s-frame-data-side-panel">
<template>
<style>
:host {
@@ -877,7 +910,6 @@
display: block;
overflow-x: hidden;
text-align: left;
- text-overflow: ellipsis;
white-space: nowrap;
}
@@ -2793,6 +2825,8 @@
padding-left: 8px;
padding-right: 8px;
flex: 1 1 auto;
+ overflow: hidden;
+ white-space: nowrap;
}
#control > #bar > #left_controls,
@@ -2800,6 +2834,7 @@
display: flex;
flex-direction: row;
align-items: stretch;
+ flex-shrink: 0;
}
#control > #bar > #left_controls > * { margin-right: 2px; }
@@ -2827,7 +2862,7 @@
tr-ui-b-drag-handle { flex: 0 0 auto; }
tr-ui-a-analysis-view { flex: 0 0 auto; }
- #view_options_dropdown, #process_filter_dropdown {
+ tr-ui-b-dropdown {
--dropdown-button: {
-webkit-appearance: none;
align-items: normal;
@@ -2848,6 +2883,7 @@
<div id="left_controls"></div>
<div id="title">^_^</div>
<div id="right_controls">
+ <tr-ui-b-dropdown id="flow_event_filter_dropdown" label="Flow events"></tr-ui-b-dropdown>
<tr-ui-b-dropdown id="process_filter_dropdown" label="Processes"></tr-ui-b-dropdown>
<tr-ui-b-toolbar-button id="view_metadata_button">
M
@@ -2865,6 +2901,8 @@
<div id="collapsing_controls"></div>
<tr-ui-b-info-bar-group id="import-warnings">
</tr-ui-b-info-bar-group>
+ <tr-ui-b-info-bar-group id="polyfill-warning">
+ </tr-ui-b-info-bar-group>
</div>
<middle-container>
<slot></slot>
@@ -3075,10 +3113,6 @@
margin-right: 20px;
}
- #show_visualization {
- margin-right: 20px;
- }
-
#export {
margin-right: 20px;
}
@@ -3124,8 +3158,6 @@
<select id="statistic" value="{{displayStatisticName::change}}">
</select>
- <button id="show_visualization" on-tap="loadVisualization_">Visualize</button>
-
<tr-ui-b-dropdown label="Export">
<tr-v-ui-histogram-set-controls-export>
</tr-v-ui-histogram-set-controls-export>
@@ -3351,221 +3383,6 @@
<tr-ui-b-table id="table">
</tr-ui-b-table></template>
-</dom-module><dom-module id="tr-v-ui-metrics-visualization">
- <template>
- <style>
- button {
- padding: 5px;
- font-size: 14px;
- }
-
- .text_input {
- width: 50px;
- padding: 4px;
- font-size: 14px;
- }
-
- .error {
- color: red;
- display: none;
- }
-
- .container {
- position: relative;
- display: inline-block;
- margin-left: 15px;
- }
-
- #title {
- font-size: 20px;
- font-weight: bold;
- padding-bottom: 5px;
- }
-
- #selectors {
- display: block;
- padding-bottom: 10px;
- }
-
- #search_page {
- width: 200px;
- margin-left: 30px;
- }
-
- #close {
- display: none;
- vertical-align: top;
- }
-
- #close svg{
- height: 1em;
- }
-
- #close svg line {
- stroke-width: 18;
- stroke: black;
- }
-
- #close:hover svg {
- background: black;
- }
-
- #close:hover svg line {
- stroke: white;
- }
- </style>
- <span class="container" id="aggregateContainer">
- </span>
- <span class="container" id="pageByPageContainer">
- <span id="selectors">
- <span id="percentile_label">Percentile Range:</span>
- <input class="text_input" id="start" placeholder="0"/>
- <input class="text_input" id="end" placeholder="100"/>
- <button id="filter" on-tap="filterByPercentile_">Filter</button>
- <input class="text_input" id="search_page" placeholder="Page Name"/>
- <button id="search" on-tap="searchByPage_">Search</button>
- <span class="error" id="search_error">Sorry, could not find that page!</span>
- </span>
- </span>
- <div display="block" id="submetricsContainer">
- <span id="close">
- <svg viewBox="0 0 128 128">
- <line x1="28" x2="100" y1="28" y2="100"></line>
- <line x1="28" x2="100" y1="100" y2="28"></line>
- </svg>
- </span>
- </div>
- </template>
-</dom-module><dom-module id="tr-v-ui-raster-visualization">
- <template>
- <style>
- button {
- padding: 5px;
- font-size: 14px;
- }
- .error {
- color: red;
- display: none;
- }
-
- .text_input {
- width: 200px;
- padding: 4px;
- font-size: 14px;
- }
-
- .selector_container{
- padding: 5px;
- }
-
- #search {
- display: inline-block;
- padding-bottom: 10px;
- }
-
- #search_page {
- width: 200px;
- }
-
- #pageSelector {
- display: inline-block;
- font-size: 12pt;
- }
-
- #close {
- display: none;
- vertical-align: top;
- }
-
- #close svg{
- height: 1em;
- }
-
- #close svg line {
- stroke-width: 18;
- stroke: black;
- }
-
- #close:hover svg {
- background: black;
- }
-
- #close:hover svg line {
- stroke: white;
- }
- </style>
- <span id="aggregateContainer">
- <div>
- <div class="selector_container">
- <span id="select_page_label">Individual Page Results:</span>
- <select id="pageSelector">
- <option id="select_page" value="">Select a page</option>
- </select>
- </div>
- <div class="selector_container">
- <div id="search_page_label">Search for a page:</div>
- <input class="text_input" id="search_page" placeholder="Page Name"/>
- <button id="search_button">Search</button>
- <div class="error" id="search_error">Sorry, could not find that page!</div>
- </div>
- </div>
- </span>
- <span id="pageContainer">
- <span id="close">
- <svg viewBox="0 0 128 128">
- <line x1="28" x2="100" y1="28" y2="100"></line>
- <line x1="28" x2="100" y1="100" y2="28"></line>
- </svg>
- </span>
- </span>
- </template>
-</dom-module><meta charset="utf-8"/><dom-module id="tr-v-ui-visualizations-data-container">
- <template>
- <style>
- .error {
- color: red;
- display: none;
- }
-
- .sample{
- display: none;
- }
-
- .subtitle{
- font-size: 20px;
- font-weight: bold;
- padding-bottom: 5px;
- }
-
- .description{
- font-size: 15px;
- padding-bottom: 5px;
- }
-
- #title {
- font-size: 30px;
- font-weight: bold;
- padding-bottom: 5px;
- }
- </style>
- <div id="title">Visualizations</div>
- <div class="error" id="data_error">Invalid data provided.</div>
- <div id="pipeline_per_frame_container">
- <div class="subtitle">Graphics Pipeline and Raster Tasks</div>
- <div class="description">
- When raster tasks are completed in comparison to the rest of the graphics pipeline.<br/>
- Only pages where raster tasks are completed after beginFrame is issued are included.
- </div>
- <tr-v-ui-raster-visualization id="rasterVisualization">
- </tr-v-ui-raster-visualization>
- </div>
- <div id="metrics_container">
- <div class="subtitle">Metrics</div>
- <div class="description">Total amount of time taken for the indicated metrics.</div>
- <tr-v-ui-metrics-visualization class="sample" id="metricsVisualization">
- </tr-v-ui-metrics-visualization>
- </div>
- </template>
</dom-module><dom-module id="tr-v-ui-histogram-set-view">
<template>
<style>
@@ -3587,9 +3404,6 @@
display: none;
}
- #visualizations{
- display: none;
- }
</style>
<div id="zero">zero Histograms</div>
@@ -3598,9 +3412,6 @@
<tr-v-ui-histogram-set-controls id="controls">
</tr-v-ui-histogram-set-controls>
- <tr-v-ui-visualizations-data-container id="visualizations">
- </tr-v-ui-visualizations-data-container>
-
<tr-v-ui-histogram-set-table id="table"></tr-v-ui-histogram-set-table>
</div>
</template>
@@ -3657,8 +3468,9 @@
* Do not edit directly.
*/
-'use strict';if(window.Polymer){throw new Error('Cannot proceed. Polymer already present.');}
-window.Polymer={};window.Polymer.dom='shadow';(function(){function resolve(){document.body.removeAttribute('unresolved');}
+'use strict';if(!window.CustomElements||window.CustomElements.hasNative){if(window.Polymer){throw new Error('Cannot proceed. Polymer already present.');}
+window.Polymer={};window.Polymer.dom='shadow';}
+(function(){function resolve(){document.body.removeAttribute('unresolved');}
if(window.WebComponents){addEventListener('WebComponentsReady',resolve);}else{if(document.readyState==='interactive'||document.readyState==='complete'){resolve();}else{addEventListener('DOMContentLoaded',resolve);}}}());window.Polymer={Settings:function(){var settings=window.Polymer||{};if(!settings.noUrlSettings){var parts=location.search.slice(1).split('&');for(var i=0,o;i<parts.length&&(o=parts[i]);i++){o=o.split('=');o[0]&&(settings[o[0]]=o[1]||true);}}
settings.wantShadow=settings.dom==='shadow';settings.hasShadow=Boolean(Element.prototype.createShadowRoot);settings.nativeShadow=settings.hasShadow&&!window.ShadowDOMPolyfill;settings.useShadow=settings.wantShadow&&settings.hasShadow;settings.hasNativeImports=Boolean('import'in document.createElement('link'));settings.useNativeImports=settings.hasNativeImports;settings.useNativeCustomElements=!window.CustomElements||window.CustomElements.useNative;settings.useNativeShadow=settings.useShadow&&settings.nativeShadow;settings.usePolyfillProto=!settings.useNativeCustomElements&&!Object.__proto__;settings.hasNativeCSSProperties=!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)&&window.CSS&&CSS.supports&&CSS.supports('box-shadow','0 0 0 var(--foo)');settings.useNativeCSSProperties=settings.hasNativeCSSProperties&&settings.lazyRegister&&settings.useNativeCSSProperties;settings.isIE=navigator.userAgent.match('Trident');settings.passiveTouchGestures=settings.passiveTouchGestures||false;return settings;}()};(function(){var userPolymer=window.Polymer;window.Polymer=function(prototype){if(typeof prototype==='function'){prototype=prototype.prototype;}
if(!prototype){prototype={};}
@@ -4250,7 +4062,7 @@ if(parentNode){if(!this._instance){this._instance=this.stamp();var root=this._in
this._instance=null;}},_showHideChildren:function(){var hidden=this.__hideTemplateChildren__||!this.if;if(this._instance){this._instance._showHideChildren(hidden);}},_forwardParentProp:function(prop,value){if(this._instance){this._instance.__setProperty(prop,value,true);}},_forwardParentPath:function(path,value){if(this._instance){this._instance._notifyPath(path,value,true);}}});Polymer({is:'dom-bind',properties:{notifyDomChange:{type:Boolean}},extends:'template',_template:null,created:function(){var self=this;Polymer.RenderStatus.whenReady(function(){if(document.readyState=='loading'){document.addEventListener('DOMContentLoaded',function(){self._markImportsReady();});}else{self._markImportsReady();}});},_ensureReady:function(){if(!this._readied){this._readySelf();}},_markImportsReady:function(){this._importsReady=true;this._ensureReady();},_registerFeatures:function(){this._prepConstructor();},_insertChildren:function(){var refNode;var parentNode=Polymer.dom(this).parentNode;if(parentNode.localName==this.is){refNode=parentNode;parentNode=Polymer.dom(parentNode).parentNode;}else{refNode=this;}
Polymer.dom(parentNode).insertBefore(this.root,refNode);},_removeChildren:function(){if(this._children){for(var i=0;i<this._children.length;i++){this.root.appendChild(this._children[i]);}}},_initFeatures:function(){},_scopeElementClass:function(element,selector){if(this.dataHost){return this.dataHost._scopeElementClass(element,selector);}else{return selector;}},_configureInstanceProperties:function(){},_prepConfigure:function(){var config={};for(var prop in this._propertyEffects){config[prop]=this[prop];}
var setupConfigure=this._setupConfigure;this._setupConfigure=function(){setupConfigure.call(this,config);};},attached:function(){if(this._importsReady){this.render();}},detached:function(){this._removeChildren();},render:function(){this._ensureReady();if(!this._children){this._template=this;this._prepAnnotations();this._prepEffects();this._prepBehaviors();this._prepConfigure();this._prepBindings();this._prepPropertyInfo();Polymer.Base._initFeatures.call(this);this._children=Polymer.TreeApi.arrayCopyChildNodes(this.root);}
-this._insertChildren();if(!Polymer.Settings.suppressTemplateNotifications||this.notifyDomChange){this.fire('dom-change');}}});'use strict';if(!Polymer.Settings.useNativeShadow){tr.showPanic('Polymer error','base only works in shadow mode');}'use strict';const global=this.window||this.global;this.tr=(function(){if(global.tr)return global.tr;function exportPath(name){const parts=name.split('.');let cur=global;for(let part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{cur=cur[part]={};}}
+this._insertChildren();if(!Polymer.Settings.suppressTemplateNotifications||this.notifyDomChange){this.fire('dom-change');}}});'use strict';if(!window.CustomElements||window.CustomElements.hasNative){if(!Polymer.Settings.useNativeShadow){tr.showPanic('Polymer error','base should use native shadow when possible.');}}'use strict';const global=this.window||this.global;this.tr=(function(){if(global.tr)return global.tr;function exportPath(name){const parts=name.split('.');let cur=global;for(let part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{cur=cur[part]={};}}
return cur;}
function isExported(name){const parts=name.split('.');let cur=global;for(let part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{return false;}}
return true;}
@@ -4527,7 +4339,7 @@ throw new Error(`Unrecognized unit "${object}"`);};Unit.define=function(params){
const baseUnit=Unit.byName[params.baseUnitName];definedUnits.forEach(u=>u.baseUnit=baseUnit);};Unit.nameSuffixForImprovementDirection=function(improvementDirection){switch(improvementDirection){case ImprovementDirection.DONT_CARE:return'';case ImprovementDirection.BIGGER_IS_BETTER:return'_biggerIsBetter';case ImprovementDirection.SMALLER_IS_BETTER:return'_smallerIsBetter';default:throw new Error('Unknown improvement direction: '+improvementDirection);}};Unit.defineUnitVariant_=function(params,isDelta,improvementDirection){let nameSuffix=isDelta?'Delta':'';nameSuffix+=Unit.nameSuffixForImprovementDirection(improvementDirection);const unitName=params.baseUnitName+nameSuffix;const jsonName=params.baseJsonName+nameSuffix;if(Unit.byName[unitName]!==undefined){throw new Error('Unit \''+unitName+'\' already exists');}
if(Unit.byJSONName[jsonName]!==undefined){throw new Error('JSON unit \''+jsonName+'\' alread exists');}
let scaleBaseUnit=params.scaleBaseUnit;if(!scaleBaseUnit){let formatSpec=params.formatSpec;if(typeof formatSpec==='function')formatSpec=formatSpec();const baseSymbol=formatSpec.unitScale?formatSpec.unitScale[0].baseSymbol:(formatSpec.baseSymbol||'');scaleBaseUnit={value:1,symbol:baseSymbol,baseSymbol};}
-const unit=new Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,params.formatSpec);Unit.byName[unitName]=unit;Unit.byJSONName[jsonName]=unit;return unit;};tr.b.EventTarget.decorate(Unit);Unit.reset();Unit.define({baseUnitName:'timeInMsAutoFormat',baseJsonName:'msBestFitFormat',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:{unitScale:tr.b.UnitScale.TIME.AUTO,minimumFractionDigits:0,maximumFractionDigits:3}});Unit.define({baseUnitName:'timeDurationInMs',baseJsonName:'ms',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'timeStampInMs',baseJsonName:'tsMs',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'normalizedPercentage',baseJsonName:'n%',formatSpec:{unitScale:[{value:0.01,symbol:'%'}],avoidSpacePrecedingUnit:true,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'sizeInBytes',baseJsonName:'sizeInBytes',formatSpec:{unitScale:tr.b.UnitScale.MEMORY.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'bandwidthInBytesPerSecond',baseJsonName:'bytesPerSecond',formatSpec:{unitScale:tr.b.UnitScale.BANDWIDTH_BYTES.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'energyInJoules',baseJsonName:'J',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('J','JOULE',tr.b.UnitPrefixScale.METRIC,'JOULE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'powerInWatts',baseJsonName:'W',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('W','WATT',tr.b.UnitPrefixScale.METRIC,'WATT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricCurrentInAmperes',baseJsonName:'A',formatSpec:{baseSymbol:'A',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('A','AMPERE',tr.b.UnitPrefixScale.METRIC,'AMPERE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricPotentialInVolts',baseJsonName:'V',formatSpec:{baseSymbol:'V',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('V','VOLT',tr.b.UnitPrefixScale.METRIC,'VOLT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'frequencyInHertz',baseJsonName:'Hz',formatSpec:{baseSymbol:'Hz',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('Hz','HERTZ',tr.b.UnitPrefixScale.METRIC,'HERTZ').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'unitlessNumber',baseJsonName:'unitless',formatSpec:{minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'count',baseJsonName:'count',formatSpec:{minimumFractionDigits:0,maximumFractionDigits:0}});Unit.define({baseUnitName:'sigma',baseJsonName:'sigma',formatSpec:{baseSymbol:String.fromCharCode(963),minimumFractionDigits:1,maximumFractionDigits:1}});return{ImprovementDirection,Unit,};});'use strict';tr.exportTo('tr.b',function(){class Scalar{constructor(unit,value){if(!(unit instanceof tr.b.Unit)){throw new Error('Expected Unit');}
+const unit=new Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,params.formatSpec);Unit.byName[unitName]=unit;Unit.byJSONName[jsonName]=unit;return unit;};tr.b.EventTarget.decorate(Unit);Unit.reset();Unit.define({baseUnitName:'timeInMsAutoFormat',baseJsonName:'msBestFitFormat',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:{unitScale:tr.b.UnitScale.TIME.AUTO,minimumFractionDigits:0,maximumFractionDigits:3}});Unit.define({baseUnitName:'timeDurationInMs',baseJsonName:'ms',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'timeStampInMs',baseJsonName:'tsMs',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'normalizedPercentage',baseJsonName:'n%',formatSpec:{unitScale:[{value:0.01,symbol:'%'}],avoidSpacePrecedingUnit:true,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'sizeInBytes',baseJsonName:'sizeInBytes',formatSpec:{unitScale:tr.b.UnitScale.MEMORY.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'bandwidthInBytesPerSecond',baseJsonName:'bytesPerSecond',formatSpec:{unitScale:tr.b.UnitScale.BANDWIDTH_BYTES.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'energyInJoules',baseJsonName:'J',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('J','JOULE',tr.b.UnitPrefixScale.METRIC,'JOULE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'powerInWatts',baseJsonName:'W',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('W','WATT',tr.b.UnitPrefixScale.METRIC,'WATT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricCurrentInAmperes',baseJsonName:'A',formatSpec:{baseSymbol:'A',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('A','AMPERE',tr.b.UnitPrefixScale.METRIC,'AMPERE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'batteryChargeInAmpereHours',baseJsonName:'Ah',formatSpec:{baseSymbol:'Ah',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('Ah','AMPEREHOUR',tr.b.UnitPrefixScale.METRIC,'AMPEREHOUR').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricPotentialInVolts',baseJsonName:'V',formatSpec:{baseSymbol:'V',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('V','VOLT',tr.b.UnitPrefixScale.METRIC,'VOLT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'frequencyInHertz',baseJsonName:'Hz',formatSpec:{baseSymbol:'Hz',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('Hz','HERTZ',tr.b.UnitPrefixScale.METRIC,'HERTZ').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'unitlessNumber',baseJsonName:'unitless',formatSpec:{minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'count',baseJsonName:'count',formatSpec:{minimumFractionDigits:0,maximumFractionDigits:0}});Unit.define({baseUnitName:'sigma',baseJsonName:'sigma',formatSpec:{baseSymbol:String.fromCharCode(963),minimumFractionDigits:1,maximumFractionDigits:1}});return{ImprovementDirection,Unit,};});'use strict';tr.exportTo('tr.b',function(){class Scalar{constructor(unit,value){if(!(unit instanceof tr.b.Unit)){throw new Error('Expected Unit');}
if(!(typeof(value)==='number')){throw new Error('Expected value to be number');}
this.unit=unit;this.value=value;}
asDict(){return{unit:this.unit.asJSON(),value:tr.b.numberToJson(this.value),};}
@@ -4788,7 +4600,7 @@ ChromeModelHelper.guid=tr.b.GUID.allocateSimple();ChromeModelHelper.supportsMode
for(const rendererHelper of Object.values(this.rendererHelpers)){this.chromeBounds_.addRange(rendererHelper.process.bounds);}
if(this.gpuHelper){this.chromeBounds_.addRange(this.gpuHelper.process.bounds);}}
if(this.chromeBounds_.isEmpty){return undefined;}
-return this.chromeBounds_;},get telemetryHelper(){return this.telemetryHelper_;}};return{ChromeModelHelper,};});'use strict';tr.exportTo('tr.e.cc',function(){const AsyncSlice=tr.model.AsyncSlice;const EventSet=tr.model.EventSet;const UI_COMP_NAME='INPUT_EVENT_LATENCY_UI_COMPONENT';const ORIGINAL_COMP_NAME='INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';const BEGIN_COMP_NAME='INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';const END_COMP_NAME='INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT';const LEGACY_END_COMP_NAME='INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';const MAIN_RENDERER_THREAD_NAME='CrRendererMain';const COMPOSITOR_THREAD_NAME='Compositor';const POSTTASK_FLOW_EVENT='disabled-by-default-toplevel.flow';const IPC_FLOW_EVENT='disabled-by-default-ipc.flow';const INPUT_EVENT_TYPE_NAMES={CHAR:'Char',CLICK:'GestureClick',CONTEXT_MENU:'ContextMenu',FLING_CANCEL:'GestureFlingCancel',FLING_START:'GestureFlingStart',KEY_DOWN:'KeyDown',KEY_DOWN_RAW:'RawKeyDown',KEY_UP:'KeyUp',LATENCY_SCROLL_UPDATE:'ScrollUpdate',MOUSE_DOWN:'MouseDown',MOUSE_ENTER:'MouseEnter',MOUSE_LEAVE:'MouseLeave',MOUSE_MOVE:'MouseMove',MOUSE_UP:'MouseUp',MOUSE_WHEEL:'MouseWheel',PINCH_BEGIN:'GesturePinchBegin',PINCH_END:'GesturePinchEnd',PINCH_UPDATE:'GesturePinchUpdate',SCROLL_BEGIN:'GestureScrollBegin',SCROLL_END:'GestureScrollEnd',SCROLL_UPDATE:'GestureScrollUpdate',SCROLL_UPDATE_RENDERER:'ScrollUpdate',SHOW_PRESS:'GestureShowPress',TAP:'GestureTap',TAP_CANCEL:'GestureTapCancel',TAP_DOWN:'GestureTapDown',TOUCH_CANCEL:'TouchCancel',TOUCH_END:'TouchEnd',TOUCH_MOVE:'TouchMove',TOUCH_START:'TouchStart',UNKNOWN:'UNKNOWN'};function InputLatencyAsyncSlice(){AsyncSlice.apply(this,arguments);this.associatedEvents_=new EventSet();this.typeName_=undefined;if(!this.isLegacyEvent){this.determineModernTypeName_();}}
+return this.chromeBounds_;},get telemetryHelper(){return this.telemetryHelper_;}};return{ChromeModelHelper,};});'use strict';tr.exportTo('tr.e.cc',function(){const AsyncSlice=tr.model.AsyncSlice;const EventSet=tr.model.EventSet;const UI_COMP_NAME='INPUT_EVENT_LATENCY_UI_COMPONENT';const ORIGINAL_COMP_NAME='INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';const BEGIN_COMP_NAME='INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';const END_COMP_NAME='INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT';const LEGACY_END_COMP_NAME='INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';const MAIN_RENDERER_THREAD_NAME='CrRendererMain';const COMPOSITOR_THREAD_NAME='Compositor';const OLD_IPC_FLOW_EVENT='disabled-by-default-ipc.flow';const OLD_POSTTASK_FLOW_EVENT='disabled-by-default-toplevel.flow';const NEW_POSTTASK_FLOW_EVENT='toplevel.flow';const INPUT_EVENT_TYPE_NAMES={CHAR:'Char',CLICK:'GestureClick',CONTEXT_MENU:'ContextMenu',FLING_CANCEL:'GestureFlingCancel',FLING_START:'GestureFlingStart',KEY_DOWN:'KeyDown',KEY_DOWN_RAW:'RawKeyDown',KEY_UP:'KeyUp',LATENCY_SCROLL_UPDATE:'ScrollUpdate',MOUSE_DOWN:'MouseDown',MOUSE_ENTER:'MouseEnter',MOUSE_LEAVE:'MouseLeave',MOUSE_MOVE:'MouseMove',MOUSE_UP:'MouseUp',MOUSE_WHEEL:'MouseWheel',PINCH_BEGIN:'GesturePinchBegin',PINCH_END:'GesturePinchEnd',PINCH_UPDATE:'GesturePinchUpdate',SCROLL_BEGIN:'GestureScrollBegin',SCROLL_END:'GestureScrollEnd',SCROLL_UPDATE:'GestureScrollUpdate',SCROLL_UPDATE_RENDERER:'ScrollUpdate',SHOW_PRESS:'GestureShowPress',TAP:'GestureTap',TAP_CANCEL:'GestureTapCancel',TAP_DOWN:'GestureTapDown',TOUCH_CANCEL:'TouchCancel',TOUCH_END:'TouchEnd',TOUCH_MOVE:'TouchMove',TOUCH_START:'TouchStart',UNKNOWN:'UNKNOWN'};function InputLatencyAsyncSlice(){AsyncSlice.apply(this,arguments);this.associatedEvents_=new EventSet();this.typeName_=undefined;if(!this.isLegacyEvent){this.determineModernTypeName_();}}
InputLatencyAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get isLegacyEvent(){return this.title==='InputLatency';},get typeName(){if(!this.typeName_){this.determineLegacyTypeName_();}
return this.typeName_;},checkTypeName_(){if(!this.typeName_){throw new Error('Unable to determine typeName');}
let found=false;for(const typeName in INPUT_EVENT_TYPE_NAMES){if(this.typeName===INPUT_EVENT_TYPE_NAMES[typeName]){found=true;break;}}
@@ -4799,23 +4611,20 @@ this.checkTypeName_();},getRendererHelper(sourceSlices){const traceModel=this.st
if(mainThread&&compositorThread)break;}
const rendererHelpers=modelHelper.rendererHelpers;const pids=Object.keys(rendererHelpers);for(let i=0;i<pids.length;i++){const pid=pids[i];const rendererHelper=rendererHelpers[pid];if(rendererHelper.mainThread===mainThread||rendererHelper.compositorThread===compositorThread){return rendererHelper;}}
return undefined;},addEntireSliceHierarchy(slice){this.associatedEvents_.push(slice);slice.iterateAllSubsequentSlices(function(subsequentSlice){this.associatedEvents_.push(subsequentSlice);},this);},addDirectlyAssociatedEvents(flowEvents){const slices=[];flowEvents.forEach(function(flowEvent){this.associatedEvents_.push(flowEvent);const newSource=flowEvent.startSlice.mostTopLevelSlice;if(slices.indexOf(newSource)===-1){slices.push(newSource);}},this);const lastFlowEvent=flowEvents[flowEvents.length-1];const lastSource=lastFlowEvent.endSlice.mostTopLevelSlice;if(slices.indexOf(lastSource)===-1){slices.push(lastSource);}
-return slices;},addScrollUpdateEvents(rendererHelper){if(!rendererHelper||!rendererHelper.compositorThread){return;}
-const compositorThread=rendererHelper.compositorThread;const gestureScrollUpdateStart=this.start;const gestureScrollUpdateEnd=this.end;const allCompositorAsyncSlices=compositorThread.asyncSliceGroup.slices;for(const i in allCompositorAsyncSlices){const slice=allCompositorAsyncSlices[i];if(slice.title!=='Latency::ScrollUpdate')continue;const parentId=slice.args.data.INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT.sequence_number;if(parentId===undefined){if(slice.start<gestureScrollUpdateStart||slice.start>=gestureScrollUpdateEnd){continue;}}else{if(parseInt(parentId)!==parseInt(this.id)){continue;}}
-slice.associatedEvents.forEach(function(event){this.associatedEvents_.push(event);},this);break;}},belongToOtherInputs(slice,flowEvents){let fromOtherInputs=false;slice.iterateEntireHierarchy(function(subsequentSlice){if(fromOtherInputs)return;subsequentSlice.inFlowEvents.forEach(function(inflow){if(fromOtherInputs)return;if(inflow.category.indexOf('input')>-1){if(flowEvents.indexOf(inflow)===-1){fromOtherInputs=true;}}},this);},this);return fromOtherInputs;},triggerOtherInputs(event,flowEvents){if(event.outFlowEvents===undefined||event.outFlowEvents.length===0){return false;}
-const flow=event.outFlowEvents[0];if(flow.category!==POSTTASK_FLOW_EVENT||!flow.endSlice){return false;}
+return slices;},belongToOtherInputs(slice,flowEvents){let fromOtherInputs=false;slice.iterateEntireHierarchy(function(subsequentSlice){if(fromOtherInputs)return;subsequentSlice.inFlowEvents.forEach(function(inflow){if(fromOtherInputs)return;if(inflow.category.indexOf('input')>-1){if(flowEvents.indexOf(inflow)===-1){fromOtherInputs=true;}}},this);},this);return fromOtherInputs;},triggerOtherInputs(event,flowEvents){if(event.outFlowEvents===undefined||event.outFlowEvents.length===0){return false;}
+const flow=event.outFlowEvents[0];const isPostTask=flow.category===NEW_POSTTASK_FLOW_EVENT||flow.category===OLD_POSTTASK_FLOW_EVENT;if(!isPostTask||!flow.endSlice){return false;}
const endSlice=flow.endSlice;if(this.belongToOtherInputs(endSlice.mostTopLevelSlice,flowEvents)){return true;}
return false;},followSubsequentSlices(event,queue,visited,flowEvents){let stopFollowing=false;let inputAck=false;event.iterateAllSubsequentSlices(function(slice){if(stopFollowing)return;if(slice.title==='TaskQueueManager::RunTask')return;if(slice.title==='ThreadProxy::ScheduledActionSendBeginMainFrame'){return;}
if(slice.title==='Scheduler::ScheduleBeginImplFrameDeadline'){if(this.triggerOtherInputs(slice,flowEvents))return;}
if(slice.title==='CompositorImpl::PostComposite'){if(this.triggerOtherInputs(slice,flowEvents))return;}
if(slice.title==='InputRouterImpl::ProcessInputEventAck'){inputAck=true;}
if(inputAck&&slice.title==='InputRouterImpl::FilterAndSendWebInputEvent'){stopFollowing=true;}
-this.followCurrentSlice(slice,queue,visited);},this);},followCurrentSlice(event,queue,visited){event.outFlowEvents.forEach(function(outflow){if((outflow.category===POSTTASK_FLOW_EVENT||outflow.category===IPC_FLOW_EVENT)&&outflow.endSlice){this.associatedEvents_.push(outflow);const nextEvent=outflow.endSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);queue.push(nextEvent);}}},this);},backtraceFromDraw(beginImplFrame,visited){const pendingEventQueue=[];pendingEventQueue.push(beginImplFrame.mostTopLevelSlice);while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);event.inFlowEvents.forEach(function(inflow){if(inflow.category===POSTTASK_FLOW_EVENT&&inflow.startSlice){const nextEvent=inflow.startSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);pendingEventQueue.push(nextEvent);}}},this);}},sortRasterizerSlices(rasterWorkerThreads,sortedRasterizerSlices){rasterWorkerThreads.forEach(function(rasterizer){Array.prototype.push.apply(sortedRasterizerSlices,rasterizer.sliceGroup.slices);},this);sortedRasterizerSlices.sort(function(a,b){if(a.start!==b.start){return a.start-b.start;}
+this.followCurrentSlice(slice,queue,visited);},this);},followCurrentSlice(event,queue,visited){event.outFlowEvents.forEach(function(outflow){if((outflow.category===NEW_POSTTASK_FLOW_EVENT||outflow.category===OLD_POSTTASK_FLOW_EVENT||outflow.category===OLD_IPC_FLOW_EVENT)&&outflow.endSlice){this.associatedEvents_.push(outflow);const nextEvent=outflow.endSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);queue.push(nextEvent);}}},this);},backtraceFromDraw(beginImplFrame,visited){const pendingEventQueue=[];pendingEventQueue.push(beginImplFrame.mostTopLevelSlice);while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);event.inFlowEvents.forEach(function(inflow){if(inflow.category===POSTTASK_FLOW_EVENT&&inflow.startSlice){const nextEvent=inflow.startSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);pendingEventQueue.push(nextEvent);}}},this);}},sortRasterizerSlices(rasterWorkerThreads,sortedRasterizerSlices){rasterWorkerThreads.forEach(function(rasterizer){Array.prototype.push.apply(sortedRasterizerSlices,rasterizer.sliceGroup.slices);},this);sortedRasterizerSlices.sort(function(a,b){if(a.start!==b.start){return a.start-b.start;}
return a.guid-b.guid;});},addRasterizationEvents(prepareTiles,rendererHelper,visited,flowEvents,sortedRasterizerSlices){if(!prepareTiles.args.prepare_tiles_id)return;if(!rendererHelper||!rendererHelper.rasterWorkerThreads){return;}
const rasterWorkerThreads=rendererHelper.rasterWorkerThreads;const prepareTileId=prepareTiles.args.prepare_tiles_id;const pendingEventQueue=[];if(sortedRasterizerSlices.length===0){this.sortRasterizerSlices(rasterWorkerThreads,sortedRasterizerSlices);}
let numFinishedTasks=0;const RASTER_TASK_TITLE='RasterizerTaskImpl::RunOnWorkerThread';const IMAGEDECODE_TASK_TITLE='ImageDecodeTaskImpl::RunOnWorkerThread';const FINISHED_TASK_TITLE='TaskSetFinishedTaskImpl::RunOnWorkerThread';for(let i=0;i<sortedRasterizerSlices.length;i++){const task=sortedRasterizerSlices[i];if(task.title===RASTER_TASK_TITLE||task.title===IMAGEDECODE_TASK_TITLE){if(task.args.source_prepare_tiles_id===prepareTileId){this.addEntireSliceHierarchy(task.mostTopLevelSlice);}}else if(task.title===FINISHED_TASK_TITLE){if(task.start>prepareTiles.start){pendingEventQueue.push(task.mostTopLevelSlice);if(++numFinishedTasks===3)break;}}}
while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followSubsequentSlices(event,pendingEventQueue,visited,flowEvents);}},addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents,sortedRasterizerSlices){const pendingEventQueue=[];const visitedEvents=new EventSet();let beginImplFrame=undefined;let prepareTiles=undefined;sortedRasterizerSlices=[];sourceSlices.forEach(function(sourceSlice){if(!visitedEvents.contains(sourceSlice)){visitedEvents.push(sourceSlice);pendingEventQueue.push(sourceSlice);}},this);while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followCurrentSlice(event,pendingEventQueue,visitedEvents);this.followSubsequentSlices(event,pendingEventQueue,visitedEvents,flowEvents);const COMPOSITOR_PREPARE_TILES='TileManager::PrepareTiles';prepareTiles=event.findDescendentSlice(COMPOSITOR_PREPARE_TILES);if(prepareTiles){this.addRasterizationEvents(prepareTiles,rendererHelper,visitedEvents,flowEvents,sortedRasterizerSlices);}
-const COMPOSITOR_ON_BIFD='Scheduler::OnBeginImplFrameDeadline';beginImplFrame=event.findDescendentSlice(COMPOSITOR_ON_BIFD);if(beginImplFrame){this.backtraceFromDraw(beginImplFrame,visitedEvents);}}
-const INPUT_GSU='InputLatency::GestureScrollUpdate';if(this.title===INPUT_GSU){this.addScrollUpdateEvents(rendererHelper);}},get associatedEvents(){if(this.associatedEvents_.length!==0){return this.associatedEvents_;}
+const COMPOSITOR_ON_BIFD='Scheduler::OnBeginImplFrameDeadline';beginImplFrame=event.findDescendentSlice(COMPOSITOR_ON_BIFD);if(beginImplFrame){this.backtraceFromDraw(beginImplFrame,visitedEvents);}}},get associatedEvents(){if(this.associatedEvents_.length!==0){return this.associatedEvents_;}
const modelIndices=this.startThread.parent.model.modelIndices;const flowEvents=modelIndices.getFlowEventsWithId(this.id);if(flowEvents.length===0){return this.associatedEvents_;}
const sourceSlices=this.addDirectlyAssociatedEvents(flowEvents);const rendererHelper=this.getRendererHelper(sourceSlices);this.addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents);return this.associatedEvents_;},get inputLatency(){if(!('data'in this.args))return undefined;const data=this.args.data;const endTimeComp=data[END_COMP_NAME]||data[LEGACY_END_COMP_NAME];if(endTimeComp===undefined)return undefined;let latency=0;const endTime=endTimeComp.time;if(ORIGINAL_COMP_NAME in data){latency=endTime-data[ORIGINAL_COMP_NAME].time;}else if(UI_COMP_NAME in data){latency=endTime-data[UI_COMP_NAME].time;}else if(BEGIN_COMP_NAME in data){latency=endTime-data[BEGIN_COMP_NAME].time;}else{throw new Error('No valid begin latency component');}
return latency;}};const eventTypeNames=['Char','ContextMenu','GestureClick','GestureFlingCancel','GestureFlingStart','GestureScrollBegin','GestureScrollEnd','GestureScrollUpdate','GestureShowPress','GestureTap','GestureTapCancel','GestureTapDown','GesturePinchBegin','GesturePinchEnd','GesturePinchUpdate','KeyDown','KeyUp','MouseDown','MouseEnter','MouseLeave','MouseMove','MouseUp','MouseWheel','RawKeyDown','ScrollUpdate','TouchCancel','TouchEnd','TouchMove','TouchStart'];const allTypeNames=['InputLatency'];eventTypeNames.forEach(function(eventTypeName){allTypeNames.push('InputLatency:'+eventTypeName);allTypeNames.push('InputLatency::'+eventTypeName);});AsyncSlice.subTypes.register(InputLatencyAsyncSlice,{typeNames:allTypeNames,categoryParts:['latencyInfo']});return{InputLatencyAsyncSlice,INPUT_EVENT_TYPE_NAMES,};});'use strict';tr.exportTo('tr.e.chrome',function(){const SAME_AS_PARENT='same-as-parent';const TITLES_FOR_USER_FRIENDLY_CATEGORY={composite:['CompositingInputsUpdater::update','ThreadProxy::SetNeedsUpdateLayers','LayerTreeHost::DoUpdateLayers','LayerTreeHost::UpdateLayers::BuildPropertyTrees','LocalFrameView::pushPaintArtifactToCompositor','LocalFrameView::updateCompositedSelectionIfNeeded','LocalFrameView::RunCompositingLifecyclePhase','UpdateLayerTree',],gc:['minorGC','majorGC','MajorGC','MinorGC','V8.GCScavenger','V8.GCIncrementalMarking','V8.GCIdleNotification','V8.GCContext','V8.GCCompactor','V8GCController::traceDOMWrappers',],iframe_creation:['WebLocalFrameImpl::createChildframe',],imageDecode:['Decode Image','ImageFrameGenerator::decode','ImageFrameGenerator::decodeAndScale','ImageFrameGenerator::decodeToYUV','ImageResourceContent::updateImage',],input:['HitTest','ScrollableArea::scrollPositionChanged','EventHandler::handleMouseMoveEvent',],layout:['IntersectionObserverController::computeTrackedIntersectionObservations','LocalFrameView::invalidateTree','LocalFrameView::layout','LocalFrameView::performLayout','LocalFrameView::performPostLayoutTasks','LocalFrameView::performPreLayoutTasks','LocalFrameView::RunStyleAndLayoutCompositingPhases','Layout','PaintLayer::updateLayerPositionsAfterLayout','ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities','WebViewImpl::updateAllLifecyclePhases','WebViewImpl::beginFrame',],parseHTML:['BackgroundHTMLParser::pumpTokenizer','BackgroundHTMLParser::sendTokensToMainThread','HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser','HTMLDocumentParser::documentElementAvailable','HTMLDocumentParser::notifyPendingTokenizedChunks','HTMLDocumentParser::processParsedChunkFromBackgroundParser','HTMLDocumentParser::processTokenizedChunkFromBackgroundParser','ParseHTML',],raster:['DisplayListRasterSource::PerformSolidColorAnalysis','Picture::Raster','RasterBufferImpl::Playback','RasterTask','RasterizerTaskImpl::RunOnWorkerThread','SkCanvas::drawImageRect()','SkCanvas::drawPicture()','SkCanvas::drawTextBlob()','TileTaskWorkerPool::PlaybackToMemory',],record:['Canvas2DLayerBridge::flushRecordingOnly','CompositingInputsUpdater::update','CompositingRequirementsUpdater::updateRecursive','ContentLayerDelegate::paintContents','DisplayItemList::Finalize','LocalFrameView::RunPaintLifecyclePhase','LocalFrameView::RunPrePaintLifecyclePhase','Paint','PaintController::commitNewDisplayItems','PaintLayerCompositor::updateIfNeededRecursive','Picture::Record','PictureLayer::Update',],style:['CSSParserImpl::parseStyleSheet.parse','CSSParserImpl::parseStyleSheet.tokenize','Document::rebuildLayoutTree','Document::recalcStyle','Document::updateActiveStyle','Document::updateStyle','Document::updateStyleInvalidationIfNeeded','LocalFrameView::updateStyleAndLayoutIfNeededRecursive','ParseAuthorStyleSheet','RuleSet::addRulesFromSheet','StyleElement::processStyleSheet','StyleEngine::createResolver','StyleEngine::updateActiveStyleSheets','StyleSheetContents::parseAuthorStyleSheet','UpdateLayoutTree',],script_parse_and_compile:['V8.CompileFullCode','V8.NewContext','V8.Parse','V8.ParseLazy','V8.RecompileSynchronous','V8.ScriptCompiler','v8.compile','v8.parseOnBackground',],script_execute:['EvaluateScript','FunctionCall','HTMLParserScriptRunner ExecuteScript','V8.Execute','V8.RunMicrotasks','V8.Task','WindowProxy::initialize','v8.callFunction','v8.run',],resource_loading:['RenderFrameImpl::didFinishDocumentLoad','RenderFrameImpl::didFinishLoad','Resource::appendData','ResourceDispatcher::OnReceivedData','ResourceDispatcher::OnReceivedResponse','ResourceDispatcher::OnRequestComplete','ResourceFetcher::requestResource','WebURLLoaderImpl::Context::Cancel','WebURLLoaderImpl::Context::OnCompletedRequest','WebURLLoaderImpl::Context::OnReceivedData','WebURLLoaderImpl::Context::OnReceivedRedirect','WebURLLoaderImpl::Context::OnReceivedResponse','WebURLLoaderImpl::Context::Start','WebURLLoaderImpl::loadAsynchronously','WebURLLoaderImpl::loadSynchronously','content::mojom::URLLoaderClient',],renderer_misc:['DecodeFont','ThreadState::completeSweep',],v8_runtime:[],[SAME_AS_PARENT]:['SyncChannel::Send',]};const COLOR_FOR_USER_FRIENDLY_CATEGORY=new tr.b.SinebowColorGenerator();const USER_FRIENDLY_CATEGORY_FOR_TITLE=new Map();for(const category in TITLES_FOR_USER_FRIENDLY_CATEGORY){TITLES_FOR_USER_FRIENDLY_CATEGORY[category].forEach(function(title){USER_FRIENDLY_CATEGORY_FOR_TITLE.set(title,category);});}
@@ -4826,7 +4635,7 @@ return'other';};ChromeUserFriendlyCategoryDriver.getColor=function(ufc){return C
for(const category of Object.values(USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY)){ChromeUserFriendlyCategoryDriver.ALL_TITLES.push(category);}
ChromeUserFriendlyCategoryDriver.ALL_TITLES.sort();for(const category of ChromeUserFriendlyCategoryDriver.ALL_TITLES){ChromeUserFriendlyCategoryDriver.getColor(category);}
return{ChromeUserFriendlyCategoryDriver,};});'use strict';tr.exportTo('tr.model',function(){return{BROWSER_PROCESS_PID_REF:-1,OBJECT_DEFAULT_SCOPE:'ptr',LOCAL_ID_PHASES:new Set(['N','D','O','(',')'])};});'use strict';tr.exportTo('tr.e.audits',function(){const Auditor=tr.c.Auditor;const Alert=tr.model.Alert;const EventInfo=tr.model.EventInfo;function ChromeAuditor(model){Auditor.call(this,model);const modelHelper=this.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper&&modelHelper.browserHelper){this.modelHelper=modelHelper;}else{this.modelHelper=undefined;}}
-function getMissedFrameAlerts(rendererHelpers){const alerts=[];for(const rendererHelper of rendererHelpers){if(!rendererHelper.compositorThread)continue;const thread=rendererHelper.compositorThread;const asyncSlices=Object.values(thread.asyncSliceGroup.slices);for(const slice of asyncSlices){if(slice.title!=='PipelineReporter'||!slice.args.termination_status||slice.args.termination_status!=='missed_frame')continue;const alertSlices=[slice].concat(slice.subSlices);alerts.push(new Alert(new EventInfo('Missed Frame','Frame was not submitted before deadline.'),slice.start,alertSlices));}}
+function getMissedFrameAlerts(rendererHelpers){const alerts=[];for(const rendererHelper of rendererHelpers){if(!rendererHelper.compositorThread)continue;const thread=rendererHelper.compositorThread;const asyncSlices=Object.values(thread.asyncSliceGroup.slices);for(const slice of asyncSlices){if(slice.title==='PipelineReporter'&&slice.args.chrome_frame_reporter&&slice.args.chrome_frame_reporter.state==='STATE_DROPPED'){const alertSlices=[slice].concat(slice.subSlices);alerts.push(new Alert(new EventInfo('Dropped Frame','Frame was dropped (i.e. not produced/presented).'),slice.start,alertSlices));}}}
return alerts;}
ChromeAuditor.prototype={__proto__:Auditor.prototype,runAnnotate(){if(!this.modelHelper)return;for(const pid in this.modelHelper.rendererHelpers){const rendererHelper=this.modelHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI){rendererHelper.process.important=false;}}},installUserFriendlyCategoryDriverIfNeeded(){this.model.addUserFriendlyCategoryDriver(tr.e.chrome.ChromeUserFriendlyCategoryDriver);},runAudit(){if(!this.modelHelper)return;this.model.replacePIDRefsInPatchups(tr.model.BROWSER_PROCESS_PID_REF,this.modelHelper.browserProcess.pid);this.model.applyObjectRefPatchups();const alerts=getMissedFrameAlerts(Object.values(this.modelHelper.rendererHelpers));this.model.alerts=this.model.alerts.concat(alerts);}};Auditor.register(ChromeAuditor);return{ChromeAuditor,};});'use strict';tr.exportTo('tr.e.chrome',function(){const KNOWN_PROPERTIES={absX:1,absY:1,address:1,anonymous:1,childNeeds:1,children:1,classNames:1,col:1,colSpan:1,float:1,height:1,htmlId:1,name:1,posChildNeeds:1,positioned:1,positionedMovement:1,relX:1,relY:1,relativePositioned:1,row:1,rowSpan:1,selfNeeds:1,stickyPositioned:1,tag:1,width:1};function LayoutObject(snapshot,args){this.snapshot_=snapshot;this.id_=args.address;this.name_=args.name;this.childLayoutObjects_=[];this.otherProperties_={};this.tag_=args.tag;this.relativeRect_=tr.b.math.Rect.fromXYWH(args.relX,args.relY,args.width,args.height);this.absoluteRect_=tr.b.math.Rect.fromXYWH(args.absX,args.absY,args.width,args.height);this.isFloat_=args.float;this.isStickyPositioned_=args.stickyPositioned;this.isPositioned_=args.positioned;this.isRelativePositioned_=args.relativePositioned;this.isAnonymous_=args.anonymous;this.htmlId_=args.htmlId;this.classNames_=args.classNames;this.needsLayoutReasons_=[];if(args.selfNeeds){this.needsLayoutReasons_.push('self');}
if(args.childNeeds){this.needsLayoutReasons_.push('child');}
@@ -4867,7 +4676,8 @@ if(e.selfTime===0||e.selfTime===undefined||e.cpuSelfTime===undefined){continue;}
const usage=tr.b.math.clamp(e.cpuSelfTime/e.selfTime,0,1);let lastTime=e.start;for(const subslice of e.subSlices){result.push({usage,start:lastTime,end:subslice.start});lastTime=subslice.end;}
result.push({usage,start:lastTime,end:e.end});}}
return result;}}
-tr.c.Auditor.register(CpuUsageAuditor);return{CpuUsageAuditor};});'use strict';tr.exportTo('tr.b',function(){function Base64(){}
+tr.c.Auditor.register(CpuUsageAuditor);return{CpuUsageAuditor};});'use strict';tr.exportTo('tr.e.img',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function ImageSnapshot(){ObjectSnapshot.apply(this,arguments);}
+ImageSnapshot.prototype={__proto__:ObjectSnapshot.prototype,initialize(){this.data_=this.args.data;this.type_=this.args.params.type;},get data(){return this.data_;},get type(){return this.type_;},};ObjectSnapshot.subTypes.register(ImageSnapshot,{typeNames:['gfx::Image']});return{ImageSnapshot,};});'use strict';tr.exportTo('tr.b',function(){function Base64(){}
function b64ToUint6(nChr){if(nChr>64&&nChr<91)return nChr-65;if(nChr>96&&nChr<123)return nChr-71;if(nChr>47&&nChr<58)return nChr+4;if(nChr===43)return 62;if(nChr===47)return 63;return 0;}
Base64.getDecodedBufferLength=function(input){let pad=0;if(input.substr(-2)==='=='){pad=2;}else if(input.substr(-1)==='='){pad=1;}
return((input.length*3+1)>>2)-pad;};Base64.EncodeArrayBufferToString=function(input){let binary='';const bytes=new Uint8Array(input);const len=bytes.byteLength;for(let i=0;i<len;i++){binary+=String.fromCharCode(bytes[i]);}
@@ -5131,7 +4941,7 @@ for(let i=0;i<this.kernelSliceGroup.length;i++){categoriesDict[this.kernelSliceG
for(let i=0;i<this.asyncSliceGroup.length;i++){categoriesDict[this.asyncSliceGroup.slices[i].category]=true;}
if(this.samples_){for(let i=0;i<this.samples_.length;i++){categoriesDict[this.samples_[i].category]=true;}}},autoCloseOpenSlices(){this.sliceGroup.autoCloseOpenSlices();this.asyncSliceGroup.autoCloseOpenSlices();this.kernelSliceGroup.autoCloseOpenSlices();},mergeKernelWithUserland(){if(this.kernelSliceGroup.length>0){const newSlices=SliceGroup.merge(this.sliceGroup,this.kernelSliceGroup);this.sliceGroup.slices=newSlices.slices;this.kernelSliceGroup=new SliceGroup(this);this.updateBounds();}},createSubSlices(){this.sliceGroup.createSubSlices();this.samples_=this.parent.model.samples.filter(sample=>sample.thread===this);},get userFriendlyName(){return this.name||this.tid;},get userFriendlyDetails(){return'tid: '+this.tid+
(this.name?', name: '+this.name:'');},getSettingsKey(){if(!this.name)return undefined;const parentKey=this.parent.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name;},getProcess(){return this.parent;},indexOfTimeSlice(timeSlice){const i=tr.b.findLowIndexInSortedArray(this.timeSlices,function(slice){return slice.start;},timeSlice.start);if(this.timeSlices[i]!==timeSlice)return undefined;return i;},sumOverToplevelSlicesInRange(range,func){let sum=0;tr.b.iterateOverIntersectingIntervals(this.sliceGroup.topLevelSlices,slice=>slice.start,slice=>slice.end,range.min,range.max,slice=>{let fractionOfSliceInsideRangeOfInterest=1;if(slice.duration>0){const intersection=range.findIntersection(slice.range);fractionOfSliceInsideRangeOfInterest=intersection.duration/slice.duration;}
-sum+=func(slice)*fractionOfSliceInsideRangeOfInterest;});return sum;},getCpuTimeForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>slice.cpuDuration||0);},getNumToplevelSlicesForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>1);},getSchedulingStatsForRange(start,end){const stats={};if(!this.timeSlices)return stats;function addStatsForSlice(threadTimeSlice){const overlapStart=Math.max(threadTimeSlice.start,start);const overlapEnd=Math.min(threadTimeSlice.end,end);const schedulingState=threadTimeSlice.schedulingState;if(!(schedulingState in stats))stats[schedulingState]=0;stats[schedulingState]+=overlapEnd-overlapStart;}
+sum+=func(slice)*fractionOfSliceInsideRangeOfInterest;});return sum;},getCpuTimeForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>slice.cpuDuration||0);},getNumToplevelSlicesForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>1);},getWallTimeForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>slice.duration||0);},getSchedulingStatsForRange(start,end){const stats={};if(!this.timeSlices)return stats;function addStatsForSlice(threadTimeSlice){const overlapStart=Math.max(threadTimeSlice.start,start);const overlapEnd=Math.min(threadTimeSlice.end,end);const schedulingState=threadTimeSlice.schedulingState;if(!(schedulingState in stats))stats[schedulingState]=0;stats[schedulingState]+=overlapEnd-overlapStart;}
tr.b.iterateOverIntersectingIntervals(this.timeSlices,function(x){return x.start;},function(x){return x.end;},start,end,addStatsForSlice);return stats;},get samples(){return this.samples_;},get type(){const re=/^[^0-9|\/]+/;const matches=re.exec(this.name);if(matches&&matches[0])return matches[0];throw new Error('Could not determine thread type for thread name '+
this.name);}};Thread.compare=function(x,y){let tmp=x.parent.compareTo(y.parent);if(tmp)return tmp;tmp=x.sortIndex-y.sortIndex;if(tmp)return tmp;if(x.name!==undefined){if(y.name!==undefined){tmp=x.name.localeCompare(y.name);}else{tmp=-1;}}else if(y.name!==undefined){tmp=1;}
if(tmp)return tmp;return x.tid-y.tid;};return{Thread,};});'use strict';tr.exportTo('tr.model',function(){const Thread=tr.model.Thread;const Counter=tr.model.Counter;function ProcessBase(model){if(!model){throw new Error('Must provide a model');}
@@ -5861,9 +5671,9 @@ event.name+' in endSlice'});}
slice.endStackFrame=this.getStackFrameForEvent_(event);this.mergeArgsInto_(slice.args,event.args,slice.title);}},mergeArgsInto_(dstArgs,srcArgs,eventName){for(const arg in srcArgs){if(dstArgs[arg]!==undefined){this.model_.importWarning({type:'arg_merge_error',message:'Different phases of '+eventName+' provided values for argument '+arg+'.'+' The last provided value will be used.'});}
dstArgs[arg]=this.deepCopyIfNeeded_(srcArgs[arg]);}},processCompleteEvent(event){if(event.cat!==undefined&&event.cat.indexOf('trace_event_overhead')>-1){return undefined;}
const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(event.flow_out){if(event.flow_in){event.flowPhase=STEP;}else{event.flowPhase=PRODUCER;}}else if(event.flow_in){event.flowPhase=CONSUMER;}
-const slice=thread.sliceGroup.pushCompleteSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.maybeToModelTimeFromUs_(event.dur),this.maybeToModelTimeFromUs_(event.tts),this.maybeToModelTimeFromUs_(event.tdur),this.deepCopyIfNeeded_(event.args),event.argsStripped,getEventColor(event),event.bind_id);slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=this.getStackFrameForEvent_(event,true);this.setContextsFromThread_(thread,slice);return slice;},processJitCodeEvent(event){if(this.v8ProcessCodeMaps_[event.pid]===undefined){this.v8ProcessCodeMaps_[event.pid]=new tr.e.importer.TraceCodeMap();}
+const slice=thread.sliceGroup.pushCompleteSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.durationFromUs_(event.dur),this.maybeToModelTimeFromUs_(event.tts),this.durationFromUs_(event.tdur),this.deepCopyIfNeeded_(event.args),event.argsStripped,getEventColor(event),event.bind_id);slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=this.getStackFrameForEvent_(event,true);this.setContextsFromThread_(thread,slice);return slice;},processJitCodeEvent(event){if(this.v8ProcessCodeMaps_[event.pid]===undefined){this.v8ProcessCodeMaps_[event.pid]=new tr.e.importer.TraceCodeMap();}
const map=this.v8ProcessCodeMaps_[event.pid];const data=event.args.data;if(event.name==='JitCodeMoved'){map.moveEntry(data.code_start,data.new_code_start,data.code_len);}else{map.addEntry(data.code_start,data.code_len,data.name,data.script_id);}},processMetadataEvent(event){if(event.name==='JitCodeAdded'||event.name==='JitCodeMoved'){this.v8SamplingData_.push(event);return;}
-if(event.argsStripped)return;if(event.name==='process_name'){const process=this.model_.getOrCreateProcess(event.pid);process.name=event.args.name;}else if(event.name==='process_labels'){const process=this.model_.getOrCreateProcess(event.pid);const labels=event.args.labels.split(',');for(let i=0;i<labels.length;i++){process.addLabelIfNeeded(labels[i]);}}else if(event.name==='process_uptime_seconds'){const process=this.model_.getOrCreateProcess(event.pid);process.uptime_seconds=event.args.uptime;}else if(event.name==='process_sort_index'){const process=this.model_.getOrCreateProcess(event.pid);process.sortIndex=event.args.sort_index;}else if(event.name==='thread_name'){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.name=event.args.name;}else if(event.name==='thread_sort_index'){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.sortIndex=event.args.sort_index;}else if(event.name==='num_cpus'){let n=event.args.number;if(this.softwareMeasuredCpuCount_!==undefined){n=Math.max(n,this.softwareMeasuredCpuCount_);}
+if(event.argsStripped)return;if(event.name==='process_name'){const process=this.model_.getOrCreateProcess(event.pid);process.name=event.args.name;}else if(event.name==='process_labels'){const process=this.model_.getOrCreateProcess(event.pid);const stackFrames=event.args.stackFrames;if(event.args.labels===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No labels found in a \''+event.name+'\' metadata event'});}else{const labels=event.args.labels.split(',');for(let i=0;i<labels.length;i++){process.addLabelIfNeeded(labels[i]);}}}else if(event.name==='process_uptime_seconds'){const process=this.model_.getOrCreateProcess(event.pid);process.uptime_seconds=event.args.uptime;}else if(event.name==='process_sort_index'){const process=this.model_.getOrCreateProcess(event.pid);process.sortIndex=event.args.sort_index;}else if(event.name==='thread_name'){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.name=event.args.name;}else if(event.name==='thread_sort_index'){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.sortIndex=event.args.sort_index;}else if(event.name==='num_cpus'){let n=event.args.number;if(this.softwareMeasuredCpuCount_!==undefined){n=Math.max(n,this.softwareMeasuredCpuCount_);}
this.softwareMeasuredCpuCount_=n;}else if(event.name==='stackFrames'){const stackFrames=event.args.stackFrames;if(stackFrames===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No stack frames found in a \''+event.name+'\' metadata event'});}else{this.importStackFrames_(stackFrames,'p'+event.pid+':');}}else if(event.name==='typeNames'){const objectTypeNameMap=event.args.typeNames;if(objectTypeNameMap===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No mapping from object type IDs to names found in a \''+
event.name+'\' metadata event'});}else{this.importObjectTypeNameMap_(objectTypeNameMap,event.pid);}}else if(event.name==='TraceConfig'){this.model_.metadata.push({name:'TraceConfig',value:event.args.value});}else{this.model_.importWarning({type:'metadata_parse_error',message:'Unrecognized metadata name: '+event.name});}},processInstantEvent(event){if(event.name==='JitCodeAdded'||event.name==='JitCodeMoved'){this.v8SamplingData_.push(event);return;}
if(event.s==='t'||event.s===undefined){this.processDurationEvent(event);return;}
@@ -6093,7 +5903,8 @@ const importance=rawEdge.importance;const edge=new tr.model.MemoryAllocatorDumpL
sourceDump.owns.target.fullName+').'});}else{sourceDump.owns=edge;targetDump.ownedBy.push(edge);}
break;case'retention':sourceDump.retains.push(edge);targetDump.retainedBy.push(edge);break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'Invalid edge type: '+rawEdge.type+' (PID='+pid+', dump ID='+dumpId+', source='+sourceGuid+', target='+targetGuid+', importance='+importance+').'});}}}}},toModelTimeFromUs_(ts){if(!this.toModelTime_){this.toModelTime_=this.model_.clockSyncManager.getModelTimeTransformer(this.clockDomainId_);}
return this.toModelTime_(tr.b.Unit.timestampFromUs(ts));},maybeToModelTimeFromUs_(ts){if(ts===undefined){return undefined;}
-return this.toModelTimeFromUs_(ts);}};tr.importer.Importer.register(TraceEventImporter);return{TraceEventImporter,};});'use strict';tr.exportTo('tr.e.net',function(){const AsyncSlice=tr.model.AsyncSlice;function NetAsyncSlice(){AsyncSlice.apply(this,arguments);this.url_=undefined;this.byteCount_=undefined;this.isTitleComputed_=false;this.isUrlComputed_=false;}
+return this.toModelTimeFromUs_(ts);},durationFromUs_(dur){if(dur===undefined){return undefined;}
+return tr.b.Unit.timestampFromUs(dur);}};tr.importer.Importer.register(TraceEventImporter);return{TraceEventImporter,};});'use strict';tr.exportTo('tr.e.net',function(){const AsyncSlice=tr.model.AsyncSlice;function NetAsyncSlice(){AsyncSlice.apply(this,arguments);this.url_=undefined;this.byteCount_=undefined;this.isTitleComputed_=false;this.isUrlComputed_=false;}
NetAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){return'NetLog';},get title(){if(this.isTitleComputed_||!this.isTopLevel){return this.title_;}
if(this.url!==undefined&&this.url.length>0){this.title_=this.url;}else if(this.args!==undefined&&this.args.source_type!==undefined){this.title_=this.args.source_type;}
this.isTitleComputed_=true;return this.title_;},set title(title){this.title_=title;},get url(){if(this.isUrlComputed_){return this.url_;}
@@ -6253,9 +6064,9 @@ this.importer.registerEventHandler(hwcEventName,handler.bind(this));},l2Sample(c
this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addL2Cycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.l2Sample(hwcTitle,'cycles',ts,eventBase);}
this.importer.registerEventHandler(hwcEventName,handler.bind(this));}};Parser.register(MaliParser);return{MaliParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const Parser=tr.e.importer.linux_perf.Parser;function MemReclaimParser(importer){Parser.call(this,importer);importer.registerEventHandler('mm_vmscan_kswapd_wake',MemReclaimParser.prototype.kswapdWake.bind(this));importer.registerEventHandler('mm_vmscan_kswapd_sleep',MemReclaimParser.prototype.kswapdSleep.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_begin',MemReclaimParser.prototype.reclaimBegin.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_end',MemReclaimParser.prototype.reclaimEnd.bind(this));importer.registerEventHandler('lowmemory_kill',MemReclaimParser.prototype.lowmemoryKill.bind(this));}
const kswapdWakeRE=/nid=(\d+) order=(\d+)/;const kswapdSleepRE=/nid=(\d+)/;const reclaimBeginRE=/order=(\d+) may_writepage=\d+ gfp_flags=(.+)/;const reclaimEndRE=/nr_reclaimed=(\d+)/;const lowmemoryRE=/([^ ]+) \((\d+)\), page cache (\d+)kB \(limit (\d+)kB\), free (-?\d+)Kb/;MemReclaimParser.prototype={__proto__:Parser.prototype,kswapdWake(eventName,cpuNumber,pid,ts,eventBase){const event=kswapdWakeRE.exec(eventBase.details);if(!event)return false;const tgid=parseInt(eventBase.tgid);const nid=parseInt(event[1]);const order=parseInt(event[2]);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS){if(order>kthread.order){kthread.order=order;}}else{kthread.openSliceTS=ts;kthread.order=order;}
-return true;},kswapdSleep(eventName,cpuNumber,pid,ts,eventBase){const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim',eventBase.threadName,kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order});}
-kthread.openSliceTS=undefined;kthread.order=undefined;return true;},reclaimBegin(eventName,cpuNumber,pid,ts,eventBase){const event=reclaimBeginRE.exec(eventBase.details);if(!event)return false;const order=parseInt(event[1]);const gfp=event[2];const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.openSliceTS=ts;kthread.order=order;kthread.gfp=gfp;return true;},reclaimEnd(eventName,cpuNumber,pid,ts,eventBase){const event=reclaimEndRE.exec(eventBase.details);if(!event)return false;const nrReclaimed=parseInt(event[1]);const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS!==undefined){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim','direct reclaim',kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order,gfp:kthread.gfp,nr_reclaimed:nrReclaimed});}
-kthread.openSliceTS=undefined;kthread.order=undefined;kthread.gfp=undefined;return true;},lowmemoryKill(eventName,cpuNumber,pid,ts,eventBase){const event=lowmemoryRE.exec(eventBase.details);if(!event)return false;const tgid=parseInt(eventBase.tgid);const killedName=event[1];const killedPid=parseInt(event[2]);const cache=parseInt(event[3]);const free=parseInt(event[5]);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.thread.sliceGroup.pushCompleteSlice('lowmemory','low memory kill',ts,0,0,0,{killed_name:killedName,killed_pid:killedPid,cache,free});return true;}};Parser.register(MemReclaimParser);return{MemReclaimParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function PowerParser(importer){Parser.call(this,importer);importer.registerEventHandler('power_start',PowerParser.prototype.powerStartEvent.bind(this));importer.registerEventHandler('power_frequency',PowerParser.prototype.powerFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency',PowerParser.prototype.cpuFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency_limits',PowerParser.prototype.cpuFrequencyLimitsEvent.bind(this));importer.registerEventHandler('cpu_idle',PowerParser.prototype.cpuIdleEvent.bind(this));}
+kthread.waitingFor='kswapSleep';return true;},kswapdSleep(eventName,cpuNumber,pid,ts,eventBase){const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.waitingFor!=='kswapSleep')return false;kthread.waitingFor=undefined;if(kthread.openSliceTS){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim',eventBase.threadName,kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order});}
+kthread.openSliceTS=undefined;kthread.order=undefined;return true;},reclaimBegin(eventName,cpuNumber,pid,ts,eventBase){const event=reclaimBeginRE.exec(eventBase.details);if(!event)return false;const order=parseInt(event[1]);const gfp=event[2];const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.openMemReclaimSliceTS=ts;kthread.order=order;kthread.gfp=gfp;kthread.waitingFor='reclaimEnd';return true;},reclaimEnd(eventName,cpuNumber,pid,ts,eventBase){const event=reclaimEndRE.exec(eventBase.details);if(!event)return false;const nrReclaimed=parseInt(event[1]);const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.waitingFor!=='reclaimEnd')return false;kthread.waitingFor=undefined;if(kthread.openMemReclaimSliceTS!==undefined){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim','direct reclaim',kthread.openMemReclaimSliceTS,ts-kthread.openMemReclaimSliceTS,0,0,{order:kthread.order,gfp:kthread.gfp,nr_reclaimed:nrReclaimed});kthread.openMemReclaimSliceTS=undefined;kthread.order=undefined;kthread.gfp=undefined;return true;}
+return false;},lowmemoryKill(eventName,cpuNumber,pid,ts,eventBase){const event=lowmemoryRE.exec(eventBase.details);if(!event)return false;const tgid=parseInt(eventBase.tgid);const killedName=event[1];const killedPid=parseInt(event[2]);const cache=parseInt(event[3]);const free=parseInt(event[5]);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.thread.sliceGroup.pushCompleteSlice('lowmemory','low memory kill',ts,0,0,0,{killed_name:killedName,killed_pid:killedPid,cache,free});return true;}};Parser.register(MemReclaimParser);return{MemReclaimParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function PowerParser(importer){Parser.call(this,importer);importer.registerEventHandler('power_start',PowerParser.prototype.powerStartEvent.bind(this));importer.registerEventHandler('power_frequency',PowerParser.prototype.powerFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency',PowerParser.prototype.cpuFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency_limits',PowerParser.prototype.cpuFrequencyLimitsEvent.bind(this));importer.registerEventHandler('cpu_idle',PowerParser.prototype.cpuIdleEvent.bind(this));}
PowerParser.prototype={__proto__:Parser.prototype,cpuStateSlice(ts,targetCpuNumber,eventType,cpuState){const targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);if(eventType!=='1'){this.importer.model.importWarning({type:'parse_error',message:'Don\'t understand power_start events of '+'type '+eventType});return;}
const powerCounter=targetCpu.getOrCreateCounter('','C-State');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'state')));}
powerCounter.series.forEach(function(series){series.addCounterSample(ts,cpuState);});},cpuIdleSlice(ts,targetCpuNumber,cpuState){const targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);const powerCounter=targetCpu.getOrCreateCounter('','C-State');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name)));}
@@ -6277,7 +6088,9 @@ const syncTimelineRE=/name=(\S+) value=(\S*)/;const syncWaitRE=/(\S+) name=(\S+)
thread.lastActiveTs=ts;thread.lastActiveValue=event[2];return true;},syncWaitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=syncWaitRE.exec(eventBase.details);if(!event)return false;if(eventBase.tgid===undefined){return false;}
const tgid=parseInt(eventBase.tgid);const thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;const slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
const name='fence_wait("'+event[2]+'")';if(event[1]==='begin'){const slice=slices.beginSlice(null,name,ts,{'Start state':event[3]});}else if(event[1]==='end'){if(slices.openSliceCount>0){slices.endSlice(ts);}}else{return false;}
-return true;},syncPtEvent(eventName,cpuNumber,pid,ts,eventBase){return!!syncPtRE.exec(eventBase.details);}};Parser.register(SyncParser);return{SyncParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function WorkqueueParser(importer){Parser.call(this,importer);importer.registerEventHandler('workqueue_execute_start',WorkqueueParser.prototype.executeStartEvent.bind(this));importer.registerEventHandler('workqueue_execute_end',WorkqueueParser.prototype.executeEndEvent.bind(this));importer.registerEventHandler('workqueue_queue_work',WorkqueueParser.prototype.executeQueueWork.bind(this));importer.registerEventHandler('workqueue_activate_work',WorkqueueParser.prototype.executeActivateWork.bind(this));}
+return true;},syncPtEvent(eventName,cpuNumber,pid,ts,eventBase){return!!syncPtRE.exec(eventBase.details);}};Parser.register(SyncParser);return{SyncParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function ThermalParser(importer){Parser.call(this,importer);importer.registerEventHandler('thermal_temperature',ThermalParser.prototype.traceMarkWriteTemperatureEvent.bind(this));importer.registerEventHandler('cdev_update',ThermalParser.prototype.traceMarkWriteCdevEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+ThermalParser.prototype={__proto__:Parser.prototype,thermalMark(name,subName,value,ts){const ctr=this.model_.kernel.getOrCreateCounter(null,name+' '+subName);if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,value);});},traceMarkWriteTemperatureEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=/thermal_zone=(\S+) id=(\d+) temp_prev=(\d+) temp=(\d+)/.exec(eventBase.details);const name=event[1];const temp=parseInt(event[4]);this.thermalMark(name,'Temperature',temp,ts);return true;},traceMarkWriteCdevEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=/type=(\S+) target=(\d+)/.exec(eventBase.details);const name=event[1];const rate=parseInt(event[2]);this.thermalMark(name,'CoolingDevice',rate,ts);return true;}};Parser.register(ThermalParser);return{ThermalParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function WorkqueueParser(importer){Parser.call(this,importer);importer.registerEventHandler('workqueue_execute_start',WorkqueueParser.prototype.executeStartEvent.bind(this));importer.registerEventHandler('workqueue_execute_end',WorkqueueParser.prototype.executeEndEvent.bind(this));importer.registerEventHandler('workqueue_queue_work',WorkqueueParser.prototype.executeQueueWork.bind(this));importer.registerEventHandler('workqueue_activate_work',WorkqueueParser.prototype.executeActivateWork.bind(this));}
const workqueueExecuteStartRE=/work struct (.+): function (\S+)/;const workqueueExecuteEndRE=/work struct (.+)/;WorkqueueParser.prototype={__proto__:Parser.prototype,executeStartEvent(eventName,cpuNumber,pid,ts,eventBase){const event=workqueueExecuteStartRE.exec(eventBase.details);if(!event)return false;const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);kthread.openSliceTS=ts;kthread.openSlice=event[2];return true;},executeEndEvent(eventName,cpuNumber,pid,ts,eventBase){const event=workqueueExecuteEndRE.exec(eventBase.details);if(!event)return false;const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);if(kthread.openSlice){const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,{},ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
kthread.openSlice=undefined;return true;},executeQueueWork(eventName,cpuNumber,pid,ts,eventBase){return true;},executeActivateWork(eventName,cpuNumber,pid,ts,eventBase){return true;}};Parser.register(WorkqueueParser);return{WorkqueueParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID='linux_clock_monotonic_to_ftrace_global';const IMPORT_PRIORITY=2;function FTraceImporter(model,events){this.importPriority=IMPORT_PRIORITY;this.model_=model;this.events_=events;this.wakeups_=[];this.blockedReasons_=[];this.kernelThreadStates_={};this.buildMapFromLinuxPidsToThreads_();this.lines_=[];this.pseudoThreadCounter=1;this.parsers_=[];this.eventHandlers_={};this.haveClockSyncedMonotonicToGlobal_=false;this.clockDomainId_=tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL;}
const TestExports={};const lineREWithTGID=new RegExp('^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]'+'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]'+'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');const lineParserWithTGID=function(line){const groups=lineREWithTGID.exec(line);if(!groups)return groups;let tgid=groups[3];if(tgid[0]==='-')tgid=undefined;return{threadName:groups[1],pid:groups[2],tgid,cpuNumber:groups[4],timestamp:groups[5],eventName:groups[6],details:groups[7]};};TestExports.lineParserWithTGID=lineParserWithTGID;const lineREWithIRQInfo=new RegExp('^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]'+'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]'+'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');const lineParserWithIRQInfo=function(line){const groups=lineREWithIRQInfo.exec(line);if(!groups)return groups;return{threadName:groups[1],pid:groups[2],cpuNumber:groups[3],timestamp:groups[4],eventName:groups[5],details:groups[6]};};TestExports.lineParserWithIRQInfo=lineParserWithIRQInfo;const lineREWithLegacyFmt=/^\s*(.+)-(\d+)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;const lineParserWithLegacyFmt=function(line){const groups=lineREWithLegacyFmt.exec(line);if(!groups){return groups;}
@@ -6578,20 +6391,22 @@ quietWindowBegin=cluster.end;}
if(fiCandidate===undefined){if(reachedFCIQuiescence_(searchEnd,quietWindowBegin,searchBegin)){fiCandidate=quietWindowBegin;}else{return undefined;}}
return Math.max(fiCandidate,domContentLoadedEnd);}
return{findInteractiveTime,findFirstCpuIdleTime,requiredFCIWindowSizeMs,findFCITaskClusters,};});'use strict';tr.exportTo('tr.model.um',function(){const LOAD_SUBTYPE_NAMES={SUCCESSFUL:'Successful',FAILED:'Failed',};const DOES_LOAD_SUBTYPE_NAME_EXIST={};for(const key in LOAD_SUBTYPE_NAMES){DOES_LOAD_SUBTYPE_NAME_EXIST[LOAD_SUBTYPE_NAMES[key]]=true;}
-function LoadExpectation(parentModel,initiatorTitle,start,duration,renderer,navigationStart,fmpEvent,dclEndEvent,cpuIdleTime,timeToInteractive,url,frameId){if(!DOES_LOAD_SUBTYPE_NAME_EXIST[initiatorTitle]){throw new Error(initiatorTitle+' is not in LOAD_SUBTYPE_NAMES');}
-tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.renderProcess=renderer;this.renderMainThread=undefined;this.routingId=undefined;this.parentRoutingId=undefined;this.loadFinishedEvent=undefined;this.navigationStart=navigationStart;this.fmpEvent=fmpEvent;this.domContentLoadedEndEvent=dclEndEvent;this.firstCpuIdleTime=cpuIdleTime;this.timeToInteractive=timeToInteractive;this.url=url;this.frameId=frameId;}
+function LoadExpectation(parentModel,initiatorTitle,start,duration,renderer,navigationStart,fmpEvent,fcpEvent,dclEndEvent,cpuIdleTime,timeToInteractive,totalBlockingTime,url,frameId){if(!DOES_LOAD_SUBTYPE_NAME_EXIST[initiatorTitle]){throw new Error(initiatorTitle+' is not in LOAD_SUBTYPE_NAMES');}
+tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.renderProcess=renderer;this.renderMainThread=undefined;this.routingId=undefined;this.parentRoutingId=undefined;this.loadFinishedEvent=undefined;this.navigationStart=navigationStart;this.fmpEvent=fmpEvent;this.fcpEvent=fcpEvent;this.domContentLoadedEndEvent=dclEndEvent;this.firstCpuIdleTime=cpuIdleTime;this.timeToInteractive=timeToInteractive;this.totalBlockingTime=totalBlockingTime;this.url=url;this.frameId=frameId;}
LoadExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:LoadExpectation};tr.model.um.UserExpectation.subTypes.register(LoadExpectation,{stageTitle:'Load',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_load')});return{LOAD_SUBTYPE_NAMES,LoadExpectation,};});'use strict';tr.exportTo('tr.importer',function(){const LONG_TASK_THRESHOLD_MS=50;const IGNORE_URLS=['','about:blank',];function findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ts){const objects=rendererHelper.process.objects;const frameLoaderInstances=objects.instancesByTypeName_.FrameLoader;if(frameLoaderInstances===undefined)return undefined;let snapshot;for(const instance of frameLoaderInstances){if(!instance.isAliveAt(ts))continue;const maybeSnapshot=instance.getSnapshotAt(ts);if(frameIdRef!==maybeSnapshot.args.frame.id_ref)continue;snapshot=maybeSnapshot;}
return snapshot;}
function findFirstMeaningfulPaintCandidates(rendererHelper){const candidatesForFrameId={};for(const ev of rendererHelper.process.getDescendantEvents()){if(!tr.e.chrome.EventFinderUtils.hasCategoryAndName(ev,'loading','firstMeaningfulPaintCandidate')){continue;}
if(rendererHelper.isTelemetryInternalEvent(ev))continue;const frameIdRef=ev.args.frame;if(frameIdRef===undefined)continue;let list=candidatesForFrameId[frameIdRef];if(list===undefined){candidatesForFrameId[frameIdRef]=list=[];}
list.push(ev);}
return candidatesForFrameId;}
-function computeInteractivityMetricSample_(rendererHelper,navigationStart,fmpEvent,domContentLoadedEndEvent,searchWindowEnd){if(domContentLoadedEndEvent===undefined||fmpEvent===undefined){return{interactiveTime:undefined,firstCpuIdleTime:undefined};}
-const firstMeaningfulPaintTime=fmpEvent.start;const mainThreadTasks=tr.e.chrome.EventFinderUtils.findToplevelSchedulerTasks(rendererHelper.mainThread);const longTasks=mainThreadTasks.filter(task=>task.duration>=LONG_TASK_THRESHOLD_MS);const longTasksInWindow=longTasks.filter(task=>task.range.intersectsExplicitRangeInclusive(firstMeaningfulPaintTime,searchWindowEnd));const resourceLoadEvents=tr.e.chrome.EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,tr.b.math.Range.fromExplicitRange(navigationStart.start,searchWindowEnd));const firstCpuIdleTime=tr.e.chrome.findFirstCpuIdleTime(firstMeaningfulPaintTime,searchWindowEnd,domContentLoadedEndEvent.start,longTasksInWindow);const interactiveTime=resourceLoadEvents.length>0?tr.e.chrome.findInteractiveTime(firstMeaningfulPaintTime,searchWindowEnd,domContentLoadedEndEvent.start,longTasksInWindow,resourceLoadEvents):undefined;return{interactiveTime,firstCpuIdleTime};}
-function constructLoadingExpectation_(rendererHelper,frameToDomContentLoadedEndEvents,navigationStart,fmpEvent,searchWindowEnd,url,frameId){const dclTimesForFrame=frameToDomContentLoadedEndEvents.get(frameId)||[];const dclSearchRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,searchWindowEnd);const dclTimesInWindow=dclSearchRange.filterArray(dclTimesForFrame,event=>event.start);let domContentLoadedEndEvent=undefined;if(dclTimesInWindow.length!==0){domContentLoadedEndEvent=dclTimesInWindow[dclTimesInWindow.length-1];}
-const{interactiveTime,firstCpuIdleTime}=computeInteractivityMetricSample_(rendererHelper,navigationStart,fmpEvent,domContentLoadedEndEvent,searchWindowEnd);const duration=(interactiveTime===undefined)?searchWindowEnd-navigationStart.start:interactiveTime-navigationStart.start;return new tr.model.um.LoadExpectation(rendererHelper.modelHelper.model,tr.model.um.LOAD_SUBTYPE_NAMES.SUCCESSFUL,navigationStart.start,duration,rendererHelper.process,navigationStart,fmpEvent,domContentLoadedEndEvent,firstCpuIdleTime,interactiveTime,url,frameId);}
-function collectLoadExpectationsForRenderer(rendererHelper){const samples=[];const frameToNavStartEvents=tr.e.chrome.EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const frameToDomContentLoadedEndEvents=tr.e.chrome.EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'domContentLoadedEventEnd','blink.user_timing');function addSamples(frameIdRef,navigationStart,fmpCandidateEvents,searchWindowEnd,url){let fmpMarkerEvent=tr.e.chrome.EventFinderUtils.findLastEventStartingOnOrBeforeTimestamp(fmpCandidateEvents,searchWindowEnd);if(fmpMarkerEvent!==undefined&&navigationStart.start>fmpMarkerEvent.start){fmpMarkerEvent=undefined;}
-samples.push(constructLoadingExpectation_(rendererHelper,frameToDomContentLoadedEndEvents,navigationStart,fmpMarkerEvent,searchWindowEnd,url,frameIdRef));}
+function computeTotalBlockingTime_(fcpTime,interactiveTime,topLevelTasks){let sumBlockingTime=0;for(const event of topLevelTasks){if(event.duration<LONG_TASK_THRESHOLD_MS)continue;if(event.end<fcpTime)continue;if(event.start>interactiveTime)continue;const clippedStart=Math.max(event.start,fcpTime);const clippedEnd=Math.min(event.end,interactiveTime);const clippedDuration=clippedEnd-clippedStart;if(clippedDuration<LONG_TASK_THRESHOLD_MS)continue;sumBlockingTime+=(clippedDuration-LONG_TASK_THRESHOLD_MS);}
+return sumBlockingTime;}
+function computeInteractivityMetricSample_(rendererHelper,navigationStart,fcpEvent,domContentLoadedEndEvent,searchWindowEnd){if(domContentLoadedEndEvent===undefined||fcpEvent===undefined){return{interactiveTime:undefined,firstCpuIdleTime:undefined,totalBlockingTime:undefined};}
+const firstContentfulPaintTime=fcpEvent.start;const mainThreadTasks=tr.e.chrome.EventFinderUtils.findToplevelSchedulerTasks(rendererHelper.mainThread);const longTasks=mainThreadTasks.filter(task=>task.duration>=LONG_TASK_THRESHOLD_MS);const longTasksInWindow=longTasks.filter(task=>task.range.intersectsExplicitRangeInclusive(firstContentfulPaintTime,searchWindowEnd));const resourceLoadEvents=tr.e.chrome.EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,tr.b.math.Range.fromExplicitRange(navigationStart.start,searchWindowEnd));const firstCpuIdleTime=tr.e.chrome.findFirstCpuIdleTime(firstContentfulPaintTime,searchWindowEnd,domContentLoadedEndEvent.start,longTasksInWindow);const interactiveTime=resourceLoadEvents.length>0?tr.e.chrome.findInteractiveTime(firstContentfulPaintTime,searchWindowEnd,domContentLoadedEndEvent.start,longTasksInWindow,resourceLoadEvents):undefined;const totalBlockingTime=interactiveTime?computeTotalBlockingTime_(fcpEvent.start,interactiveTime,longTasks):undefined;return{interactiveTime,firstCpuIdleTime,totalBlockingTime};}
+function constructLoadingExpectation_(rendererHelper,frameToDomContentLoadedEndEvents,frameToFcpEvents,navigationStart,fmpEvent,searchWindowEnd,url,frameId){const searchRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,searchWindowEnd);const dclTimesForFrame=frameToDomContentLoadedEndEvents.get(frameId)||[];const dclTimesInWindow=searchRange.filterArray(dclTimesForFrame,event=>event.start);let domContentLoadedEndEvent=undefined;if(dclTimesInWindow.length!==0){domContentLoadedEndEvent=dclTimesInWindow[dclTimesInWindow.length-1];}
+const fcpForFrame=frameToFcpEvents.get(frameId)||[];const fcpInWindow=searchRange.filterArray(fcpForFrame,event=>event.start);const fcpEvent=fcpInWindow[0];const{interactiveTime,firstCpuIdleTime,totalBlockingTime}=computeInteractivityMetricSample_(rendererHelper,navigationStart,fcpEvent,domContentLoadedEndEvent,searchWindowEnd);const duration=(interactiveTime===undefined)?searchWindowEnd-navigationStart.start:interactiveTime-navigationStart.start;return new tr.model.um.LoadExpectation(rendererHelper.modelHelper.model,tr.model.um.LOAD_SUBTYPE_NAMES.SUCCESSFUL,navigationStart.start,duration,rendererHelper.process,navigationStart,fmpEvent,fcpEvent,domContentLoadedEndEvent,firstCpuIdleTime,interactiveTime,totalBlockingTime,url,frameId);}
+function collectLoadExpectationsForRenderer(rendererHelper){const samples=[];const frameToNavStartEvents=tr.e.chrome.EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const frameToDomContentLoadedEndEvents=tr.e.chrome.EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'domContentLoadedEventEnd','blink.user_timing');const frameToFcpEvents=tr.e.chrome.EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'firstContentfulPaint','loading');function addSamples(frameIdRef,navigationStart,fmpCandidateEvents,searchWindowEnd,url){let fmpMarkerEvent=tr.e.chrome.EventFinderUtils.findLastEventStartingOnOrBeforeTimestamp(fmpCandidateEvents,searchWindowEnd);if(fmpMarkerEvent!==undefined&&navigationStart.start>fmpMarkerEvent.start){fmpMarkerEvent=undefined;}
+samples.push(constructLoadingExpectation_(rendererHelper,frameToDomContentLoadedEndEvents,frameToFcpEvents,navigationStart,fmpMarkerEvent,searchWindowEnd,url,frameIdRef));}
const candidatesForFrameId=findFirstMeaningfulPaintCandidates(rendererHelper);for(const[frameIdRef,navStartEvents]of frameToNavStartEvents){const fmpCandidateEvents=candidatesForFrameId[frameIdRef]||[];let prevNavigation={navigationEvent:undefined,url:undefined};for(let index=0;index<navStartEvents.length;index++){const currNavigation=navStartEvents[index];let url;let isLoadingMainFrame=false;if(currNavigation.args.data){url=currNavigation.args.data.documentLoaderURL;isLoadingMainFrame=currNavigation.args.data.isLoadingMainFrame;}else{const snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,currNavigation.start);if(snapshot){url=snapshot.args.documentLoaderURL;isLoadingMainFrame=snapshot.args.isLoadingMainFrame;}}
if(!isLoadingMainFrame)continue;if(url===undefined||IGNORE_URLS.includes(url))continue;if(prevNavigation.navigationEvent!==undefined){addSamples(frameIdRef,prevNavigation.navigationEvent,fmpCandidateEvents,currNavigation.start,prevNavigation.url);}
prevNavigation={navigationEvent:currNavigation,url};}
@@ -6658,7 +6473,7 @@ function scrollIntoViewIfNeeded(el){const pr=el.parentElement.getBoundingClientR
function extractUrlString(url){let extracted=url.replace(/url\((.*)\)/,'$1');extracted=extracted.replace(/\"(.*)\"/,'$1');return extracted;}
function toThreeDigitLocaleString(value){return value.toLocaleString(undefined,{minimumFractionDigits:3,maximumFractionDigits:3});}
function isUnknownElementName(name){return document.createElement(name)instanceof HTMLUnknownElement;}
-return{isUnknownElementName,toThreeDigitLocaleString,instantiateTemplate,windowRectForElement,scrollIntoViewIfNeeded,extractUrlString,};});'use strict';tr.exportTo('tr.ui.b',function(){if(tr.isHeadless)return{};const THIS_DOC=document.currentScript.ownerDocument;const Overlay=tr.ui.b.define('overlay');Overlay.prototype={__proto__:HTMLDivElement.prototype,decorate(){Polymer.dom(this).classList.add('overlay');this.parentEl_=this.ownerDocument.body;this.visible_=false;this.userCanClose_=true;this.onKeyDown_=this.onKeyDown_.bind(this);this.onClick_=this.onClick_.bind(this);this.onFocusIn_=this.onFocusIn_.bind(this);this.onDocumentClick_=this.onDocumentClick_.bind(this);this.onClose_=this.onClose_.bind(this);this.addEventListener('visible-change',tr.ui.b.Overlay.prototype.onVisibleChange_.bind(this),true);const createShadowRoot=this.createShadowRoot||this.webkitCreateShadowRoot;this.shadow_=createShadowRoot.call(this);Polymer.dom(this.shadow_).appendChild(tr.ui.b.instantiateTemplate('#overlay-template',THIS_DOC));this.closeBtn_=Polymer.dom(this.shadow_).querySelector('close-button');this.closeBtn_.addEventListener('click',this.onClose_);Polymer.dom(this.shadow_).querySelector('overlay-frame').addEventListener('click',this.onClick_);this.observer_=new WebKitMutationObserver(this.didButtonBarMutate_.bind(this));this.observer_.observe(Polymer.dom(this.shadow_).querySelector('button-bar'),{childList:true});Object.defineProperty(this,'title',{get(){return Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent;},set(title){Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent=title;}});},set userCanClose(userCanClose){this.userCanClose_=userCanClose;this.closeBtn_.style.display=userCanClose?'block':'none';},get buttons(){return Polymer.dom(this.shadow_).querySelector('button-bar');},get visible(){return this.visible_;},set visible(newValue){if(this.visible_===newValue)return;this.visible_=newValue;const e=new tr.b.Event('visible-change');this.dispatchEvent(e);},onVisibleChange_(){this.visible_?this.show_():this.hide_();},show_(){Polymer.dom(this.parentEl_).appendChild(this);if(this.userCanClose_){this.addEventListener('keydown',this.onKeyDown_.bind(this));this.addEventListener('click',this.onDocumentClick_.bind(this));this.closeBtn_.addEventListener('click',this.onClose_);}
+return{isUnknownElementName,toThreeDigitLocaleString,instantiateTemplate,windowRectForElement,scrollIntoViewIfNeeded,extractUrlString,};});'use strict';tr.exportTo('tr.ui.b',function(){if(tr.isHeadless)return{};const THIS_DOC=document._currentScript.ownerDocument;const Overlay=tr.ui.b.define('overlay');Overlay.prototype={__proto__:HTMLDivElement.prototype,decorate(){Polymer.dom(this).classList.add('overlay');this.parentEl_=this.ownerDocument.body;this.visible_=false;this.userCanClose_=true;this.onKeyDown_=this.onKeyDown_.bind(this);this.onClick_=this.onClick_.bind(this);this.onFocusIn_=this.onFocusIn_.bind(this);this.onDocumentClick_=this.onDocumentClick_.bind(this);this.onClose_=this.onClose_.bind(this);this.addEventListener('visible-change',tr.ui.b.Overlay.prototype.onVisibleChange_.bind(this),true);const createShadowRoot=this.createShadowRoot||this.webkitCreateShadowRoot;this.shadow_=createShadowRoot.call(this);Polymer.dom(this.shadow_).appendChild(tr.ui.b.instantiateTemplate('#overlay-template',THIS_DOC));this.closeBtn_=Polymer.dom(this.shadow_).querySelector('close-button');this.closeBtn_.addEventListener('click',this.onClose_);Polymer.dom(this.shadow_).querySelector('overlay-frame').addEventListener('click',this.onClick_);this.observer_=new MutationObserver(this.didButtonBarMutate_.bind(this));this.observer_.observe(Polymer.dom(this.shadow_).querySelector('button-bar'),{childList:true});Object.defineProperty(this,'title',{get(){return Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent;},set(title){Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent=title;}});},set userCanClose(userCanClose){this.userCanClose_=userCanClose;this.closeBtn_.style.display=userCanClose?'block':'none';},get buttons(){return Polymer.dom(this.shadow_).querySelector('button-bar');},get visible(){return this.visible_;},set visible(newValue){if(this.visible_===newValue)return;this.visible_=newValue;const e=new tr.b.Event('visible-change');this.dispatchEvent(e);},onVisibleChange_(){this.visible_?this.show_():this.hide_();},show_(){Polymer.dom(this.parentEl_).appendChild(this);if(this.userCanClose_){this.addEventListener('keydown',this.onKeyDown_.bind(this));this.addEventListener('click',this.onDocumentClick_.bind(this));this.closeBtn_.addEventListener('click',this.onClose_);}
this.parentEl_.addEventListener('focusin',this.onFocusIn_);this.tabIndex=0;const elList=Polymer.dom(this).querySelectorAll('button, input, list, select, a');if(elList.length>0){if(elList[0]===this.closeBtn_){if(elList.length>1)return elList[1].focus();}else{return elList[0].focus();}}
this.focus();},hide_(){Polymer.dom(this.parentEl_).removeChild(this);this.parentEl_.removeEventListener('focusin',this.onFocusIn_);if(this.closeBtn_){this.closeBtn_.removeEventListener('click',this.onClose_);}
document.removeEventListener('keydown',this.onKeyDown_);document.removeEventListener('click',this.onDocumentClick_);},onClose_(e){this.visible=false;if((e.type!=='keydown')||(e.type==='keydown'&&e.keyCode===27)){e.stopPropagation();}
@@ -6873,7 +6688,8 @@ if(opt_dictionary.backgroundColor){spanEl.style.backgroundColor=opt_dictionary.b
if(opt_dictionary.color){spanEl.style.color=opt_dictionary.color;}}
return spanEl;}
function createLink(opt_args){let ownerDocument=document;if(opt_args&&opt_args.ownerDocument){ownerDocument=opt_args.ownerDocument;}
-const linkEl=ownerDocument.createElement('a');if(opt_args){if(opt_args.href)linkEl.href=opt_args.href;if(opt_args.tooltip)linkEl.title=opt_args.tooltip;if(opt_args.color)linkEl.style.color=opt_args.color;if(opt_args.bold)linkEl.style.fontWeight='bold';if(opt_args.italic)linkEl.style.fontStyle='italic';if(opt_args.className)linkEl.className=opt_args.className;if(opt_args.parent)Polymer.dom(opt_args.parent).appendChild(linkEl);if(opt_args.marginLeft)linkEl.style.marginLeft=opt_args.marginLeft;if(opt_args.marginRight)linkEl.style.marginRight=opt_args.marginRight;if(opt_args.backgroundColor){linkEl.style.backgroundColor=opt_args.backgroundColor;}
+const linkEl=ownerDocument.createElement('a');if(opt_args){if(opt_args.href){linkEl.href=opt_args.href;linkEl.target='_blank';}
+if(opt_args.tooltip)linkEl.title=opt_args.tooltip;if(opt_args.color)linkEl.style.color=opt_args.color;if(opt_args.bold)linkEl.style.fontWeight='bold';if(opt_args.italic)linkEl.style.fontStyle='italic';if(opt_args.className)linkEl.className=opt_args.className;if(opt_args.parent)Polymer.dom(opt_args.parent).appendChild(linkEl);if(opt_args.marginLeft)linkEl.style.marginLeft=opt_args.marginLeft;if(opt_args.marginRight)linkEl.style.marginRight=opt_args.marginRight;if(opt_args.backgroundColor){linkEl.style.backgroundColor=opt_args.backgroundColor;}
if(opt_args.textContent){Polymer.dom(linkEl).textContent=opt_args.textContent;}}
return linkEl;}
function createDiv(opt_dictionary){const divEl=document.createElement('div');if(opt_dictionary){if(opt_dictionary.className){divEl.className=opt_dictionary.className;}
@@ -6887,7 +6703,7 @@ return a===b;}
function createSelector(targetEl,targetElProperty,settingsKey,defaultValue,items,opt_namespace){let defaultValueIndex;for(let i=0;i<items.length;i++){const item=items[i];if(valuesEqual(item.value,defaultValue)){defaultValueIndex=i;break;}}
if(defaultValueIndex===undefined){throw new Error('defaultValue must be in the items list');}
const selectorEl=document.createElement('select');selectorEl.addEventListener('change',onChange);for(let i=0;i<items.length;i++){const item=items[i];const optionEl=document.createElement('option');Polymer.dom(optionEl).textContent=item.label;optionEl.targetPropertyValue=item.value;optionEl.item=item;Polymer.dom(selectorEl).appendChild(optionEl);}
-function onChange(e){const value=selectorEl.selectedOptions[0].targetPropertyValue;tr.b.Settings.set(settingsKey,value,opt_namespace);targetEl[targetElProperty]=value;}
+function onChange(e){const value=selectorEl.selectedValue;tr.b.Settings.set(settingsKey,value,opt_namespace);targetEl[targetElProperty]=value;}
const oldSetter=targetEl.__lookupSetter__('selectedIndex');selectorEl.__defineGetter__('selectedValue',function(v){return selectorEl.children[selectorEl.selectedIndex].targetPropertyValue;});selectorEl.__defineGetter__('selectedItem',function(v){return selectorEl.children[selectorEl.selectedIndex].item;});selectorEl.__defineSetter__('selectedValue',function(v){for(let i=0;i<selectorEl.children.length;i++){const value=selectorEl.children[i].targetPropertyValue;if(valuesEqual(value,v)){const changed=selectorEl.selectedIndex!==i;if(changed){selectorEl.selectedIndex=i;onChange();}
return;}}
throw new Error('Not a valid value');});const initialValue=tr.b.Settings.get(settingsKey,defaultValue,opt_namespace);let didSet=false;for(let i=0;i<selectorEl.children.length;i++){if(valuesEqual(selectorEl.children[i].targetPropertyValue,initialValue)){didSet=true;targetEl[targetElProperty]=initialValue;selectorEl.selectedIndex=i;break;}}
@@ -6973,14 +6789,14 @@ get scaleX(){return this.target_.currentDisplayTransform_.scaleX;}
set scaleX(scaleX){this.target_.currentDisplayTransform_.scaleX=scaleX;}
cloneAnimationState(){return this.target_.currentDisplayTransform_.clone();}
xPanWorldPosToViewPos(xWorld,xView){this.target_.currentDisplayTransform_.xPanWorldPosToViewPos(xWorld,xView,this.target_.modelTrackContainer_.canvas.clientWidth);}}
-function TimelineViewport(parentEl){this.parentEl_=parentEl;this.modelTrackContainer_=undefined;this.currentDisplayTransform_=new TimelineDisplayTransform();this.initAnimationController_();this.showFlowEvents_=false;this.highlightVSync_=false;this.highDetails_=false;this.gridTimebase_=0;this.gridStep_=1000/60;this.gridEnabled_=false;this.hasCalledSetupFunction_=false;this.onResize_=this.onResize_.bind(this);this.onModelTrackControllerScroll_=this.onModelTrackControllerScroll_.bind(this);this.timeMode_=TimelineViewport.TimeMode.TIME_IN_MS;this.majorMarkWorldPositions_=[];this.majorMarkUnit_=undefined;this.interestRange_=new TimelineInterestRange(this);this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.containerToTrackMap=new tr.ui.tracks.ContainerToTrackMap();this.dispatchChangeEvent=this.dispatchChangeEvent.bind(this);}
+function TimelineViewport(parentEl){this.parentEl_=parentEl;this.modelTrackContainer_=undefined;this.currentDisplayTransform_=new TimelineDisplayTransform();this.initAnimationController_();this.selectedFlowEvents_=new Set();this.highlightVSync_=false;this.highDetails_=false;this.gridTimebase_=0;this.gridStep_=1000/60;this.gridEnabled_=false;this.hasCalledSetupFunction_=false;this.onResize_=this.onResize_.bind(this);this.onModelTrackControllerScroll_=this.onModelTrackControllerScroll_.bind(this);this.timeMode_=TimelineViewport.TimeMode.TIME_IN_MS;this.majorMarkWorldPositions_=[];this.majorMarkUnit_=undefined;this.interestRange_=new TimelineInterestRange(this);this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.containerToTrackMap=new tr.ui.tracks.ContainerToTrackMap();this.dispatchChangeEvent=this.dispatchChangeEvent.bind(this);}
TimelineViewport.TimeMode={TIME_IN_MS:0,REVISIONS:1};TimelineViewport.prototype={__proto__:tr.b.EventTarget.prototype,get isAttachedToDocumentOrInTestMode(){if(this.parentEl_===undefined)return;return tr.ui.b.isElementAttachedToDocument(this.parentEl_);},onResize_(){this.dispatchChangeEvent();},dispatchChangeEvent(){tr.b.dispatchSimpleEvent(this,'change');},detach(){window.removeEventListener('resize',this.dispatchChangeEvent);},initAnimationController_(){this.dtAnimationController_=new tr.ui.b.AnimationController();this.dtAnimationController_.addEventListener('didtick',function(e){this.onCurentDisplayTransformChange_(e.oldTargetState);}.bind(this));this.dtAnimationController_.target=new AnimationControllerProxy(this);},get currentDisplayTransform(){return this.currentDisplayTransform_;},setDisplayTransformImmediately(displayTransform){this.dtAnimationController_.cancelActiveAnimation();const oldDisplayTransform=this.dtAnimationController_.target.cloneAnimationState();this.currentDisplayTransform_.set(displayTransform);this.onCurentDisplayTransformChange_(oldDisplayTransform);},queueDisplayTransformAnimation(animation){if(!(animation instanceof tr.ui.b.Animation)){throw new Error('animation must be instanceof tr.ui.b.Animation');}
this.dtAnimationController_.queueAnimation(animation);},onCurentDisplayTransformChange_(oldDisplayTransform){if(this.modelTrackContainer_){this.currentDisplayTransform.panY=tr.b.math.clamp(this.currentDisplayTransform.panY,0,this.modelTrackContainer_.scrollHeight-
this.modelTrackContainer_.clientHeight);}
const changed=!this.currentDisplayTransform.equals(oldDisplayTransform);const yChanged=this.currentDisplayTransform.panY!==oldDisplayTransform.panY;if(yChanged){this.modelTrackContainer_.scrollTop=this.currentDisplayTransform.panY;}
if(changed){this.dispatchChangeEvent();}},onModelTrackControllerScroll_(e){if(this.dtAnimationController_.activeAnimation&&this.dtAnimationController_.activeAnimation.affectsPanY){this.dtAnimationController_.cancelActiveAnimation();}
const panY=this.modelTrackContainer_.scrollTop;this.currentDisplayTransform_.panY=panY;},get modelTrackContainer(){return this.modelTrackContainer_;},set modelTrackContainer(m){if(this.modelTrackContainer_){this.modelTrackContainer_.removeEventListener('scroll',this.onModelTrackControllerScroll_);}
-this.modelTrackContainer_=m;this.modelTrackContainer_.addEventListener('scroll',this.onModelTrackControllerScroll_);},get showFlowEvents(){return this.showFlowEvents_;},set showFlowEvents(showFlowEvents){this.showFlowEvents_=showFlowEvents;this.dispatchChangeEvent();},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;this.dispatchChangeEvent();},get highDetails(){return this.highDetails_;},set highDetails(highDetails){this.highDetails_=highDetails;this.dispatchChangeEvent();},get gridEnabled(){return this.gridEnabled_;},set gridEnabled(enabled){if(this.gridEnabled_===enabled)return;this.gridEnabled_=enabled&&true;this.dispatchChangeEvent();},get gridTimebase(){return this.gridTimebase_;},set gridTimebase(timebase){if(this.gridTimebase_===timebase)return;this.gridTimebase_=timebase;this.dispatchChangeEvent();},get gridStep(){return this.gridStep_;},get interestRange(){return this.interestRange_;},get majorMarkWorldPositions(){return this.majorMarkWorldPositions_;},get majorMarkUnit(){switch(this.timeMode_){case TimelineViewport.TimeMode.TIME_IN_MS:return tr.b.Unit.byName.timeInMsAutoFormat;case TimelineViewport.TimeMode.REVISIONS:return tr.b.Unit.byName.count;default:throw new Error('Cannot get Unit for unsupported time mode '+this.timeMode_);}},get timeMode(){return this.timeMode_;},set timeMode(mode){this.timeMode_=mode;this.dispatchChangeEvent();},updateMajorMarkData(viewLWorld,viewRWorld){const pixelRatio=window.devicePixelRatio||1;const dt=this.currentDisplayTransform;const idealMajorMarkDistancePix=IDEAL_MAJOR_MARK_DISTANCE_PX*pixelRatio;const idealMajorMarkDistanceWorld=dt.xViewVectorToWorld(idealMajorMarkDistancePix);const majorMarkDistanceWorld=tr.b.math.preferredNumberLargerThanMin(idealMajorMarkDistanceWorld);const firstMajorMark=Math.floor(viewLWorld/majorMarkDistanceWorld)*majorMarkDistanceWorld;this.majorMarkWorldPositions_=[];for(let curX=firstMajorMark;curX<viewRWorld;curX+=majorMarkDistanceWorld){this.majorMarkWorldPositions_.push(Math.floor(MAJOR_MARK_ROUNDING_FACTOR*curX)/MAJOR_MARK_ROUNDING_FACTOR);}},drawMajorMarkLines(ctx,viewHeight){ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();for(const majorMark of this.majorMarkWorldPositions_){const x=this.currentDisplayTransform.xWorldToView(majorMark);tr.ui.b.drawLine(ctx,x,0,x,viewHeight);}
+this.modelTrackContainer_=m;this.modelTrackContainer_.addEventListener('scroll',this.onModelTrackControllerScroll_);},get selectedFlowEvents(){return this.selectedFlowEvents_;},set selectedFlowEvents(selectedFlowEvents){this.selectedFlowEvents_=selectedFlowEvents;this.dispatchChangeEvent();},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;this.dispatchChangeEvent();},get highDetails(){return this.highDetails_;},set highDetails(highDetails){this.highDetails_=highDetails;this.dispatchChangeEvent();},get gridEnabled(){return this.gridEnabled_;},set gridEnabled(enabled){if(this.gridEnabled_===enabled)return;this.gridEnabled_=enabled&&true;this.dispatchChangeEvent();},get gridTimebase(){return this.gridTimebase_;},set gridTimebase(timebase){if(this.gridTimebase_===timebase)return;this.gridTimebase_=timebase;this.dispatchChangeEvent();},get gridStep(){return this.gridStep_;},get interestRange(){return this.interestRange_;},get majorMarkWorldPositions(){return this.majorMarkWorldPositions_;},get majorMarkUnit(){switch(this.timeMode_){case TimelineViewport.TimeMode.TIME_IN_MS:return tr.b.Unit.byName.timeInMsAutoFormat;case TimelineViewport.TimeMode.REVISIONS:return tr.b.Unit.byName.count;default:throw new Error('Cannot get Unit for unsupported time mode '+this.timeMode_);}},get timeMode(){return this.timeMode_;},set timeMode(mode){this.timeMode_=mode;this.dispatchChangeEvent();},updateMajorMarkData(viewLWorld,viewRWorld){const pixelRatio=window.devicePixelRatio||1;const dt=this.currentDisplayTransform;const idealMajorMarkDistancePix=IDEAL_MAJOR_MARK_DISTANCE_PX*pixelRatio;const idealMajorMarkDistanceWorld=dt.xViewVectorToWorld(idealMajorMarkDistancePix);const majorMarkDistanceWorld=tr.b.math.preferredNumberLargerThanMin(idealMajorMarkDistanceWorld);const firstMajorMark=Math.floor(viewLWorld/majorMarkDistanceWorld)*majorMarkDistanceWorld;this.majorMarkWorldPositions_=[];if(firstMajorMark/majorMarkDistanceWorld>1e15)return;for(let curX=firstMajorMark;curX<viewRWorld;curX+=majorMarkDistanceWorld){this.majorMarkWorldPositions_.push(Math.floor(MAJOR_MARK_ROUNDING_FACTOR*curX)/MAJOR_MARK_ROUNDING_FACTOR);}},drawMajorMarkLines(ctx,viewHeight){ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();for(const majorMark of this.majorMarkWorldPositions_){const x=this.currentDisplayTransform.xWorldToView(majorMark);tr.ui.b.drawLine(ctx,x,0,x,viewHeight);}
ctx.strokeStyle='#ddd';ctx.stroke();ctx.restore();},drawGridLines(ctx,viewLWorld,viewRWorld,viewHeight){if(!this.gridEnabled)return;const dt=this.currentDisplayTransform;let x=this.gridTimebase;ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();while(x<viewRWorld){if(x>=viewLWorld){const vx=Math.floor(dt.xWorldToView(x));tr.ui.b.drawLine(ctx,vx,0,vx,viewHeight);}
x+=this.gridStep;}
ctx.strokeStyle='rgba(255, 0, 0, 0.25)';ctx.stroke();ctx.restore();},getShiftedSelection(selection,offset){const newSelection=new tr.model.EventSet();for(const event of selection){if(event instanceof tr.model.FlowEvent){if(offset>0){newSelection.push(event.endSlice);}else if(offset<0){newSelection.push(event.startSlice);}else{}
@@ -7274,7 +7090,7 @@ asDictInto_(d){d.diagnostics=this._diagnostics.map(d=>d.asDictOrReference());}
static deserialize(data,deserializer){return new UnmergeableDiagnosticSet(d.map(i=>deserializer.getDiagnostic(i).diagnostic));}
serialize(serializer){return this._diagnostics.map(d=>serializer.getOrAllocateDiagnosticId('',d));}
static fromDict(d){return new UnmergeableDiagnosticSet(d.diagnostics.map(d=>((typeof d==='string')?new tr.v.d.DiagnosticRef(d):tr.v.d.Diagnostic.fromDict(d))));}}
-tr.v.d.Diagnostic.register(UnmergeableDiagnosticSet,{elementName:'tr-v-ui-unmergeable-diagnostic-set-span'});return{UnmergeableDiagnosticSet,};});'use strict';tr.exportTo('tr.v.d',function(){const RESERVED_INFOS={ANGLE_REVISIONS:{name:'angleRevisions',type:tr.v.d.GenericSet},ARCHITECTURES:{name:'architectures',type:tr.v.d.GenericSet},BENCHMARKS:{name:'benchmarks',type:tr.v.d.GenericSet},BENCHMARK_START:{name:'benchmarkStart',type:tr.v.d.DateRange},BENCHMARK_DESCRIPTIONS:{name:'benchmarkDescriptions',type:tr.v.d.GenericSet},BOTS:{name:'bots',type:tr.v.d.GenericSet},BUG_COMPONENTS:{name:'bugComponents',type:tr.v.d.GenericSet},BUILDS:{name:'builds',type:tr.v.d.GenericSet},CATAPULT_REVISIONS:{name:'catapultRevisions',type:tr.v.d.GenericSet},CHROMIUM_COMMIT_POSITIONS:{name:'chromiumCommitPositions',type:tr.v.d.GenericSet},CHROMIUM_REVISIONS:{name:'chromiumRevisions',type:tr.v.d.GenericSet},DESCRIPTION:{name:'description',type:tr.v.d.GenericSet},DEVICE_IDS:{name:'deviceIds',type:tr.v.d.GenericSet},DOCUMENTATION_URLS:{name:'documentationUrls',type:tr.v.d.GenericSet},FUCHSIA_GARNET_REVISIONS:{name:'fuchsiaGarnetRevisions',type:tr.v.d.GenericSet},FUCHSIA_PERIDOT_REVISIONS:{name:'fuchsiaPeridotRevisions',type:tr.v.d.GenericSet},FUCHSIA_TOPAZ_REVISIONS:{name:'fuchsiaTopazRevisions',type:tr.v.d.GenericSet},FUCHSIA_ZIRCON_REVISIONS:{name:'fuchsiaZirconRevisions',type:tr.v.d.GenericSet},GPUS:{name:'gpus',type:tr.v.d.GenericSet},IS_REFERENCE_BUILD:{name:'isReferenceBuild',type:tr.v.d.GenericSet},LABELS:{name:'labels',type:tr.v.d.GenericSet},LOG_URLS:{name:'logUrls',type:tr.v.d.GenericSet},MASTERS:{name:'masters',type:tr.v.d.GenericSet},MEMORY_AMOUNTS:{name:'memoryAmounts',type:tr.v.d.GenericSet},OS_NAMES:{name:'osNames',type:tr.v.d.GenericSet},OS_VERSIONS:{name:'osVersions',type:tr.v.d.GenericSet},OWNERS:{name:'owners',type:tr.v.d.GenericSet},POINT_ID:{name:'pointId',type:tr.v.d.GenericSet},PRODUCT_VERSIONS:{name:'productVersions',type:tr.v.d.GenericSet},REVISION_TIMESTAMPS:{name:'revisionTimestamps',type:tr.v.d.DateRange},SKIA_REVISIONS:{name:'skiaRevisions',type:tr.v.d.GenericSet},STATISTICS_NAMES:{name:'statisticsNames',type:tr.v.d.GenericSet},STORIES:{name:'stories',type:tr.v.d.GenericSet},STORYSET_REPEATS:{name:'storysetRepeats',type:tr.v.d.GenericSet},STORY_TAGS:{name:'storyTags',type:tr.v.d.GenericSet},SUMMARY_KEYS:{name:'summaryKeys',type:tr.v.d.GenericSet},TEST_PATH:{name:'testPath',type:tr.v.d.GenericSet},TRACE_START:{name:'traceStart',type:tr.v.d.DateRange},TRACE_URLS:{name:'traceUrls',type:tr.v.d.GenericSet},V8_COMMIT_POSITIONS:{name:'v8CommitPositions',type:tr.v.d.DateRange},V8_REVISIONS:{name:'v8Revisions',type:tr.v.d.GenericSet},WEBRTC_REVISIONS:{name:'webrtcRevisions',type:tr.v.d.GenericSet},};const RESERVED_NAMES={};const RESERVED_NAMES_TO_TYPES=new Map();for(const[codename,info]of Object.entries(RESERVED_INFOS)){RESERVED_NAMES[codename]=info.name;if(RESERVED_NAMES_TO_TYPES.has(info.name)){throw new Error(`Duplicate reserved name "${info.name}"`);}
+tr.v.d.Diagnostic.register(UnmergeableDiagnosticSet,{elementName:'tr-v-ui-unmergeable-diagnostic-set-span'});return{UnmergeableDiagnosticSet,};});'use strict';tr.exportTo('tr.v.d',function(){const RESERVED_INFOS={ANGLE_REVISIONS:{name:'angleRevisions',type:tr.v.d.GenericSet},ARCHITECTURES:{name:'architectures',type:tr.v.d.GenericSet},BENCHMARKS:{name:'benchmarks',type:tr.v.d.GenericSet},BENCHMARK_START:{name:'benchmarkStart',type:tr.v.d.DateRange},BENCHMARK_DESCRIPTIONS:{name:'benchmarkDescriptions',type:tr.v.d.GenericSet},BOTS:{name:'bots',type:tr.v.d.GenericSet},BUG_COMPONENTS:{name:'bugComponents',type:tr.v.d.GenericSet},BUILDS:{name:'builds',type:tr.v.d.GenericSet},CATAPULT_REVISIONS:{name:'catapultRevisions',type:tr.v.d.GenericSet},CHROMIUM_COMMIT_POSITIONS:{name:'chromiumCommitPositions',type:tr.v.d.GenericSet},CHROMIUM_REVISIONS:{name:'chromiumRevisions',type:tr.v.d.GenericSet},DESCRIPTION:{name:'description',type:tr.v.d.GenericSet},DEVICE_IDS:{name:'deviceIds',type:tr.v.d.GenericSet},DOCUMENTATION_URLS:{name:'documentationUrls',type:tr.v.d.GenericSet},INFO_BLURB:{name:'infoBlurb',type:tr.v.d.GenericSet},FUCHSIA_GARNET_REVISIONS:{name:'fuchsiaGarnetRevisions',type:tr.v.d.GenericSet},FUCHSIA_PERIDOT_REVISIONS:{name:'fuchsiaPeridotRevisions',type:tr.v.d.GenericSet},FUCHSIA_TOPAZ_REVISIONS:{name:'fuchsiaTopazRevisions',type:tr.v.d.GenericSet},FUCHSIA_ZIRCON_REVISIONS:{name:'fuchsiaZirconRevisions',type:tr.v.d.GenericSet},GPUS:{name:'gpus',type:tr.v.d.GenericSet},IS_REFERENCE_BUILD:{name:'isReferenceBuild',type:tr.v.d.GenericSet},LABELS:{name:'labels',type:tr.v.d.GenericSet},LOG_URLS:{name:'logUrls',type:tr.v.d.GenericSet},MASTERS:{name:'masters',type:tr.v.d.GenericSet},MEMORY_AMOUNTS:{name:'memoryAmounts',type:tr.v.d.GenericSet},OS_NAMES:{name:'osNames',type:tr.v.d.GenericSet},OS_VERSIONS:{name:'osVersions',type:tr.v.d.GenericSet},OWNERS:{name:'owners',type:tr.v.d.GenericSet},POINT_ID:{name:'pointId',type:tr.v.d.GenericSet},PRODUCT_VERSIONS:{name:'productVersions',type:tr.v.d.GenericSet},REVISION_TIMESTAMPS:{name:'revisionTimestamps',type:tr.v.d.DateRange},SKIA_REVISIONS:{name:'skiaRevisions',type:tr.v.d.GenericSet},STATISTICS_NAMES:{name:'statisticsNames',type:tr.v.d.GenericSet},STORIES:{name:'stories',type:tr.v.d.GenericSet},STORYSET_REPEATS:{name:'storysetRepeats',type:tr.v.d.GenericSet},STORY_TAGS:{name:'storyTags',type:tr.v.d.GenericSet},SUMMARY_KEYS:{name:'summaryKeys',type:tr.v.d.GenericSet},TEST_PATH:{name:'testPath',type:tr.v.d.GenericSet},TRACE_START:{name:'traceStart',type:tr.v.d.DateRange},TRACE_URLS:{name:'traceUrls',type:tr.v.d.GenericSet},V8_COMMIT_POSITIONS:{name:'v8CommitPositions',type:tr.v.d.DateRange},V8_REVISIONS:{name:'v8Revisions',type:tr.v.d.GenericSet},WEBRTC_REVISIONS:{name:'webrtcRevisions',type:tr.v.d.GenericSet},WEBRTC_INTERNAL_REVISIONS:{name:'webrtcInternalRevisions',type:tr.v.d.GenericSet},};const RESERVED_NAMES={};const RESERVED_NAMES_TO_TYPES=new Map();for(const[codename,info]of Object.entries(RESERVED_INFOS)){RESERVED_NAMES[codename]=info.name;if(RESERVED_NAMES_TO_TYPES.has(info.name)){throw new Error(`Duplicate reserved name "${info.name}"`);}
RESERVED_NAMES_TO_TYPES.set(info.name,info.type);}
const RESERVED_NAMES_SET=new Set(Object.values(RESERVED_NAMES));return{RESERVED_INFOS,RESERVED_NAMES,RESERVED_NAMES_SET,RESERVED_NAMES_TO_TYPES,};});'use strict';tr.exportTo('tr.v.d',function(){class DiagnosticMap extends Map{constructor(opt_allowReservedNames){super();if(opt_allowReservedNames===undefined){opt_allowReservedNames=true;}
this.allowReservedNames_=opt_allowReservedNames;}
@@ -7517,7 +7333,7 @@ return undefined;}
function findParentOrHost(node){if(node.parentElement){return node.parentElement;}
while(Polymer.dom(node).parentNode){node=Polymer.dom(node).parentNode;}
return node.host;}
-return{getScalarContextControllerForElement,};});'use strict';tr.exportTo('tr.v.ui',function(){function createScalarSpan(value,opt_config){if(value===undefined)return'';const config=opt_config||{};const ownerDocument=config.ownerDocument||document;const span=ownerDocument.createElement('tr-v-ui-scalar-span');let numericValue;if(value instanceof tr.b.Scalar){span.value=value;numericValue=value.value;}else if(value instanceof tr.v.Histogram){numericValue=value.average;if(numericValue===undefined)return'';span.setValueAndUnit(numericValue,value.unit);}else{const unit=config.unit;if(unit===undefined){throw new Error('Unit must be provided in config when value is a number');}
+return{getScalarContextControllerForElement,};});'use strict';tr.exportTo('tr.v.ui',function(){function createScalarSpan(value,opt_config){if(value===undefined)return'';const config=opt_config||{};const ownerDocument=config.ownerDocument||document;const span=unwrap(ownerDocument).createElement('tr-v-ui-scalar-span');let numericValue;if(value instanceof tr.b.Scalar){span.value=value;numericValue=value.value;}else if(value instanceof tr.v.Histogram){numericValue=value.average;if(numericValue===undefined)return'';span.setValueAndUnit(numericValue,value.unit);}else{const unit=config.unit;if(unit===undefined){throw new Error('Unit must be provided in config when value is a number');}
span.setValueAndUnit(value,unit);numericValue=value;}
if(config.context){span.context=config.context;}
if(config.customContextRange){span.customContextRange=config.customContextRange;}
@@ -7567,7 +7383,7 @@ table.tableColumns=columns;table.tableRows=object;this.appendElementWithLabel_(l
this.appendElementsForType_(label+'[',object[0],indent,depth+1,maxDepth,object.length>1?',':']'+suffix);for(let i=1;i<object.length;i++){this.appendElementsForType_('',object[i],indent+label.length+1,depth+1,maxDepth,i<object.length-1?',':']'+suffix);}
return;},appendElementsForObject_(label,object,indent,depth,maxDepth,suffix){const keys=Object.keys(object);if(keys.length===0){this.appendSimpleText_(label,indent,'{}',suffix);return;}
this.appendElementsForType_(label+'{'+keys[0]+': ',object[keys[0]],indent,depth,maxDepth,keys.length>1?',':'}'+suffix);for(let i=1;i<keys.length;i++){this.appendElementsForType_(keys[i]+': ',object[keys[i]],indent+label.length+1,depth+1,maxDepth,i<keys.length-1?',':'}'+suffix);}},appendElementWithLabel_(label,indent,dataElement,suffix){const row=document.createElement('div');const indentSpan=document.createElement('span');indentSpan.style.whiteSpace='pre';for(let i=0;i<indent;i++){Polymer.dom(indentSpan).textContent+=' ';}
-Polymer.dom(row).appendChild(indentSpan);const labelSpan=document.createElement('span');Polymer.dom(labelSpan).textContent=label;Polymer.dom(row).appendChild(labelSpan);Polymer.dom(row).appendChild(dataElement);const suffixSpan=document.createElement('span');Polymer.dom(suffixSpan).textContent=suffix;Polymer.dom(row).appendChild(suffixSpan);row.dataElement=dataElement;Polymer.dom(this.$.content).appendChild(row);},appendSimpleText_(label,indent,text,suffix){const el=this.ownerDocument.createElement('span');Polymer.dom(el).textContent=text;this.appendElementWithLabel_(label,indent,el,suffix);return el;}});'use strict';Polymer({is:'tr-ui-a-generic-object-view-with-label',ready(){this.labelEl_=document.createElement('div');this.genericObjectView_=document.createElement('tr-ui-a-generic-object-view');Polymer.dom(this.root).appendChild(this.labelEl_);Polymer.dom(this.root).appendChild(this.genericObjectView_);},get label(){return Polymer.dom(this.labelEl_).textContent;},set label(label){Polymer.dom(this.labelEl_).textContent=label;},get object(){return this.genericObjectView_.object;},set object(object){this.genericObjectView_.object=object;}});'use strict';tr.exportTo('tr.ui.analysis',function(){const ObjectSnapshotView=tr.ui.b.define('object-snapshot-view');ObjectSnapshotView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.objectSnapshot_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectSnapshot=obj;},get modelEvent(){return this.objectSnapshot;},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(i){this.objectSnapshot_=i;this.updateContents();},updateContents(){throw new Error('Not implemented');}};const options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectSnapshotView;options.defaultMetadata={showInstances:true,showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectSnapshotView,options);return{ObjectSnapshotView,};});'use strict';Polymer({is:'tr-ui-b-drag-handle',created(){this.lastMousePos_=0;this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.target_=undefined;this.horizontal=true;this.observer_=new WebKitMutationObserver(this.didTargetMutate_.bind(this));this.targetSizesByModeKey_={};this.currentDraggingSize_=undefined;},get modeKey_(){return this.target_.className===''?'.':this.target_.className;},get target(){return this.target_;},set target(target){this.observer_.disconnect();this.target_=target;if(!this.target_)return;this.observer_.observe(this.target_,{attributes:true,attributeFilter:['class']});},get horizontal(){return this.horizontal_;},set horizontal(h){this.horizontal_=h;if(this.horizontal_){this.className='horizontal-drag-handle';}else{this.className='vertical-drag-handle';}},get vertical(){return!this.horizontal_;},set vertical(v){this.horizontal=!v;},forceMutationObserverFlush_(){const records=this.observer_.takeRecords();if(records.length){this.didTargetMutate_(records);}},didTargetMutate_(e){const modeSize=this.targetSizesByModeKey_[this.modeKey_];if(modeSize!==undefined){this.setTargetSize_(modeSize);return;}
+Polymer.dom(row).appendChild(indentSpan);const labelSpan=document.createElement('span');Polymer.dom(labelSpan).textContent=label;Polymer.dom(row).appendChild(labelSpan);Polymer.dom(row).appendChild(dataElement);const suffixSpan=document.createElement('span');Polymer.dom(suffixSpan).textContent=suffix;Polymer.dom(row).appendChild(suffixSpan);row.dataElement=dataElement;Polymer.dom(this.$.content).appendChild(row);},appendSimpleText_(label,indent,text,suffix){const el=this.ownerDocument.createElement('span');Polymer.dom(el).textContent=text;this.appendElementWithLabel_(label,indent,el,suffix);return el;}});'use strict';Polymer({is:'tr-ui-a-generic-object-view-with-label',ready(){this.labelEl_=document.createElement('div');this.genericObjectView_=document.createElement('tr-ui-a-generic-object-view');Polymer.dom(this.root).appendChild(this.labelEl_);Polymer.dom(this.root).appendChild(this.genericObjectView_);},get label(){return Polymer.dom(this.labelEl_).textContent;},set label(label){Polymer.dom(this.labelEl_).textContent=label;},get object(){return this.genericObjectView_.object;},set object(object){this.genericObjectView_.object=object;}});'use strict';tr.exportTo('tr.ui.analysis',function(){const ObjectSnapshotView=tr.ui.b.define('object-snapshot-view');ObjectSnapshotView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.objectSnapshot_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectSnapshot=obj;},get modelEvent(){return this.objectSnapshot;},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(i){this.objectSnapshot_=i;this.updateContents();},updateContents(){throw new Error('Not implemented');}};const options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectSnapshotView;options.defaultMetadata={showInstances:true,showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectSnapshotView,options);return{ObjectSnapshotView,};});'use strict';Polymer({is:'tr-ui-b-drag-handle',created(){this.lastMousePos_=0;this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.target_=undefined;this.horizontal=true;this.observer_=new MutationObserver(this.didTargetMutate_.bind(this));this.targetSizesByModeKey_={};this.currentDraggingSize_=undefined;},get modeKey_(){return this.target_.className===''?'.':this.target_.className;},get target(){return this.target_;},set target(target){this.observer_.disconnect();this.target_=target;if(!this.target_)return;this.observer_.observe(this.target_,{attributes:true,attributeFilter:['class']});},get horizontal(){return this.horizontal_;},set horizontal(h){this.horizontal_=h;if(this.horizontal_){this.className='horizontal-drag-handle';}else{this.className='vertical-drag-handle';}},get vertical(){return!this.horizontal_;},set vertical(v){this.horizontal=!v;},forceMutationObserverFlush_(){const records=this.observer_.takeRecords();if(records.length){this.didTargetMutate_(records);}},didTargetMutate_(e){const modeSize=this.targetSizesByModeKey_[this.modeKey_];if(modeSize!==undefined){this.setTargetSize_(modeSize);return;}
this.target_.style[this.targetStyleKey_]='';},get targetStyleKey_(){return this.horizontal_?'height':'width';},getTargetSize_(){const size=parseInt(window.getComputedStyle(this.target_)[this.targetStyleKey_]);this.targetSizesByModeKey_[this.modeKey_]=size;return size;},setTargetSize_(s){this.target_.style[this.targetStyleKey_]=s+'px';this.targetSizesByModeKey_[this.modeKey_]=this.getTargetSize_();tr.b.dispatchSimpleEvent(this,'drag-handle-resize',true,false);},applyDelta_(delta){if(this.target_===this.nextElementSibling){this.currentDraggingSize_+=delta;}else{this.currentDraggingSize_-=delta;}
this.setTargetSize_(this.currentDraggingSize_);},onMouseMove_(e){const curMousePos=this.horizontal_?e.clientY:e.clientX;const delta=this.lastMousePos_-curMousePos;this.applyDelta_(delta);this.lastMousePos_=curMousePos;e.preventDefault();return true;},onMouseDown_(e){if(!this.target_)return;this.forceMutationObserverFlush_();this.currentDraggingSize_=this.getTargetSize_();this.lastMousePos_=this.horizontal_?e.clientY:e.clientX;document.addEventListener('mousemove',this.onMouseMove_);document.addEventListener('mouseup',this.onMouseUp_);e.preventDefault();return true;},onMouseUp_(e){document.removeEventListener('mousemove',this.onMouseMove_);document.removeEventListener('mouseup',this.onMouseUp_);e.preventDefault();this.currentDraggingSize_=undefined;}});'use strict';tr.exportTo('tr.ui.b',function(){function HotKey(dict){if(dict.eventType===undefined){throw new Error('eventType must be given');}
if(dict.keyCode===undefined&&dict.keyCodes===undefined){throw new Error('keyCode or keyCodes must be given');}
@@ -7594,7 +7410,7 @@ keyMap[keyCode]=hotKey;}
for(let i=0;i<hotKey.keyCodes.length;i++){const keyCode=hotKey.keyCodes[i];delete keyMap[keyCode];}
return hotKey;},get globalMode(){return this.globalMode_;},set globalMode(globalMode){const wasAttached=this.isAttached_;if(wasAttached){this.detached();}
this.globalMode_=!!globalMode;if(wasAttached){this.attached();}},get topmostConroller_(){if(this.slavedToParentController_){return this.slavedToParentController_.topmostConroller_;}
-return this;},childRequestsGeneralFocus(child){const topmost=this.topmostConroller_;if(topmost.curHost_){if(topmost.curHost_.hasAttribute('tabIndex')){topmost.curHost_.focus();}else{if(document.activeElement){document.activeElement.blur();}}}else{if(document.activeElement){document.activeElement.blur();}}},childRequestsBlur(child){child.blur();const topmost=this.topmostConroller_;if(topmost.curHost_){topmost.curHost_.focus();}},findHost_(){if(this.globalMode_)return document.body;if(this.parentElement)return this.parentElement;if(!Polymer.dom(this).parentNode)return this.host;let node=this.parentNode;while(Polymer.dom(node).parentNode)node=Polymer.dom(node).parentNode;return node.host;},appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e){const localKeyMap=this.getKeyMapForEventType_(e.type,useCapture);const localHotKey=localKeyMap[e.keyCode];if(localHotKey){matchedHotKeys.push(localHotKey);}
+return this;},childRequestsGeneralFocus(child){const topmost=this.topmostConroller_;if(topmost.curHost_){if(topmost.curHost_.hasAttribute('tabIndex')){topmost.curHost_.focus();}else{if(document.activeElement){document.activeElement.blur();}}}else{if(document.activeElement){document.activeElement.blur();}}},childRequestsBlur(child){child.blur();const topmost=this.topmostConroller_;if(topmost.curHost_){topmost.curHost_.focus();}},findHost_(){if(this.globalMode_)return wrap(document.body);if(this.parentElement)return this.parentElement;if(!Polymer.dom(this).parentNode)return this.host;let node=this.parentNode;while(Polymer.dom(node).parentNode)node=Polymer.dom(node).parentNode;return node.host;},appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e){const localKeyMap=this.getKeyMapForEventType_(e.type,useCapture);const localHotKey=localKeyMap[e.keyCode];if(localHotKey){matchedHotKeys.push(localHotKey);}
for(let i=0;i<this.childControllers_.length;i++){const controller=this.childControllers_[i];controller.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);}},onKey_(useCapture,e){if(!useCapture&&e.path[0].tagName==='INPUT')return;let sortedControllers;const matchedHotKeys=[];this.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);if(matchedHotKeys.length===0)return false;if(matchedHotKeys.length>1){throw new Error('More than one hotKey is currently unsupported');}
const hotKey=matchedHotKeys[0];let prevented=0;prevented|=hotKey.call(e);return!prevented&&e.defaultPrevented;}});'use strict';tr.exportTo('tr.b',function(){function getHotkeyControllerForElement(refElement){let curElement=refElement;while(curElement){if(curElement.tagName==='tv-ui-b-hotkey-controller'){return curElement;}
if(curElement.__hotkeyController){return curElement.__hotkeyController;}
@@ -7603,7 +7419,7 @@ curElement=findHost(curElement);}
return undefined;}
function findHost(initialNode){let node=initialNode;while(Polymer.dom(node).parentNode){node=Polymer.dom(node).parentNode;}
return node.host;}
-return{getHotkeyControllerForElement,};});'use strict';Polymer({is:'tr-ui-b-info-bar',ready(){this.messageEl_=this.$.message;this.buttonsEl_=this.$.buttons;this.message='';},get message(){return Polymer.dom(this.messageEl_).textContent;},set message(message){Polymer.dom(this.messageEl_).textContent=message;},get visible(){return!this.hidden;},set visible(visible){this.hidden=!visible;},removeAllButtons(){Polymer.dom(this.buttonsEl_).textContent='';},addButton(text,clickCallback){const button=document.createElement('button');Polymer.dom(button).textContent=text;button.addEventListener('click',event=>clickCallback(event,this));Polymer.dom(this.buttonsEl_).appendChild(button);return button;}});'use strict';tr.exportTo('tr.ui.b',function(){const ContainerThatDecoratesItsChildren=tr.ui.b.define('div');ContainerThatDecoratesItsChildren.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.observer_=new WebKitMutationObserver(this.didMutate_.bind(this));this.observer_.observe(this,{childList:true});Object.defineProperty(this,'textContent',{get:undefined,set:this.onSetTextContent_});},appendChild(x){HTMLDivElement.prototype.appendChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},insertBefore(x,y){HTMLDivElement.prototype.insertBefore.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},removeChild(x){HTMLDivElement.prototype.removeChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},replaceChild(x,y){HTMLDivElement.prototype.replaceChild.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},onSetTextContent_(textContent){if(textContent!==''){throw new Error('textContent can only be set to \'\'.');}
+return{getHotkeyControllerForElement,};});'use strict';Polymer({is:'tr-ui-b-info-bar',ready(){this.messageEl_=this.$.message;this.buttonsEl_=this.$.buttons;this.message='';},get message(){return Polymer.dom(this.messageEl_).textContent;},set message(message){Polymer.dom(this.messageEl_).textContent=message;},get visible(){return!this.hidden;},set visible(visible){this.hidden=!visible;},removeAllButtons(){Polymer.dom(this.buttonsEl_).textContent='';},addButton(text,clickCallback){const button=document.createElement('button');Polymer.dom(button).textContent=text;button.addEventListener('click',event=>clickCallback(event,this));Polymer.dom(this.buttonsEl_).appendChild(button);return button;}});'use strict';tr.exportTo('tr.ui.b',function(){const ContainerThatDecoratesItsChildren=tr.ui.b.define('div');ContainerThatDecoratesItsChildren.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.observer_=new MutationObserver(this.didMutate_.bind(this));this.observer_.observe(this,{childList:true});Object.defineProperty(this,'textContent',{get:undefined,set:this.onSetTextContent_});},appendChild(x){HTMLDivElement.prototype.appendChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},insertBefore(x,y){HTMLDivElement.prototype.insertBefore.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},removeChild(x){HTMLDivElement.prototype.removeChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},replaceChild(x,y){HTMLDivElement.prototype.replaceChild.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},onSetTextContent_(textContent){if(textContent!==''){throw new Error('textContent can only be set to \'\'.');}
this.clear();},clear(){while(Polymer.dom(this).lastChild){HTMLDivElement.prototype.removeChild.call(this,Polymer.dom(this).lastChild);}
this.didMutate_(this.observer_.takeRecords());},didMutate_(records){this.beginDecorating_();for(let i=0;i<records.length;i++){const addedNodes=records[i].addedNodes;if(addedNodes){for(let j=0;j<addedNodes.length;j++){this.decorateChild_(addedNodes[j]);}}
const removedNodes=records[i].removedNodes;if(removedNodes){for(let j=0;j<removedNodes.length;j++){this.undecorateChild_(removedNodes[j]);}}}
@@ -7806,7 +7622,7 @@ this.layerTreeQuadStackView_.style.height=window.getComputedStyle(this).height;}
tr.b.dispatchSimpleEvent(this,'selection-change');},createPictureBtn_(pictures){if(!(pictures instanceof Array)){pictures=[pictures];}
const link=document.createElement('tr-ui-a-analysis-link');link.selection=function(){const layeredPicture=new tr.e.cc.LayeredPicture(pictures);const snapshot=new tr.e.cc.PictureSnapshot(layeredPicture);snapshot.picture=layeredPicture;const selection=new tr.model.EventSet();selection.push(snapshot);return selection;};Polymer.dom(link).textContent='View in Picture Debugger';return link;},onRequestSelectionChangeFromAnalysisEl_(e){if(!(e.selection instanceof tr.ui.e.chrome.cc.Selection)){return;}
e.stopPropagation();this.selection=e.selection;},get extraHighlightsByLayerId(){return this.layerTreeQuadStackView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerTreeQuadStackView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};return{LayerView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const LayerTreeHostImplSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-tree-host-impl-snapshot-view',tr.ui.analysis.ObjectSnapshotView);LayerTreeHostImplSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-lthi-s-view');this.style.display='flex';this.style.flexDirection='row';this.style.flexGrow=1;this.style.flexShrink=1;this.style.flexBasis='auto';this.style.minWidth=0;this.selection_=undefined;this.layerPicker_=new tr.ui.e.chrome.cc.LayerPicker();this.layerPicker_.style.flexGrow=0;this.layerPicker_.style.flexShrink=0;this.layerPicker_.style.flexBasis='auto';this.layerPicker_.style.minWidth='200px';this.layerPicker_.addEventListener('selection-change',this.onLayerPickerSelectionChanged_.bind(this));this.layerView_=new tr.ui.e.chrome.cc.LayerView();this.layerView_.addEventListener('selection-change',this.onLayerViewSelectionChanged_.bind(this));this.layerView_.style.flexGrow=1;this.layerView_.style.flexShrink=1;this.layerView_.style.flexBasis='auto';this.layerView_.style.minWidth=0;this.dragHandle_=document.createElement('tr-ui-b-drag-handle');this.dragHandle_.style.flexGrow=0;this.dragHandle_.style.flexShrink=0;this.dragHandle_.style.flexBasis='auto';this.dragHandle_.horizontal=false;this.dragHandle_.target=this.layerPicker_;Polymer.dom(this).appendChild(this.layerPicker_);Polymer.dom(this).appendChild(this.dragHandle_);Polymer.dom(this).appendChild(this.layerView_);this.onLayerViewSelectionChanged_();this.onLayerPickerSelectionChanged_();},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(objectSnapshot){this.objectSnapshot_=objectSnapshot;const lthi=this.objectSnapshot;let layerTreeImpl;if(lthi){layerTreeImpl=lthi.getTree(this.layerPicker_.whichTree);}
-this.layerPicker_.lthiSnapshot=lthi;this.layerView_.layerTreeImpl=layerTreeImpl;this.layerView_.regenerateContent();if(!this.selection_)return;this.selection=this.selection_.findEquivalent(lthi);},get selection(){return this.selection_;},set selection(selection){if(this.selection_===selection)return;this.selection_=selection;this.layerPicker_.selection=selection;this.layerView_.selection=selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerPickerSelectionChanged_(){this.selection_=this.layerPicker_.selection;this.layerView_.selection=this.selection;this.layerView_.layerTreeImpl=this.layerPicker_.layerTreeImpl;this.layerView_.isRenderPassQuads=this.layerPicker_.isRenderPassQuads;this.layerView_.regenerateContent();tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerViewSelectionChanged_(){this.selection_=this.layerView_.selection;this.layerPicker_.selection=this.selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},get extraHighlightsByLayerId(){return this.layerView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};tr.ui.analysis.ObjectSnapshotView.register(LayerTreeHostImplSnapshotView,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const OPS_TIMING_ITERATIONS=3;const CHART_PADDING_LEFT=65;const CHART_PADDING_RIGHT=40;const AXIS_PADDING_LEFT=60;const AXIS_PADDING_RIGHT=35;const AXIS_PADDING_TOP=25;const AXIS_PADDING_BOTTOM=45;const AXIS_LABEL_PADDING=5;const AXIS_TICK_SIZE=10;const LABEL_PADDING=5;const LABEL_INTERLEAVE_OFFSET=15;const BAR_PADDING=5;const VERTICAL_TICKS=5;const HUE_CHAR_CODE_ADJUSTMENT=5.7;const PictureOpsChartSummaryView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-summary-view');PictureOpsChartSummaryView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.flexGrow=0;this.style.flexShrink=0;this.style.flexBasis='auto';this.style.fontSize=0;this.style.margin=0;this.style.minHeight='200px';this.style.minWidth='200px';this.style.overflow='hidden';this.style.padding=0;this.picture_=undefined;this.pictureDataProcessed_=false;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.opsTimingData_=[];this.chartWidth_=0;this.chartHeight_=0;this.requiresRedraw_=true;this.currentBarMouseOverTarget_=null;this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));new ResizeObserver(this.onResize_.bind(this)).observe(this);},get requiresRedraw(){return this.requiresRedraw_;},set requiresRedraw(requiresRedraw){this.requiresRedraw_=requiresRedraw;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureDataProcessed_=false;if(Polymer.dom(this).classList.contains('hidden'))return;this.processPictureData_();this.requiresRedraw=true;this.updateChartContents();},hide(){Polymer.dom(this).classList.add('hidden');this.style.display='none';},show(){Polymer.dom(this).classList.remove('hidden');this.style.display='';if(!this.pictureDataProcessed_){this.processPictureData_();}
+this.layerPicker_.lthiSnapshot=lthi;this.layerView_.layerTreeImpl=layerTreeImpl;this.layerView_.regenerateContent();if(!this.selection_)return;this.selection=this.selection_.findEquivalent(lthi);},get selection(){return this.selection_;},set selection(selection){if(this.selection_===selection)return;this.selection_=selection;this.layerPicker_.selection=selection;this.layerView_.selection=selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerPickerSelectionChanged_(){this.selection_=this.layerPicker_.selection;this.layerView_.selection=this.selection;this.layerView_.layerTreeImpl=this.layerPicker_.layerTreeImpl;this.layerView_.isRenderPassQuads=this.layerPicker_.isRenderPassQuads;this.layerView_.regenerateContent();tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerViewSelectionChanged_(){this.selection_=this.layerView_.selection;this.layerPicker_.selection=this.selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},get extraHighlightsByLayerId(){return this.layerView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};tr.ui.analysis.ObjectSnapshotView.register(LayerTreeHostImplSnapshotView,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const OPS_TIMING_ITERATIONS=3;const CHART_PADDING_LEFT=65;const CHART_PADDING_RIGHT=40;const AXIS_PADDING_LEFT=60;const AXIS_PADDING_RIGHT=35;const AXIS_PADDING_TOP=25;const AXIS_PADDING_BOTTOM=45;const AXIS_LABEL_PADDING=5;const AXIS_TICK_SIZE=10;const LABEL_PADDING=5;const LABEL_INTERLEAVE_OFFSET=15;const BAR_PADDING=5;const VERTICAL_TICKS=5;const HUE_CHAR_CODE_ADJUSTMENT=5.7;const PictureOpsChartSummaryView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-summary-view');PictureOpsChartSummaryView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.flexGrow=0;this.style.flexShrink=0;this.style.flexBasis='auto';this.style.fontSize=0;this.style.margin=0;this.style.minHeight='200px';this.style.minWidth='200px';this.style.overflow='hidden';this.style.padding=0;this.picture_=undefined;this.pictureDataProcessed_=false;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.opsTimingData_=[];this.chartWidth_=0;this.chartHeight_=0;this.requiresRedraw_=true;this.currentBarMouseOverTarget_=null;this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));try{new ResizeObserver(this.onResize_.bind(this)).observe(this);}catch(e){}},get requiresRedraw(){return this.requiresRedraw_;},set requiresRedraw(requiresRedraw){this.requiresRedraw_=requiresRedraw;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureDataProcessed_=false;if(Polymer.dom(this).classList.contains('hidden'))return;this.processPictureData_();this.requiresRedraw=true;this.updateChartContents();},hide(){Polymer.dom(this).classList.add('hidden');this.style.display='none';},show(){Polymer.dom(this).classList.remove('hidden');this.style.display='';if(!this.pictureDataProcessed_){this.processPictureData_();}
this.requiresRedraw=true;this.updateChartContents();},onMouseMove_(e){const lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=null;const x=e.offsetX;const y=e.offsetY;const chartLeft=CHART_PADDING_LEFT;const chartRight=this.chartWidth_-CHART_PADDING_RIGHT;const chartTop=AXIS_PADDING_TOP;const chartBottom=this.chartHeight_-AXIS_PADDING_BOTTOM;const chartInnerWidth=chartRight-chartLeft;if(x>chartLeft&&x<chartRight&&y>chartTop&&y<chartBottom){this.currentBarMouseOverTarget_=Math.floor((x-chartLeft)/chartInnerWidth*this.opsTimingData_.length);this.currentBarMouseOverTarget_=tr.b.math.clamp(this.currentBarMouseOverTarget_,0,this.opsTimingData_.length-1);}
if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget)return;this.drawChartContents_();},onResize_(){this.requiresRedraw=true;this.updateChartContents();},updateChartContents(){if(this.requiresRedraw){this.updateChartDimensions_();}
this.drawChartContents_();},updateChartDimensions_(){this.chartWidth_=this.offsetWidth;this.chartHeight_=this.offsetHeight;this.chart_.width=this.chartWidth_*this.chartScale_;this.chart_.height=this.chartHeight_*this.chartScale_;this.chart_.style.width=this.chartWidth_+'px';this.chart_.style.height=this.chartHeight_+'px';this.chartCtx_.scale(this.chartScale_,this.chartScale_);},processPictureData_(){this.resetOpsTimingData_();this.pictureDataProcessed_=true;if(!this.picture_)return;let ops=this.picture_.getOps();if(!ops)return;ops=this.picture_.tagOpsWithTimings(ops);if(ops[0].cmd_time===undefined)return;this.collapseOpsToTimingBuckets_(ops);},drawChartContents_(){this.clearChartContents_();if(this.opsTimingData_.length===0){this.showNoTimingDataMessage_();return;}
@@ -7817,7 +7633,8 @@ CHART_PADDING_RIGHT;const barWidth=Math.floor(totalBarWidth/len);const tickYInte
this.chartCtx_.stroke();this.chartCtx_.restore();this.chartCtx_.save();this.chartCtx_.translate(CHART_PADDING_LEFT+Math.round(barWidth*0.5),AXIS_PADDING_TOP+height+LABEL_PADDING);this.chartCtx_.font='10px Arial';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='top';let labelTickLeft;let labelTickBottom;for(let l=0;l<len;l++){labelTickLeft=Math.round(l*barWidth);labelTickBottom=l%2*LABEL_INTERLEAVE_OFFSET;this.chartCtx_.save();this.chartCtx_.moveTo(labelTickLeft,-LABEL_PADDING);this.chartCtx_.lineTo(labelTickLeft,labelTickBottom);this.chartCtx_.stroke();this.chartCtx_.restore();this.chartCtx_.fillText(this.opsTimingData_[l].cmd_string,labelTickLeft,labelTickBottom);}
this.chartCtx_.restore();this.chartCtx_.restore();},clearChartContents_(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);},collapseOpsToTimingBuckets_(ops){const opsTimingDataIndexHash_={};const timingData=this.opsTimingData_;let op;let opIndex;for(let i=0;i<ops.length;i++){op=ops[i];if(op.cmd_time===undefined)continue;opIndex=opsTimingDataIndexHash_[op.cmd_string]||null;if(opIndex===null){timingData.push({cmd_time:0,cmd_string:op.cmd_string});opIndex=timingData.length-1;opsTimingDataIndexHash_[op.cmd_string]=opIndex;}
timingData[opIndex].cmd_time+=op.cmd_time;}
-timingData.sort(this.sortTimingBucketsByOpTimeDescending_);this.collapseTimingBucketsToOther_(4);},collapseTimingBucketsToOther_(count){const timingData=this.opsTimingData_;const otherSource=timingData.splice(count,timingData.length-count);let otherDestination=null;if(!otherSource.length)return;timingData.push({cmd_time:0,cmd_string:'Other'});otherDestination=timingData[timingData.length-1];for(let i=0;i<otherSource.length;i++){otherDestination.cmd_time+=otherSource[i].cmd_time;}},sortTimingBucketsByOpTimeDescending_(a,b){return b.cmd_time-a.cmd_time;},resetOpsTimingData_(){this.opsTimingData_.length=0;}};return{PictureOpsChartSummaryView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const BAR_PADDING=1;const BAR_WIDTH=5;const CHART_PADDING_LEFT=65;const CHART_PADDING_RIGHT=30;const CHART_PADDING_BOTTOM=35;const CHART_PADDING_TOP=20;const AXIS_PADDING_LEFT=55;const AXIS_PADDING_RIGHT=30;const AXIS_PADDING_BOTTOM=35;const AXIS_PADDING_TOP=20;const AXIS_TICK_SIZE=5;const AXIS_LABEL_PADDING=5;const VERTICAL_TICKS=5;const HUE_CHAR_CODE_ADJUSTMENT=5.7;const PictureOpsChartView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-view');PictureOpsChartView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.display='block';this.style.height='180px';this.style.margin=0;this.style.padding=0;this.style.position='relative';this.picture_=undefined;this.pictureOps_=undefined;this.opCosts_=undefined;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.selectedOpIndex_=undefined;this.chartWidth_=0;this.chartHeight_=0;this.dimensionsHaveChanged_=true;this.currentBarMouseOverTarget_=undefined;this.ninetyFifthPercentileCost_=0;this.totalOpCost_=0;this.chart_.addEventListener('click',this.onClick_.bind(this));this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));new ResizeObserver(this.onResize_.bind(this)).observe(this);this.usePercentileScale_=false;this.usePercentileScaleCheckbox_=tr.ui.b.createCheckBox(this,'usePercentileScale','PictureOpsChartView.usePercentileScale',false,'Limit to 95%-ile');Polymer.dom(this.usePercentileScaleCheckbox_).classList.add('use-percentile-scale');this.usePercentileScaleCheckbox_.style.position='absolute';this.usePercentileScaleCheckbox_.style.left=0;this.usePercentileScaleCheckbox_.style.top=0;Polymer.dom(this).appendChild(this.usePercentileScaleCheckbox_);},get dimensionsHaveChanged(){return this.dimensionsHaveChanged_;},set dimensionsHaveChanged(dimensionsHaveChanged){this.dimensionsHaveChanged_=dimensionsHaveChanged;},get usePercentileScale(){return this.usePercentileScale_;},set usePercentileScale(usePercentileScale){this.usePercentileScale_=usePercentileScale;this.drawChartContents_();},get numOps(){return this.opCosts_.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(selectedOpIndex){if(selectedOpIndex<0)throw new Error('Invalid index');if(selectedOpIndex>=this.numOps)throw new Error('Invalid index');this.selectedOpIndex_=selectedOpIndex;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureOps_=picture.tagOpsWithTimings(picture.getOps());this.currentBarMouseOverTarget_=undefined;this.processPictureData_();this.dimensionsHaveChanged=true;},processPictureData_(){if(this.pictureOps_===undefined)return;let totalOpCost=0;this.opCosts_=this.pictureOps_.map(function(op){totalOpCost+=op.cmd_time;return op.cmd_time;});this.opCosts_.sort();const ninetyFifthPercentileCostIndex=Math.floor(this.opCosts_.length*0.95);this.ninetyFifthPercentileCost_=this.opCosts_[ninetyFifthPercentileCostIndex];this.maxCost_=this.opCosts_[this.opCosts_.length-1];this.totalOpCost_=totalOpCost;},extractBarIndex_(e){let index=undefined;if(this.pictureOps_===undefined||this.pictureOps_.length===0){return index;}
+timingData.sort(this.sortTimingBucketsByOpTimeDescending_);this.collapseTimingBucketsToOther_(4);},collapseTimingBucketsToOther_(count){const timingData=this.opsTimingData_;const otherSource=timingData.splice(count,timingData.length-count);let otherDestination=null;if(!otherSource.length)return;timingData.push({cmd_time:0,cmd_string:'Other'});otherDestination=timingData[timingData.length-1];for(let i=0;i<otherSource.length;i++){otherDestination.cmd_time+=otherSource[i].cmd_time;}},sortTimingBucketsByOpTimeDescending_(a,b){return b.cmd_time-a.cmd_time;},resetOpsTimingData_(){this.opsTimingData_.length=0;}};return{PictureOpsChartSummaryView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const BAR_PADDING=1;const BAR_WIDTH=5;const CHART_PADDING_LEFT=65;const CHART_PADDING_RIGHT=30;const CHART_PADDING_BOTTOM=35;const CHART_PADDING_TOP=20;const AXIS_PADDING_LEFT=55;const AXIS_PADDING_RIGHT=30;const AXIS_PADDING_BOTTOM=35;const AXIS_PADDING_TOP=20;const AXIS_TICK_SIZE=5;const AXIS_LABEL_PADDING=5;const VERTICAL_TICKS=5;const HUE_CHAR_CODE_ADJUSTMENT=5.7;const PictureOpsChartView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-view');PictureOpsChartView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.display='block';this.style.height='180px';this.style.margin=0;this.style.padding=0;this.style.position='relative';this.picture_=undefined;this.pictureOps_=undefined;this.opCosts_=undefined;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.selectedOpIndex_=undefined;this.chartWidth_=0;this.chartHeight_=0;this.dimensionsHaveChanged_=true;this.currentBarMouseOverTarget_=undefined;this.ninetyFifthPercentileCost_=0;this.totalOpCost_=0;this.chart_.addEventListener('click',this.onClick_.bind(this));this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));try{new ResizeObserver(this.onResize_.bind(this)).observe(this);}catch(e){}
+this.usePercentileScale_=false;this.usePercentileScaleCheckbox_=tr.ui.b.createCheckBox(this,'usePercentileScale','PictureOpsChartView.usePercentileScale',false,'Limit to 95%-ile');Polymer.dom(this.usePercentileScaleCheckbox_).classList.add('use-percentile-scale');this.usePercentileScaleCheckbox_.style.position='absolute';this.usePercentileScaleCheckbox_.style.left=0;this.usePercentileScaleCheckbox_.style.top=0;Polymer.dom(this).appendChild(this.usePercentileScaleCheckbox_);},get dimensionsHaveChanged(){return this.dimensionsHaveChanged_;},set dimensionsHaveChanged(dimensionsHaveChanged){this.dimensionsHaveChanged_=dimensionsHaveChanged;},get usePercentileScale(){return this.usePercentileScale_;},set usePercentileScale(usePercentileScale){this.usePercentileScale_=usePercentileScale;this.drawChartContents_();},get numOps(){return this.opCosts_.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(selectedOpIndex){if(selectedOpIndex<0)throw new Error('Invalid index');if(selectedOpIndex>=this.numOps)throw new Error('Invalid index');this.selectedOpIndex_=selectedOpIndex;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureOps_=picture.tagOpsWithTimings(picture.getOps());this.currentBarMouseOverTarget_=undefined;this.processPictureData_();this.dimensionsHaveChanged=true;},processPictureData_(){if(this.pictureOps_===undefined)return;let totalOpCost=0;this.opCosts_=this.pictureOps_.map(function(op){totalOpCost+=op.cmd_time;return op.cmd_time;});this.opCosts_.sort();const ninetyFifthPercentileCostIndex=Math.floor(this.opCosts_.length*0.95);this.ninetyFifthPercentileCost_=this.opCosts_[ninetyFifthPercentileCostIndex];this.maxCost_=this.opCosts_[this.opCosts_.length-1];this.totalOpCost_=totalOpCost;},extractBarIndex_(e){let index=undefined;if(this.pictureOps_===undefined||this.pictureOps_.length===0){return index;}
const x=e.offsetX;const y=e.offsetY;const totalBarWidth=(BAR_WIDTH+BAR_PADDING)*this.pictureOps_.length;const chartLeft=CHART_PADDING_LEFT;const chartTop=0;const chartBottom=this.chartHeight_-CHART_PADDING_BOTTOM;const chartRight=chartLeft+totalBarWidth;if(x<chartLeft||x>chartRight||y<chartTop||y>chartBottom){return index;}
index=Math.floor((x-chartLeft)/totalBarWidth*this.pictureOps_.length);index=tr.b.math.clamp(index,0,this.pictureOps_.length-1);return index;},onClick_(e){const barClicked=this.extractBarIndex_(e);if(barClicked===undefined)return;if(barClicked===this.selectedOpIndex){this.selectedOpIndex=undefined;}else{this.selectedOpIndex=barClicked;}
e.preventDefault();tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onMouseMove_(e){const lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=this.extractBarIndex_(e);if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget){return;}
@@ -7838,7 +7655,7 @@ CHART_PADDING_LEFT;const barWidth=BAR_WIDTH+BAR_PADDING;const tooltipOffset=Math
toolTipTimePercentage+'%)',left+8,top+22);},drawBars_(){let op;let opColor=0;let opHeight=0;const opWidth=BAR_WIDTH+BAR_PADDING;let opHover=false;const bottom=this.chartHeight_-CHART_PADDING_BOTTOM;const maxHeight=this.chartHeight_-CHART_PADDING_BOTTOM-
CHART_PADDING_TOP;let maxValue;if(this.usePercentileScale){maxValue=this.ninetyFifthPercentileCost_;}else{maxValue=this.maxCost_;}
for(let b=0;b<this.pictureOps_.length;b++){op=this.pictureOps_[b];opHeight=Math.round((op.cmd_time/maxValue)*maxHeight);opHeight=Math.max(opHeight,1);opHover=(b===this.currentBarMouseOverTarget_);opColor=this.getOpColor_(op.cmd_string,opHover);if(b===this.selectedOpIndex){this.chartCtx_.fillStyle='#FFFF00';}else{this.chartCtx_.fillStyle=opColor;}
-this.chartCtx_.fillRect(CHART_PADDING_LEFT+b*opWidth,bottom-opHeight,BAR_WIDTH,opHeight);}},getOpColor_(opName,hover){const characters=opName.split('');const hue=characters.reduce(this.reduceNameToHue,0)%360;const saturation=30;const lightness=hover?'75%':'50%';return'hsl('+hue+', '+saturation+'%, '+lightness+'%)';},reduceNameToHue(previousValue,currentValue,index,array){return Math.round(previousValue+currentValue.charCodeAt(0)*HUE_CHAR_CODE_ADJUSTMENT);},clearChartContents_(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);}};return{PictureOpsChartView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const THIS_DOC=document.currentScript.ownerDocument;const PictureDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-picture-debugger');PictureDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate(){const node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-picture-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.style.display='flex';this.style.flexDirection='row';const title=this.querySelector('.title');title.style.fontWeight='bold';title.style.marginLeft='5px';title.style.marginRight='5px';this.pictureAsImageData_=undefined;this.showOverdraw_=false;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterArea_.style.backgroundColor='#ddd';this.rasterArea_.style.minHeight='100px';this.rasterArea_.style.minWidth='200px';this.rasterArea_.style.overflow='auto';this.rasterArea_.style.paddingLeft='5px';this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');this.filename_=Polymer.dom(this).querySelector('.filename');this.filename_.style.userSelect='text';this.filename_.style.marginLeft='5px';this.drawOpsChartSummaryView_=new tr.ui.e.chrome.cc.PictureOpsChartSummaryView();this.drawOpsChartView_=new tr.ui.e.chrome.cc.PictureOpsChartView();this.drawOpsChartView_.addEventListener('selection-changed',this.onChartBarClicked_.bind(this));this.exportButton_=Polymer.dom(this).querySelector('.export');this.exportButton_.addEventListener('click',this.onSaveAsSkPictureClicked_.bind(this));this.trackMouse_();const overdrawCheckbox=tr.ui.b.createCheckBox(this,'showOverdraw','pictureView.showOverdraw',false,'Show overdraw');const chartCheckbox=tr.ui.b.createCheckBox(this,'showSummaryChart','pictureView.showSummaryChart',false,'Show timing summary');const pictureInfo=Polymer.dom(this).querySelector('picture-info');pictureInfo.style.flexGrow=0;pictureInfo.style.flexShrink=0;pictureInfo.style.flexBasis='auto';pictureInfo.style.paddingTop='2px';Polymer.dom(pictureInfo).appendChild(overdrawCheckbox);Polymer.dom(pictureInfo).appendChild(chartCheckbox);this.drawOpsView_=new tr.ui.e.chrome.cc.PictureOpsListView();this.drawOpsView_.flexGrow=1;this.drawOpsView_.flexShrink=1;this.drawOpsView_.flexBasis='auto';this.drawOpsView_.addEventListener('selection-changed',this.onChangeDrawOps_.bind(this));const leftPanel=Polymer.dom(this).querySelector('left-panel');leftPanel.style.flexDirection='column';leftPanel.style.display='flex';leftPanel.style.flexGrow=0;leftPanel.style.flexShrink=0;leftPanel.style.flexBasis='auto';leftPanel.style.minWidth='200px';leftPanel.style.overflow='auto';Polymer.dom(leftPanel).appendChild(this.drawOpsChartSummaryView_);Polymer.dom(leftPanel).appendChild(this.drawOpsView_);const middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.style.flexGrow=0;middleDragHandle.style.flexShrink=0;middleDragHandle.style.flexBasis='auto';middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;const rightPanel=Polymer.dom(this).querySelector('right-panel');rightPanel.style.flexGrow=1;rightPanel.style.flexShrink=1;rightPanel.style.flexBasis='auto';rightPanel.style.minWidth=0;rightPanel.style.flexDirection='column';rightPanel.style.display='flex';const chartView=Polymer.dom(rightPanel).querySelector('tr-ui-e-chrome-cc-picture-ops-chart-view');this.drawOpsChartView_.style.flexGrow=0;this.drawOpsChartView_.style.flexShrink=0;this.drawOpsChartView_.style.flexBasis='auto';this.drawOpsChartView_.style.minWidth=0;this.drawOpsChartView_.style.overflowX='auto';this.drawOpsChartView_.style.overflowY='hidden';rightPanel.replaceChild(this.drawOpsChartView_,chartView);this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).appendChild(this.infoBar_);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;const hkc=document.createElement('tv-ui-b-hotkey-controller');hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'h'.charCodeAt(0),callback(e){this.moveSelectedOpBy(-1);e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'l'.charCodeAt(0),callback(e){this.moveSelectedOpBy(1);e.stopPropagation();}}));Polymer.dom(this).appendChild(hkc);},onSaveAsSkPictureClicked_(){const rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());const length=rawData.length;const arrayBuffer=new ArrayBuffer(length);const uint8Array=new Uint8Array(arrayBuffer);for(let c=0;c<length;c++){uint8Array[c]=rawData.charCodeAt(c);}
+this.chartCtx_.fillRect(CHART_PADDING_LEFT+b*opWidth,bottom-opHeight,BAR_WIDTH,opHeight);}},getOpColor_(opName,hover){const characters=opName.split('');const hue=characters.reduce(this.reduceNameToHue,0)%360;const saturation=30;const lightness=hover?'75%':'50%';return'hsl('+hue+', '+saturation+'%, '+lightness+'%)';},reduceNameToHue(previousValue,currentValue,index,array){return Math.round(previousValue+currentValue.charCodeAt(0)*HUE_CHAR_CODE_ADJUSTMENT);},clearChartContents_(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);}};return{PictureOpsChartView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const THIS_DOC=document._currentScript.ownerDocument;const PictureDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-picture-debugger');PictureDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate(){const node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-picture-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.style.display='flex';this.style.flexDirection='row';const title=this.querySelector('.title');title.style.fontWeight='bold';title.style.marginLeft='5px';title.style.marginRight='5px';this.pictureAsImageData_=undefined;this.showOverdraw_=false;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterArea_.style.backgroundColor='#ddd';this.rasterArea_.style.minHeight='100px';this.rasterArea_.style.minWidth='200px';this.rasterArea_.style.overflow='auto';this.rasterArea_.style.paddingLeft='5px';this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');this.filename_=Polymer.dom(this).querySelector('.filename');this.filename_.style.userSelect='text';this.filename_.style.marginLeft='5px';this.drawOpsChartSummaryView_=new tr.ui.e.chrome.cc.PictureOpsChartSummaryView();this.drawOpsChartView_=new tr.ui.e.chrome.cc.PictureOpsChartView();this.drawOpsChartView_.addEventListener('selection-changed',this.onChartBarClicked_.bind(this));this.exportButton_=Polymer.dom(this).querySelector('.export');this.exportButton_.addEventListener('click',this.onSaveAsSkPictureClicked_.bind(this));this.trackMouse_();const overdrawCheckbox=tr.ui.b.createCheckBox(this,'showOverdraw','pictureView.showOverdraw',false,'Show overdraw');const chartCheckbox=tr.ui.b.createCheckBox(this,'showSummaryChart','pictureView.showSummaryChart',false,'Show timing summary');const pictureInfo=Polymer.dom(this).querySelector('picture-info');pictureInfo.style.flexGrow=0;pictureInfo.style.flexShrink=0;pictureInfo.style.flexBasis='auto';pictureInfo.style.paddingTop='2px';Polymer.dom(pictureInfo).appendChild(overdrawCheckbox);Polymer.dom(pictureInfo).appendChild(chartCheckbox);this.drawOpsView_=new tr.ui.e.chrome.cc.PictureOpsListView();this.drawOpsView_.flexGrow=1;this.drawOpsView_.flexShrink=1;this.drawOpsView_.flexBasis='auto';this.drawOpsView_.addEventListener('selection-changed',this.onChangeDrawOps_.bind(this));const leftPanel=Polymer.dom(this).querySelector('left-panel');leftPanel.style.flexDirection='column';leftPanel.style.display='flex';leftPanel.style.flexGrow=0;leftPanel.style.flexShrink=0;leftPanel.style.flexBasis='auto';leftPanel.style.minWidth='200px';leftPanel.style.overflow='auto';Polymer.dom(leftPanel).appendChild(this.drawOpsChartSummaryView_);Polymer.dom(leftPanel).appendChild(this.drawOpsView_);const middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.style.flexGrow=0;middleDragHandle.style.flexShrink=0;middleDragHandle.style.flexBasis='auto';middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;const rightPanel=Polymer.dom(this).querySelector('right-panel');rightPanel.style.flexGrow=1;rightPanel.style.flexShrink=1;rightPanel.style.flexBasis='auto';rightPanel.style.minWidth=0;rightPanel.style.flexDirection='column';rightPanel.style.display='flex';const chartView=Polymer.dom(rightPanel).querySelector('tr-ui-e-chrome-cc-picture-ops-chart-view');this.drawOpsChartView_.style.flexGrow=0;this.drawOpsChartView_.style.flexShrink=0;this.drawOpsChartView_.style.flexBasis='auto';this.drawOpsChartView_.style.minWidth=0;this.drawOpsChartView_.style.overflowX='auto';this.drawOpsChartView_.style.overflowY='hidden';rightPanel.replaceChild(this.drawOpsChartView_,chartView);this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).appendChild(this.infoBar_);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;const hkc=document.createElement('tv-ui-b-hotkey-controller');hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'h'.charCodeAt(0),callback(e){this.moveSelectedOpBy(-1);e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'l'.charCodeAt(0),callback(e){this.moveSelectedOpBy(1);e.stopPropagation();}}));Polymer.dom(this).appendChild(hkc);},onSaveAsSkPictureClicked_(){const rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());const length=rawData.length;const arrayBuffer=new ArrayBuffer(length);const uint8Array=new Uint8Array(arrayBuffer);for(let c=0;c<length;c++){uint8Array[c]=rawData.charCodeAt(c);}
const blob=new Blob([uint8Array],{type:'application/octet-binary'});const blobUrl=window.webkitURL.createObjectURL(blob);const link=document.createElementNS('http://www.w3.org/1999/xhtml','a');link.href=blobUrl;link.download=this.filename_.value;const event=document.createEvent('MouseEvents');event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null);link.dispatchEvent(event);},get picture(){return this.picture_;},set picture(picture){this.drawOpsView_.picture=picture;this.drawOpsChartView_.picture=picture;this.drawOpsChartSummaryView_.picture=picture;this.picture_=picture;this.exportButton_.disabled=!this.picture_.canSave;if(picture){const size=this.getRasterCanvasSize_();this.rasterCanvas_.width=size.width;this.rasterCanvas_.height=size.height;}
const bounds=this.rasterArea_.getBoundingClientRect();const selectorBounds=this.mouseModeSelector_.getBoundingClientRect();this.mouseModeSelector_.pos={x:(bounds.right-selectorBounds.width-10),y:bounds.top};this.rasterize_();this.scheduleUpdateContents_();},getRasterCanvasSize_(){const style=window.getComputedStyle(this.rasterArea_);const width=Math.max(parseInt(style.width),this.picture_.layerRect.width);const height=Math.max(parseInt(style.height),this.picture_.layerRect.height);return{width,height};},scheduleUpdateContents_(){if(this.updateContentsPending_)return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_.bind(this));},updateContents_(){this.updateContentsPending_=false;if(this.picture_){Polymer.dom(this.sizeInfo_).textContent='('+
this.picture_.layerRect.width+' x '+
@@ -7930,7 +7747,8 @@ this.objectSnapshot_.screenshot;}}};tr.ui.analysis.ObjectSnapshotView.register(S
return layoutObject.tableRow;}},{title:'col',value(layoutObject){if(layoutObject.tableCol===undefined){return'';}
return layoutObject.tableCol;}},{title:'rowSpan',value(layoutObject){if(layoutObject.tableRowSpan===undefined){return'';}
return layoutObject.tableRowSpan;}},{title:'colSpan',value(layoutObject){if(layoutObject.tableColSpan===undefined){return'';}
-return layoutObject.tableColSpan;}},{title:'address',value(layoutObject){return layoutObject.id.toString(16);}}];const table=this.ownerDocument.createElement('tr-ui-b-table');table.defaultExpansionStateCallback=function(layoutObject,parentLayoutObject){return true;};table.subRowsPropertyName='childLayoutObjects';table.tableColumns=columns;table.tableRows=this.currentSelection_.map(function(snapshot){return snapshot.rootLayoutObject;});table.rebuild();Polymer.dom(this.$.content).appendChild(table);},});return{};});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:false,title:'Layout Tree',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:true,title:'Layout Trees',});'use strict';tr.exportTo('tr.ui.behaviors',function(){const SidePanel={get rangeOfInterest(){throw new Error('Not implemented');},set rangeOfInterest(rangeOfInterest){throw new Error('Not implemented');},get selection(){throw new Error('Not implemented');},set selection(selection){throw new Error('Not implemented');},get model(){throw new Error('Not implemented');},set model(model){throw new Error('Not implemented');},supportsModel(m){throw new Error('Not implemented');}};return{SidePanel,};});'use strict';tr.exportTo('tr.ui.side_panel',function(){function SidePanelRegistry(){}
+return layoutObject.tableColSpan;}},{title:'address',value(layoutObject){return layoutObject.id.toString(16);}}];const table=this.ownerDocument.createElement('tr-ui-b-table');table.defaultExpansionStateCallback=function(layoutObject,parentLayoutObject){return true;};table.subRowsPropertyName='childLayoutObjects';table.tableColumns=columns;table.tableRows=this.currentSelection_.map(function(snapshot){return snapshot.rootLayoutObject;});table.rebuild();Polymer.dom(this.$.content).appendChild(table);},});return{};});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:false,title:'Layout Tree',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:true,title:'Layout Trees',});'use strict';tr.exportTo('tr.ui.e.img',function(){const THIS_DOC=document.currentScript.ownerDocument;const ImageSnapshotView=tr.ui.b.define('tr-ui-e-img-image-snapshot-view',tr.ui.analysis.ObjectSnapshotView);ImageSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){const node=tr.ui.b.instantiateTemplate('#tr-ui-e-img-image-snapshot-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);const info=Polymer.dom(this).querySelector('.image-info');this.sizeInfo_=Polymer.dom(info).querySelector('.size');this.imageContainer_=Polymer.dom(this).querySelector('.image-container');this.image_=Polymer.dom(this.imageContainer_).querySelector('img');this.zoomScaleValue_=1;this.trackMouse_();},updateContents(){if(this.objectSnapshot_&&this.objectSnapshot_.data&&this.objectSnapshot_.type){this.image_.onload=this.drawPicture_.bind(this);this.image_.src=`data:image/${this.objectSnapshot_.type};`+`base64,${this.objectSnapshot_.data}`;}
+this.drawPicture_();},drawPicture_(){if(!this.image_.complete)return;const naturalWidth=this.image_.naturalWidth;const naturalHeight=this.image_.naturalHeight;this.sizeInfo_.textContent=`(${naturalWidth} x ${naturalHeight})`;this.image_.width=naturalWidth*this.zoomScaleValue_;this.image_.height=naturalHeight*this.zoomScaleValue_;},trackMouse_(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.imageContainer_;Polymer.dom(this.imageContainer_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_(e){if(!this.isZooming_)return;const currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_(e){return{x:e.clientX-this.imageContainer_.offsetLeft,y:e.clientY-this.imageContainer_.offsetTop};},};tr.ui.analysis.ObjectSnapshotView.register(ImageSnapshotView,{typeName:'gfx::Image'});return{ImageSnapshotView,};});'use strict';tr.exportTo('tr.ui.behaviors',function(){const SidePanel={get rangeOfInterest(){throw new Error('Not implemented');},set rangeOfInterest(rangeOfInterest){throw new Error('Not implemented');},get selection(){throw new Error('Not implemented');},set selection(selection){throw new Error('Not implemented');},get model(){throw new Error('Not implemented');},set model(model){throw new Error('Not implemented');},supportsModel(m){throw new Error('Not implemented');}};return{SidePanel,};});'use strict';tr.exportTo('tr.ui.side_panel',function(){function SidePanelRegistry(){}
const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(SidePanelRegistry,options);return{SidePanelRegistry,};});'use strict';tr.exportTo('tr.ui.e.s',function(){const BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;const FrameTreeNodeSnapshot=tr.e.chrome.FrameTreeNodeSnapshot;const RenderFrameSnapshot=tr.e.chrome.RenderFrameSnapshot;const TopLevelSnapshot=tr.e.chrome.TopLevelSnapshot;const BlameContextInstance=tr.e.chrome.BlameContextInstance;const FrameTreeNodeInstance=tr.e.chrome.FrameTreeNodeInstance;const RenderFrameInstance=tr.e.chrome.RenderFrameInstance;const TopLevelInstance=tr.e.chrome.TopLevelInstance;function Row(context){this.subRows=undefined;this.contexts=[];this.type=undefined;this.renderer='N/A';this.url=undefined;this.time=0;this.eventsOfInterest=new tr.model.EventSet();if(context===undefined)return;this.type=context.objectInstance.blameContextType;this.contexts.push(context);if(context instanceof FrameTreeNodeSnapshot){if(context.renderFrame){this.contexts.push(context.renderFrame);this.renderer=context.renderFrame.objectInstance.parent.pid;}}else if(context instanceof RenderFrameSnapshot){if(context.frameTreeNode){this.contexts.push(context.frameTreeNode);}
this.renderer=context.objectInstance.parent.pid;}else if(context instanceof TopLevelSnapshot){this.renderer=context.objectInstance.parent.pid;}else{throw new Error('Unknown context type');}
this.eventsOfInterest.addEventSet(this.contexts);this.url=context.url;}
@@ -7947,7 +7765,7 @@ const select=this.$.select;const groupOption=select.options[select.selectedIndex
const ans={supported:false};for(const proc of Object.values(m.processes)){proc.objects.iterObjectInstances(function(instance){if(instance instanceof BlameContextInstance){ans.supported=true;}});}
if(!ans.supported){ans.reason='No frame data available';}
return ans;},get currentRangeOfInterest(){if(this.rangeOfInterest_.isEmpty){return this.model_.bounds;}
-return this.rangeOfInterest_;},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},get selection(){},set selection(_){},get textLabel(){return'Frame Data';},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-frame-data-side-panel');});});'use strict';Polymer({is:'tr-ui-b-chart-legend-key',ready(){this.$.checkbox.addEventListener('change',this.onCheckboxChange_.bind(this));},onCheckboxChange_(){tr.b.dispatchSimpleEvent(this,tr.ui.b.DataSeriesEnableChangeEventType,true,false,{key:Polymer.dom(this).textContent,enabled:this.enabled});},set textContent(t){Polymer.dom(this.$.label).textContent=t;Polymer.dom(this.$.link).textContent=t;this.updateContents_();},set width(w){w-=20;this.$.link.style.width=w+'px';this.$.label.style.width=w+'px';},get textContent(){return Polymer.dom(this.$.label).textContent;},set optional(optional){this.$.checkbox.style.visibility=optional?'visible':'hidden';},get optional(){return this.$.checkbox.style.visibility==='visible';},set enabled(enabled){this.$.checkbox.checked=enabled?'checked':'';},get enabled(){return this.$.checkbox.checked;},set color(c){this.$.label.style.color=c;this.$.link.color=c;},set target(target){this.$.link.setSelectionAndContent(target,Polymer.dom(this.$.label).textContent);this.updateContents_();},get target(){return this.$.link.selection;},set title(title){this.$.link.title=title;},updateContents_(){this.$.link.style.display=this.target?'':'none';this.$.label.style.display=this.target?'none':'';this.$.label.htmlFor=this.optional?'checkbox':'';}});'use strict';(function(window){window.define=function(x){window.d3=x;};window.define.amd=true;})(this);!function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function u(){}function i(n){return aa+n in this}function o(n){return n=aa+n,n in this&&delete this[n]}function a(){var n=[];return this.forEach(function(t){n.push(t)}),n}function c(){var n=0;for(var t in this)t.charCodeAt(0)===ca&&++n;return n}function s(){for(var n in this)if(n.charCodeAt(0)===ca)return!1;return!0}function l(){}function f(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function h(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=sa.length;r>e;++e){var u=sa[e]+t;if(u in n)return u}}function g(){}function p(){}function v(n){function t(){for(var t,r=e,u=-1,i=r.length;++u<i;)(t=r[u].on)&&t.apply(this,arguments);return n}var e=[],r=new u;return t.on=function(t,u){var i,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,i=e.indexOf(o)).concat(e.slice(i+1)),r.remove(t)),u&&e.push(r.set(t,{on:u})),n)},t}function d(){Xo.event.preventDefault()}function m(){for(var n,t=Xo.event;n=t.sourceEvent;)t=n;return t}function y(n){for(var t=new p,e=0,r=arguments.length;++e<r;)t[arguments[e]]=v(t);return t.of=function(e,r){return function(u){try{var i=u.sourceEvent=Xo.event;u.target=n,Xo.event=u,t[u.type].apply(e,r)}finally{Xo.event=i}}},t}function x(n){return fa(n,da),n}function M(n){return"function"==typeof n?n:function(){return ha(n,this)}}function _(n){return"function"==typeof n?n:function(){return ga(n,this)}}function b(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function u(){this.setAttribute(n,t)}function i(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=Xo.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?i:u}function w(n){return n.trim().replace(/\s+/g," ")}function S(n){return new RegExp("(?:^|\\s+)"+Xo.requote(n)+"(?:\\s+|$)","g")}function k(n){return n.trim().split(/^|\s+/)}function E(n,t){function e(){for(var e=-1;++e<u;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<u;)n[e](this,r)}n=k(n).map(A);var u=n.length;return"function"==typeof t?r:e}function A(n){var t=S(n);return function(e,r){if(u=e.classList)return r?u.add(n):u.remove(n);var u=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(u)||e.setAttribute("class",w(u+" "+n))):e.setAttribute("class",w(u.replace(t," ")))}}function C(n,t,e){function r(){this.style.removeProperty(n)}function u(){this.style.setProperty(n,t,e)}function i(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?i:u}function N(n,t){function e(){delete this[n]}function r(){this[n]=t}function u(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?u:r}function L(n){return"function"==typeof n?n:(n=Xo.ns.qualify(n)).local?function(){return this.ownerDocument.createElementNS(n.space,n.local)}:function(){return this.ownerDocument.createElementNS(this.namespaceURI,n)}}function T(n){return{__data__:n}}function q(n){return function(){return va(this,n)}}function z(n){return arguments.length||(n=Xo.ascending),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function R(n,t){for(var e=0,r=n.length;r>e;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function D(n){return fa(n,ya),n}function P(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t<c;);return o}}function U(){var n=this.__transition__;n&&++n.active}function j(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function u(){var u=c(t,Bo(arguments));r.call(this),this.addEventListener(n,this[o]=u,u.$=e),u._=t}function i(){var t,e=new RegExp("^__on([^.]+)"+Xo.requote(n)+"$");for(var r in this)if(t=r.match(e)){var u=this[r];this.removeEventListener(t[1],u,u.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),c=H;a>0&&(n=n.substring(0,a));var s=Ma.get(n);return s&&(n=s,c=F),a?t?u:r:t?g:i}function H(n,t){return function(e){var r=Xo.event;Xo.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{Xo.event=r}}}function F(n,t){var e=H(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function O(){var n=".dragsuppress-"+ ++ba,t="click"+n,e=Xo.select(Go).on("touchmove"+n,d).on("dragstart"+n,d).on("selectstart"+n,d);if(_a){var r=Jo.style,u=r[_a];r[_a]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),_a&&(r[_a]=u),i&&(e.on(t,function(){d(),o()},!0),setTimeout(o,0))}}function Y(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>wa&&(Go.scrollX||Go.scrollY)){e=Xo.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var u=e[0][0].getScreenCTM();wa=!(u.f||u.e),e.remove()}return wa?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}function I(n){return n>0?1:0>n?-1:0}function Z(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function V(n){return n>1?0:-1>n?Sa:Math.acos(n)}function X(n){return n>1?Ea:-1>n?-Ea:Math.asin(n)}function $(n){return((n=Math.exp(n))-1/n)/2}function B(n){return((n=Math.exp(n))+1/n)/2}function W(n){return((n=Math.exp(2*n))-1)/(n+1)}function J(n){return(n=Math.sin(n/2))*n}function G(){}function K(n,t,e){return new Q(n,t,e)}function Q(n,t,e){this.h=n,this.s=t,this.l=e}function nt(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,gt(u(n+120),u(n),u(n-120))}function tt(n,t,e){return new et(n,t,e)}function et(n,t,e){this.h=n,this.c=t,this.l=e}function rt(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),ut(e,Math.cos(n*=Na)*t,Math.sin(n)*t)}function ut(n,t,e){return new it(n,t,e)}function it(n,t,e){this.l=n,this.a=t,this.b=e}function ot(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=ct(u)*Fa,r=ct(r)*Oa,i=ct(i)*Ya,gt(lt(3.2404542*u-1.5371385*r-.4985314*i),lt(-.969266*u+1.8760108*r+.041556*i),lt(.0556434*u-.2040259*r+1.0572252*i))}function at(n,t,e){return n>0?tt(Math.atan2(e,t)*La,Math.sqrt(t*t+e*e),n):tt(0/0,0/0,n)}function ct(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function st(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function lt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function ft(n){return gt(n>>16,255&n>>8,255&n)}function ht(n){return ft(n)+""}function gt(n,t,e){return new pt(n,t,e)}function pt(n,t,e){this.r=n,this.g=t,this.b=e}function vt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function dt(n,t,e){var r,u,i,o,a=0,c=0,s=0;if(u=/([a-z]+)\((.*)\)/i.exec(n))switch(i=u[2].split(","),u[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Mt(i[0]),Mt(i[1]),Mt(i[2]))}return(o=Va.get(n))?t(o.r,o.g,o.b):(null!=n&&"#"===n.charAt(0)&&(r=parseInt(n.substring(1),16),isNaN(r)||(4===n.length?(a=(3840&r)>>4,a=a>>4|a,c=240&r,c=c>>4|c,s=15&r,s=s<<4|s):7===n.length&&(a=(16711680&r)>>16,c=(65280&r)>>8,s=255&r))),t(a,c,s))}function mt(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),K(r,u,c)}function yt(n,t,e){n=xt(n),t=xt(t),e=xt(e);var r=st((.4124564*n+.3575761*t+.1804375*e)/Fa),u=st((.2126729*n+.7151522*t+.072175*e)/Oa),i=st((.0193339*n+.119192*t+.9503041*e)/Ya);return ut(116*u-16,500*(r-u),200*(u-i))}function xt(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Mt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function _t(n){return"function"==typeof n?n:function(){return n}}function bt(n){return n}function wt(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),St(t,e,n,r)}}function St(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=Xo.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!Go.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=Xo.event;Xo.event=n;try{o.progress.call(i,c)}finally{Xo.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Bo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},Xo.rebind(i,o,"on"),null==r?i:i.get(kt(r))}function kt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Et(){var n=At(),t=Ct()-n;t>24?(isFinite(t)&&(clearTimeout(Wa),Wa=setTimeout(Et,t)),Ba=0):(Ba=1,Ga(Et))}function At(){var n=Date.now();for(Ja=Xa;Ja;)n>=Ja.t&&(Ja.f=Ja.c(n-Ja.t)),Ja=Ja.n;return n}function Ct(){for(var n,t=Xa,e=1/0;t;)t.f?t=n?n.n=t.n:Xa=t.n:(t.t<e&&(e=t.t),t=(n=t).n);return $a=n,e}function Nt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Lt(n,t){var e=Math.pow(10,3*oa(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Tt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r?function(n){for(var t=n.length,u=[],i=0,o=r[0];t>0&&o>0;)u.push(n.substring(t-=o,t+o)),o=r[i=(i+1)%r.length];return u.reverse().join(e)}:bt;return function(n){var e=Qa.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"",c=e[4]||"",s=e[5],l=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1;switch(h&&(h=+h.substring(1)),(s||"0"===r&&"="===o)&&(s=r="0",o="=",f&&(l-=Math.floor((l-1)/4))),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=nc.get(g)||qt;var y=s&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):a;if(0>p){var c=Xo.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x=n.lastIndexOf("."),M=0>x?n:n.substring(0,x),_=0>x?"":t+n.substring(x+1);!s&&f&&(M=i(M));var b=v.length+M.length+_.length+(y?0:u.length),w=l>b?new Array(b=l-b+1).join(r):"";return y&&(M=i(w+M)),u+=v,n=M+_,("<"===o?u+n+w:">"===o?w+u+n:"^"===o?w.substring(0,b>>=1)+u+n+w.substring(b):u+(y?n:w+n))+e}}}function qt(n){return n+""}function zt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Rt(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new ec(e-1)),1),e}function i(n,e){return t(n=new ec(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{ec=zt;var r=new zt;return r._=n,o(r,t,e)}finally{ec=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Dt(n);return c.floor=c,c.round=Dt(r),c.ceil=Dt(u),c.offset=Dt(i),c.range=a,n}function Dt(n){return function(t,e){try{ec=zt;var r=new zt;return r._=t,n(r,e)._}finally{ec=Date}}}function Pt(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.substring(c,a)),null!=(u=uc[e=n.charAt(++a)])&&(e=n.charAt(++a)),(i=C[e])&&(e=i(t,null==u?"e"===e?" ":"0":u)),o.push(e),c=a+1);return o.push(n.substring(c,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},u=e(r,n,t,0);if(u!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var i=null!=r.Z&&ec!==zt,o=new(i?zt:ec);return"j"in r?o.setFullYear(r.y,0,r.j):"w"in r&&("W"in r||"U"in r)?(o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+Math.floor(r.Z/100),r.M+r.Z%100,r.S,r.L),i?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var u,i,o,a=0,c=t.length,s=e.length;c>a;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=N[o in uc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){b.lastIndex=0;var r=b.exec(t.substring(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){M.lastIndex=0;var r=M.exec(t.substring(e));return r?(n.w=_.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.substring(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.substring(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,C.c.toString(),t,r)}function c(n,t,r){return e(n,C.x.toString(),t,r)}function s(n,t,r){return e(n,C.X.toString(),t,r)}function l(n,t,e){var r=x.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{ec=zt;var t=new ec;return t._=n,r(t)}finally{ec=Date}}var r=t(n);return e.parse=function(n){try{ec=zt;var t=r.parse(n);return t&&t._}finally{ec=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ee;var x=Xo.map(),M=jt(v),_=Ht(v),b=jt(d),w=Ht(d),S=jt(m),k=Ht(m),E=jt(y),A=Ht(y);p.forEach(function(n,t){x.set(n.toLowerCase(),t)});var C={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return Ut(n.getDate(),t,2)},e:function(n,t){return Ut(n.getDate(),t,2)},H:function(n,t){return Ut(n.getHours(),t,2)},I:function(n,t){return Ut(n.getHours()%12||12,t,2)},j:function(n,t){return Ut(1+tc.dayOfYear(n),t,3)},L:function(n,t){return Ut(n.getMilliseconds(),t,3)},m:function(n,t){return Ut(n.getMonth()+1,t,2)},M:function(n,t){return Ut(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return Ut(n.getSeconds(),t,2)},U:function(n,t){return Ut(tc.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Ut(tc.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return Ut(n.getFullYear()%100,t,2)},Y:function(n,t){return Ut(n.getFullYear()%1e4,t,4)},Z:ne,"%":function(){return"%"}},N={a:r,A:u,b:i,B:o,c:a,d:Bt,e:Bt,H:Jt,I:Jt,j:Wt,L:Qt,m:$t,M:Gt,p:l,S:Kt,U:Ot,w:Ft,W:Yt,x:c,X:s,y:Zt,Y:It,Z:Vt,"%":te};return t}function Ut(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function jt(n){return new RegExp("^(?:"+n.map(Xo.requote).join("|")+")","i")}function Ht(n){for(var t=new u,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function Ft(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Ot(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Yt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function It(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Zt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.y=Xt(+r[0]),e+r[0].length):-1}function Vt(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function Xt(n){return n+(n>68?1900:2e3)}function $t(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Bt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function Wt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function Jt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function Gt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function Kt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function Qt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ne(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(oa(t)/60),u=oa(t)%60;return e+Ut(r,"0",2)+Ut(u,"0",2)}function te(n,t,e){oc.lastIndex=0;var r=oc.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function ee(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function re(){}function ue(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function ie(n,t){n&&lc.hasOwnProperty(n.type)&&lc[n.type](n,t)}function oe(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++u<i;)r=n[u],t.point(r[0],r[1],r[2]);t.lineEnd()}function ae(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)oe(n[e],t,1);t.polygonEnd()}function ce(){function n(n,t){n*=Na,t=t*Na/2+Sa/4;var e=n-r,o=e>=0?1:-1,a=o*e,c=Math.cos(t),s=Math.sin(t),l=i*s,f=u*c+l*Math.cos(a),h=l*o*Math.sin(a);hc.add(Math.atan2(h,f)),r=n,u=c,i=s}var t,e,r,u,i;gc.point=function(o,a){gc.point=n,r=(t=o)*Na,u=Math.cos(a=(e=a)*Na/2+Sa/4),i=Math.sin(a)},gc.lineEnd=function(){n(t,e)}}function se(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function le(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function fe(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function he(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ge(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function pe(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function ve(n){return[Math.atan2(n[1],n[0]),X(n[2])]}function de(n,t){return oa(n[0]-t[0])<Aa&&oa(n[1]-t[1])<Aa}function me(n,t){n*=Na;var e=Math.cos(t*=Na);ye(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function ye(n,t,e){++pc,dc+=(n-dc)/pc,mc+=(t-mc)/pc,yc+=(e-yc)/pc}function xe(){function n(n,u){n*=Na;var i=Math.cos(u*=Na),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),s=Math.atan2(Math.sqrt((s=e*c-r*a)*s+(s=r*o-t*c)*s+(s=t*a-e*o)*s),t*o+e*a+r*c);vc+=s,xc+=s*(t+(t=o)),Mc+=s*(e+(e=a)),_c+=s*(r+(r=c)),ye(t,e,r)}var t,e,r;kc.point=function(u,i){u*=Na;var o=Math.cos(i*=Na);t=o*Math.cos(u),e=o*Math.sin(u),r=Math.sin(i),kc.point=n,ye(t,e,r)}}function Me(){kc.point=me}function _e(){function n(n,t){n*=Na;var e=Math.cos(t*=Na),o=e*Math.cos(n),a=e*Math.sin(n),c=Math.sin(t),s=u*c-i*a,l=i*o-r*c,f=r*a-u*o,h=Math.sqrt(s*s+l*l+f*f),g=r*o+u*a+i*c,p=h&&-V(g)/h,v=Math.atan2(h,g);bc+=p*s,wc+=p*l,Sc+=p*f,vc+=v,xc+=v*(r+(r=o)),Mc+=v*(u+(u=a)),_c+=v*(i+(i=c)),ye(r,u,i)}var t,e,r,u,i;kc.point=function(o,a){t=o,e=a,kc.point=n,o*=Na;var c=Math.cos(a*=Na);r=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),ye(r,u,i)},kc.lineEnd=function(){n(t,e),kc.lineEnd=Me,kc.point=me}}function be(){return!0}function we(n,t,e,r,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(de(e,r)){u.lineStart();for(var a=0;t>a;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new ke(e,n,null,!0),s=new ke(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=new ke(r,n,null,!1),s=new ke(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),Se(i),Se(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function Se(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r<t;)u.n=e=n[r],e.p=u,u=e;u.n=e=n[0],e.p=u}}function ke(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Ee(n,t,e,r){return function(u,i){function o(t,e){var r=u(t,e);n(t=r[0],e=r[1])&&i.point(t,e)}function a(n,t){var e=u(n,t);d.point(e[0],e[1])}function c(){y.point=a,d.lineStart()}function s(){y.point=o,d.lineEnd()}function l(n,t){v.push([n,t]);var e=u(n,t);M.point(e[0],e[1])}function f(){M.lineStart(),v=[]}function h(){l(v[0][0],v[0][1]),M.lineEnd();var n,t=M.clean(),e=x.buffer(),r=e.length;if(v.pop(),p.push(v),v=null,r){if(1&t){n=e[0];var u,r=n.length-1,o=-1;for(i.lineStart();++o<r;)i.point((u=n[o])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Ae))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=Xo.merge(g);var n=Le(m,p);g.length?we(g,Ne,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=Ce(),M=t(x);return y}}function Ae(n){return n.length>1}function Ce(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:g,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ne(n,t){return((n=n.x)[0]<0?n[1]-Ea-Aa:Ea-n[1])-((t=t.x)[0]<0?t[1]-Ea-Aa:Ea-t[1])}function Le(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;hc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+Sa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+Sa/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=_>=0?1:-1,w=b*_,S=w>Sa,k=p*x;if(hc.add(Math.atan2(k*b*Math.sin(w),v*M+k*Math.cos(w))),i+=S?_+b*ka:_,S^h>=e^m>=e){var E=fe(se(f),se(n));pe(E);var A=fe(u,E);pe(A);var C=(S^_>=0?-1:1)*X(A[2]);(r>C||r===C&&(E[0]||E[1]))&&(o+=S^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Aa>i||Aa>i&&0>hc)^1&o}function Te(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Sa:-Sa,c=oa(i-e);oa(c-Sa)<Aa?(n.point(e,r=(r+o)/2>0?Ea:-Ea),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=Sa&&(oa(e-u)<Aa&&(e-=u*Aa),oa(i-a)<Aa&&(i-=a*Aa),r=qe(e,r,i,o),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=i,r=o),u=a},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function qe(n,t,e,r){var u,i,o=Math.sin(n-e);return oa(o)>Aa?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function ze(n,t,e,r){var u;if(null==n)u=e*Ea,r.point(-Sa,u),r.point(0,u),r.point(Sa,u),r.point(Sa,0),r.point(Sa,-u),r.point(0,-u),r.point(-Sa,-u),r.point(-Sa,0),r.point(-Sa,u);else if(oa(n[0]-t[0])>Aa){var i=n[0]<t[0]?Sa:-Sa;u=e*i/2,r.point(-i,u),r.point(0,u),r.point(i,u)}else r.point(t[0],t[1])}function Re(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Sa:-Sa),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(de(e,g)||de(p,g))&&(p[0]+=Aa,p[1]+=Aa,v=t(p[0],p[1]))),v!==c)l=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&de(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=se(n),u=se(t),o=[1,0,0],a=fe(r,u),c=le(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=fe(o,a),p=ge(o,f),v=ge(a,h);he(p,v);var d=g,m=le(p,d),y=le(d,d),x=m*m-y*(le(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=ge(d,(-m-M)/y);if(he(_,p),_=ve(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var A=S-w,C=oa(A-Sa)<Aa,N=C||Aa>A;if(!C&&k>E&&(b=k,k=E,E=b),N?C?k+E>0^_[1]<(oa(_[0]-w)<Aa?k:E):k<=_[1]&&_[1]<=E:A>Sa^(w<=_[0]&&_[0]<=S)){var L=ge(d,(-m+M)/y);return he(L,p),[_,ve(L)]}}}function u(t,e){var r=o?n:Sa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=oa(i)>Aa,c=cr(n,6*Na);return Ee(t,e,c,o?[0,-n]:[-Sa,n-Sa])}function De(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function Pe(n,t,e,r){function u(r,u){return oa(r[0]-n)<Aa?u>0?0:3:oa(r[0]-e)<Aa?u>0?2:1:oa(r[1]-t)<Aa?u>0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,s=a[0];c>o;++o)i=a[o],s[1]<=r?i[1]>r&&Z(s,i,n)>0&&++t:i[1]<=r&&Z(s,i,n)<0&&--t,s=i;return 0!==t}function s(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function l(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){l(n,t)&&a.point(n,t)}function h(){N.point=p,d&&d.push(m=[]),S=!0,w=!1,_=b=0/0}function g(){v&&(p(y,x),M&&w&&A.rejoin(),v.push(A.buffer())),N.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Ac,Math.min(Ac,n)),t=Math.max(-Ac,Math.min(Ac,t));var e=l(n,t);if(d&&m.push([n,t]),S)y=n,x=t,M=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:_,y:b},b:{x:n,y:t}};C(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}_=n,b=t,w=e}var v,d,m,y,x,M,_,b,w,S,k,E=a,A=Ce(),C=De(n,t,e,r),N={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=Xo.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),s(null,null,1,a),a.lineEnd()),u&&we(v,i,t,s,a),a.polygonEnd()),v=d=m=null}};return N}}function Ue(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function je(n){var t=0,e=Sa/3,r=nr(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*Sa/180,e=n[1]*Sa/180):[180*(t/Sa),180*(e/Sa)]},u}function He(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,X((i-(n*n+e*e)*u*u)/(2*u))]},e}function Fe(){function n(n,t){Nc+=u*n-r*t,r=n,u=t}var t,e,r,u;Rc.point=function(i,o){Rc.point=n,t=r=i,e=u=o},Rc.lineEnd=function(){n(t,e)}}function Oe(n,t){Lc>n&&(Lc=n),n>qc&&(qc=n),Tc>t&&(Tc=t),t>zc&&(zc=t)}function Ye(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Ie(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Ie(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Ie(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Ze(n,t){dc+=n,mc+=t,++yc}function Ve(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);xc+=o*(t+n)/2,Mc+=o*(e+r)/2,_c+=o,Ze(t=n,e=r)}var t,e;Pc.point=function(r,u){Pc.point=n,Ze(t=r,e=u)}}function Xe(){Pc.point=Ze}function $e(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);xc+=o*(r+n)/2,Mc+=o*(u+t)/2,_c+=o,o=u*n-r*t,bc+=o*(r+n),wc+=o*(u+t),Sc+=3*o,Ze(r=n,u=t)}var t,e,r,u;Pc.point=function(i,o){Pc.point=n,Ze(t=r=i,e=u=o)},Pc.lineEnd=function(){n(t,e)}}function Be(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,ka)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:g};return a}function We(n){function t(n){return(a?r:e)(n)}function e(t){return Ke(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=se([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=oa(oa(w)-1)<Aa||oa(r-h)<Aa?(r+h)/2:Math.atan2(b,_),A=n(E,k),C=A[0],N=A[1],L=C-t,T=N-e,q=x*L-y*T;(q*q/M>i||oa((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,C,N,E,_/=S,b/=S,w,d,m),m.point(C,N),u(C,N,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Na),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function Je(n){var t=We(function(t,e){return n([t*La,e*La])});return function(n){return tr(t(n))}}function Ge(n){this.stream=n}function Ke(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function Qe(n){return nr(function(){return n})()}function nr(n){function t(n){return n=a(n[0]*Na,n[1]*Na),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*La,n[1]*La]}function r(){a=Ue(o=ur(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=We(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Ec,_=bt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=tr(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Ec):Re((b=+n)*Na),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?Pe(n[0][0],n[0][1],n[1][0],n[1][1]):bt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Na,d=n[1]%360*Na,r()):[v*La,d*La]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Na,y=n[1]%360*Na,x=n.length>2?n[2]%360*Na:0,r()):[m*La,y*La,x*La]},Xo.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function tr(n){return Ke(n,function(t,e){n.point(t*Na,e*Na)})}function er(n,t){return[n,t]}function rr(n,t){return[n>Sa?n-ka:-Sa>n?n+ka:n,t]}function ur(n,t,e){return n?t||e?Ue(or(n),ar(t,e)):or(n):t||e?ar(t,e):rr}function ir(n){return function(t,e){return t+=n,[t>Sa?t-ka:-Sa>t?t+ka:t,e]}}function or(n){var t=ir(n);return t.invert=ir(-n),t}function ar(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),X(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),X(l*r-a*u)]},e}function cr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=sr(e,u),i=sr(e,i),(o>0?i>u:u>i)&&(u+=o*ka)):(u=n+o*ka,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=ve([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function sr(n,t){var e=se(t);e[0]-=n,pe(e);var r=V(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Aa)%(2*Math.PI)}function lr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function fr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function hr(n){return n.source}function gr(n){return n.target}function pr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(J(r-t)+u*o*J(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+t*a;return[Math.atan2(u,r)*La,Math.atan2(o,Math.sqrt(r*r+u*u))*La]}:function(){return[n*La,t*La]};return p.distance=h,p}function vr(){function n(n,u){var i=Math.sin(u*=Na),o=Math.cos(u),a=oa((n*=Na)-t),c=Math.cos(a);Uc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;jc.point=function(u,i){t=u*Na,e=Math.sin(i*=Na),r=Math.cos(i),jc.point=n},jc.lineEnd=function(){jc.point=jc.lineEnd=g}}function dr(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function mr(n,t){function e(n,t){var e=oa(oa(t)-Ea)<Aa?0:o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(Sa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=I(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ea]},e):xr}function yr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return oa(u)<Aa?er:(e.invert=function(n,t){var e=i-t;return[Math.atan2(n,e)/u,i-I(u)*Math.sqrt(n*n+e*e)]},e)}function xr(n,t){return[n,Math.log(Math.tan(Sa/4+t/2))]}function Mr(n){var t,e=Qe(n),r=e.scale,u=e.translate,i=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=u.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=i.apply(e,arguments);if(o===e){if(t=null==n){var a=Sa*r(),c=u();i([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function _r(n,t){return[Math.log(Math.tan(Sa/4+t/2)),-n]}function br(n){return n[0]}function wr(n){return n[1]}function Sr(n){for(var t=n.length,e=[0,1],r=2,u=2;t>u;u++){for(;r>1&&Z(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function kr(n,t){return n[0]-t[0]||n[1]-t[1]}function Er(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Ar(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function Cr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Nr(){Jr(this),this.edge=this.site=this.circle=null}function Lr(n){var t=Jc.pop()||new Nr;return t.site=n,t}function Tr(n){Or(n),$c.remove(n),Jc.push(n),Jr(n)}function qr(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Tr(n);for(var c=i;c.circle&&oa(e-c.circle.x)<Aa&&oa(r-c.circle.cy)<Aa;)i=c.P,a.unshift(c),Tr(c),c=i;a.unshift(c),Or(c);for(var s=o;s.circle&&oa(e-s.circle.x)<Aa&&oa(r-s.circle.cy)<Aa;)o=s.N,a.push(s),Tr(s),s=o;a.push(s),Or(s);var l,f=a.length;for(l=1;f>l;++l)s=a[l],c=a[l-1],$r(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=Vr(c.site,s.site,null,u),Fr(c),Fr(s)}function zr(n){for(var t,e,r,u,i=n.x,o=n.y,a=$c._;a;)if(r=Rr(a,o)-i,r>Aa)a=a.L;else{if(u=i-Dr(a,o),!(u>Aa)){r>-Aa?(t=a.P,e=a):u>-Aa?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Lr(n);if($c.insert(t,c),t||e){if(t===e)return Or(t),e=Lr(t.site),$c.insert(c,e),c.edge=e.edge=Vr(t.site,c.site),Fr(t),Fr(e),void 0;if(!e)return c.edge=Vr(t.site,c.site),void 0;Or(t),Or(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};$r(e.edge,s,p,M),c.edge=Vr(s,n,null,M),e.edge=Vr(n,p,null,M),Fr(t),Fr(e)}}function Rr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function Dr(n,t){var e=n.N;if(e)return Rr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Pr(n){this.site=n,this.edges=[]}function Ur(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Xc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(oa(r-t)>Aa||oa(u-e)>Aa)&&(a.splice(o,0,new Br(Xr(i.site,l,oa(r-f)<Aa&&p-u>Aa?{x:f,y:oa(t-f)<Aa?e:p}:oa(u-p)<Aa&&h-r>Aa?{x:oa(e-p)<Aa?t:h,y:p}:oa(r-h)<Aa&&u-g>Aa?{x:h,y:oa(t-h)<Aa?e:g}:oa(u-g)<Aa&&r-f>Aa?{x:oa(e-g)<Aa?t:f,y:g}:null),i.site,null)),++c)}function jr(n,t){return t.angle-n.angle}function Hr(){Jr(this),this.x=this.y=this.arc=this.site=this.cy=null}function Fr(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,u=n.site,i=e.site;if(r!==i){var o=u.x,a=u.y,c=r.x-o,s=r.y-a,l=i.x-o,f=i.y-a,h=2*(c*f-s*l);if(!(h>=-Ca)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=Gc.pop()||new Hr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Wc._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}Wc.insert(y,m),y||(Bc=m)}}}}function Or(n){var t=n.circle;t&&(t.P||(Bc=t.N),Wc.remove(t),Gc.push(t),Jr(t),n.circle=null)}function Yr(n){for(var t,e=Vc,r=De(n[0][0],n[0][1],n[1][0],n[1][1]),u=e.length;u--;)t=e[u],(!Ir(t,n)||!r(t)||oa(t.a.x-t.b.x)<Aa&&oa(t.a.y-t.b.y)<Aa)&&(t.a=t.b=null,e.splice(u,1))}function Ir(n,t){var e=n.b;if(e)return!0;var r,u,i=n.a,o=t[0][0],a=t[1][0],c=t[0][1],s=t[1][1],l=n.l,f=n.r,h=l.x,g=l.y,p=f.x,v=f.y,d=(h+p)/2,m=(g+v)/2;if(v===g){if(o>d||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.y<c)return}else i={x:d,y:s};e={x:d,y:c}}}else if(r=(h-p)/(v-g),u=m-r*d,-1>r||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.y<c)return}else i={x:(s-u)/r,y:s};e={x:(c-u)/r,y:c}}else if(v>g){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.x<o)return}else i={x:a,y:r*a+u};e={x:o,y:r*o+u}}return n.a=i,n.b=e,!0}function Zr(n,t){this.l=n,this.r=t,this.a=this.b=null}function Vr(n,t,e,r){var u=new Zr(n,t);return Vc.push(u),e&&$r(u,n,t,e),r&&$r(u,t,n,r),Xc[n.i].edges.push(new Br(u,n,t)),Xc[t.i].edges.push(new Br(u,t,n)),u}function Xr(n,t,e){var r=new Zr(n,null);return r.a=t,r.b=e,Vc.push(r),r}function $r(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function Br(n,t,e){var r=n.a,u=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(u.x-r.x,r.y-u.y):Math.atan2(r.x-u.x,u.y-r.y)}function Wr(){this._=null}function Jr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function Gr(n,t){var e=t,r=t.R,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function Kr(n,t){var e=t,r=t.L,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function Qr(n){for(;n.L;)n=n.L;return n}function nu(n,t){var e,r,u,i=n.sort(tu).pop();for(Vc=[],Xc=new Array(n.length),$c=new Wr,Wc=new Wr;;)if(u=Bc,i&&(!u||i.y<u.y||i.y===u.y&&i.x<u.x))(i.x!==e||i.y!==r)&&(Xc[i.i]=new Pr(i),zr(i),e=i.x,r=i.y),i=n.pop();else{if(!u)break;qr(u.arc)}t&&(Yr(t),Ur(t));var o={cells:Xc,edges:Vc};return $c=Wc=Vc=Xc=null,o}function tu(n,t){return t.y-n.y||t.x-n.x}function eu(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function ru(n){return n.x}function uu(n){return n.y}function iu(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function ou(n,t,e,r,u,i){if(!n(t,e,r,u,i)){var o=.5*(e+u),a=.5*(r+i),c=t.nodes;c[0]&&ou(n,c[0],e,r,o,a),c[1]&&ou(n,c[1],o,r,u,a),c[2]&&ou(n,c[2],e,a,o,i),c[3]&&ou(n,c[3],o,a,u,i)}}function au(n,t){n=Xo.rgb(n),t=Xo.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+vt(Math.round(e+i*n))+vt(Math.round(r+o*n))+vt(Math.round(u+a*n))}}function cu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=fu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function su(n,t){return t-=n=+n,function(e){return n+t*e}}function lu(n,t){var e,r,u,i,o,a=0,c=0,s=[],l=[];for(n+="",t+="",Qc.lastIndex=0,r=0;e=Qc.exec(t);++r)e.index&&s.push(t.substring(a,c=e.index)),l.push({i:s.length,x:e[0]}),s.push(null),a=Qc.lastIndex;for(a<t.length&&s.push(t.substring(a)),r=0,i=l.length;(e=Qc.exec(n))&&i>r;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=su(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function fu(n,t){for(var e,r=Xo.interpolators.length;--r>=0&&!(e=Xo.interpolators[r](n,t)););return e}function hu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(fu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function gu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function pu(n){return function(t){return 1-n(1-t)}}function vu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function du(n){return n*n}function mu(n){return n*n*n}function yu(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function xu(n){return function(t){return Math.pow(t,n)}}function Mu(n){return 1-Math.cos(n*Ea)}function _u(n){return Math.pow(2,10*(n-1))}function bu(n){return 1-Math.sqrt(1-n*n)}function wu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/ka*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*ka/t)}}function Su(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function ku(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Eu(n,t){n=Xo.hcl(n),t=Xo.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return rt(e+i*n,r+o*n,u+a*n)+""}}function Au(n,t){n=Xo.hsl(n),t=Xo.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return nt(e+i*n,r+o*n,u+a*n)+""}}function Cu(n,t){n=Xo.lab(n),t=Xo.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ot(e+i*n,r+o*n,u+a*n)+""}}function Nu(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Lu(n){var t=[n.a,n.b],e=[n.c,n.d],r=qu(t),u=Tu(t,e),i=qu(zu(e,t,-u))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,u*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*La,this.translate=[n.e,n.f],this.scale=[r,i],this.skew=i?Math.atan2(u,i)*La:0}function Tu(n,t){return n[0]*t[0]+n[1]*t[1]}function qu(n){var t=Math.sqrt(Tu(n,n));return t&&(n[0]/=t,n[1]/=t),t}function zu(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ru(n,t){var e,r=[],u=[],i=Xo.transform(n),o=Xo.transform(t),a=i.translate,c=o.translate,s=i.rotate,l=o.rotate,f=i.skew,h=o.skew,g=i.scale,p=o.scale;return a[0]!=c[0]||a[1]!=c[1]?(r.push("translate(",null,",",null,")"),u.push({i:1,x:su(a[0],c[0])},{i:3,x:su(a[1],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),s!=l?(s-l>180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:su(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:su(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:su(g[0],p[0])},{i:e-2,x:su(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i<e;)r[(t=u[i]).i]=t.x(n);return r.join("")}}function Du(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function Pu(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function Uu(n){for(var t=n.source,e=n.target,r=Hu(t,e),u=[t];t!==r;)t=t.parent,u.push(t);for(var i=u.length;e!==r;)u.splice(i,0,e),e=e.parent;return u}function ju(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Hu(n,t){if(n===t)return n;for(var e=ju(n),r=ju(t),u=e.pop(),i=r.pop(),o=null;u===i;)o=u,u=e.pop(),i=r.pop();return o}function Fu(n){n.fixed|=2}function Ou(n){n.fixed&=-7}function Yu(n){n.fixed|=4,n.px=n.x,n.py=n.y}function Iu(n){n.fixed&=-5}function Zu(n,t,e){var r=0,u=0;if(n.charge=0,!n.leaf)for(var i,o=n.nodes,a=o.length,c=-1;++c<a;)i=o[c],null!=i&&(Zu(i,t,e),n.charge+=i.charge,r+=i.charge*i.cx,u+=i.charge*i.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var s=t*e[n.point.index];n.charge+=n.pointCharge=s,r+=s*n.point.x,u+=s*n.point.y}n.cx=r/n.charge,n.cy=u/n.charge}function Vu(n,t){return Xo.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=Wu,n}function Xu(n){return n.children}function $u(n){return n.value}function Bu(n,t){return t.value-n.value}function Wu(n){return Xo.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function Ju(n){return n.x}function Gu(n){return n.y}function Ku(n,t,e){n.y0=t,n.y=e}function Qu(n){return Xo.range(n.length)}function ni(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function ti(n){for(var t,e=1,r=0,u=n[0][1],i=n.length;i>e;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function ei(n){return n.reduce(ri,0)}function ri(n,t){return n+t[1]}function ui(n,t){return ii(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ii(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function oi(n){return[Xo.min(n),Xo.max(n)]}function ai(n,t){return n.parent==t.parent?1:2}function ci(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function si(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function li(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i<u;)t(r=li(e[i],t),n)>0&&(n=r);return n}function fi(n,t){return n.x-t.x}function hi(n,t){return t.x-n.x}function gi(n,t){return n.depth-t.depth}function pi(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c<o;)i=u[c],e(i,a),a=i;t(n,r)}e(n,null)}function vi(n){for(var t,e=0,r=0,u=n.children,i=u.length;--i>=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function di(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function mi(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function yi(n,t){return n.value-t.value}function xi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Mi(n,t){n._pack_next=t,t._pack_prev=n}function _i(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function bi(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(wi),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],Ei(r,u,i),t(i),xi(r,i),r._pack_prev=i,xi(i,u),u=r._pack_next,o=3;s>o;o++){Ei(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(_i(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!_i(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.r<r.r?Mi(r,u=a):Mi(r=c,u),o--):(xi(r,i),u=i,t(i))}var m=(l+f)/2,y=(h+g)/2,x=0;for(o=0;s>o;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Si)}}function wi(n){n._pack_next=n._pack_prev=n}function Si(n){delete n._pack_next,delete n._pack_prev}function ki(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i<o;)ki(u[i],t,e,r)}function Ei(n,t,e){var r=n.r+e.r,u=t.x-n.x,i=t.y-n.y;if(r&&(u||i)){var o=t.r+e.r,a=u*u+i*i;o*=o,r*=r;var c=.5+(r-o)/(2*a),s=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+c*u+s*i,e.y=n.y+c*i-s*u}else e.x=n.x+r,e.y=n.y}function Ai(n){return 1+Xo.max(n,function(n){return n.y})}function Ci(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Ni(n){var t=n.children;return t&&t.length?Ni(t[0]):n}function Li(n){var t,e=n.children;return e&&(t=e.length)?Li(e[t-1]):n}function Ti(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function qi(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function zi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ri(n){return n.rangeExtent?n.rangeExtent():zi(n.range())}function Di(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Pi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Ui(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ls}function ji(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)u.push(e(n[o-1],n[o])),i.push(r(t[o-1],t[o]));return function(t){var e=Xo.bisect(n,t,1,a)-1;return i[e](u[e](t))}}function Hi(n,t,e,r){function u(){var u=Math.min(n.length,t.length)>2?ji:Di,c=r?Pu:Du;return o=u(n,t,c,e),a=u(t,n,c,fu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Nu)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Ii(n,t)},i.tickFormat=function(t,e){return Zi(n,t,e)},i.nice=function(t){return Oi(n,t),u()},i.copy=function(){return Hi(n,t,e,r)},u()}function Fi(n,t){return Xo.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Oi(n,t){return Pi(n,Ui(Yi(n,t)[2]))}function Yi(n,t){null==t&&(t=10);var e=zi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Ii(n,t){return Xo.range.apply(Xo,Yi(n,t))}function Zi(n,t,e){var r=Yi(n,t);return Xo.format(e?e.replace(Qa,function(n,t,e,u,i,o,a,c,s,l){return[t,e,u,i,o,a,c,s||"."+Xi(l,r),l].join("")}):",."+Vi(r[2])+"f")}function Vi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Xi(n,t){var e=Vi(t[2]);return n in fs?Math.abs(e-Vi(Math.max(Math.abs(t[0]),Math.abs(t[1]))))+ +("e"!==n):e-2*("%"===n)}function $i(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Pi(r.map(u),e?Math:gs);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=zi(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++<l;)for(var h=f-1;h>0;h--)o.push(i(s)*h);for(s=0;o[s]<a;s++);for(l=o.length;o[l-1]>c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return hs;arguments.length<2?t=hs:"function"!=typeof t&&(t=Xo.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return $i(n.copy(),t,e,r)},Fi(o,n)}function Bi(n,t,e){function r(t){return n(u(t))}var u=Wi(t),i=Wi(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Ii(e,n)},r.tickFormat=function(n,t){return Zi(e,n,t)},r.nice=function(n){return r.domain(Oi(e,n))},r.exponent=function(o){return arguments.length?(u=Wi(t=o),i=Wi(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Bi(n.copy(),t,e)},Fi(r,n)}function Wi(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Ji(n,t){function e(e){return o[((i.get(e)||"range"===t.t&&i.set(e,n.push(e)))-1)%o.length]}function r(t,e){return Xo.range(n.length).map(function(n){return t+e*n})}var i,o,a;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new u;for(var o,a=-1,c=r.length;++a<c;)i.has(o=r[a])||i.set(o,n.push(o));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(o=n,a=0,t={t:"range",a:arguments},e):o},e.rangePoints=function(u,i){arguments.length<2&&(i=0);var c=u[0],s=u[1],l=(s-c)/(Math.max(1,n.length-1)+i);return o=r(n.length<2?(c+s)/2:c+l*i/2,l),a=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=(f-l)/(n.length-i+2*c);return o=r(l+h*c,h),s&&o.reverse(),a=h*(1-i),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=Math.floor((f-l)/(n.length-i+2*c)),g=f-l-(n.length-i)*h;return o=r(l+Math.round(g/2),h),s&&o.reverse(),a=Math.round(h*(1-i)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return a},e.rangeExtent=function(){return zi(t.a[0])},e.copy=function(){return Ji(n,t)},e.domain(n)}function Gi(n,t){function e(){var e=0,i=t.length;for(u=[];++e<i;)u[e-1]=Xo.quantile(n,e/i);return r}function r(n){return isNaN(n=+n)?void 0:t[Xo.bisect(u,n)]}var u;return r.domain=function(t){return arguments.length?(n=t.filter(function(n){return!isNaN(n)}).sort(Xo.ascending),e()):n},r.range=function(n){return arguments.length?(t=n,e()):t},r.quantiles=function(){return u},r.invertExtent=function(e){return e=t.indexOf(e),0>e?[0/0,0/0]:[e>0?u[e-1]:n[0],e<u.length?u[e]:n[n.length-1]]},r.copy=function(){return Gi(n,t)},e()}function Ki(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(i*(t-n))))]}function u(){return i=e.length/(t-n),o=e.length-1,r}var i,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],u()):[n,t]},r.range=function(n){return arguments.length?(e=n,u()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return Ki(n,t,e)},u()}function Qi(n,t){function e(e){return e>=e?t[Xo.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return Qi(n,t)},e}function no(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Ii(n,t)},t.tickFormat=function(t,e){return Zi(n,t,e)},t.copy=function(){return no(n)},t}function to(n){return n.innerRadius}function eo(n){return n.outerRadius}function ro(n){return n.startAngle}function uo(n){return n.endAngle}function io(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=_t(e),p=_t(r);++f<h;)u.call(this,c=t[f],f)?l.push([+g.call(this,c,f),+p.call(this,c,f)]):l.length&&(o(),l=[]);return l.length&&o(),s.length?s.join(""):null}var e=br,r=wr,u=be,i=oo,o=i.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(u=n,t):u},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?i=n:(i=Ms.get(n)||oo).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function oo(n){return n.join("L")}function ao(n){return oo(n)+"Z"}function co(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&u.push("H",r[0]),u.join("")}function so(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("V",(r=n[t])[1],"H",r[0]);return u.join("")}function lo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r=n[t])[0],"V",r[1]);return u.join("")}function fo(n,t){return n.length<4?oo(n):n[1]+po(n.slice(1,n.length-1),vo(n,t))}function ho(n,t){return n.length<3?oo(n):n[0]+po((n.push(n[0]),n),vo([n[n.length-2]].concat(n,[n[1]]),t))}function go(n,t){return n.length<3?oo(n):n[0]+po(n,vo(n,t))}function po(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return oo(n);var e=n.length!=t.length,r="",u=n[0],i=n[1],o=t[0],a=o,c=1;if(e&&(r+="Q"+(i[0]-2*o[0]/3)+","+(i[1]-2*o[1]/3)+","+i[0]+","+i[1],u=n[1],c=2),t.length>1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s<t.length;s++,c++)i=n[c],a=t[s],r+="S"+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1]}if(e){var l=n[c];r+="Q"+(i[0]+2*a[0]/3)+","+(i[1]+2*a[1]/3)+","+l[0]+","+l[1]}return r}function vo(n,t){for(var e,r=[],u=(1-t)/2,i=n[0],o=n[1],a=1,c=n.length;++a<c;)e=i,i=o,o=n[a],r.push([u*(o[0]-e[0]),u*(o[1]-e[1])]);return r}function mo(n){if(n.length<3)return oo(n);var t=1,e=n.length,r=n[0],u=r[0],i=r[1],o=[u,u,u,(r=n[1])[0]],a=[i,i,i,r[1]],c=[u,",",i,"L",_o(ws,o),",",_o(ws,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),bo(c,o,a);return n.pop(),c.push("L",r),c.join("")}function yo(n){if(n.length<4)return oo(n);for(var t,e=[],r=-1,u=n.length,i=[0],o=[0];++r<3;)t=n[r],i.push(t[0]),o.push(t[1]);for(e.push(_o(ws,i)+","+_o(ws,o)),--r;++r<u;)t=n[r],i.shift(),i.push(t[0]),o.shift(),o.push(t[1]),bo(e,i,o);return e.join("")}function xo(n){for(var t,e,r=-1,u=n.length,i=u+4,o=[],a=[];++r<4;)e=n[r%u],o.push(e[0]),a.push(e[1]);for(t=[_o(ws,o),",",_o(ws,a)],--r;++r<i;)e=n[r%u],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),bo(t,o,a);return t.join("")}function Mo(n,t){var e=n.length-1;if(e)for(var r,u,i=n[0][0],o=n[0][1],a=n[e][0]-i,c=n[e][1]-o,s=-1;++s<=e;)r=n[s],u=s/e,r[0]=t*r[0]+(1-t)*(i+u*a),r[1]=t*r[1]+(1-t)*(o+u*c);return mo(n)}function _o(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function bo(n,t,e){n.push("C",_o(_s,t),",",_o(_s,e),",",_o(bs,t),",",_o(bs,e),",",_o(ws,t),",",_o(ws,e))}function wo(n,t){return(t[1]-n[1])/(t[0]-n[0])}function So(n){for(var t=0,e=n.length-1,r=[],u=n[0],i=n[1],o=r[0]=wo(u,i);++t<e;)r[t]=(o+(o=wo(u=i,i=n[t+1])))/2;return r[t]=o,r}function ko(n){for(var t,e,r,u,i=[],o=So(n),a=-1,c=n.length-1;++a<c;)t=wo(n[a],n[a+1]),oa(t)<Aa?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,u=e*e+r*r,u>9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function Eo(n){return n.length<3?oo(n):n[0]+po(n,ko(n))}function Ao(n){for(var t,e,r,u=-1,i=n.length;++u<i;)t=n[u],e=t[0],r=t[1]+ys,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Co(n){function t(t){function c(){v.push("M",a(n(m),f),l,s(n(d.reverse()),f),"Z")}for(var h,g,p,v=[],d=[],m=[],y=-1,x=t.length,M=_t(e),_=_t(u),b=e===r?function(){return g}:_t(r),w=u===i?function(){return p}:_t(i);++y<x;)o.call(this,h=t[y],y)?(d.push([g=+M.call(this,h,y),p=+_.call(this,h,y)]),m.push([+b.call(this,h,y),+w.call(this,h,y)])):d.length&&(c(),d=[],m=[]);return d.length&&c(),v.length?v.join(""):null}var e=br,r=br,u=0,i=wr,o=be,a=oo,c=a.key,s=a,l="L",f=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(u=i=n,t):i},t.y0=function(n){return arguments.length?(u=n,t):u},t.y1=function(n){return arguments.length?(i=n,t):i},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?a=n:(a=Ms.get(n)||oo).key,s=a.reverse||a,l=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(f=n,t):f},t}function No(n){return n.radius}function Lo(n){return[n.x,n.y]}function To(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]+ys;return[e*Math.cos(r),e*Math.sin(r)]}}function qo(){return 64}function zo(){return"circle"}function Ro(n){var t=Math.sqrt(n/Sa);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Do(n,t){return fa(n,Ns),n.id=t,n}function Po(n,t,e,r){var u=n.id;return R(n,"function"==typeof e?function(n,i,o){n.__transition__[u].tween.set(t,r(e.call(n,n.__data__,i,o)))}:(e=r(e),function(n){n.__transition__[u].tween.set(t,e)}))}function Uo(n){return null==n&&(n=""),function(){this.textContent=n}}function jo(n,t,e,r){var i=n.__transition__||(n.__transition__={active:0,count:0}),o=i[e];if(!o){var a=r.time;o=i[e]={tween:new u,time:a,ease:r.ease,delay:r.delay,duration:r.duration},++i.count,Xo.timer(function(r){function u(r){return i.active>e?s():(i.active=e,o.event&&o.event.start.call(n,l,t),o.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),Xo.timer(function(){return p.c=c(r||1)?be:c,1},0,a),void 0)}function c(r){if(i.active!==e)return s();for(var u=r/g,a=f(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,l,t),s()):void 0}function s(){return--i.count?delete i[e]:delete n.__transition__,1}var l=n.__data__,f=o.ease,h=o.delay,g=o.duration,p=Ja,v=[];return p.t=h+a,r>=h?u(r-h):(p.c=u,void 0)},0,a)}}function Ho(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function Fo(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function Oo(n){return n.toISOString()}function Yo(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=Xo.bisect(js,u);return i==js.length?[t.year,Yi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/js[i-1]<js[i]/u?i-1:i]:[Os,Yi(n,e)[2]]}return r.invert=function(t){return Io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(Io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,Io(+e+1),t).length}var i=r.domain(),o=zi(i),a=null==n?u(o,10):"number"==typeof n&&u(o,n);return a&&(n=a[0],t=a[1]),r.domain(Pi(i,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=Io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=zi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Yo(n.copy(),t,e)},Fi(r,n)}function Io(n){return new Date(n)}function Zo(n){return JSON.parse(n.responseText)}function Vo(n){var t=Wo.createRange();return t.selectNode(Wo.body),t.createContextualFragment(n.responseText)}var Xo={version:"3.4.3"};Date.now||(Date.now=function(){return+new Date});var $o=[].slice,Bo=function(n){return $o.call(n)},Wo=document,Jo=Wo.documentElement,Go=window;try{Bo(Jo.childNodes)[0].nodeType}catch(Ko){Bo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{Wo.createElement("div").style.setProperty("opacity",0,"")}catch(Qo){var na=Go.Element.prototype,ta=na.setAttribute,ea=na.setAttributeNS,ra=Go.CSSStyleDeclaration.prototype,ua=ra.setProperty;na.setAttribute=function(n,t){ta.call(this,n,t+"")},na.setAttributeNS=function(n,t,e){ea.call(this,n,t,e+"")},ra.setProperty=function(n,t,e){ua.call(this,n,t+"",e)}}Xo.ascending=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},Xo.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},Xo.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&e>r&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&e>r&&(e=r)}return e},Xo.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&r>e&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&r>e&&(e=r)}return e},Xo.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i<o&&!(null!=(e=u=n[i])&&e>=e);)e=u=void 0;for(;++i<o;)null!=(r=n[i])&&(e>r&&(e=r),r>u&&(u=r))}else{for(;++i<o&&!(null!=(e=u=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<o;)null!=(r=t.call(n,n[i],i))&&(e>r&&(e=r),r>u&&(u=r))}return[e,u]},Xo.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i<u;)isNaN(e=+n[i])||(r+=e);else for(;++i<u;)isNaN(e=+t.call(n,n[i],i))||(r+=e);return r},Xo.mean=function(t,e){var r,u=t.length,i=0,o=-1,a=0;if(1===arguments.length)for(;++o<u;)n(r=t[o])&&(i+=(r-i)/++a);else for(;++o<u;)n(r=e.call(t,t[o],o))&&(i+=(r-i)/++a);return a?i:void 0},Xo.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),u=+n[r-1],i=e-r;return i?u+i*(n[r]-u):u},Xo.median=function(t,e){return arguments.length>1&&(t=t.map(e)),t=t.filter(n),t.length?Xo.quantile(t.sort(Xo.ascending),.5):void 0},Xo.bisector=function(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n.call(t,t[i],i)<e?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;e<n.call(t,t[i],i)?u=i:r=i+1}return r}}};var ia=Xo.bisector(function(n){return n});Xo.bisectLeft=ia.left,Xo.bisect=Xo.bisectRight=ia.right,Xo.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},Xo.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},Xo.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},Xo.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,e=Xo.min(arguments,t),r=new Array(e);++n<e;)for(var u,i=-1,o=r[n]=new Array(u);++i<u;)o[i]=arguments[i][n];return r},Xo.transpose=function(n){return Xo.zip.apply(Xo,n)},Xo.keys=function(n){var t=[];for(var e in n)t.push(e);return t},Xo.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},Xo.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Xo.merge=function(n){for(var t,e,r,u=n.length,i=-1,o=0;++i<u;)o+=n[i].length;for(e=new Array(o);--u>=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var oa=Math.abs;Xo.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var u,i=[],o=e(oa(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(u=n+r*++a)>t;)i.push(u/o);else for(;(u=n+r*++a)<t;)i.push(u/o);return i},Xo.map=function(n){var t=new u;if(n instanceof u)n.forEach(function(n,e){t.set(n,e)});else for(var e in n)t.set(e,n[e]);return t},r(u,{has:i,get:function(n){return this[aa+n]},set:function(n,t){return this[aa+n]=t},remove:o,keys:a,values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1),this[t])}});var aa="\x00",ca=aa.charCodeAt(0);Xo.nest=function(){function n(t,a,c){if(c>=o.length)return r?r.call(i,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=o[c++],d=new u;++g<p;)(h=d.get(s=v(l=a[g])))?h.push(l):d.set(s,[l]);return t?(l=t(),f=function(e,r){l.set(e,n(t,r,c))}):(l={},f=function(e,r){l[e]=n(t,r,c)}),d.forEach(f),l}function t(n,e){if(e>=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(Xo.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},Xo.set=function(n){var t=new l;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(l,{has:i,add:function(n){return this[aa+n]=!0,n},remove:function(n){return n=aa+n,n in this&&delete this[n]},values:a,size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1))}}),Xo.behavior={},Xo.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r<u;)n[e=arguments[r]]=f(n,t,t[e]);return n};var sa=["webkit","ms","moz","Moz","o","O"];Xo.dispatch=function(){for(var n=new p,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=v(n);return n},p.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Xo.event=null,Xo.requote=function(n){return n.replace(la,"\\$&")};var la=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,fa={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ha=function(n,t){return t.querySelector(n)},ga=function(n,t){return t.querySelectorAll(n)},pa=Jo[h(Jo,"matchesSelector")],va=function(n,t){return pa.call(n,t)};"function"==typeof Sizzle&&(ha=function(n,t){return Sizzle(n,t)[0]||null},ga=Sizzle,va=Sizzle.matchesSelector),Xo.selection=function(){return xa};var da=Xo.selection.prototype=[];da.select=function(n){var t,e,r,u,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var c=-1,s=r.length;++c<s;)(u=r[c])?(t.push(e=n.call(u,u.__data__,c,o)),e&&"__data__"in u&&(e.__data__=u.__data__)):t.push(null)}return x(i)},da.selectAll=function(n){var t,e,r=[];n=_(n);for(var u=-1,i=this.length;++u<i;)for(var o=this[u],a=-1,c=o.length;++a<c;)(e=o[a])&&(r.push(t=Bo(n.call(e,e.__data__,a,u))),t.parentNode=e);return x(r)};var ma={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};Xo.ns={prefix:ma,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),ma.hasOwnProperty(e)?{space:ma[e],local:n}:n}},da.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Xo.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(b(t,n[t]));return this}return this.each(b(n,t))},da.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=k(n)).length,u=-1;if(t=e.classList){for(;++u<r;)if(!t.contains(n[u]))return!1}else for(t=e.getAttribute("class");++u<r;)if(!S(n[u]).test(t))return!1;return!0}for(t in n)this.each(E(t,n[t]));return this}return this.each(E(n,t))},da.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(C(e,n[e],t));return this}if(2>r)return Go.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(C(n,t,e))},da.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(N(t,n[t]));return this}return this.each(N(n,t))},da.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},da.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},da.append=function(n){return n=L(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},da.insert=function(n,t){return n=L(n),t=M(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},da.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},da.data=function(n,t){function e(n,e){var r,i,o,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new u,y=new u,x=[];for(r=-1;++r<a;)d=t.call(i=n[r],i.__data__,r),m.has(d)?v[r]=i:m.set(d,i),x.push(d);for(r=-1;++r<f;)d=t.call(e,o=e[r],r),(i=m.get(d))?(g[r]=i,i.__data__=o):y.has(d)||(p[r]=T(o)),y.set(d,o),m.remove(d);for(r=-1;++r<a;)m.has(x[r])&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],o=e[r],i?(i.__data__=o,g[r]=i):p[r]=T(o);for(;f>r;++r)p[r]=T(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),s.push(g),l.push(v)}var r,i,o=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++o<a;)(i=r[o])&&(n[o]=i.__data__);return n}var c=D([]),s=x([]),l=x([]);if("function"==typeof n)for(;++o<a;)e(r=this[o],n.call(r,r.parentNode.__data__,o));else for(;++o<a;)e(r=this[o],n);return s.enter=function(){return c},s.exit=function(){return l},s},da.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},da.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return x(u)},da.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],u=r.length-1,i=r[u];--u>=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},da.sort=function(n){n=z.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},da.each=function(n){return R(this,function(t,e,r){n.call(t,t.__data__,e,r)})},da.call=function(n){var t=Bo(arguments);return n.apply(t[0]=this,t),this},da.empty=function(){return!this.node()},da.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},da.size=function(){var n=0;return this.each(function(){++n}),n};var ya=[];Xo.selection.enter=D,Xo.selection.enter.prototype=ya,ya.append=da.append,ya.empty=da.empty,ya.node=da.node,ya.call=da.call,ya.size=da.size,ya.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++a<c;){r=(u=this[a]).update,o.push(t=[]),t.parentNode=u.parentNode;for(var s=-1,l=u.length;++s<l;)(i=u[s])?(t.push(r[s]=e=n.call(u.parentNode,i.__data__,s,a)),e.__data__=i.__data__):t.push(null)}return x(o)},ya.insert=function(n,t){return arguments.length<2&&(t=P(this)),da.insert.call(this,n,t)},da.transition=function(){for(var n,t,e=ks||++Ls,r=[],u=Es||{time:Date.now(),ease:yu,delay:0,duration:250},i=-1,o=this.length;++i<o;){r.push(n=[]);for(var a=this[i],c=-1,s=a.length;++c<s;)(t=a[c])&&jo(t,c,e,u),n.push(t)}return Do(r,e)},da.interrupt=function(){return this.each(U)},Xo.select=function(n){var t=["string"==typeof n?ha(n,Wo):n];return t.parentNode=Jo,x([t])},Xo.selectAll=function(n){var t=Bo("string"==typeof n?ga(n,Wo):n);return t.parentNode=Jo,x([t])};var xa=Xo.select(Jo);da.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(j(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(j(n,t,e))};var Ma=Xo.map({mouseenter:"mouseover",mouseleave:"mouseout"});Ma.forEach(function(n){"on"+n in Wo&&Ma.remove(n)});var _a="onselectstart"in Wo?null:h(Jo.style,"userSelect"),ba=0;Xo.mouse=function(n){return Y(n,m())};var wa=/WebKit/.test(Go.navigator.userAgent)?-1:0;Xo.touches=function(n,t){return arguments.length<2&&(t=m().touches),t?Bo(t).map(function(t){var e=Y(n,t);return e.identifier=t.identifier,e}):[]},Xo.behavior.drag=function(){function n(){this.on("mousedown.drag",o).on("touchstart.drag",a)}function t(){return Xo.event.changedTouches[0].identifier}function e(n,t){return Xo.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function o(){var n=t(l,g),e=n[0]-v[0],r=n[1]-v[1];d|=e|r,v=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function a(){m.on(e+"."+p,null).on(r+"."+p,null),y(d&&Xo.event.target===h),f({type:"dragend"})}var c,s=this,l=s.parentNode,f=u.of(s,arguments),h=Xo.event.target,g=n(),p=null==g?"drag":"drag-"+g,v=t(l,g),d=0,m=Xo.select(Go).on(e+"."+p,o).on(r+"."+p,a),y=O();i?(c=i.apply(s,arguments),c=[c.x-v[0],c.y-v[1]]):c=[0,0],f({type:"dragstart"})}}var u=y(n,"drag","dragstart","dragend"),i=null,o=r(g,Xo.mouse,"mousemove","mouseup"),a=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},Xo.rebind(n,u,"on")};var Sa=Math.PI,ka=2*Sa,Ea=Sa/2,Aa=1e-6,Ca=Aa*Aa,Na=Sa/180,La=180/Sa,Ta=Math.SQRT2,qa=2,za=4;Xo.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=B(v),o=i/(qa*h)*(e*W(Ta*t+v)-$(v));return[r+o*s,u+o*l,i*e/B(Ta*t+v)]}return[r+n*s,u+n*l,i*Math.exp(Ta*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+za*f)/(2*i*qa*h),p=(c*c-i*i-za*f)/(2*c*qa*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ta;return e.duration=1e3*y,e},Xo.behavior.zoom=function(){function n(n){n.on(A,s).on(Pa+".zoom",f).on(C,h).on("dblclick.zoom",g).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(M.range().map(function(n){return(n-S.x)/S.k}).map(M.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u(Xo.mouse(r),g),a(i)}function e(){f.on(C,Go===r?h:null).on(N,null),p(l&&Xo.event.target===s),c(i)}var r=this,i=T.of(r,arguments),s=Xo.event.target,l=0,f=Xo.select(Go).on(C,n).on(N,e),g=t(Xo.mouse(r)),p=O();U.call(r),o(i)}function l(){function n(){var n=Xo.touches(g);return h=S.k,n.forEach(function(n){n.identifier in v&&(v[n.identifier]=t(n))}),n}function e(){for(var t=Xo.event.changedTouches,e=0,i=t.length;i>e;++e)v[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-x){var s=o[0],l=v[s.identifier];r(2*S.k),u(s,l),d(),a(p)}x=c}else if(o.length>1){var s=o[0],f=o[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function i(){for(var n,t,e,i,o=Xo.touches(g),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=v[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=m&&Math.sqrt(l/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*h)}x=null,u(n,t),a(p)}function f(){if(Xo.event.touches.length){for(var t=Xo.event.changedTouches,e=0,r=t.length;r>e;++e)delete v[t[e].identifier];for(var u in v)return void n()}b.on(M,null).on(_,null),w.on(A,s).on(L,l),k(),c(p)}var h,g=this,p=T.of(g,arguments),v={},m=0,y=Xo.event.changedTouches[0].identifier,M="touchmove.zoom-"+y,_="touchend.zoom-"+y,b=Xo.select(Go).on(M,i).on(_,f),w=Xo.select(g).on(A,null).on(L,e),k=O();U.call(g),e(),o(p)}function f(){var n=T.of(this,arguments);m?clearTimeout(m):(U.call(this),o(n)),m=setTimeout(function(){m=null,c(n)},50),d();var e=v||Xo.mouse(this);p||(p=t(e)),r(Math.pow(2,.002*Ra())*S.k),u(e,p),a(n)}function h(){p=null}function g(){var n=T.of(this,arguments),e=Xo.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,Xo.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var p,v,m,x,M,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Da,A="mousedown.zoom",C="mousemove.zoom",N="mouseup.zoom",L="touchstart.zoom",T=y(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=T.of(this,arguments),t=S;ks?Xo.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=Xo.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Da:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(v=t&&[+t[0],+t[1]],n):v},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,M=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},Xo.rebind(n,T,"on")};var Ra,Da=[0,1/0],Pa="onwheel"in Wo?(Ra=function(){return-Xo.event.deltaY*(Xo.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Wo?(Ra=function(){return Xo.event.wheelDelta},"mousewheel"):(Ra=function(){return-Xo.event.detail},"MozMousePixelScroll");G.prototype.toString=function(){return this.rgb()+""},Xo.hsl=function(n,t,e){return 1===arguments.length?n instanceof Q?K(n.h,n.s,n.l):dt(""+n,mt,K):K(+n,+t,+e)};var Ua=Q.prototype=new G;Ua.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,this.l/n)},Ua.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,n*this.l)},Ua.rgb=function(){return nt(this.h,this.s,this.l)},Xo.hcl=function(n,t,e){return 1===arguments.length?n instanceof et?tt(n.h,n.c,n.l):n instanceof it?at(n.l,n.a,n.b):at((n=yt((n=Xo.rgb(n)).r,n.g,n.b)).l,n.a,n.b):tt(+n,+t,+e)};var ja=et.prototype=new G;ja.brighter=function(n){return tt(this.h,this.c,Math.min(100,this.l+Ha*(arguments.length?n:1)))},ja.darker=function(n){return tt(this.h,this.c,Math.max(0,this.l-Ha*(arguments.length?n:1)))},ja.rgb=function(){return rt(this.h,this.c,this.l).rgb()},Xo.lab=function(n,t,e){return 1===arguments.length?n instanceof it?ut(n.l,n.a,n.b):n instanceof et?rt(n.l,n.c,n.h):yt((n=Xo.rgb(n)).r,n.g,n.b):ut(+n,+t,+e)};var Ha=18,Fa=.95047,Oa=1,Ya=1.08883,Ia=it.prototype=new G;Ia.brighter=function(n){return ut(Math.min(100,this.l+Ha*(arguments.length?n:1)),this.a,this.b)},Ia.darker=function(n){return ut(Math.max(0,this.l-Ha*(arguments.length?n:1)),this.a,this.b)},Ia.rgb=function(){return ot(this.l,this.a,this.b)},Xo.rgb=function(n,t,e){return 1===arguments.length?n instanceof pt?gt(n.r,n.g,n.b):dt(""+n,gt,nt):gt(~~n,~~t,~~e)};var Za=pt.prototype=new G;Za.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),gt(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):gt(u,u,u)},Za.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),gt(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Za.hsl=function(){return mt(this.r,this.g,this.b)},Za.toString=function(){return"#"+vt(this.r)+vt(this.g)+vt(this.b)};var Va=Xo.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Va.forEach(function(n,t){Va.set(n,ft(t))}),Xo.functor=_t,Xo.xhr=wt(bt),Xo.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=St(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=s)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++<s;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}l=e+2;var r=n.charCodeAt(e+1);return 13===r?(u=!0,10===n.charCodeAt(e+2)&&++l):10===r&&(u=!0),n.substring(t+1,e).replace(/""/g,'"')}for(;s>l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==c)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],s=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new l,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},Xo.csv=Xo.dsv(",","text/csv"),Xo.tsv=Xo.dsv(" ","text/tab-separated-values");var Xa,$a,Ba,Wa,Ja,Ga=Go[h(Go,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Xo.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};$a?$a.n=i:Xa=i,$a=i,Ba||(Wa=clearTimeout(Wa),Ba=1,Ga(Et))},Xo.timer.flush=function(){At(),Ct()},Xo.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var Ka=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Lt);Xo.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Xo.round(n,Nt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),Ka[8+e/3]};var Qa=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,nc=Xo.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Xo.round(n,Nt(n,t))).toFixed(Math.max(0,Math.min(20,Nt(n*(1+1e-15),t))))}}),tc=Xo.time={},ec=Date;zt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){rc.setUTCDate.apply(this._,arguments)},setDay:function(){rc.setUTCDay.apply(this._,arguments)},setFullYear:function(){rc.setUTCFullYear.apply(this._,arguments)},setHours:function(){rc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){rc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){rc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){rc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){rc.setUTCSeconds.apply(this._,arguments)},setTime:function(){rc.setTime.apply(this._,arguments)}};var rc=Date.prototype;tc.year=Rt(function(n){return n=tc.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),tc.years=tc.year.range,tc.years.utc=tc.year.utc.range,tc.day=Rt(function(n){var t=new ec(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),tc.days=tc.day.range,tc.days.utc=tc.day.utc.range,tc.dayOfYear=function(n){var t=tc.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=tc[n]=Rt(function(n){return(n=tc.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});tc[n+"s"]=e.range,tc[n+"s"].utc=e.utc.range,tc[n+"OfYear"]=function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)}}),tc.week=tc.sunday,tc.weeks=tc.sunday.range,tc.weeks.utc=tc.sunday.utc.range,tc.weekOfYear=tc.sundayOfYear;var uc={"-":"",_:" ",0:"0"},ic=/^\s*\d+/,oc=/^%/;Xo.locale=function(n){return{numberFormat:Tt(n),timeFormat:Pt(n)}};var ac=Xo.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Xo.format=ac.numberFormat,Xo.geo={},re.prototype={s:0,t:0,add:function(n){ue(n,this.t,cc),ue(cc.s,this.s,this),this.s?this.t+=cc.t:this.s=cc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cc=new re;Xo.geo.stream=function(n,t){n&&sc.hasOwnProperty(n.type)?sc[n.type](n,t):ie(n,t)};var sc={Feature:function(n,t){ie(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++r<u;)ie(e[r].geometry,t)}},lc={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){oe(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)oe(e[r],t,0)},Polygon:function(n,t){ae(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)ae(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,u=e.length;++r<u;)ie(e[r],t)}};Xo.geo.area=function(n){return fc=0,Xo.geo.stream(n,gc),fc};var fc,hc=new re,gc={sphere:function(){fc+=4*Sa},point:g,lineStart:g,lineEnd:g,polygonStart:function(){hc.reset(),gc.lineStart=ce},polygonEnd:function(){var n=2*hc;fc+=0>n?4*Sa+n:n,gc.lineStart=gc.lineEnd=gc.point=g}};Xo.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=se([t*Na,e*Na]);if(m){var u=fe(m,r),i=[u[1],-u[0],0],o=fe(i,u);pe(o),o=ve(o);var c=t-p,s=c>0?1:-1,v=o[0]*La*s,d=oa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*La;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*La;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=oa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;gc.point(n,e),t(n,e)}function i(){gc.lineStart()}function o(){u(v,d),gc.lineEnd(),oa(y)>Aa&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var l,f,h,g,p,v,d,m,y,x,M,_={point:n,lineStart:e,lineEnd:r,polygonStart:function(){_.point=u,_.lineStart=i,_.lineEnd=o,y=0,gc.polygonStart()},polygonEnd:function(){gc.polygonEnd(),_.point=n,_.lineStart=e,_.lineEnd=r,0>hc?(l=-(h=180),f=-(g=90)):y>Aa?g=90:-Aa>y&&(f=-90),M[0]=l,M[1]=h}};return function(n){g=h=-(l=f=1/0),x=[],Xo.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),Xo.geo.centroid=function(n){pc=vc=dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,kc);var t=bc,e=wc,r=Sc,u=t*t+e*e+r*r;return Ca>u&&(t=xc,e=Mc,r=_c,Aa>vc&&(t=dc,e=mc,r=yc),u=t*t+e*e+r*r,Ca>u)?[0/0,0/0]:[Math.atan2(e,t)*La,X(r/Math.sqrt(u))*La]};var pc,vc,dc,mc,yc,xc,Mc,_c,bc,wc,Sc,kc={sphere:g,point:me,lineStart:xe,lineEnd:Me,polygonStart:function(){kc.lineStart=_e},polygonEnd:function(){kc.lineStart=xe}},Ec=Ee(be,Te,ze,[-Sa,-Sa/2]),Ac=1e9;Xo.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Pe(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(Xo.geo.conicEqualArea=function(){return je(He)}).raw=He,Xo.geo.albers=function(){return Xo.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Xo.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=Xo.geo.albers(),o=Xo.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=Xo.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Aa,f+.12*s+Aa],[l-.214*s-Aa,f+.234*s-Aa]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Aa,f+.166*s+Aa],[l-.115*s-Aa,f+.234*s-Aa]]).stream(c).point,n},n.scale(1070)};var Cc,Nc,Lc,Tc,qc,zc,Rc={point:g,lineStart:g,lineEnd:g,polygonStart:function(){Nc=0,Rc.lineStart=Fe},polygonEnd:function(){Rc.lineStart=Rc.lineEnd=Rc.point=g,Cc+=oa(Nc/2)}},Dc={point:Oe,lineStart:g,lineEnd:g,polygonStart:g,polygonEnd:g},Pc={point:Ze,lineStart:Ve,lineEnd:Xe,polygonStart:function(){Pc.lineStart=$e},polygonEnd:function(){Pc.point=Ze,Pc.lineStart=Ve,Pc.lineEnd=Xe}};Xo.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),Xo.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Cc=0,Xo.geo.stream(n,u(Rc)),Cc},n.centroid=function(n){return dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,u(Pc)),Sc?[bc/Sc,wc/Sc]:_c?[xc/_c,Mc/_c]:yc?[dc/yc,mc/yc]:[0/0,0/0]},n.bounds=function(n){return qc=zc=-(Lc=Tc=1/0),Xo.geo.stream(n,u(Dc)),[[Lc,Tc],[qc,zc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||Je(n):bt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new Ye:new Be(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(Xo.geo.albersUsa()).context(null)},Xo.geo.transform=function(n){return{stream:function(t){var e=new Ge(t);for(var r in n)e[r]=n[r];return e}}},Ge.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Xo.geo.projection=Qe,Xo.geo.projectionMutator=nr,(Xo.geo.equirectangular=function(){return Qe(er)}).raw=er.invert=er,Xo.geo.rotation=function(n){function t(t){return t=n(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t}return n=ur(n[0]%360*Na,n[1]*Na,n.length>2?n[2]*Na:0),t.invert=function(t){return t=n.invert(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t},t},rr.invert=er,Xo.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=ur(-n[0]*Na,-n[1]*Na,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=La,n[1]*=La}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=cr((t=+r)*Na,u*Na),n):t},n.precision=function(r){return arguments.length?(e=cr(t*Na,(u=+r)*Na),n):u},n.angle(90)},Xo.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Na,u=n[1]*Na,i=t[1]*Na,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},Xo.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return Xo.range(Math.ceil(i/d)*d,u,d).map(h).concat(Xo.range(Math.ceil(s/m)*m,c,m).map(g)).concat(Xo.range(Math.ceil(r/p)*p,e,p).filter(function(n){return oa(n%d)>Aa}).map(l)).concat(Xo.range(Math.ceil(a/v)*v,o,v).filter(function(n){return oa(n%m)>Aa}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=lr(a,o,90),f=fr(r,e,y),h=lr(s,c,90),g=fr(i,u,y),n):y},n.majorExtent([[-180,-90+Aa],[180,90-Aa]]).minorExtent([[-180,-80-Aa],[180,80+Aa]])},Xo.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=hr,u=gr;return n.distance=function(){return Xo.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},Xo.geo.interpolate=function(n,t){return pr(n[0]*Na,n[1]*Na,t[0]*Na,t[1]*Na)},Xo.geo.length=function(n){return Uc=0,Xo.geo.stream(n,jc),Uc};var Uc,jc={sphere:g,point:g,lineStart:vr,lineEnd:g,polygonStart:g,polygonEnd:g},Hc=dr(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(Xo.geo.azimuthalEqualArea=function(){return Qe(Hc)}).raw=Hc;var Fc=dr(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},bt);(Xo.geo.azimuthalEquidistant=function(){return Qe(Fc)}).raw=Fc,(Xo.geo.conicConformal=function(){return je(mr)}).raw=mr,(Xo.geo.conicEquidistant=function(){return je(yr)}).raw=yr;var Oc=dr(function(n){return 1/n},Math.atan);(Xo.geo.gnomonic=function(){return Qe(Oc)}).raw=Oc,xr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ea]},(Xo.geo.mercator=function(){return Mr(xr)}).raw=xr;var Yc=dr(function(){return 1},Math.asin);(Xo.geo.orthographic=function(){return Qe(Yc)}).raw=Yc;var Ic=dr(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(Xo.geo.stereographic=function(){return Qe(Ic)}).raw=Ic,_r.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ea]},(Xo.geo.transverseMercator=function(){var n=Mr(_r),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=_r,Xo.geom={},Xo.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=_t(e),i=_t(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(kr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var s=Sr(a),l=Sr(c),f=l[0]===s[0],h=l[l.length-1]===s[s.length-1],g=[];for(t=s.length-1;t>=0;--t)g.push(n[a[s[t]][2]]);for(t=+f;t<l.length-h;++t)g.push(n[a[l[t]][2]]);return g}var e=br,r=wr;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},Xo.geom.polygon=function(n){return fa(n,Zc),n};var Zc=Xo.geom.polygon.prototype=[];Zc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t<e;)n=r,r=this[t],u+=n[1]*r[0]-n[0]*r[1];return.5*u},Zc.centroid=function(n){var t,e,r=-1,u=this.length,i=0,o=0,a=this[u-1];for(arguments.length||(n=-1/(6*this.area()));++r<u;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],i+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[i*n,o*n]},Zc.clip=function(n){for(var t,e,r,u,i,o,a=Cr(n),c=-1,s=this.length-Cr(this),l=this[s-1];++c<s;){for(t=n.slice(),n.length=0,u=this[c],i=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Er(o,l,u)?(Er(i,l,u)||n.push(Ar(i,o,l,u)),n.push(o)):Er(i,l,u)&&n.push(Ar(i,o,l,u)),i=o;a&&n.push(n[0]),l=u}return n};var Vc,Xc,$c,Bc,Wc,Jc=[],Gc=[];Pr.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(jr),t.length},Br.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},Wr.prototype={insert:function(n,t){var e,r,u;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=Qr(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(u=r.R,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.R&&(Gr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Kr(this,r))):(u=r.L,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.L&&(Kr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Gr(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,u=n.U,i=n.L,o=n.R;if(e=i?o?Qr(o):i:o,u?u.L===n?u.L=e:u.R=e:this._=e,i&&o?(r=e.C,e.C=n.C,e.L=i,i.U=e,e!==o?(u=e.U,e.U=n.U,n=e.R,u.L=n,e.R=o,o.U=e):(e.U=u,u=e,n=e.R)):(r=n.C,n=e),n&&(n.U=u),!r){if(n&&n.C)return n.C=!1,void 0;do{if(n===this._)break;if(n===u.L){if(t=u.R,t.C&&(t.C=!1,u.C=!0,Gr(this,u),t=u.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,Kr(this,t),t=u.R),t.C=u.C,u.C=t.R.C=!1,Gr(this,u),n=this._;break}}else if(t=u.L,t.C&&(t.C=!1,u.C=!0,Kr(this,u),t=u.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,Gr(this,t),t=u.L),t.C=u.C,u.C=t.L.C=!1,Kr(this,u),n=this._;break}t.C=!0,n=u,u=u.U}while(!n.C);n&&(n.C=!1)}}},Xo.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],u=a[0][1],i=a[1][0],o=a[1][1];return nu(e(n),a).cells.forEach(function(e,a){var c=e.edges,s=e.site,l=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):s.x>=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Aa)*Aa,y:Math.round(o(n,t)/Aa)*Aa,i:t}})}var r=br,u=wr,i=r,o=u,a=Kc;return n?t(n):(t.links=function(n){return nu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return nu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(jr),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c<s;)u=l,i=f,l=a[c].edge,f=l.l===o?l.r:l.l,r<i.i&&r<f.i&&eu(o,i,f)<0&&t.push([n[r],n[i.i],n[f.i]])}),t},t.x=function(n){return arguments.length?(i=_t(r=n),t):r},t.y=function(n){return arguments.length?(o=_t(u=n),t):u},t.clipExtent=function(n){return arguments.length?(a=null==n?Kc:n,t):a===Kc?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===Kc?null:a&&a[1]},t)};var Kc=[[-1e6,-1e6],[1e6,1e6]];Xo.geom.delaunay=function(n){return Xo.geom.voronoi().triangles(n)},Xo.geom.quadtree=function(n,t,e,r,u){function i(n){function i(n,t,e,r,u,i,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,l=n.y;if(null!=c)if(oa(c-e)+oa(l-r)<.01)s(n,t,e,r,u,i,o,a);else{var f=n.point;n.x=n.y=n.point=null,s(n,f,c,l,u,i,o,a),s(n,t,e,r,u,i,o,a)}else n.x=e,n.y=r,n.point=t}else s(n,t,e,r,u,i,o,a)}function s(n,t,e,r,u,o,a,c){var s=.5*(u+a),l=.5*(o+c),f=e>=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=iu()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=_t(a),M=_t(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.x<v&&(v=l.x),l.y<d&&(d=l.y),l.x>m&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g],g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=iu();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){ou(n,k,v,d,m,y)},g=-1,null==t){for(;++g<p;)i(k,n[g],f[g],h[g],v,d,m,y);--g}else n.forEach(k.add);return f=h=n=l=null,k}var o,a=br,c=wr;return(o=arguments.length)?(a=ru,c=uu,3===o&&(u=e,r=t,e=t=0),i(n)):(i.x=function(n){return arguments.length?(a=n,i):a},i.y=function(n){return arguments.length?(c=n,i):c},i.extent=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],u=+n[1][1]),i):null==t?null:[[t,e],[r,u]]},i.size=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=e=0,r=+n[0],u=+n[1]),i):null==t?null:[r-t,u-e]},i)},Xo.interpolateRgb=au,Xo.interpolateObject=cu,Xo.interpolateNumber=su,Xo.interpolateString=lu;var Qc=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;Xo.interpolate=fu,Xo.interpolators=[function(n,t){var e=typeof t;return("string"===e?Va.has(t)||/^(#|rgb\(|hsl\()/.test(t)?au:lu:t instanceof G?au:"object"===e?Array.isArray(t)?hu:cu:su)(n,t)}],Xo.interpolateArray=hu;var ns=function(){return bt},ts=Xo.map({linear:ns,poly:xu,quad:function(){return du},cubic:function(){return mu},sin:function(){return Mu},exp:function(){return _u},circle:function(){return bu},elastic:wu,back:Su,bounce:function(){return ku}}),es=Xo.map({"in":bt,out:pu,"in-out":vu,"out-in":function(n){return vu(pu(n))}});Xo.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=ts.get(e)||ns,r=es.get(r)||bt,gu(r(e.apply(null,$o.call(arguments,1))))},Xo.interpolateHcl=Eu,Xo.interpolateHsl=Au,Xo.interpolateLab=Cu,Xo.interpolateRound=Nu,Xo.transform=function(n){var t=Wo.createElementNS(Xo.ns.prefix.svg,"g");return(Xo.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Lu(e?e.matrix:rs)})(n)},Lu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var rs={a:1,b:0,c:0,d:1,e:0,f:0};Xo.interpolateTransform=Ru,Xo.layout={},Xo.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Uu(n[e]));return t}},Xo.layout.chord=function(){function n(){var n,s,f,h,g,p={},v=[],d=Xo.range(i),m=[];for(e=[],r=[],n=0,h=-1;++h<i;){for(s=0,g=-1;++g<i;)s+=u[h][g];v.push(s),m.push(Xo.range(i)),n+=s}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&m.forEach(function(n,t){n.sort(function(n,e){return a(u[t][n],u[t][e])})}),n=(ka-l*i)/n,s=0,h=-1;++h<i;){for(f=s,g=-1;++g<i;){var y=d[h],x=m[y][g],M=u[y][x],_=s,b=s+=M*n;p[y+"-"+x]={index:y,subindex:x,startAngle:_,endAngle:b,value:M}}r[y]={index:y,startAngle:f,endAngle:s,value:(s-f)/n},s+=l}for(h=-1;++h<i;)for(g=h-1;++g<i;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,u,i,o,a,c,s={},l=0;return s.matrix=function(n){return arguments.length?(i=(u=n)&&u.length,e=r=null,s):u},s.padding=function(n){return arguments.length?(l=n,e=r=null,s):l},s.sortGroups=function(n){return arguments.length?(o=n,e=r=null,s):o},s.sortSubgroups=function(n){return arguments.length?(a=n,e=null,s):a},s.sortChords=function(n){return arguments.length?(c=n,e&&t(),s):c},s.chords=function(){return e||n(),e},s.groups=function(){return r||n(),r},s},Xo.layout.force=function(){function n(n){return function(t,e,r,u){if(t.point!==n){var i=t.cx-n.x,o=t.cy-n.y,a=u-e,c=i*i+o*o;if(c>a*a/d){if(p>c){var s=t.charge/c;n.px-=i*s,n.py-=o*s}return!0}if(t.point&&c&&p>c){var s=t.pointCharge/c;n.px-=i*s,n.py-=o*s}}return!t.charge}}function t(n){n.px=Xo.event.x,n.py=Xo.event.y,a.resume()}var e,r,u,i,o,a={},c=Xo.dispatch("start","tick","end"),s=[1,1],l=.9,f=us,h=is,g=-30,p=os,v=.1,d=.64,m=[],y=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,x,M,_=m.length,b=y.length;for(e=0;b>e;++e)a=y[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(p=x*x+M*M)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,x*=p,M*=p,h.x-=x*(d=f.weight/(h.weight+f.weight)),h.y-=M*d,f.x+=x*(d=1-d),f.y+=M*d);if((d=r*v)&&(x=s[0]/2,M=s[1]/2,e=-1,d))for(;++e<_;)a=m[e],a.x+=(x-a.x)*d,a.y+=(M-a.y)*d;if(g)for(Zu(t=Xo.geom.quadtree(m),r,o),e=-1;++e<_;)(a=m[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=m[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(m=n,a):m},a.links=function(n){return arguments.length?(y=n,a):y},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.chargeDistance=function(n){return arguments.length?(p=n*n,a):Math.sqrt(p)},a.gravity=function(n){return arguments.length?(v=+n,a):v},a.theta=function(n){return arguments.length?(d=n*n,a):Math.sqrt(d)},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),Xo.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=y[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++a<s;)if(!isNaN(i=o[a][n]))return i;return Math.random()*r}var t,e,r,c=m.length,l=y.length,p=s[0],v=s[1];for(t=0;c>t;++t)(r=m[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=y[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Xo.behavior.drag().origin(bt).on("dragstart.force",Fu).on("drag.force",t).on("dragend.force",Ou)),arguments.length?(this.on("mouseover.force",Yu).on("mouseout.force",Iu).call(e),void 0):e},Xo.rebind(a,c,"on")};var us=20,is=1,os=1/0;Xo.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++f<s;)l=h[f]=n(c[f],p,a),l.parent=t,g+=l.value;r&&h.sort(r),i&&(t.value=g)}else delete t.children,i&&(t.value=+i.call(e,t,o)||0);return t}function t(n,r){var u=n.children,o=0;if(u&&(a=u.length))for(var a,c=-1,s=r+1;++c<a;)o+=t(u[c],s);else i&&(o=+i.call(e,n,r)||0);return i&&(n.value=o),o}function e(t){var e=[];return n(t,0,e),e}var r=Bu,u=Xu,i=$u;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(u=n,e):u},e.value=function(n){return arguments.length?(i=n,e):i},e.revalue=function(n){return t(n,0),n},e},Xo.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,s=-1;for(r=t.value?r/t.value:0;++s<o;)n(a=i[s],e,c=a.value*r,u),e+=c}}function t(n){var e=n.children,r=0;if(e&&(u=e.length))for(var u,i=-1;++i<u;)r=Math.max(r,t(e[i]));return 1+r}function e(e,i){var o=r.call(this,e,i);return n(o[0],0,u[0],u[1]/t(o[0])),o}var r=Xo.layout.hierarchy(),u=[1,1];return e.size=function(n){return arguments.length?(u=n,e):u},Vu(e,r)},Xo.layout.pie=function(){function n(i){var o=i.map(function(e,r){return+t.call(n,e,r)}),a=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof u?u.apply(this,arguments):u)-a)/Xo.sum(o),s=Xo.range(i.length);null!=e&&s.sort(e===as?function(n,t){return o[t]-o[n]}:function(n,t){return e(i[n],i[t])});var l=[];return s.forEach(function(n){var t;l[n]={data:i[n],value:t=o[n],startAngle:a,endAngle:a+=t*c}}),l}var t=Number,e=as,r=0,u=ka;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n};var as={};Xo.layout.stack=function(){function n(a,c){var s=a.map(function(e,r){return t.call(n,e,r)}),l=s.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,l,c);s=Xo.permute(s,f),l=Xo.permute(l,f);var h,g,p,v=r.call(n,l,c),d=s.length,m=s[0].length;for(g=0;m>g;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=bt,e=Qu,r=ni,u=Ku,i=Ju,o=Gu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:cs.get(t)||Qu,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:ss.get(t)||ni,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var cs=Xo.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(ti),i=n.map(ei),o=Xo.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Xo.range(n.length).reverse()},"default":Qu}),ss=Xo.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ni});Xo.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i<g;)o=c[i]=[],o.dx=f[i+1]-(o.x=f[i]),o.y=0;if(g>0)for(i=-1;++i<h;)a=s[i],a>=l[0]&&a<=l[1]&&(o=c[Xo.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=oi,u=ui;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=_t(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return ii(n,t)}:_t(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},Xo.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h<i;)s=r[h],o(s,a),f=c(s,a,f),a=s;vi(n);var g=.5*(l._tree.prelim+s._tree.prelim);t?(u.prelim=t._tree.prelim+e(n,t),u.mod=u.prelim-g):u.prelim=g}else t&&(u.prelim=t._tree.prelim+e(n,t))}function a(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,u=-1;for(t+=n._tree.mod;++u<r;)a(e[u],t)}}function c(n,t,r){if(t){for(var u,i=n,o=n,a=t,c=n.parent.children[0],s=i._tree.mod,l=o._tree.mod,f=a._tree.mod,h=c._tree.mod;a=si(a),i=ci(i),a&&i;)c=ci(c),o=si(o),o._tree.ancestor=n,u=a._tree.prelim+f-i._tree.prelim-s+e(a,i),u>0&&(di(mi(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!si(o)&&(o._tree.thread=a,o._tree.mod+=f-l),i&&!ci(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];pi(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=li(l,hi),h=li(l,fi),g=li(l,gi),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return pi(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,pi(a,function(n){n.r=+l(n.value)}),pi(a,bi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/s))/2;pi(a,function(n){n.r+=f}),pi(a,bi),pi(a,function(n){n.r-=f})}return ki(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=Xo.layout.hierarchy().sort(yi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Vu(n,e)},Xo.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;pi(c,function(n){var t=n.children;t&&t.length?(n.x=Ci(t),n.y=Ai(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Ni(c),f=Li(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return pi(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++u<i;)r=(e=n[u]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(i>e&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++i<o;)u=n[i],u.x=a,u.y=s,u.dy=l,a+=u.dx=Math.min(e.x+e.dx-a,l?c(u.area/l):0);u.z=!0,u.dx+=e.x+e.dx-a,e.y+=l,e.dy-=l}else{for((r||l>e.dx)&&(l=e.dx);++i<o;)u=n[i],u.x=a,u.y=s,u.dx=l,s+=u.dy=Math.min(e.y+e.dy-s,l?c(u.area/l):0);u.z=!1,u.dy+=e.y+e.dy-s,e.x+=l,e.dx-=l}}function i(r){var u=o||a(r),i=u[0];return i.x=0,i.y=0,i.dx=s[0],i.dy=s[1],o&&a.revalue(i),n([i],i.dx*i.dy/i.value),(o?e:t)(i),h&&(o=u),u}var o,a=Xo.layout.hierarchy(),c=Math.round,s=[1,1],l=null,f=Ti,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return i.size=function(n){return arguments.length?(s=n,i):s},i.padding=function(n){function t(t){var e=n.call(i,t,t.depth);return null==e?Ti(t):qi(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return qi(t,n)}if(!arguments.length)return l;var r;return f=null==(l=n)?Ti:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,i},i.round=function(n){return arguments.length?(c=n?Math.round:Number,i):c!=Number},i.sticky=function(n){return arguments.length?(h=n,o=null,i):h},i.ratio=function(n){return arguments.length?(p=n,i):p},i.mode=function(n){return arguments.length?(g=n+"",i):g},Vu(i,a)},Xo.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=Xo.random.normal.apply(Xo,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=Xo.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},Xo.scale={};var ls={floor:bt,ceil:bt};Xo.scale.linear=function(){return Hi([0,1],[0,1],fu,!1)};var fs={s:1,g:1,p:1,r:1,e:1};Xo.scale.log=function(){return $i(Xo.scale.linear().domain([0,1]),10,!0,[1,10])};var hs=Xo.format(".0e"),gs={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};Xo.scale.pow=function(){return Bi(Xo.scale.linear(),1,[0,1])},Xo.scale.sqrt=function(){return Xo.scale.pow().exponent(.5)},Xo.scale.ordinal=function(){return Ji([],{t:"range",a:[[]]})},Xo.scale.category10=function(){return Xo.scale.ordinal().range(ps)},Xo.scale.category20=function(){return Xo.scale.ordinal().range(vs)},Xo.scale.category20b=function(){return Xo.scale.ordinal().range(ds)},Xo.scale.category20c=function(){return Xo.scale.ordinal().range(ms)};var ps=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(ht),vs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(ht),ds=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(ht),ms=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(ht);Xo.scale.quantile=function(){return Gi([],[])},Xo.scale.quantize=function(){return Ki(0,1,[0,1])},Xo.scale.threshold=function(){return Qi([.5],[0,1])},Xo.scale.identity=function(){return no([0,1])},Xo.svg={},Xo.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+ys,a=u.apply(this,arguments)+ys,c=(o>a&&(c=o,o=a,a=c),a-o),s=Sa>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=xs?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z":"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=to,e=eo,r=ro,u=uo;return n.innerRadius=function(e){return arguments.length?(t=_t(e),n):t},n.outerRadius=function(t){return arguments.length?(e=_t(t),n):e},n.startAngle=function(t){return arguments.length?(r=_t(t),n):r},n.endAngle=function(t){return arguments.length?(u=_t(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+ys;return[Math.cos(i)*n,Math.sin(i)*n]},n};var ys=-Ea,xs=ka-Aa;Xo.svg.line=function(){return io(bt)};var Ms=Xo.map({linear:oo,"linear-closed":ao,step:co,"step-before":so,"step-after":lo,basis:mo,"basis-open":yo,"basis-closed":xo,bundle:Mo,cardinal:go,"cardinal-open":fo,"cardinal-closed":ho,monotone:Eo});Ms.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var _s=[0,2/3,1/3,0],bs=[0,1/3,2/3,0],ws=[0,1/6,2/3,1/6];Xo.svg.line.radial=function(){var n=io(Ao);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},so.reverse=lo,lo.reverse=so,Xo.svg.area=function(){return Co(bt)},Xo.svg.area.radial=function(){var n=Co(Ao);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},Xo.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+ys,l=s.call(n,u,r)+ys;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Sa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=hr,o=gr,a=No,c=ro,s=uo;return n.radius=function(t){return arguments.length?(a=_t(t),n):a},n.source=function(t){return arguments.length?(i=_t(t),n):i},n.target=function(t){return arguments.length?(o=_t(t),n):o},n.startAngle=function(t){return arguments.length?(c=_t(t),n):c},n.endAngle=function(t){return arguments.length?(s=_t(t),n):s},n},Xo.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=hr,e=gr,r=Lo;return n.source=function(e){return arguments.length?(t=_t(e),n):t},n.target=function(t){return arguments.length?(e=_t(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},Xo.svg.diagonal.radial=function(){var n=Xo.svg.diagonal(),t=Lo,e=n.projection;return n.projection=function(n){return arguments.length?e(To(t=n)):t},n},Xo.svg.symbol=function(){function n(n,r){return(Ss.get(t.call(this,n,r))||Ro)(e.call(this,n,r))}var t=zo,e=qo;return n.type=function(e){return arguments.length?(t=_t(e),n):t},n.size=function(t){return arguments.length?(e=_t(t),n):e},n};var Ss=Xo.map({circle:Ro,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Cs)),e=t*Cs;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});Xo.svg.symbolTypes=Ss.keys();var ks,Es,As=Math.sqrt(3),Cs=Math.tan(30*Na),Ns=[],Ls=0;Ns.call=da.call,Ns.empty=da.empty,Ns.node=da.node,Ns.size=da.size,Xo.transition=function(n){return arguments.length?ks?n.transition():n:xa.transition()},Xo.transition.prototype=Ns,Ns.select=function(n){var t,e,r,u=this.id,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]);for(var c=this[o],s=-1,l=c.length;++s<l;)(r=c[s])&&(e=n.call(r,r.__data__,s,o))?("__data__"in r&&(e.__data__=r.__data__),jo(e,s,u,r.__transition__[u]),t.push(e)):t.push(null)}return Do(i,u)},Ns.selectAll=function(n){var t,e,r,u,i,o=this.id,a=[];n=_(n);for(var c=-1,s=this.length;++c<s;)for(var l=this[c],f=-1,h=l.length;++f<h;)if(r=l[f]){i=r.__transition__[o],e=n.call(r,r.__data__,f,c),a.push(t=[]);for(var g=-1,p=e.length;++g<p;)(u=e[g])&&jo(u,g,o,i),t.push(u)}return Do(a,o)},Ns.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Do(u,this.id)},Ns.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):R(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Ns.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Ru:fu,a=Xo.ns.qualify(n);return Po(this,"attr."+n,t,a.local?i:u)},Ns.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=Xo.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Ns.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=Go.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=fu(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return Po(this,"style."+n,t,u)},Ns.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,Go.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Ns.text=function(n){return Po(this,"text",n,Uo)},Ns.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Ns.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=Xo.ease.apply(Xo,arguments)),R(this,function(e){e.__transition__[t].ease=n}))},Ns.delay=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},Ns.duration=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},Ns.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Es,u=ks;ks=e,R(this,function(t,r,u){Es=t.__transition__[e],n.call(t,t.__data__,r,u)}),Es=r,ks=u}else R(this,function(r){var u=r.__transition__[e];(u.event||(u.event=Xo.dispatch("start","end"))).on(n,t)});return this},Ns.transition=function(){for(var n,t,e,r,u=this.id,i=++Ls,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,jo(e,s,i,r)),n.push(e)}return Do(o,i)},Xo.svg.axis=function(){function n(n){n.each(function(){var n,s=Xo.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):bt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Aa),d=Xo.transition(p.exit()).style("opacity",Aa).remove(),m=Xo.transition(p).style("opacity",1),y=Ri(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),Xo.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=Ho,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=Ho,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=Fo,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=Fo,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,A=E.rangeBand()/2;l=f=function(n){return E(n)+A}}else l.rangeBand?l=f:d.call(n,f);v.call(n,l),m.call(n,f)})}var t,e=Xo.scale.linear(),r=Ts,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in qs?t+"":Ts,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ts="bottom",qs={top:1,right:1,bottom:1,left:1};Xo.svg.brush=function(){function n(i){i.each(function(){var i=Xo.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(p,bt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return zs[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=Xo.transition(i),h=Xo.transition(o);c&&(l=Ri(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=Ri(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+f[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",f[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1]-f[0])}function u(){function u(){32==Xo.event.keyCode&&(C||(x=null,L[0]-=l[1],L[1]-=f[1],C=2),d())}function p(){32==Xo.event.keyCode&&2==C&&(L[0]+=l[1],L[1]+=f[1],C=0,d())}function v(){var n=Xo.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),C||(Xo.event.altKey?(x||(x=[(l[0]+l[1])/2,(f[0]+f[1])/2]),L[0]=l[+(n[0]<x[0])],L[1]=f[+(n[1]<x[1])]):x=null),E&&m(n,c,0)&&(e(S),u=!0),A&&m(n,s,1)&&(r(S),u=!0),u&&(t(S),w({type:"brush",mode:C?"move":"resize"}))}function m(n,t,e){var r,u,a=Ri(t),c=a[0],s=a[1],p=L[e],v=e?f:l,d=v[1]-v[0];return C&&(c-=p,s-=d+p),r=(e?g:h)?Math.max(c,Math.min(s,n[e])):n[e],C?u=(r+=p)+d:(x&&(p=Math.max(c,Math.min(s,2*x[e]-r))),r>p?(u=r,r=p):u=p),v[0]!=r||v[1]!=u?(e?o=null:i=null,v[0]=r,v[1]=u,!0):void 0}function y(){v(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),Xo.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),N(),w({type:"brushend"})}var x,M,_=this,b=Xo.select(Xo.event.target),w=a.of(_,arguments),S=Xo.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,A=!/^(e|w)$/.test(k)&&s,C=b.classed("extent"),N=O(),L=Xo.mouse(_),T=Xo.select(Go).on("keydown.brush",u).on("keyup.brush",p);if(Xo.event.changedTouches?T.on("touchmove.brush",v).on("touchend.brush",y):T.on("mousemove.brush",v).on("mouseup.brush",y),S.interrupt().selectAll("*").interrupt(),C)L[0]=l[0]-L[0],L[1]=f[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],f[1-z]-L[1]],L[0]=l[q],L[1]=f[z]}else Xo.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),Xo.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),v()}var i,o,a=y(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],f=[0,0],h=!0,g=!0,p=Rs[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:f,i:i,j:o},e=this.__chart__||t;this.__chart__=t,ks?Xo.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,f=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=hu(l,t.x),r=hu(f,t.y);return i=o=null,function(u){l=t.x=e(u),f=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,p=Rs[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,p=Rs[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(h=!!t[0],g=!!t[1]):c?h=!!t:s&&(g=!!t),n):c&&s?[h,g]:c?h:s?g:null},n.extent=function(t){var e,r,u,a,h;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(h=e,e=r,r=h),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(h=u,u=a,a=h),(u!=f[0]||a!=f[1])&&(f=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(h=e,e=r,r=h))),s&&(o?(u=o[0],a=o[1]):(u=f[0],a=f[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(h=u,u=a,a=h))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],f=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&f[0]==f[1]},Xo.rebind(n,a,"on")};var zs={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Rs=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Ds=tc.format=ac.timeFormat,Ps=Ds.utc,Us=Ps("%Y-%m-%dT%H:%M:%S.%LZ");Ds.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Oo:Us,Oo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Oo.toString=Us.toString,tc.second=Rt(function(n){return new ec(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),tc.seconds=tc.second.range,tc.seconds.utc=tc.second.utc.range,tc.minute=Rt(function(n){return new ec(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),tc.minutes=tc.minute.range,tc.minutes.utc=tc.minute.utc.range,tc.hour=Rt(function(n){var t=n.getTimezoneOffset()/60;return new ec(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),tc.hours=tc.hour.range,tc.hours.utc=tc.hour.utc.range,tc.month=Rt(function(n){return n=tc.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),tc.months=tc.month.range,tc.months.utc=tc.month.utc.range;var js=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Hs=[[tc.second,1],[tc.second,5],[tc.second,15],[tc.second,30],[tc.minute,1],[tc.minute,5],[tc.minute,15],[tc.minute,30],[tc.hour,1],[tc.hour,3],[tc.hour,6],[tc.hour,12],[tc.day,1],[tc.day,2],[tc.week,1],[tc.month,1],[tc.month,3],[tc.year,1]],Fs=Ds.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",be]]),Os={range:function(n,t,e){return Xo.range(Math.ceil(n/e)*e,+t,e).map(Io)},floor:bt,ceil:bt};Hs.year=tc.year,tc.scale=function(){return Yo(Xo.scale.linear(),Hs,Fs)};var Ys=Hs.map(function(n){return[n[0].utc,n[1]]}),Is=Ps.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",be]]);Ys.year=tc.year.utc,tc.scale.utc=function(){return Yo(Xo.scale.linear(),Ys,Is)},Xo.text=wt(function(n){return n.responseText}),Xo.json=function(n,t){return St(n,"application/json",Zo,t)},Xo.html=function(n,t){return St(n,"text/html",Vo,t)},Xo.xml=wt(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(Xo):"object"==typeof module&&module.exports?module.exports=Xo:this.d3=Xo}();'use strict';(function(window){window.define=undefined;}).call(this,this);'use strict';tr.exportTo('tr.ui.b',function(){const DataSeriesEnableChangeEventType='data-series-enabled-change';const THIS_DOC=document.currentScript.ownerDocument;const svgNS='http://www.w3.org/2000/svg';const ColorScheme=tr.b.ColorScheme;function getColorOfKey(key,selected){let id=ColorScheme.getColorIdForGeneralPurposeString(key);if(selected){id+=ColorScheme.properties.brightenedOffsets[0];}
+return this.rangeOfInterest_;},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},get selection(){},set selection(_){},get textLabel(){return'Frame Data';},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-frame-data-side-panel');});});'use strict';Polymer({is:'tr-ui-b-chart-legend-key',ready(){this.$.checkbox.addEventListener('change',this.onCheckboxChange_.bind(this));},onCheckboxChange_(){tr.b.dispatchSimpleEvent(this,tr.ui.b.DataSeriesEnableChangeEventType,true,false,{key:Polymer.dom(this).textContent,enabled:this.enabled});},set textContent(t){Polymer.dom(this.$.label).textContent=t;Polymer.dom(this.$.link).textContent=t;this.updateContents_();},set width(w){w-=20;this.$.link.style.width=w+'px';this.$.label.style.width=w+'px';},get textContent(){return Polymer.dom(this.$.label).textContent;},set optional(optional){this.$.checkbox.style.visibility=optional?'visible':'hidden';},get optional(){return this.$.checkbox.style.visibility==='visible';},set enabled(enabled){this.$.checkbox.checked=enabled?'checked':'';},get enabled(){return this.$.checkbox.checked;},set color(c){this.$.label.style.color=c;this.$.link.color=c;},set target(target){this.$.link.setSelectionAndContent(target,Polymer.dom(this.$.label).textContent);this.updateContents_();},get target(){return this.$.link.selection;},set title(title){this.$.link.title=title;},updateContents_(){this.$.link.style.display=this.target?'':'none';this.$.label.style.display=this.target?'none':'';this.$.label.htmlFor=this.optional?'checkbox':'';}});'use strict';(function(window){window.define=function(x){window.d3=x;};window.define.amd=true;})(this);!function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function u(){}function i(n){return aa+n in this}function o(n){return n=aa+n,n in this&&delete this[n]}function a(){var n=[];return this.forEach(function(t){n.push(t)}),n}function c(){var n=0;for(var t in this)t.charCodeAt(0)===ca&&++n;return n}function s(){for(var n in this)if(n.charCodeAt(0)===ca)return!1;return!0}function l(){}function f(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function h(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=sa.length;r>e;++e){var u=sa[e]+t;if(u in n)return u}}function g(){}function p(){}function v(n){function t(){for(var t,r=e,u=-1,i=r.length;++u<i;)(t=r[u].on)&&t.apply(this,arguments);return n}var e=[],r=new u;return t.on=function(t,u){var i,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,i=e.indexOf(o)).concat(e.slice(i+1)),r.remove(t)),u&&e.push(r.set(t,{on:u})),n)},t}function d(){Xo.event.preventDefault()}function m(){for(var n,t=Xo.event;n=t.sourceEvent;)t=n;return t}function y(n){for(var t=new p,e=0,r=arguments.length;++e<r;)t[arguments[e]]=v(t);return t.of=function(e,r){return function(u){try{var i=u.sourceEvent=Xo.event;u.target=n,Xo.event=u,t[u.type].apply(e,r)}finally{Xo.event=i}}},t}function x(n){return fa(n,da),n}function M(n){return"function"==typeof n?n:function(){return ha(n,this)}}function _(n){return"function"==typeof n?n:function(){return ga(n,this)}}function b(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function u(){this.setAttribute(n,t)}function i(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=Xo.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?i:u}function w(n){return n.trim().replace(/\s+/g," ")}function S(n){return new RegExp("(?:^|\\s+)"+Xo.requote(n)+"(?:\\s+|$)","g")}function k(n){return n.trim().split(/^|\s+/)}function E(n,t){function e(){for(var e=-1;++e<u;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<u;)n[e](this,r)}n=k(n).map(A);var u=n.length;return"function"==typeof t?r:e}function A(n){var t=S(n);return function(e,r){if(u=e.classList)return r?u.add(n):u.remove(n);var u=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(u)||e.setAttribute("class",w(u+" "+n))):e.setAttribute("class",w(u.replace(t," ")))}}function C(n,t,e){function r(){this.style.removeProperty(n)}function u(){this.style.setProperty(n,t,e)}function i(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?i:u}function N(n,t){function e(){delete this[n]}function r(){this[n]=t}function u(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?u:r}function L(n){return"function"==typeof n?n:(n=Xo.ns.qualify(n)).local?function(){return this.ownerDocument.createElementNS(n.space,n.local)}:function(){return this.ownerDocument.createElementNS(this.namespaceURI,n)}}function T(n){return{__data__:n}}function q(n){return function(){return va(this,n)}}function z(n){return arguments.length||(n=Xo.ascending),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function R(n,t){for(var e=0,r=n.length;r>e;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function D(n){return fa(n,ya),n}function P(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t<c;);return o}}function U(){var n=this.__transition__;n&&++n.active}function j(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function u(){var u=c(t,Bo(arguments));r.call(this),this.addEventListener(n,this[o]=u,u.$=e),u._=t}function i(){var t,e=new RegExp("^__on([^.]+)"+Xo.requote(n)+"$");for(var r in this)if(t=r.match(e)){var u=this[r];this.removeEventListener(t[1],u,u.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),c=H;a>0&&(n=n.substring(0,a));var s=Ma.get(n);return s&&(n=s,c=F),a?t?u:r:t?g:i}function H(n,t){return function(e){var r=Xo.event;Xo.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{Xo.event=r}}}function F(n,t){var e=H(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function O(){var n=".dragsuppress-"+ ++ba,t="click"+n,e=Xo.select(Go).on("touchmove"+n,d).on("dragstart"+n,d).on("selectstart"+n,d);if(_a){var r=Jo.style,u=r[_a];r[_a]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),_a&&(r[_a]=u),i&&(e.on(t,function(){d(),o()},!0),setTimeout(o,0))}}function Y(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>wa&&(Go.scrollX||Go.scrollY)){e=Xo.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var u=e[0][0].getScreenCTM();wa=!(u.f||u.e),e.remove()}return wa?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}function I(n){return n>0?1:0>n?-1:0}function Z(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function V(n){return n>1?0:-1>n?Sa:Math.acos(n)}function X(n){return n>1?Ea:-1>n?-Ea:Math.asin(n)}function $(n){return((n=Math.exp(n))-1/n)/2}function B(n){return((n=Math.exp(n))+1/n)/2}function W(n){return((n=Math.exp(2*n))-1)/(n+1)}function J(n){return(n=Math.sin(n/2))*n}function G(){}function K(n,t,e){return new Q(n,t,e)}function Q(n,t,e){this.h=n,this.s=t,this.l=e}function nt(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,gt(u(n+120),u(n),u(n-120))}function tt(n,t,e){return new et(n,t,e)}function et(n,t,e){this.h=n,this.c=t,this.l=e}function rt(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),ut(e,Math.cos(n*=Na)*t,Math.sin(n)*t)}function ut(n,t,e){return new it(n,t,e)}function it(n,t,e){this.l=n,this.a=t,this.b=e}function ot(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=ct(u)*Fa,r=ct(r)*Oa,i=ct(i)*Ya,gt(lt(3.2404542*u-1.5371385*r-.4985314*i),lt(-.969266*u+1.8760108*r+.041556*i),lt(.0556434*u-.2040259*r+1.0572252*i))}function at(n,t,e){return n>0?tt(Math.atan2(e,t)*La,Math.sqrt(t*t+e*e),n):tt(0/0,0/0,n)}function ct(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function st(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function lt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function ft(n){return gt(n>>16,255&n>>8,255&n)}function ht(n){return ft(n)+""}function gt(n,t,e){return new pt(n,t,e)}function pt(n,t,e){this.r=n,this.g=t,this.b=e}function vt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function dt(n,t,e){var r,u,i,o,a=0,c=0,s=0;if(u=/([a-z]+)\((.*)\)/i.exec(n))switch(i=u[2].split(","),u[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Mt(i[0]),Mt(i[1]),Mt(i[2]))}return(o=Va.get(n))?t(o.r,o.g,o.b):(null!=n&&"#"===n.charAt(0)&&(r=parseInt(n.substring(1),16),isNaN(r)||(4===n.length?(a=(3840&r)>>4,a=a>>4|a,c=240&r,c=c>>4|c,s=15&r,s=s<<4|s):7===n.length&&(a=(16711680&r)>>16,c=(65280&r)>>8,s=255&r))),t(a,c,s))}function mt(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),K(r,u,c)}function yt(n,t,e){n=xt(n),t=xt(t),e=xt(e);var r=st((.4124564*n+.3575761*t+.1804375*e)/Fa),u=st((.2126729*n+.7151522*t+.072175*e)/Oa),i=st((.0193339*n+.119192*t+.9503041*e)/Ya);return ut(116*u-16,500*(r-u),200*(u-i))}function xt(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Mt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function _t(n){return"function"==typeof n?n:function(){return n}}function bt(n){return n}function wt(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),St(t,e,n,r)}}function St(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=Xo.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!Go.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=Xo.event;Xo.event=n;try{o.progress.call(i,c)}finally{Xo.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Bo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},Xo.rebind(i,o,"on"),null==r?i:i.get(kt(r))}function kt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Et(){var n=At(),t=Ct()-n;t>24?(isFinite(t)&&(clearTimeout(Wa),Wa=setTimeout(Et,t)),Ba=0):(Ba=1,Ga(Et))}function At(){var n=Date.now();for(Ja=Xa;Ja;)n>=Ja.t&&(Ja.f=Ja.c(n-Ja.t)),Ja=Ja.n;return n}function Ct(){for(var n,t=Xa,e=1/0;t;)t.f?t=n?n.n=t.n:Xa=t.n:(t.t<e&&(e=t.t),t=(n=t).n);return $a=n,e}function Nt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Lt(n,t){var e=Math.pow(10,3*oa(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Tt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r?function(n){for(var t=n.length,u=[],i=0,o=r[0];t>0&&o>0;)u.push(n.substring(t-=o,t+o)),o=r[i=(i+1)%r.length];return u.reverse().join(e)}:bt;return function(n){var e=Qa.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"",c=e[4]||"",s=e[5],l=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1;switch(h&&(h=+h.substring(1)),(s||"0"===r&&"="===o)&&(s=r="0",o="=",f&&(l-=Math.floor((l-1)/4))),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=nc.get(g)||qt;var y=s&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):a;if(0>p){var c=Xo.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x=n.lastIndexOf("."),M=0>x?n:n.substring(0,x),_=0>x?"":t+n.substring(x+1);!s&&f&&(M=i(M));var b=v.length+M.length+_.length+(y?0:u.length),w=l>b?new Array(b=l-b+1).join(r):"";return y&&(M=i(w+M)),u+=v,n=M+_,("<"===o?u+n+w:">"===o?w+u+n:"^"===o?w.substring(0,b>>=1)+u+n+w.substring(b):u+(y?n:w+n))+e}}}function qt(n){return n+""}function zt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Rt(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new ec(e-1)),1),e}function i(n,e){return t(n=new ec(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{ec=zt;var r=new zt;return r._=n,o(r,t,e)}finally{ec=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Dt(n);return c.floor=c,c.round=Dt(r),c.ceil=Dt(u),c.offset=Dt(i),c.range=a,n}function Dt(n){return function(t,e){try{ec=zt;var r=new zt;return r._=t,n(r,e)._}finally{ec=Date}}}function Pt(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.substring(c,a)),null!=(u=uc[e=n.charAt(++a)])&&(e=n.charAt(++a)),(i=C[e])&&(e=i(t,null==u?"e"===e?" ":"0":u)),o.push(e),c=a+1);return o.push(n.substring(c,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},u=e(r,n,t,0);if(u!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var i=null!=r.Z&&ec!==zt,o=new(i?zt:ec);return"j"in r?o.setFullYear(r.y,0,r.j):"w"in r&&("W"in r||"U"in r)?(o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+Math.floor(r.Z/100),r.M+r.Z%100,r.S,r.L),i?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var u,i,o,a=0,c=t.length,s=e.length;c>a;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=N[o in uc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){b.lastIndex=0;var r=b.exec(t.substring(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){M.lastIndex=0;var r=M.exec(t.substring(e));return r?(n.w=_.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.substring(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.substring(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,C.c.toString(),t,r)}function c(n,t,r){return e(n,C.x.toString(),t,r)}function s(n,t,r){return e(n,C.X.toString(),t,r)}function l(n,t,e){var r=x.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{ec=zt;var t=new ec;return t._=n,r(t)}finally{ec=Date}}var r=t(n);return e.parse=function(n){try{ec=zt;var t=r.parse(n);return t&&t._}finally{ec=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ee;var x=Xo.map(),M=jt(v),_=Ht(v),b=jt(d),w=Ht(d),S=jt(m),k=Ht(m),E=jt(y),A=Ht(y);p.forEach(function(n,t){x.set(n.toLowerCase(),t)});var C={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return Ut(n.getDate(),t,2)},e:function(n,t){return Ut(n.getDate(),t,2)},H:function(n,t){return Ut(n.getHours(),t,2)},I:function(n,t){return Ut(n.getHours()%12||12,t,2)},j:function(n,t){return Ut(1+tc.dayOfYear(n),t,3)},L:function(n,t){return Ut(n.getMilliseconds(),t,3)},m:function(n,t){return Ut(n.getMonth()+1,t,2)},M:function(n,t){return Ut(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return Ut(n.getSeconds(),t,2)},U:function(n,t){return Ut(tc.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Ut(tc.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return Ut(n.getFullYear()%100,t,2)},Y:function(n,t){return Ut(n.getFullYear()%1e4,t,4)},Z:ne,"%":function(){return"%"}},N={a:r,A:u,b:i,B:o,c:a,d:Bt,e:Bt,H:Jt,I:Jt,j:Wt,L:Qt,m:$t,M:Gt,p:l,S:Kt,U:Ot,w:Ft,W:Yt,x:c,X:s,y:Zt,Y:It,Z:Vt,"%":te};return t}function Ut(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function jt(n){return new RegExp("^(?:"+n.map(Xo.requote).join("|")+")","i")}function Ht(n){for(var t=new u,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function Ft(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Ot(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Yt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function It(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Zt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.y=Xt(+r[0]),e+r[0].length):-1}function Vt(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function Xt(n){return n+(n>68?1900:2e3)}function $t(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Bt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function Wt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function Jt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function Gt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function Kt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function Qt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ne(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(oa(t)/60),u=oa(t)%60;return e+Ut(r,"0",2)+Ut(u,"0",2)}function te(n,t,e){oc.lastIndex=0;var r=oc.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function ee(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function re(){}function ue(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function ie(n,t){n&&lc.hasOwnProperty(n.type)&&lc[n.type](n,t)}function oe(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++u<i;)r=n[u],t.point(r[0],r[1],r[2]);t.lineEnd()}function ae(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)oe(n[e],t,1);t.polygonEnd()}function ce(){function n(n,t){n*=Na,t=t*Na/2+Sa/4;var e=n-r,o=e>=0?1:-1,a=o*e,c=Math.cos(t),s=Math.sin(t),l=i*s,f=u*c+l*Math.cos(a),h=l*o*Math.sin(a);hc.add(Math.atan2(h,f)),r=n,u=c,i=s}var t,e,r,u,i;gc.point=function(o,a){gc.point=n,r=(t=o)*Na,u=Math.cos(a=(e=a)*Na/2+Sa/4),i=Math.sin(a)},gc.lineEnd=function(){n(t,e)}}function se(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function le(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function fe(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function he(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ge(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function pe(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function ve(n){return[Math.atan2(n[1],n[0]),X(n[2])]}function de(n,t){return oa(n[0]-t[0])<Aa&&oa(n[1]-t[1])<Aa}function me(n,t){n*=Na;var e=Math.cos(t*=Na);ye(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function ye(n,t,e){++pc,dc+=(n-dc)/pc,mc+=(t-mc)/pc,yc+=(e-yc)/pc}function xe(){function n(n,u){n*=Na;var i=Math.cos(u*=Na),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),s=Math.atan2(Math.sqrt((s=e*c-r*a)*s+(s=r*o-t*c)*s+(s=t*a-e*o)*s),t*o+e*a+r*c);vc+=s,xc+=s*(t+(t=o)),Mc+=s*(e+(e=a)),_c+=s*(r+(r=c)),ye(t,e,r)}var t,e,r;kc.point=function(u,i){u*=Na;var o=Math.cos(i*=Na);t=o*Math.cos(u),e=o*Math.sin(u),r=Math.sin(i),kc.point=n,ye(t,e,r)}}function Me(){kc.point=me}function _e(){function n(n,t){n*=Na;var e=Math.cos(t*=Na),o=e*Math.cos(n),a=e*Math.sin(n),c=Math.sin(t),s=u*c-i*a,l=i*o-r*c,f=r*a-u*o,h=Math.sqrt(s*s+l*l+f*f),g=r*o+u*a+i*c,p=h&&-V(g)/h,v=Math.atan2(h,g);bc+=p*s,wc+=p*l,Sc+=p*f,vc+=v,xc+=v*(r+(r=o)),Mc+=v*(u+(u=a)),_c+=v*(i+(i=c)),ye(r,u,i)}var t,e,r,u,i;kc.point=function(o,a){t=o,e=a,kc.point=n,o*=Na;var c=Math.cos(a*=Na);r=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),ye(r,u,i)},kc.lineEnd=function(){n(t,e),kc.lineEnd=Me,kc.point=me}}function be(){return!0}function we(n,t,e,r,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(de(e,r)){u.lineStart();for(var a=0;t>a;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new ke(e,n,null,!0),s=new ke(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=new ke(r,n,null,!1),s=new ke(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),Se(i),Se(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function Se(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r<t;)u.n=e=n[r],e.p=u,u=e;u.n=e=n[0],e.p=u}}function ke(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Ee(n,t,e,r){return function(u,i){function o(t,e){var r=u(t,e);n(t=r[0],e=r[1])&&i.point(t,e)}function a(n,t){var e=u(n,t);d.point(e[0],e[1])}function c(){y.point=a,d.lineStart()}function s(){y.point=o,d.lineEnd()}function l(n,t){v.push([n,t]);var e=u(n,t);M.point(e[0],e[1])}function f(){M.lineStart(),v=[]}function h(){l(v[0][0],v[0][1]),M.lineEnd();var n,t=M.clean(),e=x.buffer(),r=e.length;if(v.pop(),p.push(v),v=null,r){if(1&t){n=e[0];var u,r=n.length-1,o=-1;for(i.lineStart();++o<r;)i.point((u=n[o])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Ae))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=Xo.merge(g);var n=Le(m,p);g.length?we(g,Ne,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=Ce(),M=t(x);return y}}function Ae(n){return n.length>1}function Ce(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:g,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ne(n,t){return((n=n.x)[0]<0?n[1]-Ea-Aa:Ea-n[1])-((t=t.x)[0]<0?t[1]-Ea-Aa:Ea-t[1])}function Le(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;hc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+Sa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+Sa/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=_>=0?1:-1,w=b*_,S=w>Sa,k=p*x;if(hc.add(Math.atan2(k*b*Math.sin(w),v*M+k*Math.cos(w))),i+=S?_+b*ka:_,S^h>=e^m>=e){var E=fe(se(f),se(n));pe(E);var A=fe(u,E);pe(A);var C=(S^_>=0?-1:1)*X(A[2]);(r>C||r===C&&(E[0]||E[1]))&&(o+=S^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Aa>i||Aa>i&&0>hc)^1&o}function Te(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Sa:-Sa,c=oa(i-e);oa(c-Sa)<Aa?(n.point(e,r=(r+o)/2>0?Ea:-Ea),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=Sa&&(oa(e-u)<Aa&&(e-=u*Aa),oa(i-a)<Aa&&(i-=a*Aa),r=qe(e,r,i,o),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=i,r=o),u=a},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function qe(n,t,e,r){var u,i,o=Math.sin(n-e);return oa(o)>Aa?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function ze(n,t,e,r){var u;if(null==n)u=e*Ea,r.point(-Sa,u),r.point(0,u),r.point(Sa,u),r.point(Sa,0),r.point(Sa,-u),r.point(0,-u),r.point(-Sa,-u),r.point(-Sa,0),r.point(-Sa,u);else if(oa(n[0]-t[0])>Aa){var i=n[0]<t[0]?Sa:-Sa;u=e*i/2,r.point(-i,u),r.point(0,u),r.point(i,u)}else r.point(t[0],t[1])}function Re(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Sa:-Sa),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(de(e,g)||de(p,g))&&(p[0]+=Aa,p[1]+=Aa,v=t(p[0],p[1]))),v!==c)l=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&de(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=se(n),u=se(t),o=[1,0,0],a=fe(r,u),c=le(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=fe(o,a),p=ge(o,f),v=ge(a,h);he(p,v);var d=g,m=le(p,d),y=le(d,d),x=m*m-y*(le(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=ge(d,(-m-M)/y);if(he(_,p),_=ve(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var A=S-w,C=oa(A-Sa)<Aa,N=C||Aa>A;if(!C&&k>E&&(b=k,k=E,E=b),N?C?k+E>0^_[1]<(oa(_[0]-w)<Aa?k:E):k<=_[1]&&_[1]<=E:A>Sa^(w<=_[0]&&_[0]<=S)){var L=ge(d,(-m+M)/y);return he(L,p),[_,ve(L)]}}}function u(t,e){var r=o?n:Sa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=oa(i)>Aa,c=cr(n,6*Na);return Ee(t,e,c,o?[0,-n]:[-Sa,n-Sa])}function De(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function Pe(n,t,e,r){function u(r,u){return oa(r[0]-n)<Aa?u>0?0:3:oa(r[0]-e)<Aa?u>0?2:1:oa(r[1]-t)<Aa?u>0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,s=a[0];c>o;++o)i=a[o],s[1]<=r?i[1]>r&&Z(s,i,n)>0&&++t:i[1]<=r&&Z(s,i,n)<0&&--t,s=i;return 0!==t}function s(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function l(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){l(n,t)&&a.point(n,t)}function h(){N.point=p,d&&d.push(m=[]),S=!0,w=!1,_=b=0/0}function g(){v&&(p(y,x),M&&w&&A.rejoin(),v.push(A.buffer())),N.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Ac,Math.min(Ac,n)),t=Math.max(-Ac,Math.min(Ac,t));var e=l(n,t);if(d&&m.push([n,t]),S)y=n,x=t,M=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:_,y:b},b:{x:n,y:t}};C(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}_=n,b=t,w=e}var v,d,m,y,x,M,_,b,w,S,k,E=a,A=Ce(),C=De(n,t,e,r),N={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=Xo.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),s(null,null,1,a),a.lineEnd()),u&&we(v,i,t,s,a),a.polygonEnd()),v=d=m=null}};return N}}function Ue(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function je(n){var t=0,e=Sa/3,r=nr(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*Sa/180,e=n[1]*Sa/180):[180*(t/Sa),180*(e/Sa)]},u}function He(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,X((i-(n*n+e*e)*u*u)/(2*u))]},e}function Fe(){function n(n,t){Nc+=u*n-r*t,r=n,u=t}var t,e,r,u;Rc.point=function(i,o){Rc.point=n,t=r=i,e=u=o},Rc.lineEnd=function(){n(t,e)}}function Oe(n,t){Lc>n&&(Lc=n),n>qc&&(qc=n),Tc>t&&(Tc=t),t>zc&&(zc=t)}function Ye(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Ie(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Ie(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Ie(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Ze(n,t){dc+=n,mc+=t,++yc}function Ve(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);xc+=o*(t+n)/2,Mc+=o*(e+r)/2,_c+=o,Ze(t=n,e=r)}var t,e;Pc.point=function(r,u){Pc.point=n,Ze(t=r,e=u)}}function Xe(){Pc.point=Ze}function $e(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);xc+=o*(r+n)/2,Mc+=o*(u+t)/2,_c+=o,o=u*n-r*t,bc+=o*(r+n),wc+=o*(u+t),Sc+=3*o,Ze(r=n,u=t)}var t,e,r,u;Pc.point=function(i,o){Pc.point=n,Ze(t=r=i,e=u=o)},Pc.lineEnd=function(){n(t,e)}}function Be(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,ka)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:g};return a}function We(n){function t(n){return(a?r:e)(n)}function e(t){return Ke(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=se([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=oa(oa(w)-1)<Aa||oa(r-h)<Aa?(r+h)/2:Math.atan2(b,_),A=n(E,k),C=A[0],N=A[1],L=C-t,T=N-e,q=x*L-y*T;(q*q/M>i||oa((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,C,N,E,_/=S,b/=S,w,d,m),m.point(C,N),u(C,N,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Na),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function Je(n){var t=We(function(t,e){return n([t*La,e*La])});return function(n){return tr(t(n))}}function Ge(n){this.stream=n}function Ke(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function Qe(n){return nr(function(){return n})()}function nr(n){function t(n){return n=a(n[0]*Na,n[1]*Na),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*La,n[1]*La]}function r(){a=Ue(o=ur(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=We(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Ec,_=bt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=tr(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Ec):Re((b=+n)*Na),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?Pe(n[0][0],n[0][1],n[1][0],n[1][1]):bt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Na,d=n[1]%360*Na,r()):[v*La,d*La]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Na,y=n[1]%360*Na,x=n.length>2?n[2]%360*Na:0,r()):[m*La,y*La,x*La]},Xo.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function tr(n){return Ke(n,function(t,e){n.point(t*Na,e*Na)})}function er(n,t){return[n,t]}function rr(n,t){return[n>Sa?n-ka:-Sa>n?n+ka:n,t]}function ur(n,t,e){return n?t||e?Ue(or(n),ar(t,e)):or(n):t||e?ar(t,e):rr}function ir(n){return function(t,e){return t+=n,[t>Sa?t-ka:-Sa>t?t+ka:t,e]}}function or(n){var t=ir(n);return t.invert=ir(-n),t}function ar(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),X(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),X(l*r-a*u)]},e}function cr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=sr(e,u),i=sr(e,i),(o>0?i>u:u>i)&&(u+=o*ka)):(u=n+o*ka,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=ve([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function sr(n,t){var e=se(t);e[0]-=n,pe(e);var r=V(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Aa)%(2*Math.PI)}function lr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function fr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function hr(n){return n.source}function gr(n){return n.target}function pr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(J(r-t)+u*o*J(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+t*a;return[Math.atan2(u,r)*La,Math.atan2(o,Math.sqrt(r*r+u*u))*La]}:function(){return[n*La,t*La]};return p.distance=h,p}function vr(){function n(n,u){var i=Math.sin(u*=Na),o=Math.cos(u),a=oa((n*=Na)-t),c=Math.cos(a);Uc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;jc.point=function(u,i){t=u*Na,e=Math.sin(i*=Na),r=Math.cos(i),jc.point=n},jc.lineEnd=function(){jc.point=jc.lineEnd=g}}function dr(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function mr(n,t){function e(n,t){var e=oa(oa(t)-Ea)<Aa?0:o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(Sa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=I(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ea]},e):xr}function yr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return oa(u)<Aa?er:(e.invert=function(n,t){var e=i-t;return[Math.atan2(n,e)/u,i-I(u)*Math.sqrt(n*n+e*e)]},e)}function xr(n,t){return[n,Math.log(Math.tan(Sa/4+t/2))]}function Mr(n){var t,e=Qe(n),r=e.scale,u=e.translate,i=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=u.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=i.apply(e,arguments);if(o===e){if(t=null==n){var a=Sa*r(),c=u();i([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function _r(n,t){return[Math.log(Math.tan(Sa/4+t/2)),-n]}function br(n){return n[0]}function wr(n){return n[1]}function Sr(n){for(var t=n.length,e=[0,1],r=2,u=2;t>u;u++){for(;r>1&&Z(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function kr(n,t){return n[0]-t[0]||n[1]-t[1]}function Er(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Ar(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function Cr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Nr(){Jr(this),this.edge=this.site=this.circle=null}function Lr(n){var t=Jc.pop()||new Nr;return t.site=n,t}function Tr(n){Or(n),$c.remove(n),Jc.push(n),Jr(n)}function qr(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Tr(n);for(var c=i;c.circle&&oa(e-c.circle.x)<Aa&&oa(r-c.circle.cy)<Aa;)i=c.P,a.unshift(c),Tr(c),c=i;a.unshift(c),Or(c);for(var s=o;s.circle&&oa(e-s.circle.x)<Aa&&oa(r-s.circle.cy)<Aa;)o=s.N,a.push(s),Tr(s),s=o;a.push(s),Or(s);var l,f=a.length;for(l=1;f>l;++l)s=a[l],c=a[l-1],$r(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=Vr(c.site,s.site,null,u),Fr(c),Fr(s)}function zr(n){for(var t,e,r,u,i=n.x,o=n.y,a=$c._;a;)if(r=Rr(a,o)-i,r>Aa)a=a.L;else{if(u=i-Dr(a,o),!(u>Aa)){r>-Aa?(t=a.P,e=a):u>-Aa?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Lr(n);if($c.insert(t,c),t||e){if(t===e)return Or(t),e=Lr(t.site),$c.insert(c,e),c.edge=e.edge=Vr(t.site,c.site),Fr(t),Fr(e),void 0;if(!e)return c.edge=Vr(t.site,c.site),void 0;Or(t),Or(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};$r(e.edge,s,p,M),c.edge=Vr(s,n,null,M),e.edge=Vr(n,p,null,M),Fr(t),Fr(e)}}function Rr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function Dr(n,t){var e=n.N;if(e)return Rr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Pr(n){this.site=n,this.edges=[]}function Ur(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Xc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(oa(r-t)>Aa||oa(u-e)>Aa)&&(a.splice(o,0,new Br(Xr(i.site,l,oa(r-f)<Aa&&p-u>Aa?{x:f,y:oa(t-f)<Aa?e:p}:oa(u-p)<Aa&&h-r>Aa?{x:oa(e-p)<Aa?t:h,y:p}:oa(r-h)<Aa&&u-g>Aa?{x:h,y:oa(t-h)<Aa?e:g}:oa(u-g)<Aa&&r-f>Aa?{x:oa(e-g)<Aa?t:f,y:g}:null),i.site,null)),++c)}function jr(n,t){return t.angle-n.angle}function Hr(){Jr(this),this.x=this.y=this.arc=this.site=this.cy=null}function Fr(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,u=n.site,i=e.site;if(r!==i){var o=u.x,a=u.y,c=r.x-o,s=r.y-a,l=i.x-o,f=i.y-a,h=2*(c*f-s*l);if(!(h>=-Ca)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=Gc.pop()||new Hr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Wc._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}Wc.insert(y,m),y||(Bc=m)}}}}function Or(n){var t=n.circle;t&&(t.P||(Bc=t.N),Wc.remove(t),Gc.push(t),Jr(t),n.circle=null)}function Yr(n){for(var t,e=Vc,r=De(n[0][0],n[0][1],n[1][0],n[1][1]),u=e.length;u--;)t=e[u],(!Ir(t,n)||!r(t)||oa(t.a.x-t.b.x)<Aa&&oa(t.a.y-t.b.y)<Aa)&&(t.a=t.b=null,e.splice(u,1))}function Ir(n,t){var e=n.b;if(e)return!0;var r,u,i=n.a,o=t[0][0],a=t[1][0],c=t[0][1],s=t[1][1],l=n.l,f=n.r,h=l.x,g=l.y,p=f.x,v=f.y,d=(h+p)/2,m=(g+v)/2;if(v===g){if(o>d||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.y<c)return}else i={x:d,y:s};e={x:d,y:c}}}else if(r=(h-p)/(v-g),u=m-r*d,-1>r||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.y<c)return}else i={x:(s-u)/r,y:s};e={x:(c-u)/r,y:c}}else if(v>g){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.x<o)return}else i={x:a,y:r*a+u};e={x:o,y:r*o+u}}return n.a=i,n.b=e,!0}function Zr(n,t){this.l=n,this.r=t,this.a=this.b=null}function Vr(n,t,e,r){var u=new Zr(n,t);return Vc.push(u),e&&$r(u,n,t,e),r&&$r(u,t,n,r),Xc[n.i].edges.push(new Br(u,n,t)),Xc[t.i].edges.push(new Br(u,t,n)),u}function Xr(n,t,e){var r=new Zr(n,null);return r.a=t,r.b=e,Vc.push(r),r}function $r(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function Br(n,t,e){var r=n.a,u=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(u.x-r.x,r.y-u.y):Math.atan2(r.x-u.x,u.y-r.y)}function Wr(){this._=null}function Jr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function Gr(n,t){var e=t,r=t.R,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function Kr(n,t){var e=t,r=t.L,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function Qr(n){for(;n.L;)n=n.L;return n}function nu(n,t){var e,r,u,i=n.sort(tu).pop();for(Vc=[],Xc=new Array(n.length),$c=new Wr,Wc=new Wr;;)if(u=Bc,i&&(!u||i.y<u.y||i.y===u.y&&i.x<u.x))(i.x!==e||i.y!==r)&&(Xc[i.i]=new Pr(i),zr(i),e=i.x,r=i.y),i=n.pop();else{if(!u)break;qr(u.arc)}t&&(Yr(t),Ur(t));var o={cells:Xc,edges:Vc};return $c=Wc=Vc=Xc=null,o}function tu(n,t){return t.y-n.y||t.x-n.x}function eu(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function ru(n){return n.x}function uu(n){return n.y}function iu(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function ou(n,t,e,r,u,i){if(!n(t,e,r,u,i)){var o=.5*(e+u),a=.5*(r+i),c=t.nodes;c[0]&&ou(n,c[0],e,r,o,a),c[1]&&ou(n,c[1],o,r,u,a),c[2]&&ou(n,c[2],e,a,o,i),c[3]&&ou(n,c[3],o,a,u,i)}}function au(n,t){n=Xo.rgb(n),t=Xo.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+vt(Math.round(e+i*n))+vt(Math.round(r+o*n))+vt(Math.round(u+a*n))}}function cu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=fu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function su(n,t){return t-=n=+n,function(e){return n+t*e}}function lu(n,t){var e,r,u,i,o,a=0,c=0,s=[],l=[];for(n+="",t+="",Qc.lastIndex=0,r=0;e=Qc.exec(t);++r)e.index&&s.push(t.substring(a,c=e.index)),l.push({i:s.length,x:e[0]}),s.push(null),a=Qc.lastIndex;for(a<t.length&&s.push(t.substring(a)),r=0,i=l.length;(e=Qc.exec(n))&&i>r;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=su(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function fu(n,t){for(var e,r=Xo.interpolators.length;--r>=0&&!(e=Xo.interpolators[r](n,t)););return e}function hu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(fu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function gu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function pu(n){return function(t){return 1-n(1-t)}}function vu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function du(n){return n*n}function mu(n){return n*n*n}function yu(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function xu(n){return function(t){return Math.pow(t,n)}}function Mu(n){return 1-Math.cos(n*Ea)}function _u(n){return Math.pow(2,10*(n-1))}function bu(n){return 1-Math.sqrt(1-n*n)}function wu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/ka*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*ka/t)}}function Su(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function ku(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Eu(n,t){n=Xo.hcl(n),t=Xo.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return rt(e+i*n,r+o*n,u+a*n)+""}}function Au(n,t){n=Xo.hsl(n),t=Xo.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return nt(e+i*n,r+o*n,u+a*n)+""}}function Cu(n,t){n=Xo.lab(n),t=Xo.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ot(e+i*n,r+o*n,u+a*n)+""}}function Nu(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Lu(n){var t=[n.a,n.b],e=[n.c,n.d],r=qu(t),u=Tu(t,e),i=qu(zu(e,t,-u))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,u*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*La,this.translate=[n.e,n.f],this.scale=[r,i],this.skew=i?Math.atan2(u,i)*La:0}function Tu(n,t){return n[0]*t[0]+n[1]*t[1]}function qu(n){var t=Math.sqrt(Tu(n,n));return t&&(n[0]/=t,n[1]/=t),t}function zu(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ru(n,t){var e,r=[],u=[],i=Xo.transform(n),o=Xo.transform(t),a=i.translate,c=o.translate,s=i.rotate,l=o.rotate,f=i.skew,h=o.skew,g=i.scale,p=o.scale;return a[0]!=c[0]||a[1]!=c[1]?(r.push("translate(",null,",",null,")"),u.push({i:1,x:su(a[0],c[0])},{i:3,x:su(a[1],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),s!=l?(s-l>180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:su(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:su(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:su(g[0],p[0])},{i:e-2,x:su(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i<e;)r[(t=u[i]).i]=t.x(n);return r.join("")}}function Du(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function Pu(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function Uu(n){for(var t=n.source,e=n.target,r=Hu(t,e),u=[t];t!==r;)t=t.parent,u.push(t);for(var i=u.length;e!==r;)u.splice(i,0,e),e=e.parent;return u}function ju(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Hu(n,t){if(n===t)return n;for(var e=ju(n),r=ju(t),u=e.pop(),i=r.pop(),o=null;u===i;)o=u,u=e.pop(),i=r.pop();return o}function Fu(n){n.fixed|=2}function Ou(n){n.fixed&=-7}function Yu(n){n.fixed|=4,n.px=n.x,n.py=n.y}function Iu(n){n.fixed&=-5}function Zu(n,t,e){var r=0,u=0;if(n.charge=0,!n.leaf)for(var i,o=n.nodes,a=o.length,c=-1;++c<a;)i=o[c],null!=i&&(Zu(i,t,e),n.charge+=i.charge,r+=i.charge*i.cx,u+=i.charge*i.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var s=t*e[n.point.index];n.charge+=n.pointCharge=s,r+=s*n.point.x,u+=s*n.point.y}n.cx=r/n.charge,n.cy=u/n.charge}function Vu(n,t){return Xo.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=Wu,n}function Xu(n){return n.children}function $u(n){return n.value}function Bu(n,t){return t.value-n.value}function Wu(n){return Xo.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function Ju(n){return n.x}function Gu(n){return n.y}function Ku(n,t,e){n.y0=t,n.y=e}function Qu(n){return Xo.range(n.length)}function ni(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function ti(n){for(var t,e=1,r=0,u=n[0][1],i=n.length;i>e;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function ei(n){return n.reduce(ri,0)}function ri(n,t){return n+t[1]}function ui(n,t){return ii(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ii(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function oi(n){return[Xo.min(n),Xo.max(n)]}function ai(n,t){return n.parent==t.parent?1:2}function ci(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function si(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function li(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i<u;)t(r=li(e[i],t),n)>0&&(n=r);return n}function fi(n,t){return n.x-t.x}function hi(n,t){return t.x-n.x}function gi(n,t){return n.depth-t.depth}function pi(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c<o;)i=u[c],e(i,a),a=i;t(n,r)}e(n,null)}function vi(n){for(var t,e=0,r=0,u=n.children,i=u.length;--i>=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function di(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function mi(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function yi(n,t){return n.value-t.value}function xi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Mi(n,t){n._pack_next=t,t._pack_prev=n}function _i(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function bi(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(wi),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],Ei(r,u,i),t(i),xi(r,i),r._pack_prev=i,xi(i,u),u=r._pack_next,o=3;s>o;o++){Ei(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(_i(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!_i(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.r<r.r?Mi(r,u=a):Mi(r=c,u),o--):(xi(r,i),u=i,t(i))}var m=(l+f)/2,y=(h+g)/2,x=0;for(o=0;s>o;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Si)}}function wi(n){n._pack_next=n._pack_prev=n}function Si(n){delete n._pack_next,delete n._pack_prev}function ki(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i<o;)ki(u[i],t,e,r)}function Ei(n,t,e){var r=n.r+e.r,u=t.x-n.x,i=t.y-n.y;if(r&&(u||i)){var o=t.r+e.r,a=u*u+i*i;o*=o,r*=r;var c=.5+(r-o)/(2*a),s=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+c*u+s*i,e.y=n.y+c*i-s*u}else e.x=n.x+r,e.y=n.y}function Ai(n){return 1+Xo.max(n,function(n){return n.y})}function Ci(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Ni(n){var t=n.children;return t&&t.length?Ni(t[0]):n}function Li(n){var t,e=n.children;return e&&(t=e.length)?Li(e[t-1]):n}function Ti(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function qi(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function zi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ri(n){return n.rangeExtent?n.rangeExtent():zi(n.range())}function Di(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Pi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Ui(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ls}function ji(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)u.push(e(n[o-1],n[o])),i.push(r(t[o-1],t[o]));return function(t){var e=Xo.bisect(n,t,1,a)-1;return i[e](u[e](t))}}function Hi(n,t,e,r){function u(){var u=Math.min(n.length,t.length)>2?ji:Di,c=r?Pu:Du;return o=u(n,t,c,e),a=u(t,n,c,fu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Nu)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Ii(n,t)},i.tickFormat=function(t,e){return Zi(n,t,e)},i.nice=function(t){return Oi(n,t),u()},i.copy=function(){return Hi(n,t,e,r)},u()}function Fi(n,t){return Xo.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Oi(n,t){return Pi(n,Ui(Yi(n,t)[2]))}function Yi(n,t){null==t&&(t=10);var e=zi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Ii(n,t){return Xo.range.apply(Xo,Yi(n,t))}function Zi(n,t,e){var r=Yi(n,t);return Xo.format(e?e.replace(Qa,function(n,t,e,u,i,o,a,c,s,l){return[t,e,u,i,o,a,c,s||"."+Xi(l,r),l].join("")}):",."+Vi(r[2])+"f")}function Vi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Xi(n,t){var e=Vi(t[2]);return n in fs?Math.abs(e-Vi(Math.max(Math.abs(t[0]),Math.abs(t[1]))))+ +("e"!==n):e-2*("%"===n)}function $i(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Pi(r.map(u),e?Math:gs);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=zi(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++<l;)for(var h=f-1;h>0;h--)o.push(i(s)*h);for(s=0;o[s]<a;s++);for(l=o.length;o[l-1]>c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return hs;arguments.length<2?t=hs:"function"!=typeof t&&(t=Xo.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return $i(n.copy(),t,e,r)},Fi(o,n)}function Bi(n,t,e){function r(t){return n(u(t))}var u=Wi(t),i=Wi(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Ii(e,n)},r.tickFormat=function(n,t){return Zi(e,n,t)},r.nice=function(n){return r.domain(Oi(e,n))},r.exponent=function(o){return arguments.length?(u=Wi(t=o),i=Wi(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Bi(n.copy(),t,e)},Fi(r,n)}function Wi(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Ji(n,t){function e(e){return o[((i.get(e)||"range"===t.t&&i.set(e,n.push(e)))-1)%o.length]}function r(t,e){return Xo.range(n.length).map(function(n){return t+e*n})}var i,o,a;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new u;for(var o,a=-1,c=r.length;++a<c;)i.has(o=r[a])||i.set(o,n.push(o));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(o=n,a=0,t={t:"range",a:arguments},e):o},e.rangePoints=function(u,i){arguments.length<2&&(i=0);var c=u[0],s=u[1],l=(s-c)/(Math.max(1,n.length-1)+i);return o=r(n.length<2?(c+s)/2:c+l*i/2,l),a=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=(f-l)/(n.length-i+2*c);return o=r(l+h*c,h),s&&o.reverse(),a=h*(1-i),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=Math.floor((f-l)/(n.length-i+2*c)),g=f-l-(n.length-i)*h;return o=r(l+Math.round(g/2),h),s&&o.reverse(),a=Math.round(h*(1-i)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return a},e.rangeExtent=function(){return zi(t.a[0])},e.copy=function(){return Ji(n,t)},e.domain(n)}function Gi(n,t){function e(){var e=0,i=t.length;for(u=[];++e<i;)u[e-1]=Xo.quantile(n,e/i);return r}function r(n){return isNaN(n=+n)?void 0:t[Xo.bisect(u,n)]}var u;return r.domain=function(t){return arguments.length?(n=t.filter(function(n){return!isNaN(n)}).sort(Xo.ascending),e()):n},r.range=function(n){return arguments.length?(t=n,e()):t},r.quantiles=function(){return u},r.invertExtent=function(e){return e=t.indexOf(e),0>e?[0/0,0/0]:[e>0?u[e-1]:n[0],e<u.length?u[e]:n[n.length-1]]},r.copy=function(){return Gi(n,t)},e()}function Ki(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(i*(t-n))))]}function u(){return i=e.length/(t-n),o=e.length-1,r}var i,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],u()):[n,t]},r.range=function(n){return arguments.length?(e=n,u()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return Ki(n,t,e)},u()}function Qi(n,t){function e(e){return e>=e?t[Xo.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return Qi(n,t)},e}function no(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Ii(n,t)},t.tickFormat=function(t,e){return Zi(n,t,e)},t.copy=function(){return no(n)},t}function to(n){return n.innerRadius}function eo(n){return n.outerRadius}function ro(n){return n.startAngle}function uo(n){return n.endAngle}function io(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=_t(e),p=_t(r);++f<h;)u.call(this,c=t[f],f)?l.push([+g.call(this,c,f),+p.call(this,c,f)]):l.length&&(o(),l=[]);return l.length&&o(),s.length?s.join(""):null}var e=br,r=wr,u=be,i=oo,o=i.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(u=n,t):u},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?i=n:(i=Ms.get(n)||oo).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function oo(n){return n.join("L")}function ao(n){return oo(n)+"Z"}function co(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&u.push("H",r[0]),u.join("")}function so(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("V",(r=n[t])[1],"H",r[0]);return u.join("")}function lo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r=n[t])[0],"V",r[1]);return u.join("")}function fo(n,t){return n.length<4?oo(n):n[1]+po(n.slice(1,n.length-1),vo(n,t))}function ho(n,t){return n.length<3?oo(n):n[0]+po((n.push(n[0]),n),vo([n[n.length-2]].concat(n,[n[1]]),t))}function go(n,t){return n.length<3?oo(n):n[0]+po(n,vo(n,t))}function po(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return oo(n);var e=n.length!=t.length,r="",u=n[0],i=n[1],o=t[0],a=o,c=1;if(e&&(r+="Q"+(i[0]-2*o[0]/3)+","+(i[1]-2*o[1]/3)+","+i[0]+","+i[1],u=n[1],c=2),t.length>1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s<t.length;s++,c++)i=n[c],a=t[s],r+="S"+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1]}if(e){var l=n[c];r+="Q"+(i[0]+2*a[0]/3)+","+(i[1]+2*a[1]/3)+","+l[0]+","+l[1]}return r}function vo(n,t){for(var e,r=[],u=(1-t)/2,i=n[0],o=n[1],a=1,c=n.length;++a<c;)e=i,i=o,o=n[a],r.push([u*(o[0]-e[0]),u*(o[1]-e[1])]);return r}function mo(n){if(n.length<3)return oo(n);var t=1,e=n.length,r=n[0],u=r[0],i=r[1],o=[u,u,u,(r=n[1])[0]],a=[i,i,i,r[1]],c=[u,",",i,"L",_o(ws,o),",",_o(ws,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),bo(c,o,a);return n.pop(),c.push("L",r),c.join("")}function yo(n){if(n.length<4)return oo(n);for(var t,e=[],r=-1,u=n.length,i=[0],o=[0];++r<3;)t=n[r],i.push(t[0]),o.push(t[1]);for(e.push(_o(ws,i)+","+_o(ws,o)),--r;++r<u;)t=n[r],i.shift(),i.push(t[0]),o.shift(),o.push(t[1]),bo(e,i,o);return e.join("")}function xo(n){for(var t,e,r=-1,u=n.length,i=u+4,o=[],a=[];++r<4;)e=n[r%u],o.push(e[0]),a.push(e[1]);for(t=[_o(ws,o),",",_o(ws,a)],--r;++r<i;)e=n[r%u],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),bo(t,o,a);return t.join("")}function Mo(n,t){var e=n.length-1;if(e)for(var r,u,i=n[0][0],o=n[0][1],a=n[e][0]-i,c=n[e][1]-o,s=-1;++s<=e;)r=n[s],u=s/e,r[0]=t*r[0]+(1-t)*(i+u*a),r[1]=t*r[1]+(1-t)*(o+u*c);return mo(n)}function _o(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function bo(n,t,e){n.push("C",_o(_s,t),",",_o(_s,e),",",_o(bs,t),",",_o(bs,e),",",_o(ws,t),",",_o(ws,e))}function wo(n,t){return(t[1]-n[1])/(t[0]-n[0])}function So(n){for(var t=0,e=n.length-1,r=[],u=n[0],i=n[1],o=r[0]=wo(u,i);++t<e;)r[t]=(o+(o=wo(u=i,i=n[t+1])))/2;return r[t]=o,r}function ko(n){for(var t,e,r,u,i=[],o=So(n),a=-1,c=n.length-1;++a<c;)t=wo(n[a],n[a+1]),oa(t)<Aa?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,u=e*e+r*r,u>9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function Eo(n){return n.length<3?oo(n):n[0]+po(n,ko(n))}function Ao(n){for(var t,e,r,u=-1,i=n.length;++u<i;)t=n[u],e=t[0],r=t[1]+ys,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Co(n){function t(t){function c(){v.push("M",a(n(m),f),l,s(n(d.reverse()),f),"Z")}for(var h,g,p,v=[],d=[],m=[],y=-1,x=t.length,M=_t(e),_=_t(u),b=e===r?function(){return g}:_t(r),w=u===i?function(){return p}:_t(i);++y<x;)o.call(this,h=t[y],y)?(d.push([g=+M.call(this,h,y),p=+_.call(this,h,y)]),m.push([+b.call(this,h,y),+w.call(this,h,y)])):d.length&&(c(),d=[],m=[]);return d.length&&c(),v.length?v.join(""):null}var e=br,r=br,u=0,i=wr,o=be,a=oo,c=a.key,s=a,l="L",f=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(u=i=n,t):i},t.y0=function(n){return arguments.length?(u=n,t):u},t.y1=function(n){return arguments.length?(i=n,t):i},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?a=n:(a=Ms.get(n)||oo).key,s=a.reverse||a,l=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(f=n,t):f},t}function No(n){return n.radius}function Lo(n){return[n.x,n.y]}function To(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]+ys;return[e*Math.cos(r),e*Math.sin(r)]}}function qo(){return 64}function zo(){return"circle"}function Ro(n){var t=Math.sqrt(n/Sa);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Do(n,t){return fa(n,Ns),n.id=t,n}function Po(n,t,e,r){var u=n.id;return R(n,"function"==typeof e?function(n,i,o){n.__transition__[u].tween.set(t,r(e.call(n,n.__data__,i,o)))}:(e=r(e),function(n){n.__transition__[u].tween.set(t,e)}))}function Uo(n){return null==n&&(n=""),function(){this.textContent=n}}function jo(n,t,e,r){var i=n.__transition__||(n.__transition__={active:0,count:0}),o=i[e];if(!o){var a=r.time;o=i[e]={tween:new u,time:a,ease:r.ease,delay:r.delay,duration:r.duration},++i.count,Xo.timer(function(r){function u(r){return i.active>e?s():(i.active=e,o.event&&o.event.start.call(n,l,t),o.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),Xo.timer(function(){return p.c=c(r||1)?be:c,1},0,a),void 0)}function c(r){if(i.active!==e)return s();for(var u=r/g,a=f(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,l,t),s()):void 0}function s(){return--i.count?delete i[e]:delete n.__transition__,1}var l=n.__data__,f=o.ease,h=o.delay,g=o.duration,p=Ja,v=[];return p.t=h+a,r>=h?u(r-h):(p.c=u,void 0)},0,a)}}function Ho(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function Fo(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function Oo(n){return n.toISOString()}function Yo(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=Xo.bisect(js,u);return i==js.length?[t.year,Yi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/js[i-1]<js[i]/u?i-1:i]:[Os,Yi(n,e)[2]]}return r.invert=function(t){return Io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(Io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,Io(+e+1),t).length}var i=r.domain(),o=zi(i),a=null==n?u(o,10):"number"==typeof n&&u(o,n);return a&&(n=a[0],t=a[1]),r.domain(Pi(i,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=Io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=zi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Yo(n.copy(),t,e)},Fi(r,n)}function Io(n){return new Date(n)}function Zo(n){return JSON.parse(n.responseText)}function Vo(n){var t=Wo.createRange();return t.selectNode(Wo.body),t.createContextualFragment(n.responseText)}var Xo={version:"3.4.3"};Date.now||(Date.now=function(){return+new Date});var $o=[].slice,Bo=function(n){return $o.call(n)},Wo=document,Jo=Wo.documentElement,Go=window;try{Bo(Jo.childNodes)[0].nodeType}catch(Ko){Bo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{Wo.createElement("div").style.setProperty("opacity",0,"")}catch(Qo){var na=Go.Element.prototype,ta=na.setAttribute,ea=na.setAttributeNS,ra=Go.CSSStyleDeclaration.prototype,ua=ra.setProperty;na.setAttribute=function(n,t){ta.call(this,n,t+"")},na.setAttributeNS=function(n,t,e){ea.call(this,n,t,e+"")},ra.setProperty=function(n,t,e){ua.call(this,n,t+"",e)}}Xo.ascending=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},Xo.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},Xo.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&e>r&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&e>r&&(e=r)}return e},Xo.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&r>e&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&r>e&&(e=r)}return e},Xo.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i<o&&!(null!=(e=u=n[i])&&e>=e);)e=u=void 0;for(;++i<o;)null!=(r=n[i])&&(e>r&&(e=r),r>u&&(u=r))}else{for(;++i<o&&!(null!=(e=u=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<o;)null!=(r=t.call(n,n[i],i))&&(e>r&&(e=r),r>u&&(u=r))}return[e,u]},Xo.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i<u;)isNaN(e=+n[i])||(r+=e);else for(;++i<u;)isNaN(e=+t.call(n,n[i],i))||(r+=e);return r},Xo.mean=function(t,e){var r,u=t.length,i=0,o=-1,a=0;if(1===arguments.length)for(;++o<u;)n(r=t[o])&&(i+=(r-i)/++a);else for(;++o<u;)n(r=e.call(t,t[o],o))&&(i+=(r-i)/++a);return a?i:void 0},Xo.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),u=+n[r-1],i=e-r;return i?u+i*(n[r]-u):u},Xo.median=function(t,e){return arguments.length>1&&(t=t.map(e)),t=t.filter(n),t.length?Xo.quantile(t.sort(Xo.ascending),.5):void 0},Xo.bisector=function(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n.call(t,t[i],i)<e?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;e<n.call(t,t[i],i)?u=i:r=i+1}return r}}};var ia=Xo.bisector(function(n){return n});Xo.bisectLeft=ia.left,Xo.bisect=Xo.bisectRight=ia.right,Xo.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},Xo.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},Xo.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},Xo.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,e=Xo.min(arguments,t),r=new Array(e);++n<e;)for(var u,i=-1,o=r[n]=new Array(u);++i<u;)o[i]=arguments[i][n];return r},Xo.transpose=function(n){return Xo.zip.apply(Xo,n)},Xo.keys=function(n){var t=[];for(var e in n)t.push(e);return t},Xo.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},Xo.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Xo.merge=function(n){for(var t,e,r,u=n.length,i=-1,o=0;++i<u;)o+=n[i].length;for(e=new Array(o);--u>=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var oa=Math.abs;Xo.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var u,i=[],o=e(oa(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(u=n+r*++a)>t;)i.push(u/o);else for(;(u=n+r*++a)<t;)i.push(u/o);return i},Xo.map=function(n){var t=new u;if(n instanceof u)n.forEach(function(n,e){t.set(n,e)});else for(var e in n)t.set(e,n[e]);return t},r(u,{has:i,get:function(n){return this[aa+n]},set:function(n,t){return this[aa+n]=t},remove:o,keys:a,values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1),this[t])}});var aa="\x00",ca=aa.charCodeAt(0);Xo.nest=function(){function n(t,a,c){if(c>=o.length)return r?r.call(i,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=o[c++],d=new u;++g<p;)(h=d.get(s=v(l=a[g])))?h.push(l):d.set(s,[l]);return t?(l=t(),f=function(e,r){l.set(e,n(t,r,c))}):(l={},f=function(e,r){l[e]=n(t,r,c)}),d.forEach(f),l}function t(n,e){if(e>=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(Xo.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},Xo.set=function(n){var t=new l;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(l,{has:i,add:function(n){return this[aa+n]=!0,n},remove:function(n){return n=aa+n,n in this&&delete this[n]},values:a,size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1))}}),Xo.behavior={},Xo.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r<u;)n[e=arguments[r]]=f(n,t,t[e]);return n};var sa=["webkit","ms","moz","Moz","o","O"];Xo.dispatch=function(){for(var n=new p,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=v(n);return n},p.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Xo.event=null,Xo.requote=function(n){return n.replace(la,"\\$&")};var la=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,fa={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ha=function(n,t){return t.querySelector(n)},ga=function(n,t){return t.querySelectorAll(n)},pa=Jo[h(Jo,"matchesSelector")],va=function(n,t){return pa.call(n,t)};"function"==typeof Sizzle&&(ha=function(n,t){return Sizzle(n,t)[0]||null},ga=Sizzle,va=Sizzle.matchesSelector),Xo.selection=function(){return xa};var da=Xo.selection.prototype=[];da.select=function(n){var t,e,r,u,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var c=-1,s=r.length;++c<s;)(u=r[c])?(t.push(e=n.call(u,u.__data__,c,o)),e&&"__data__"in u&&(e.__data__=u.__data__)):t.push(null)}return x(i)},da.selectAll=function(n){var t,e,r=[];n=_(n);for(var u=-1,i=this.length;++u<i;)for(var o=this[u],a=-1,c=o.length;++a<c;)(e=o[a])&&(r.push(t=Bo(n.call(e,e.__data__,a,u))),t.parentNode=e);return x(r)};var ma={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};Xo.ns={prefix:ma,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),ma.hasOwnProperty(e)?{space:ma[e],local:n}:n}},da.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Xo.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(b(t,n[t]));return this}return this.each(b(n,t))},da.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=k(n)).length,u=-1;if(t=e.classList){for(;++u<r;)if(!t.contains(n[u]))return!1}else for(t=e.getAttribute("class");++u<r;)if(!S(n[u]).test(t))return!1;return!0}for(t in n)this.each(E(t,n[t]));return this}return this.each(E(n,t))},da.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(C(e,n[e],t));return this}if(2>r)return Go.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(C(n,t,e))},da.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(N(t,n[t]));return this}return this.each(N(n,t))},da.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},da.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},da.append=function(n){return n=L(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},da.insert=function(n,t){return n=L(n),t=M(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},da.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},da.data=function(n,t){function e(n,e){var r,i,o,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new u,y=new u,x=[];for(r=-1;++r<a;)d=t.call(i=n[r],i.__data__,r),m.has(d)?v[r]=i:m.set(d,i),x.push(d);for(r=-1;++r<f;)d=t.call(e,o=e[r],r),(i=m.get(d))?(g[r]=i,i.__data__=o):y.has(d)||(p[r]=T(o)),y.set(d,o),m.remove(d);for(r=-1;++r<a;)m.has(x[r])&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],o=e[r],i?(i.__data__=o,g[r]=i):p[r]=T(o);for(;f>r;++r)p[r]=T(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),s.push(g),l.push(v)}var r,i,o=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++o<a;)(i=r[o])&&(n[o]=i.__data__);return n}var c=D([]),s=x([]),l=x([]);if("function"==typeof n)for(;++o<a;)e(r=this[o],n.call(r,r.parentNode.__data__,o));else for(;++o<a;)e(r=this[o],n);return s.enter=function(){return c},s.exit=function(){return l},s},da.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},da.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return x(u)},da.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],u=r.length-1,i=r[u];--u>=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},da.sort=function(n){n=z.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},da.each=function(n){return R(this,function(t,e,r){n.call(t,t.__data__,e,r)})},da.call=function(n){var t=Bo(arguments);return n.apply(t[0]=this,t),this},da.empty=function(){return!this.node()},da.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},da.size=function(){var n=0;return this.each(function(){++n}),n};var ya=[];Xo.selection.enter=D,Xo.selection.enter.prototype=ya,ya.append=da.append,ya.empty=da.empty,ya.node=da.node,ya.call=da.call,ya.size=da.size,ya.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++a<c;){r=(u=this[a]).update,o.push(t=[]),t.parentNode=u.parentNode;for(var s=-1,l=u.length;++s<l;)(i=u[s])?(t.push(r[s]=e=n.call(u.parentNode,i.__data__,s,a)),e.__data__=i.__data__):t.push(null)}return x(o)},ya.insert=function(n,t){return arguments.length<2&&(t=P(this)),da.insert.call(this,n,t)},da.transition=function(){for(var n,t,e=ks||++Ls,r=[],u=Es||{time:Date.now(),ease:yu,delay:0,duration:250},i=-1,o=this.length;++i<o;){r.push(n=[]);for(var a=this[i],c=-1,s=a.length;++c<s;)(t=a[c])&&jo(t,c,e,u),n.push(t)}return Do(r,e)},da.interrupt=function(){return this.each(U)},Xo.select=function(n){var t=["string"==typeof n?ha(n,Wo):n];return t.parentNode=Jo,x([t])},Xo.selectAll=function(n){var t=Bo("string"==typeof n?ga(n,Wo):n);return t.parentNode=Jo,x([t])};var xa=Xo.select(Jo);da.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(j(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(j(n,t,e))};var Ma=Xo.map({mouseenter:"mouseover",mouseleave:"mouseout"});Ma.forEach(function(n){"on"+n in Wo&&Ma.remove(n)});var _a="onselectstart"in Wo?null:h(Jo.style,"userSelect"),ba=0;Xo.mouse=function(n){return Y(n,m())};var wa=/WebKit/.test(Go.navigator.userAgent)?-1:0;Xo.touches=function(n,t){return arguments.length<2&&(t=m().touches),t?Bo(t).map(function(t){var e=Y(n,t);return e.identifier=t.identifier,e}):[]},Xo.behavior.drag=function(){function n(){this.on("mousedown.drag",o).on("touchstart.drag",a)}function t(){return Xo.event.changedTouches[0].identifier}function e(n,t){return Xo.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function o(){var n=t(l,g),e=n[0]-v[0],r=n[1]-v[1];d|=e|r,v=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function a(){m.on(e+"."+p,null).on(r+"."+p,null),y(d&&Xo.event.target===h),f({type:"dragend"})}var c,s=this,l=s.parentNode,f=u.of(s,arguments),h=Xo.event.target,g=n(),p=null==g?"drag":"drag-"+g,v=t(l,g),d=0,m=Xo.select(Go).on(e+"."+p,o).on(r+"."+p,a),y=O();i?(c=i.apply(s,arguments),c=[c.x-v[0],c.y-v[1]]):c=[0,0],f({type:"dragstart"})}}var u=y(n,"drag","dragstart","dragend"),i=null,o=r(g,Xo.mouse,"mousemove","mouseup"),a=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},Xo.rebind(n,u,"on")};var Sa=Math.PI,ka=2*Sa,Ea=Sa/2,Aa=1e-6,Ca=Aa*Aa,Na=Sa/180,La=180/Sa,Ta=Math.SQRT2,qa=2,za=4;Xo.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=B(v),o=i/(qa*h)*(e*W(Ta*t+v)-$(v));return[r+o*s,u+o*l,i*e/B(Ta*t+v)]}return[r+n*s,u+n*l,i*Math.exp(Ta*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+za*f)/(2*i*qa*h),p=(c*c-i*i-za*f)/(2*c*qa*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ta;return e.duration=1e3*y,e},Xo.behavior.zoom=function(){function n(n){n.on(A,s).on(Pa+".zoom",f).on(C,h).on("dblclick.zoom",g).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(M.range().map(function(n){return(n-S.x)/S.k}).map(M.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u(Xo.mouse(r),g),a(i)}function e(){f.on(C,Go===r?h:null).on(N,null),p(l&&Xo.event.target===s),c(i)}var r=this,i=T.of(r,arguments),s=Xo.event.target,l=0,f=Xo.select(Go).on(C,n).on(N,e),g=t(Xo.mouse(r)),p=O();U.call(r),o(i)}function l(){function n(){var n=Xo.touches(g);return h=S.k,n.forEach(function(n){n.identifier in v&&(v[n.identifier]=t(n))}),n}function e(){for(var t=Xo.event.changedTouches,e=0,i=t.length;i>e;++e)v[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-x){var s=o[0],l=v[s.identifier];r(2*S.k),u(s,l),d(),a(p)}x=c}else if(o.length>1){var s=o[0],f=o[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function i(){for(var n,t,e,i,o=Xo.touches(g),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=v[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=m&&Math.sqrt(l/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*h)}x=null,u(n,t),a(p)}function f(){if(Xo.event.touches.length){for(var t=Xo.event.changedTouches,e=0,r=t.length;r>e;++e)delete v[t[e].identifier];for(var u in v)return void n()}b.on(M,null).on(_,null),w.on(A,s).on(L,l),k(),c(p)}var h,g=this,p=T.of(g,arguments),v={},m=0,y=Xo.event.changedTouches[0].identifier,M="touchmove.zoom-"+y,_="touchend.zoom-"+y,b=Xo.select(Go).on(M,i).on(_,f),w=Xo.select(g).on(A,null).on(L,e),k=O();U.call(g),e(),o(p)}function f(){var n=T.of(this,arguments);m?clearTimeout(m):(U.call(this),o(n)),m=setTimeout(function(){m=null,c(n)},50),d();var e=v||Xo.mouse(this);p||(p=t(e)),r(Math.pow(2,.002*Ra())*S.k),u(e,p),a(n)}function h(){p=null}function g(){var n=T.of(this,arguments),e=Xo.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,Xo.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var p,v,m,x,M,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Da,A="mousedown.zoom",C="mousemove.zoom",N="mouseup.zoom",L="touchstart.zoom",T=y(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=T.of(this,arguments),t=S;ks?Xo.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=Xo.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Da:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(v=t&&[+t[0],+t[1]],n):v},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,M=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},Xo.rebind(n,T,"on")};var Ra,Da=[0,1/0],Pa="onwheel"in Wo?(Ra=function(){return-Xo.event.deltaY*(Xo.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Wo?(Ra=function(){return Xo.event.wheelDelta},"mousewheel"):(Ra=function(){return-Xo.event.detail},"MozMousePixelScroll");G.prototype.toString=function(){return this.rgb()+""},Xo.hsl=function(n,t,e){return 1===arguments.length?n instanceof Q?K(n.h,n.s,n.l):dt(""+n,mt,K):K(+n,+t,+e)};var Ua=Q.prototype=new G;Ua.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,this.l/n)},Ua.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,n*this.l)},Ua.rgb=function(){return nt(this.h,this.s,this.l)},Xo.hcl=function(n,t,e){return 1===arguments.length?n instanceof et?tt(n.h,n.c,n.l):n instanceof it?at(n.l,n.a,n.b):at((n=yt((n=Xo.rgb(n)).r,n.g,n.b)).l,n.a,n.b):tt(+n,+t,+e)};var ja=et.prototype=new G;ja.brighter=function(n){return tt(this.h,this.c,Math.min(100,this.l+Ha*(arguments.length?n:1)))},ja.darker=function(n){return tt(this.h,this.c,Math.max(0,this.l-Ha*(arguments.length?n:1)))},ja.rgb=function(){return rt(this.h,this.c,this.l).rgb()},Xo.lab=function(n,t,e){return 1===arguments.length?n instanceof it?ut(n.l,n.a,n.b):n instanceof et?rt(n.l,n.c,n.h):yt((n=Xo.rgb(n)).r,n.g,n.b):ut(+n,+t,+e)};var Ha=18,Fa=.95047,Oa=1,Ya=1.08883,Ia=it.prototype=new G;Ia.brighter=function(n){return ut(Math.min(100,this.l+Ha*(arguments.length?n:1)),this.a,this.b)},Ia.darker=function(n){return ut(Math.max(0,this.l-Ha*(arguments.length?n:1)),this.a,this.b)},Ia.rgb=function(){return ot(this.l,this.a,this.b)},Xo.rgb=function(n,t,e){return 1===arguments.length?n instanceof pt?gt(n.r,n.g,n.b):dt(""+n,gt,nt):gt(~~n,~~t,~~e)};var Za=pt.prototype=new G;Za.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),gt(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):gt(u,u,u)},Za.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),gt(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Za.hsl=function(){return mt(this.r,this.g,this.b)},Za.toString=function(){return"#"+vt(this.r)+vt(this.g)+vt(this.b)};var Va=Xo.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Va.forEach(function(n,t){Va.set(n,ft(t))}),Xo.functor=_t,Xo.xhr=wt(bt),Xo.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=St(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=s)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++<s;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}l=e+2;var r=n.charCodeAt(e+1);return 13===r?(u=!0,10===n.charCodeAt(e+2)&&++l):10===r&&(u=!0),n.substring(t+1,e).replace(/""/g,'"')}for(;s>l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==c)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],s=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new l,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},Xo.csv=Xo.dsv(",","text/csv"),Xo.tsv=Xo.dsv(" ","text/tab-separated-values");var Xa,$a,Ba,Wa,Ja,Ga=Go[h(Go,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Xo.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};$a?$a.n=i:Xa=i,$a=i,Ba||(Wa=clearTimeout(Wa),Ba=1,Ga(Et))},Xo.timer.flush=function(){At(),Ct()},Xo.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var Ka=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Lt);Xo.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Xo.round(n,Nt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),Ka[8+e/3]};var Qa=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,nc=Xo.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Xo.round(n,Nt(n,t))).toFixed(Math.max(0,Math.min(20,Nt(n*(1+1e-15),t))))}}),tc=Xo.time={},ec=Date;zt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){rc.setUTCDate.apply(this._,arguments)},setDay:function(){rc.setUTCDay.apply(this._,arguments)},setFullYear:function(){rc.setUTCFullYear.apply(this._,arguments)},setHours:function(){rc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){rc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){rc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){rc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){rc.setUTCSeconds.apply(this._,arguments)},setTime:function(){rc.setTime.apply(this._,arguments)}};var rc=Date.prototype;tc.year=Rt(function(n){return n=tc.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),tc.years=tc.year.range,tc.years.utc=tc.year.utc.range,tc.day=Rt(function(n){var t=new ec(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),tc.days=tc.day.range,tc.days.utc=tc.day.utc.range,tc.dayOfYear=function(n){var t=tc.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=tc[n]=Rt(function(n){return(n=tc.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});tc[n+"s"]=e.range,tc[n+"s"].utc=e.utc.range,tc[n+"OfYear"]=function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)}}),tc.week=tc.sunday,tc.weeks=tc.sunday.range,tc.weeks.utc=tc.sunday.utc.range,tc.weekOfYear=tc.sundayOfYear;var uc={"-":"",_:" ",0:"0"},ic=/^\s*\d+/,oc=/^%/;Xo.locale=function(n){return{numberFormat:Tt(n),timeFormat:Pt(n)}};var ac=Xo.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Xo.format=ac.numberFormat,Xo.geo={},re.prototype={s:0,t:0,add:function(n){ue(n,this.t,cc),ue(cc.s,this.s,this),this.s?this.t+=cc.t:this.s=cc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cc=new re;Xo.geo.stream=function(n,t){n&&sc.hasOwnProperty(n.type)?sc[n.type](n,t):ie(n,t)};var sc={Feature:function(n,t){ie(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++r<u;)ie(e[r].geometry,t)}},lc={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){oe(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)oe(e[r],t,0)},Polygon:function(n,t){ae(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)ae(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,u=e.length;++r<u;)ie(e[r],t)}};Xo.geo.area=function(n){return fc=0,Xo.geo.stream(n,gc),fc};var fc,hc=new re,gc={sphere:function(){fc+=4*Sa},point:g,lineStart:g,lineEnd:g,polygonStart:function(){hc.reset(),gc.lineStart=ce},polygonEnd:function(){var n=2*hc;fc+=0>n?4*Sa+n:n,gc.lineStart=gc.lineEnd=gc.point=g}};Xo.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=se([t*Na,e*Na]);if(m){var u=fe(m,r),i=[u[1],-u[0],0],o=fe(i,u);pe(o),o=ve(o);var c=t-p,s=c>0?1:-1,v=o[0]*La*s,d=oa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*La;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*La;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=oa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;gc.point(n,e),t(n,e)}function i(){gc.lineStart()}function o(){u(v,d),gc.lineEnd(),oa(y)>Aa&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var l,f,h,g,p,v,d,m,y,x,M,_={point:n,lineStart:e,lineEnd:r,polygonStart:function(){_.point=u,_.lineStart=i,_.lineEnd=o,y=0,gc.polygonStart()},polygonEnd:function(){gc.polygonEnd(),_.point=n,_.lineStart=e,_.lineEnd=r,0>hc?(l=-(h=180),f=-(g=90)):y>Aa?g=90:-Aa>y&&(f=-90),M[0]=l,M[1]=h}};return function(n){g=h=-(l=f=1/0),x=[],Xo.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),Xo.geo.centroid=function(n){pc=vc=dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,kc);var t=bc,e=wc,r=Sc,u=t*t+e*e+r*r;return Ca>u&&(t=xc,e=Mc,r=_c,Aa>vc&&(t=dc,e=mc,r=yc),u=t*t+e*e+r*r,Ca>u)?[0/0,0/0]:[Math.atan2(e,t)*La,X(r/Math.sqrt(u))*La]};var pc,vc,dc,mc,yc,xc,Mc,_c,bc,wc,Sc,kc={sphere:g,point:me,lineStart:xe,lineEnd:Me,polygonStart:function(){kc.lineStart=_e},polygonEnd:function(){kc.lineStart=xe}},Ec=Ee(be,Te,ze,[-Sa,-Sa/2]),Ac=1e9;Xo.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Pe(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(Xo.geo.conicEqualArea=function(){return je(He)}).raw=He,Xo.geo.albers=function(){return Xo.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Xo.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=Xo.geo.albers(),o=Xo.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=Xo.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Aa,f+.12*s+Aa],[l-.214*s-Aa,f+.234*s-Aa]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Aa,f+.166*s+Aa],[l-.115*s-Aa,f+.234*s-Aa]]).stream(c).point,n},n.scale(1070)};var Cc,Nc,Lc,Tc,qc,zc,Rc={point:g,lineStart:g,lineEnd:g,polygonStart:function(){Nc=0,Rc.lineStart=Fe},polygonEnd:function(){Rc.lineStart=Rc.lineEnd=Rc.point=g,Cc+=oa(Nc/2)}},Dc={point:Oe,lineStart:g,lineEnd:g,polygonStart:g,polygonEnd:g},Pc={point:Ze,lineStart:Ve,lineEnd:Xe,polygonStart:function(){Pc.lineStart=$e},polygonEnd:function(){Pc.point=Ze,Pc.lineStart=Ve,Pc.lineEnd=Xe}};Xo.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),Xo.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Cc=0,Xo.geo.stream(n,u(Rc)),Cc},n.centroid=function(n){return dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,u(Pc)),Sc?[bc/Sc,wc/Sc]:_c?[xc/_c,Mc/_c]:yc?[dc/yc,mc/yc]:[0/0,0/0]},n.bounds=function(n){return qc=zc=-(Lc=Tc=1/0),Xo.geo.stream(n,u(Dc)),[[Lc,Tc],[qc,zc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||Je(n):bt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new Ye:new Be(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(Xo.geo.albersUsa()).context(null)},Xo.geo.transform=function(n){return{stream:function(t){var e=new Ge(t);for(var r in n)e[r]=n[r];return e}}},Ge.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Xo.geo.projection=Qe,Xo.geo.projectionMutator=nr,(Xo.geo.equirectangular=function(){return Qe(er)}).raw=er.invert=er,Xo.geo.rotation=function(n){function t(t){return t=n(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t}return n=ur(n[0]%360*Na,n[1]*Na,n.length>2?n[2]*Na:0),t.invert=function(t){return t=n.invert(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t},t},rr.invert=er,Xo.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=ur(-n[0]*Na,-n[1]*Na,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=La,n[1]*=La}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=cr((t=+r)*Na,u*Na),n):t},n.precision=function(r){return arguments.length?(e=cr(t*Na,(u=+r)*Na),n):u},n.angle(90)},Xo.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Na,u=n[1]*Na,i=t[1]*Na,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},Xo.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return Xo.range(Math.ceil(i/d)*d,u,d).map(h).concat(Xo.range(Math.ceil(s/m)*m,c,m).map(g)).concat(Xo.range(Math.ceil(r/p)*p,e,p).filter(function(n){return oa(n%d)>Aa}).map(l)).concat(Xo.range(Math.ceil(a/v)*v,o,v).filter(function(n){return oa(n%m)>Aa}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=lr(a,o,90),f=fr(r,e,y),h=lr(s,c,90),g=fr(i,u,y),n):y},n.majorExtent([[-180,-90+Aa],[180,90-Aa]]).minorExtent([[-180,-80-Aa],[180,80+Aa]])},Xo.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=hr,u=gr;return n.distance=function(){return Xo.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},Xo.geo.interpolate=function(n,t){return pr(n[0]*Na,n[1]*Na,t[0]*Na,t[1]*Na)},Xo.geo.length=function(n){return Uc=0,Xo.geo.stream(n,jc),Uc};var Uc,jc={sphere:g,point:g,lineStart:vr,lineEnd:g,polygonStart:g,polygonEnd:g},Hc=dr(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(Xo.geo.azimuthalEqualArea=function(){return Qe(Hc)}).raw=Hc;var Fc=dr(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},bt);(Xo.geo.azimuthalEquidistant=function(){return Qe(Fc)}).raw=Fc,(Xo.geo.conicConformal=function(){return je(mr)}).raw=mr,(Xo.geo.conicEquidistant=function(){return je(yr)}).raw=yr;var Oc=dr(function(n){return 1/n},Math.atan);(Xo.geo.gnomonic=function(){return Qe(Oc)}).raw=Oc,xr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ea]},(Xo.geo.mercator=function(){return Mr(xr)}).raw=xr;var Yc=dr(function(){return 1},Math.asin);(Xo.geo.orthographic=function(){return Qe(Yc)}).raw=Yc;var Ic=dr(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(Xo.geo.stereographic=function(){return Qe(Ic)}).raw=Ic,_r.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ea]},(Xo.geo.transverseMercator=function(){var n=Mr(_r),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=_r,Xo.geom={},Xo.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=_t(e),i=_t(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(kr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var s=Sr(a),l=Sr(c),f=l[0]===s[0],h=l[l.length-1]===s[s.length-1],g=[];for(t=s.length-1;t>=0;--t)g.push(n[a[s[t]][2]]);for(t=+f;t<l.length-h;++t)g.push(n[a[l[t]][2]]);return g}var e=br,r=wr;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},Xo.geom.polygon=function(n){return fa(n,Zc),n};var Zc=Xo.geom.polygon.prototype=[];Zc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t<e;)n=r,r=this[t],u+=n[1]*r[0]-n[0]*r[1];return.5*u},Zc.centroid=function(n){var t,e,r=-1,u=this.length,i=0,o=0,a=this[u-1];for(arguments.length||(n=-1/(6*this.area()));++r<u;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],i+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[i*n,o*n]},Zc.clip=function(n){for(var t,e,r,u,i,o,a=Cr(n),c=-1,s=this.length-Cr(this),l=this[s-1];++c<s;){for(t=n.slice(),n.length=0,u=this[c],i=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Er(o,l,u)?(Er(i,l,u)||n.push(Ar(i,o,l,u)),n.push(o)):Er(i,l,u)&&n.push(Ar(i,o,l,u)),i=o;a&&n.push(n[0]),l=u}return n};var Vc,Xc,$c,Bc,Wc,Jc=[],Gc=[];Pr.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(jr),t.length},Br.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},Wr.prototype={insert:function(n,t){var e,r,u;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=Qr(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(u=r.R,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.R&&(Gr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Kr(this,r))):(u=r.L,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.L&&(Kr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Gr(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,u=n.U,i=n.L,o=n.R;if(e=i?o?Qr(o):i:o,u?u.L===n?u.L=e:u.R=e:this._=e,i&&o?(r=e.C,e.C=n.C,e.L=i,i.U=e,e!==o?(u=e.U,e.U=n.U,n=e.R,u.L=n,e.R=o,o.U=e):(e.U=u,u=e,n=e.R)):(r=n.C,n=e),n&&(n.U=u),!r){if(n&&n.C)return n.C=!1,void 0;do{if(n===this._)break;if(n===u.L){if(t=u.R,t.C&&(t.C=!1,u.C=!0,Gr(this,u),t=u.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,Kr(this,t),t=u.R),t.C=u.C,u.C=t.R.C=!1,Gr(this,u),n=this._;break}}else if(t=u.L,t.C&&(t.C=!1,u.C=!0,Kr(this,u),t=u.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,Gr(this,t),t=u.L),t.C=u.C,u.C=t.L.C=!1,Kr(this,u),n=this._;break}t.C=!0,n=u,u=u.U}while(!n.C);n&&(n.C=!1)}}},Xo.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],u=a[0][1],i=a[1][0],o=a[1][1];return nu(e(n),a).cells.forEach(function(e,a){var c=e.edges,s=e.site,l=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):s.x>=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Aa)*Aa,y:Math.round(o(n,t)/Aa)*Aa,i:t}})}var r=br,u=wr,i=r,o=u,a=Kc;return n?t(n):(t.links=function(n){return nu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return nu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(jr),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c<s;)u=l,i=f,l=a[c].edge,f=l.l===o?l.r:l.l,r<i.i&&r<f.i&&eu(o,i,f)<0&&t.push([n[r],n[i.i],n[f.i]])}),t},t.x=function(n){return arguments.length?(i=_t(r=n),t):r},t.y=function(n){return arguments.length?(o=_t(u=n),t):u},t.clipExtent=function(n){return arguments.length?(a=null==n?Kc:n,t):a===Kc?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===Kc?null:a&&a[1]},t)};var Kc=[[-1e6,-1e6],[1e6,1e6]];Xo.geom.delaunay=function(n){return Xo.geom.voronoi().triangles(n)},Xo.geom.quadtree=function(n,t,e,r,u){function i(n){function i(n,t,e,r,u,i,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,l=n.y;if(null!=c)if(oa(c-e)+oa(l-r)<.01)s(n,t,e,r,u,i,o,a);else{var f=n.point;n.x=n.y=n.point=null,s(n,f,c,l,u,i,o,a),s(n,t,e,r,u,i,o,a)}else n.x=e,n.y=r,n.point=t}else s(n,t,e,r,u,i,o,a)}function s(n,t,e,r,u,o,a,c){var s=.5*(u+a),l=.5*(o+c),f=e>=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=iu()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=_t(a),M=_t(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.x<v&&(v=l.x),l.y<d&&(d=l.y),l.x>m&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g],g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=iu();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){ou(n,k,v,d,m,y)},g=-1,null==t){for(;++g<p;)i(k,n[g],f[g],h[g],v,d,m,y);--g}else n.forEach(k.add);return f=h=n=l=null,k}var o,a=br,c=wr;return(o=arguments.length)?(a=ru,c=uu,3===o&&(u=e,r=t,e=t=0),i(n)):(i.x=function(n){return arguments.length?(a=n,i):a},i.y=function(n){return arguments.length?(c=n,i):c},i.extent=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],u=+n[1][1]),i):null==t?null:[[t,e],[r,u]]},i.size=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=e=0,r=+n[0],u=+n[1]),i):null==t?null:[r-t,u-e]},i)},Xo.interpolateRgb=au,Xo.interpolateObject=cu,Xo.interpolateNumber=su,Xo.interpolateString=lu;var Qc=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;Xo.interpolate=fu,Xo.interpolators=[function(n,t){var e=typeof t;return("string"===e?Va.has(t)||/^(#|rgb\(|hsl\()/.test(t)?au:lu:t instanceof G?au:"object"===e?Array.isArray(t)?hu:cu:su)(n,t)}],Xo.interpolateArray=hu;var ns=function(){return bt},ts=Xo.map({linear:ns,poly:xu,quad:function(){return du},cubic:function(){return mu},sin:function(){return Mu},exp:function(){return _u},circle:function(){return bu},elastic:wu,back:Su,bounce:function(){return ku}}),es=Xo.map({"in":bt,out:pu,"in-out":vu,"out-in":function(n){return vu(pu(n))}});Xo.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=ts.get(e)||ns,r=es.get(r)||bt,gu(r(e.apply(null,$o.call(arguments,1))))},Xo.interpolateHcl=Eu,Xo.interpolateHsl=Au,Xo.interpolateLab=Cu,Xo.interpolateRound=Nu,Xo.transform=function(n){var t=Wo.createElementNS(Xo.ns.prefix.svg,"g");return(Xo.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Lu(e?e.matrix:rs)})(n)},Lu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var rs={a:1,b:0,c:0,d:1,e:0,f:0};Xo.interpolateTransform=Ru,Xo.layout={},Xo.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Uu(n[e]));return t}},Xo.layout.chord=function(){function n(){var n,s,f,h,g,p={},v=[],d=Xo.range(i),m=[];for(e=[],r=[],n=0,h=-1;++h<i;){for(s=0,g=-1;++g<i;)s+=u[h][g];v.push(s),m.push(Xo.range(i)),n+=s}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&m.forEach(function(n,t){n.sort(function(n,e){return a(u[t][n],u[t][e])})}),n=(ka-l*i)/n,s=0,h=-1;++h<i;){for(f=s,g=-1;++g<i;){var y=d[h],x=m[y][g],M=u[y][x],_=s,b=s+=M*n;p[y+"-"+x]={index:y,subindex:x,startAngle:_,endAngle:b,value:M}}r[y]={index:y,startAngle:f,endAngle:s,value:(s-f)/n},s+=l}for(h=-1;++h<i;)for(g=h-1;++g<i;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,u,i,o,a,c,s={},l=0;return s.matrix=function(n){return arguments.length?(i=(u=n)&&u.length,e=r=null,s):u},s.padding=function(n){return arguments.length?(l=n,e=r=null,s):l},s.sortGroups=function(n){return arguments.length?(o=n,e=r=null,s):o},s.sortSubgroups=function(n){return arguments.length?(a=n,e=null,s):a},s.sortChords=function(n){return arguments.length?(c=n,e&&t(),s):c},s.chords=function(){return e||n(),e},s.groups=function(){return r||n(),r},s},Xo.layout.force=function(){function n(n){return function(t,e,r,u){if(t.point!==n){var i=t.cx-n.x,o=t.cy-n.y,a=u-e,c=i*i+o*o;if(c>a*a/d){if(p>c){var s=t.charge/c;n.px-=i*s,n.py-=o*s}return!0}if(t.point&&c&&p>c){var s=t.pointCharge/c;n.px-=i*s,n.py-=o*s}}return!t.charge}}function t(n){n.px=Xo.event.x,n.py=Xo.event.y,a.resume()}var e,r,u,i,o,a={},c=Xo.dispatch("start","tick","end"),s=[1,1],l=.9,f=us,h=is,g=-30,p=os,v=.1,d=.64,m=[],y=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,x,M,_=m.length,b=y.length;for(e=0;b>e;++e)a=y[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(p=x*x+M*M)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,x*=p,M*=p,h.x-=x*(d=f.weight/(h.weight+f.weight)),h.y-=M*d,f.x+=x*(d=1-d),f.y+=M*d);if((d=r*v)&&(x=s[0]/2,M=s[1]/2,e=-1,d))for(;++e<_;)a=m[e],a.x+=(x-a.x)*d,a.y+=(M-a.y)*d;if(g)for(Zu(t=Xo.geom.quadtree(m),r,o),e=-1;++e<_;)(a=m[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=m[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(m=n,a):m},a.links=function(n){return arguments.length?(y=n,a):y},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.chargeDistance=function(n){return arguments.length?(p=n*n,a):Math.sqrt(p)},a.gravity=function(n){return arguments.length?(v=+n,a):v},a.theta=function(n){return arguments.length?(d=n*n,a):Math.sqrt(d)},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),Xo.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=y[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++a<s;)if(!isNaN(i=o[a][n]))return i;return Math.random()*r}var t,e,r,c=m.length,l=y.length,p=s[0],v=s[1];for(t=0;c>t;++t)(r=m[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=y[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Xo.behavior.drag().origin(bt).on("dragstart.force",Fu).on("drag.force",t).on("dragend.force",Ou)),arguments.length?(this.on("mouseover.force",Yu).on("mouseout.force",Iu).call(e),void 0):e},Xo.rebind(a,c,"on")};var us=20,is=1,os=1/0;Xo.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++f<s;)l=h[f]=n(c[f],p,a),l.parent=t,g+=l.value;r&&h.sort(r),i&&(t.value=g)}else delete t.children,i&&(t.value=+i.call(e,t,o)||0);return t}function t(n,r){var u=n.children,o=0;if(u&&(a=u.length))for(var a,c=-1,s=r+1;++c<a;)o+=t(u[c],s);else i&&(o=+i.call(e,n,r)||0);return i&&(n.value=o),o}function e(t){var e=[];return n(t,0,e),e}var r=Bu,u=Xu,i=$u;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(u=n,e):u},e.value=function(n){return arguments.length?(i=n,e):i},e.revalue=function(n){return t(n,0),n},e},Xo.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,s=-1;for(r=t.value?r/t.value:0;++s<o;)n(a=i[s],e,c=a.value*r,u),e+=c}}function t(n){var e=n.children,r=0;if(e&&(u=e.length))for(var u,i=-1;++i<u;)r=Math.max(r,t(e[i]));return 1+r}function e(e,i){var o=r.call(this,e,i);return n(o[0],0,u[0],u[1]/t(o[0])),o}var r=Xo.layout.hierarchy(),u=[1,1];return e.size=function(n){return arguments.length?(u=n,e):u},Vu(e,r)},Xo.layout.pie=function(){function n(i){var o=i.map(function(e,r){return+t.call(n,e,r)}),a=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof u?u.apply(this,arguments):u)-a)/Xo.sum(o),s=Xo.range(i.length);null!=e&&s.sort(e===as?function(n,t){return o[t]-o[n]}:function(n,t){return e(i[n],i[t])});var l=[];return s.forEach(function(n){var t;l[n]={data:i[n],value:t=o[n],startAngle:a,endAngle:a+=t*c}}),l}var t=Number,e=as,r=0,u=ka;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n};var as={};Xo.layout.stack=function(){function n(a,c){var s=a.map(function(e,r){return t.call(n,e,r)}),l=s.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,l,c);s=Xo.permute(s,f),l=Xo.permute(l,f);var h,g,p,v=r.call(n,l,c),d=s.length,m=s[0].length;for(g=0;m>g;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=bt,e=Qu,r=ni,u=Ku,i=Ju,o=Gu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:cs.get(t)||Qu,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:ss.get(t)||ni,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var cs=Xo.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(ti),i=n.map(ei),o=Xo.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Xo.range(n.length).reverse()},"default":Qu}),ss=Xo.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ni});Xo.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i<g;)o=c[i]=[],o.dx=f[i+1]-(o.x=f[i]),o.y=0;if(g>0)for(i=-1;++i<h;)a=s[i],a>=l[0]&&a<=l[1]&&(o=c[Xo.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=oi,u=ui;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=_t(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return ii(n,t)}:_t(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},Xo.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h<i;)s=r[h],o(s,a),f=c(s,a,f),a=s;vi(n);var g=.5*(l._tree.prelim+s._tree.prelim);t?(u.prelim=t._tree.prelim+e(n,t),u.mod=u.prelim-g):u.prelim=g}else t&&(u.prelim=t._tree.prelim+e(n,t))}function a(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,u=-1;for(t+=n._tree.mod;++u<r;)a(e[u],t)}}function c(n,t,r){if(t){for(var u,i=n,o=n,a=t,c=n.parent.children[0],s=i._tree.mod,l=o._tree.mod,f=a._tree.mod,h=c._tree.mod;a=si(a),i=ci(i),a&&i;)c=ci(c),o=si(o),o._tree.ancestor=n,u=a._tree.prelim+f-i._tree.prelim-s+e(a,i),u>0&&(di(mi(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!si(o)&&(o._tree.thread=a,o._tree.mod+=f-l),i&&!ci(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];pi(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=li(l,hi),h=li(l,fi),g=li(l,gi),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return pi(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,pi(a,function(n){n.r=+l(n.value)}),pi(a,bi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/s))/2;pi(a,function(n){n.r+=f}),pi(a,bi),pi(a,function(n){n.r-=f})}return ki(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=Xo.layout.hierarchy().sort(yi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Vu(n,e)},Xo.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;pi(c,function(n){var t=n.children;t&&t.length?(n.x=Ci(t),n.y=Ai(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Ni(c),f=Li(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return pi(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++u<i;)r=(e=n[u]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(i>e&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++i<o;)u=n[i],u.x=a,u.y=s,u.dy=l,a+=u.dx=Math.min(e.x+e.dx-a,l?c(u.area/l):0);u.z=!0,u.dx+=e.x+e.dx-a,e.y+=l,e.dy-=l}else{for((r||l>e.dx)&&(l=e.dx);++i<o;)u=n[i],u.x=a,u.y=s,u.dx=l,s+=u.dy=Math.min(e.y+e.dy-s,l?c(u.area/l):0);u.z=!1,u.dy+=e.y+e.dy-s,e.x+=l,e.dx-=l}}function i(r){var u=o||a(r),i=u[0];return i.x=0,i.y=0,i.dx=s[0],i.dy=s[1],o&&a.revalue(i),n([i],i.dx*i.dy/i.value),(o?e:t)(i),h&&(o=u),u}var o,a=Xo.layout.hierarchy(),c=Math.round,s=[1,1],l=null,f=Ti,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return i.size=function(n){return arguments.length?(s=n,i):s},i.padding=function(n){function t(t){var e=n.call(i,t,t.depth);return null==e?Ti(t):qi(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return qi(t,n)}if(!arguments.length)return l;var r;return f=null==(l=n)?Ti:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,i},i.round=function(n){return arguments.length?(c=n?Math.round:Number,i):c!=Number},i.sticky=function(n){return arguments.length?(h=n,o=null,i):h},i.ratio=function(n){return arguments.length?(p=n,i):p},i.mode=function(n){return arguments.length?(g=n+"",i):g},Vu(i,a)},Xo.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=Xo.random.normal.apply(Xo,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=Xo.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},Xo.scale={};var ls={floor:bt,ceil:bt};Xo.scale.linear=function(){return Hi([0,1],[0,1],fu,!1)};var fs={s:1,g:1,p:1,r:1,e:1};Xo.scale.log=function(){return $i(Xo.scale.linear().domain([0,1]),10,!0,[1,10])};var hs=Xo.format(".0e"),gs={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};Xo.scale.pow=function(){return Bi(Xo.scale.linear(),1,[0,1])},Xo.scale.sqrt=function(){return Xo.scale.pow().exponent(.5)},Xo.scale.ordinal=function(){return Ji([],{t:"range",a:[[]]})},Xo.scale.category10=function(){return Xo.scale.ordinal().range(ps)},Xo.scale.category20=function(){return Xo.scale.ordinal().range(vs)},Xo.scale.category20b=function(){return Xo.scale.ordinal().range(ds)},Xo.scale.category20c=function(){return Xo.scale.ordinal().range(ms)};var ps=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(ht),vs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(ht),ds=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(ht),ms=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(ht);Xo.scale.quantile=function(){return Gi([],[])},Xo.scale.quantize=function(){return Ki(0,1,[0,1])},Xo.scale.threshold=function(){return Qi([.5],[0,1])},Xo.scale.identity=function(){return no([0,1])},Xo.svg={},Xo.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+ys,a=u.apply(this,arguments)+ys,c=(o>a&&(c=o,o=a,a=c),a-o),s=Sa>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=xs?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z":"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=to,e=eo,r=ro,u=uo;return n.innerRadius=function(e){return arguments.length?(t=_t(e),n):t},n.outerRadius=function(t){return arguments.length?(e=_t(t),n):e},n.startAngle=function(t){return arguments.length?(r=_t(t),n):r},n.endAngle=function(t){return arguments.length?(u=_t(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+ys;return[Math.cos(i)*n,Math.sin(i)*n]},n};var ys=-Ea,xs=ka-Aa;Xo.svg.line=function(){return io(bt)};var Ms=Xo.map({linear:oo,"linear-closed":ao,step:co,"step-before":so,"step-after":lo,basis:mo,"basis-open":yo,"basis-closed":xo,bundle:Mo,cardinal:go,"cardinal-open":fo,"cardinal-closed":ho,monotone:Eo});Ms.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var _s=[0,2/3,1/3,0],bs=[0,1/3,2/3,0],ws=[0,1/6,2/3,1/6];Xo.svg.line.radial=function(){var n=io(Ao);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},so.reverse=lo,lo.reverse=so,Xo.svg.area=function(){return Co(bt)},Xo.svg.area.radial=function(){var n=Co(Ao);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},Xo.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+ys,l=s.call(n,u,r)+ys;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Sa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=hr,o=gr,a=No,c=ro,s=uo;return n.radius=function(t){return arguments.length?(a=_t(t),n):a},n.source=function(t){return arguments.length?(i=_t(t),n):i},n.target=function(t){return arguments.length?(o=_t(t),n):o},n.startAngle=function(t){return arguments.length?(c=_t(t),n):c},n.endAngle=function(t){return arguments.length?(s=_t(t),n):s},n},Xo.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=hr,e=gr,r=Lo;return n.source=function(e){return arguments.length?(t=_t(e),n):t},n.target=function(t){return arguments.length?(e=_t(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},Xo.svg.diagonal.radial=function(){var n=Xo.svg.diagonal(),t=Lo,e=n.projection;return n.projection=function(n){return arguments.length?e(To(t=n)):t},n},Xo.svg.symbol=function(){function n(n,r){return(Ss.get(t.call(this,n,r))||Ro)(e.call(this,n,r))}var t=zo,e=qo;return n.type=function(e){return arguments.length?(t=_t(e),n):t},n.size=function(t){return arguments.length?(e=_t(t),n):e},n};var Ss=Xo.map({circle:Ro,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Cs)),e=t*Cs;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});Xo.svg.symbolTypes=Ss.keys();var ks,Es,As=Math.sqrt(3),Cs=Math.tan(30*Na),Ns=[],Ls=0;Ns.call=da.call,Ns.empty=da.empty,Ns.node=da.node,Ns.size=da.size,Xo.transition=function(n){return arguments.length?ks?n.transition():n:xa.transition()},Xo.transition.prototype=Ns,Ns.select=function(n){var t,e,r,u=this.id,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]);for(var c=this[o],s=-1,l=c.length;++s<l;)(r=c[s])&&(e=n.call(r,r.__data__,s,o))?("__data__"in r&&(e.__data__=r.__data__),jo(e,s,u,r.__transition__[u]),t.push(e)):t.push(null)}return Do(i,u)},Ns.selectAll=function(n){var t,e,r,u,i,o=this.id,a=[];n=_(n);for(var c=-1,s=this.length;++c<s;)for(var l=this[c],f=-1,h=l.length;++f<h;)if(r=l[f]){i=r.__transition__[o],e=n.call(r,r.__data__,f,c),a.push(t=[]);for(var g=-1,p=e.length;++g<p;)(u=e[g])&&jo(u,g,o,i),t.push(u)}return Do(a,o)},Ns.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Do(u,this.id)},Ns.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):R(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Ns.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Ru:fu,a=Xo.ns.qualify(n);return Po(this,"attr."+n,t,a.local?i:u)},Ns.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=Xo.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Ns.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=Go.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=fu(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return Po(this,"style."+n,t,u)},Ns.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,Go.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Ns.text=function(n){return Po(this,"text",n,Uo)},Ns.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Ns.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=Xo.ease.apply(Xo,arguments)),R(this,function(e){e.__transition__[t].ease=n}))},Ns.delay=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},Ns.duration=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},Ns.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Es,u=ks;ks=e,R(this,function(t,r,u){Es=t.__transition__[e],n.call(t,t.__data__,r,u)}),Es=r,ks=u}else R(this,function(r){var u=r.__transition__[e];(u.event||(u.event=Xo.dispatch("start","end"))).on(n,t)});return this},Ns.transition=function(){for(var n,t,e,r,u=this.id,i=++Ls,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,jo(e,s,i,r)),n.push(e)}return Do(o,i)},Xo.svg.axis=function(){function n(n){n.each(function(){var n,s=Xo.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):bt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Aa),d=Xo.transition(p.exit()).style("opacity",Aa).remove(),m=Xo.transition(p).style("opacity",1),y=Ri(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),Xo.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=Ho,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=Ho,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=Fo,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=Fo,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,A=E.rangeBand()/2;l=f=function(n){return E(n)+A}}else l.rangeBand?l=f:d.call(n,f);v.call(n,l),m.call(n,f)})}var t,e=Xo.scale.linear(),r=Ts,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in qs?t+"":Ts,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ts="bottom",qs={top:1,right:1,bottom:1,left:1};Xo.svg.brush=function(){function n(i){i.each(function(){var i=Xo.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(p,bt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return zs[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=Xo.transition(i),h=Xo.transition(o);c&&(l=Ri(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=Ri(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+f[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",f[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1]-f[0])}function u(){function u(){32==Xo.event.keyCode&&(C||(x=null,L[0]-=l[1],L[1]-=f[1],C=2),d())}function p(){32==Xo.event.keyCode&&2==C&&(L[0]+=l[1],L[1]+=f[1],C=0,d())}function v(){var n=Xo.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),C||(Xo.event.altKey?(x||(x=[(l[0]+l[1])/2,(f[0]+f[1])/2]),L[0]=l[+(n[0]<x[0])],L[1]=f[+(n[1]<x[1])]):x=null),E&&m(n,c,0)&&(e(S),u=!0),A&&m(n,s,1)&&(r(S),u=!0),u&&(t(S),w({type:"brush",mode:C?"move":"resize"}))}function m(n,t,e){var r,u,a=Ri(t),c=a[0],s=a[1],p=L[e],v=e?f:l,d=v[1]-v[0];return C&&(c-=p,s-=d+p),r=(e?g:h)?Math.max(c,Math.min(s,n[e])):n[e],C?u=(r+=p)+d:(x&&(p=Math.max(c,Math.min(s,2*x[e]-r))),r>p?(u=r,r=p):u=p),v[0]!=r||v[1]!=u?(e?o=null:i=null,v[0]=r,v[1]=u,!0):void 0}function y(){v(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),Xo.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),N(),w({type:"brushend"})}var x,M,_=this,b=Xo.select(Xo.event.target),w=a.of(_,arguments),S=Xo.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,A=!/^(e|w)$/.test(k)&&s,C=b.classed("extent"),N=O(),L=Xo.mouse(_),T=Xo.select(Go).on("keydown.brush",u).on("keyup.brush",p);if(Xo.event.changedTouches?T.on("touchmove.brush",v).on("touchend.brush",y):T.on("mousemove.brush",v).on("mouseup.brush",y),S.interrupt().selectAll("*").interrupt(),C)L[0]=l[0]-L[0],L[1]=f[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],f[1-z]-L[1]],L[0]=l[q],L[1]=f[z]}else Xo.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),Xo.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),v()}var i,o,a=y(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],f=[0,0],h=!0,g=!0,p=Rs[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:f,i:i,j:o},e=this.__chart__||t;this.__chart__=t,ks?Xo.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,f=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=hu(l,t.x),r=hu(f,t.y);return i=o=null,function(u){l=t.x=e(u),f=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,p=Rs[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,p=Rs[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(h=!!t[0],g=!!t[1]):c?h=!!t:s&&(g=!!t),n):c&&s?[h,g]:c?h:s?g:null},n.extent=function(t){var e,r,u,a,h;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(h=e,e=r,r=h),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(h=u,u=a,a=h),(u!=f[0]||a!=f[1])&&(f=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(h=e,e=r,r=h))),s&&(o?(u=o[0],a=o[1]):(u=f[0],a=f[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(h=u,u=a,a=h))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],f=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&f[0]==f[1]},Xo.rebind(n,a,"on")};var zs={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Rs=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Ds=tc.format=ac.timeFormat,Ps=Ds.utc,Us=Ps("%Y-%m-%dT%H:%M:%S.%LZ");Ds.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Oo:Us,Oo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Oo.toString=Us.toString,tc.second=Rt(function(n){return new ec(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),tc.seconds=tc.second.range,tc.seconds.utc=tc.second.utc.range,tc.minute=Rt(function(n){return new ec(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),tc.minutes=tc.minute.range,tc.minutes.utc=tc.minute.utc.range,tc.hour=Rt(function(n){var t=n.getTimezoneOffset()/60;return new ec(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),tc.hours=tc.hour.range,tc.hours.utc=tc.hour.utc.range,tc.month=Rt(function(n){return n=tc.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),tc.months=tc.month.range,tc.months.utc=tc.month.utc.range;var js=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Hs=[[tc.second,1],[tc.second,5],[tc.second,15],[tc.second,30],[tc.minute,1],[tc.minute,5],[tc.minute,15],[tc.minute,30],[tc.hour,1],[tc.hour,3],[tc.hour,6],[tc.hour,12],[tc.day,1],[tc.day,2],[tc.week,1],[tc.month,1],[tc.month,3],[tc.year,1]],Fs=Ds.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",be]]),Os={range:function(n,t,e){return Xo.range(Math.ceil(n/e)*e,+t,e).map(Io)},floor:bt,ceil:bt};Hs.year=tc.year,tc.scale=function(){return Yo(Xo.scale.linear(),Hs,Fs)};var Ys=Hs.map(function(n){return[n[0].utc,n[1]]}),Is=Ps.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",be]]);Ys.year=tc.year.utc,tc.scale.utc=function(){return Yo(Xo.scale.linear(),Ys,Is)},Xo.text=wt(function(n){return n.responseText}),Xo.json=function(n,t){return St(n,"application/json",Zo,t)},Xo.html=function(n,t){return St(n,"text/html",Vo,t)},Xo.xml=wt(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(Xo):"object"==typeof module&&module.exports?module.exports=Xo:this.d3=Xo}();'use strict';(function(window){window.define=undefined;}).call(this,this);'use strict';tr.exportTo('tr.ui.b',function(){const DataSeriesEnableChangeEventType='data-series-enabled-change';const THIS_DOC=document._currentScript.ownerDocument;const svgNS='http://www.w3.org/2000/svg';const ColorScheme=tr.b.ColorScheme;function getColorOfKey(key,selected){let id=ColorScheme.getColorIdForGeneralPurposeString(key);if(selected){id+=ColorScheme.properties.brightenedOffsets[0];}
return ColorScheme.colorsAsStrings[id];}
function getSVGTextSize(parentNode,text,opt_callback,opt_this){const textNode=document.createElementNS('http://www.w3.org/2000/svg','text');textNode.setAttributeNS(null,'x',0);textNode.setAttributeNS(null,'y',0);textNode.setAttributeNS(null,'fill','black');textNode.appendChild(document.createTextNode(text));parentNode.appendChild(textNode);if(opt_callback){opt_callback.call(opt_this||parentNode,textNode);}
const width=textNode.getComputedTextLength();const height=textNode.getBBox().height;parentNode.removeChild(textNode);return{width,height};}
@@ -7986,10 +7804,10 @@ axisModifier=axisModifier.tickValues(tickValues);tickFormat=v=>v.toString();}
if(this.unit){tickFormat=v=>this.unit.format(v);}
if(tickFormat){axisModifier=axisModifier.tickFormat(tickFormat);}
yAxis.call(axisModifier);},drawYAxisLabel_(label){const labelWidthPx=Math.ceil(tr.ui.b.getSVGTextSize(this.chartAreaElement,this.yAxisLabel).width);label.attr('x',-labelWidthPx).attr('y',-8).text(this.yAxisLabel);},drawYAxisTicks_(yAxis){let previousTop=undefined;yAxis.selectAll('.tick')[0].forEach(function(tick){const bbox=tick.getBBox();const currentTop=tick.transform.baseVal[0].matrix.f;const currentBottom=currentTop+bbox.height;if((previousTop===undefined)||(previousTop>(currentBottom+3))){previousTop=currentTop;}else{tick.style.opacity=0;}});yAxis[0][0].style.opacity=1;},updateContents_(){if(this.textHeightPx_===0){this.textHeightPx_=tr.ui.b.getSVGTextSize(this,'Ay').height;}
-this.updateScales_();super.updateContents_();const chartAreaSel=d3.select(this.chartAreaElement);this.updateXAxis_(chartAreaSel.select('.x.axis'));this.updateYAxis_(chartAreaSel.select('.y.axis'));for(const child of this.querySelectorAll('.axis path, .axis line')){child.style.fill='none';child.style.shapeRendering='crispEdges';child.style.stroke='black';}
+this.updateScales_();super.updateContents_();const chartAreaSel=d3.select(this.chartAreaElement);this.updateXAxis_(chartAreaSel.select('.x.axis'));this.updateYAxis_(chartAreaSel.select('.y.axis'));for(const child of Array.from(this.querySelectorAll('.axis path, .axis line'))){child.style.fill='none';child.style.shapeRendering='crispEdges';child.style.stroke='black';}
this.updateBrushContents_(chartAreaSel.select('#brushes'));this.updateDataContents_(chartAreaSel.select('#series'));},updateDataContents_(seriesSel){throw new Error('Not implemented');},getDataBySeriesKey_(){const dataBySeriesKey={};for(const[key,series]of this.seriesByKey_){dataBySeriesKey[key]=[];}
-this.data_.forEach(function(multiSeriesDatum,index){const x=this.getXForDatum_(multiSeriesDatum,index);d3.keys(multiSeriesDatum).forEach(function(seriesKey){if(seriesKey==='x')return;if(multiSeriesDatum[seriesKey]===undefined)return;if(!this.isDatumFieldSeries_(seriesKey))return;const singleSeriesDatum={x};singleSeriesDatum[seriesKey]=multiSeriesDatum[seriesKey];dataBySeriesKey[seriesKey].push(singleSeriesDatum);},this);},this);return dataBySeriesKey;},getChartPointAtClientPoint_(clientPoint){const rect=this.getBoundingClientRect();return{x:clientPoint.x-rect.left-this.margin.left,y:clientPoint.y-rect.top-this.margin.top};},getDataPointAtChartPoint_(chartPoint){return{x:tr.b.math.clamp(this.xScale_.invert(chartPoint.x),this.xScale_.domain()[0],this.xScale_.domain()[1]),y:tr.b.math.clamp(this.yScale_.invert(chartPoint.y),this.yScale_.domain()[0],this.yScale_.domain()[1])};},getDataPointAtClientPoint_(clientX,clientY){const chartPoint=this.getChartPointAtClientPoint_({x:clientX,y:clientY});return this.getDataPointAtChartPoint_(chartPoint);},prepareDataEvent_(mouseEvent,dataEvent){const dataPoint=this.getDataPointAtClientPoint_(mouseEvent.clientX,mouseEvent.clientY);dataEvent.x=dataPoint.x;dataEvent.y=dataPoint.y;},onMouseDown_(mouseEvent){tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_.bind(this,mouseEvent.button),this.onMouseUp_.bind(this,mouseEvent.button));mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mousedown');dataEvent.button=mouseEvent.button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of this.querySelector('#brushes').children){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseMove_(button,mouseEvent){if(mouseEvent.buttons!==undefined){mouseEvent.preventDefault();mouseEvent.stopPropagation();}
-const dataEvent=new tr.b.Event('item-mousemove');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of this.querySelector('#brushes').children){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseUp_(button,mouseEvent){mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mouseup');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of this.querySelector('#brushes').children){child.setAttribute('fill','rgb(213, 236, 229)');}}};return{ChartBase2D,};});'use strict';tr.exportTo('tr.ui.b',function(){const ChartBase2D=tr.ui.b.ChartBase2D;const ChartBase2DBrushX=tr.ui.b.define('chart-base-2d-brush-1d',ChartBase2D);ChartBase2DBrushX.prototype={__proto__:ChartBase2D.prototype,decorate(){super.decorate();this.brushedRange_=new tr.b.math.Range();},set brushedRange(range){this.brushedRange_.reset();this.brushedRange_.addRange(range);this.updateContents_();},get brushedRange(){return tr.b.math.Range.fromDict(this.brushedRange_.toJSON());},computeBrushRangeFromIndices(indexA,indexB){indexA=tr.b.math.clamp(indexA,0,this.data_.length-1);indexB=tr.b.math.clamp(indexB,0,this.data_.length-1);const leftIndex=Math.min(indexA,indexB);const rightIndex=Math.max(indexA,indexB);const brushRange=new tr.b.math.Range();brushRange.addValue(this.getXForDatum_(this.data_[leftIndex],leftIndex)-
+this.data_.forEach(function(multiSeriesDatum,index){const x=this.getXForDatum_(multiSeriesDatum,index);d3.keys(multiSeriesDatum).forEach(function(seriesKey){if(seriesKey==='x')return;if(multiSeriesDatum[seriesKey]===undefined)return;if(!this.isDatumFieldSeries_(seriesKey))return;const singleSeriesDatum={x};singleSeriesDatum[seriesKey]=multiSeriesDatum[seriesKey];dataBySeriesKey[seriesKey].push(singleSeriesDatum);},this);},this);return dataBySeriesKey;},getChartPointAtClientPoint_(clientPoint){const rect=this.getBoundingClientRect();return{x:clientPoint.x-rect.left-this.margin.left,y:clientPoint.y-rect.top-this.margin.top};},getDataPointAtChartPoint_(chartPoint){return{x:tr.b.math.clamp(this.xScale_.invert(chartPoint.x),this.xScale_.domain()[0],this.xScale_.domain()[1]),y:tr.b.math.clamp(this.yScale_.invert(chartPoint.y),this.yScale_.domain()[0],this.yScale_.domain()[1])};},getDataPointAtClientPoint_(clientX,clientY){const chartPoint=this.getChartPointAtClientPoint_({x:clientX,y:clientY});return this.getDataPointAtChartPoint_(chartPoint);},prepareDataEvent_(mouseEvent,dataEvent){const dataPoint=this.getDataPointAtClientPoint_(mouseEvent.clientX,mouseEvent.clientY);dataEvent.x=dataPoint.x;dataEvent.y=dataPoint.y;},onMouseDown_(mouseEvent){tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_.bind(this,mouseEvent.button),this.onMouseUp_.bind(this,mouseEvent.button));mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mousedown');dataEvent.button=mouseEvent.button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of Array.from(this.querySelector('#brushes').children)){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseMove_(button,mouseEvent){if(mouseEvent.buttons!==undefined){mouseEvent.preventDefault();mouseEvent.stopPropagation();}
+const dataEvent=new tr.b.Event('item-mousemove');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of Array.from(this.querySelector('#brushes').children)){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseUp_(button,mouseEvent){mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mouseup');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of Array.from(this.querySelector('#brushes').children)){child.setAttribute('fill','rgb(213, 236, 229)');}}};return{ChartBase2D,};});'use strict';tr.exportTo('tr.ui.b',function(){const ChartBase2D=tr.ui.b.ChartBase2D;const ChartBase2DBrushX=tr.ui.b.define('chart-base-2d-brush-1d',ChartBase2D);ChartBase2DBrushX.prototype={__proto__:ChartBase2D.prototype,decorate(){super.decorate();this.brushedRange_=new tr.b.math.Range();},set brushedRange(range){this.brushedRange_.reset();this.brushedRange_.addRange(range);this.updateContents_();},get brushedRange(){return tr.b.math.Range.fromDict(this.brushedRange_.toJSON());},computeBrushRangeFromIndices(indexA,indexB){indexA=tr.b.math.clamp(indexA,0,this.data_.length-1);indexB=tr.b.math.clamp(indexB,0,this.data_.length-1);const leftIndex=Math.min(indexA,indexB);const rightIndex=Math.max(indexA,indexB);const brushRange=new tr.b.math.Range();brushRange.addValue(this.getXForDatum_(this.data_[leftIndex],leftIndex)-
this.getSampleWidth_(this.data_,leftIndex,true));brushRange.addValue(this.getXForDatum_(this.data_[rightIndex],rightIndex)+
this.getSampleWidth_(this.data_,rightIndex,false));return brushRange;},getDataIndex_(dataX){if(this.data.length===0)return undefined;const bisect=d3.bisector(this.getXForDatum_.bind(this)).right;return bisect(this.data_,dataX)-1;},prepareDataEvent_(mouseEvent,dataEvent){ChartBase2D.prototype.prepareDataEvent_.call(this,mouseEvent,dataEvent);dataEvent.index=this.getDataIndex_(dataEvent.x);if(dataEvent.index!==undefined){dataEvent.data=this.data_[dataEvent.index];}},updateBrushContents_(brushSel){brushSel.selectAll('*').remove();const brushes=this.brushedRange_.isEmpty?[]:[this.brushedRange_];const brushRectsSel=brushSel.selectAll('rect').data(brushes);brushRectsSel.enter().append('rect');brushRectsSel.exit().remove();this.drawBrush_(brushRectsSel);},drawBrush_(brushRectsSel){brushRectsSel.attr('x',d=>this.xScale_(d.min)).attr('y',0).attr('width',d=>this.xScale_(d.max)-this.xScale_(d.min)).attr('height',this.graphHeight).attr('fill','rgb(213, 236, 229)');}};return{ChartBase2DBrushX,};});'use strict';tr.exportTo('tr.ui.b',function(){const ColumnChart=tr.ui.b.define('column-chart',tr.ui.b.ChartBase2DBrushX);ColumnChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,decorate(){super.decorate();this.xCushion_=1;this.isStacked_=false;this.isGrouped_=false;this.enableHoverBox=true;this.displayXInHover=false;this.enableToolTip=false;this.toolTipCallBack_=()=>{};},set toolTipCallBack(callback){this.toolTipCallBack_=callback;},get toolTipCallBack(){return this.toolTipCallBack_;},set isGrouped(grouped){this.isGrouped_=grouped;if(grouped){this.getDataSeries('group').color='transparent';}
this.updateContents_();},get isGrouped(){return this.isGrouped_;},set isStacked(stacked){this.isStacked_=true;this.updateContents_();},get isStacked(){return this.isStacked_;},get defaultGraphHeight(){return 100;},get defaultGraphWidth(){return 10*this.data_.length;},updateScales_(){if(this.data_.length===0)return;let xDifferences=0;let currentX=undefined;let previousX=undefined;this.data_.forEach(function(datum,index){previousX=currentX;currentX=this.getXForDatum_(datum,index);if(previousX!==undefined){xDifferences+=currentX-previousX;}},this);this.xScale_.range([0,this.graphWidth]);const domain=d3.extent(this.data_,this.getXForDatum_.bind(this));if(this.data_.length>1){this.xCushion_=xDifferences/(this.data_.length-1);}
@@ -8202,7 +8020,7 @@ match(name){return this.regex_&&name.match(this.regex_);}
add(entry){const value=this.entries_.get(entry.name);if(value!==undefined){value.addSample(entry.count,entry.time);}else{this.entries_.set(entry.name,entry);}
this.count_+=entry.count;this.time_+=entry.time;}
get values(){return Array.from(this.entries_.values());}}
-class RuntimeStatsGroupCollection{constructor(){this.blink_cpp_group_=new RuntimeStatsGroup('Blink C++',/.*Callback.*/);this.api_group_=new RuntimeStatsGroup('API',/.*API.*/);this.groups_=[new RuntimeStatsGroup('Total'),new RuntimeStatsGroup('IC',/.*IC_.*/),new RuntimeStatsGroup('Optimize-Background',/(.*OptimizeConcurrent.*)|RecompileConcurrent.*/),new RuntimeStatsGroup('Optimize',/StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),new RuntimeStatsGroup('Compile-Background',/(.*CompileBackground.*)/),new RuntimeStatsGroup('Compile',/(^Compile.*)|(.*_Compile.*)/),new RuntimeStatsGroup('Parse-Background',/.*ParseBackground.*/),new RuntimeStatsGroup('Parse',/.*Parse.*/),this.blink_cpp_group_,this.api_group_,new RuntimeStatsGroup('GC-Background-Marking',/.*GC.MC.BACKGROUND.*MARKING.*/),new RuntimeStatsGroup('GC-Background-Sweeping',/.*GC.MC.BACKGROUND.*SWEEPING.*/),new RuntimeStatsGroup('GC-Background-Scavenger',/.*GC.SCAVENGER.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MinorMC',/.*GC.MINOR_MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MajorMC',/.*GC.MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-Other',/.*GC.*BACKGROUND.*/),new RuntimeStatsGroup('GC',/GC|AllocateInTargetSpace/),new RuntimeStatsGroup('JavaScript',/JS_Execution/),new RuntimeStatsGroup('V8 C++',/.*/)];this.blink_group_collection_=null;}
+class RuntimeStatsGroupCollection{constructor(){this.blink_cpp_group_=new RuntimeStatsGroup('Blink C++',/.*Callback.*/);this.api_group_=new RuntimeStatsGroup('API',/.*API.*/);this.groups_=[new RuntimeStatsGroup('Total'),new RuntimeStatsGroup('IC',/.*IC_.*/),new RuntimeStatsGroup('Optimize-Background',/(.*OptimizeBackground.*)|RecompileConcurrent.*/),new RuntimeStatsGroup('Optimize',/StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),new RuntimeStatsGroup('Compile-Background',/(.*CompileBackground.*)/),new RuntimeStatsGroup('Compile',/(^Compile.*)|(.*_Compile.*)/),new RuntimeStatsGroup('Parse-Background',/.*ParseBackground.*/),new RuntimeStatsGroup('Parse',/.*Parse.*/),this.blink_cpp_group_,this.api_group_,new RuntimeStatsGroup('GC-Background-Marking',/.*GC.MC.BACKGROUND.*MARKING.*/),new RuntimeStatsGroup('GC-Background-Sweeping',/.*GC.MC.BACKGROUND.*SWEEPING.*/),new RuntimeStatsGroup('GC-Background-Scavenger',/.*GC.SCAVENGER.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MinorMC',/.*GC.MINOR_MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MajorMC',/.*GC.MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-Other',/.*GC.*BACKGROUND.*/),new RuntimeStatsGroup('GC',/GC|AllocateInTargetSpace/),new RuntimeStatsGroup('JavaScript',/JS_Execution/),new RuntimeStatsGroup('V8 C++',/.*/)];this.blink_group_collection_=null;}
addSlices(slices){const blinkEntries=[];for(const slice of slices){if(!(slice instanceof tr.e.v8.V8ThreadSlice))return;let runtimeCallStats;try{runtimeCallStats=JSON.parse(slice.runtimeCallStats);}catch(e){runtimeCallStats=slice.runtimeCallStats;}
if(runtimeCallStats===undefined)continue;for(const[name,stat]of Object.entries(runtimeCallStats)){if(name.match(/Blink_.*/)){if(name==='Blink_V8')continue;const entry=new RuntimeStatsEntry(name,stat[0],stat[1]);blinkEntries.push(entry);continue;}
for(let i=1;i<this.groups_.length;++i){if(this.groups_[i].match(name)){if(stat.length!==2)break;const entry=new RuntimeStatsEntry(name,stat[0],stat[1]);this.groups_[0].addSample(stat[0],stat[1]);this.groups_[i].add(entry);break;}}}}
@@ -8246,7 +8064,7 @@ MetricRegistry.checkFilename(metric.name);});return{MetricRegistry,};});'use str
if(slice.title==='RenderAccessibilityImpl::SendLocationChanges'){renderAccessibilityLocationsHist.addSample(slice.duration,{event:new tr.v.d.RelatedEventSet(slice)});}}}
for(const browserHelper of Object.values(chromeHelper.browserHelpers)){const mainThread=browserHelper.mainThread;if(mainThread===undefined)continue;for(const slice of mainThread.getDescendantEvents()){if(slice.title==='BrowserAccessibilityManager::OnAccessibilityEvents'){browserAccessibilityEventsHist.addSample(slice.duration,{event:new tr.v.d.RelatedEventSet(slice)});}}}
histograms.addHistogram(browserAccessibilityEventsHist);histograms.addHistogram(renderAccessibilityEventsHist);histograms.addHistogram(renderAccessibilityLocationsHist);}
-tr.metrics.MetricRegistry.register(accessibilityMetric);return{accessibilityMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MESSAGE_LOOP_EVENT_NAME='Startup.BrowserMessageLoopStartTimeFromMainEntry3';const CONTENT_START_EVENT_NAME='content::Start';const NAVIGATION_EVENT_NAME='Navigation StartToCommit';const FIRST_CONTENTFUL_PAINT_EVENT_NAME='firstContentfulPaint';function androidStartupMetric(histograms,model){let messageLoopStartEvents=[];let navigationEvents=[];const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;for(const helper of chromeHelper.browserHelpers){for(const ev of helper.mainThread.asyncSliceGroup.childEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartEvents.push(ev);}else if(ev.title===NAVIGATION_EVENT_NAME){navigationEvents.push(ev);}}}
+tr.metrics.MetricRegistry.register(accessibilityMetric);return{accessibilityMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MESSAGE_LOOP_EVENT_NAME='Startup.BrowserMessageLoopStartTime';const CONTENT_START_EVENT_NAME='content::Start';const NAVIGATION_EVENT_NAME='Navigation StartToCommit';const FIRST_CONTENTFUL_PAINT_EVENT_NAME='firstContentfulPaint';function androidStartupMetric(histograms,model){let messageLoopStartEvents=[];let navigationEvents=[];const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;for(const helper of chromeHelper.browserHelpers){for(const ev of helper.mainThread.asyncSliceGroup.childEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartEvents.push(ev);}else if(ev.title===NAVIGATION_EVENT_NAME){navigationEvents.push(ev);}}}
let contentStartEvents=[];let firstContentfulPaintEvents=[];const rendererHelpers=chromeHelper.rendererHelpers;const pids=Object.keys(rendererHelpers);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(!rendererHelper.mainThread)continue;for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(ev.title===FIRST_CONTENTFUL_PAINT_EVENT_NAME){firstContentfulPaintEvents.push(ev);break;}else if(ev.title===CONTENT_START_EVENT_NAME){contentStartEvents.push(ev);}}}
let totalBrowserStarts=messageLoopStartEvents.length;let totalContentStartEvents=contentStartEvents.length;let totalFcpEvents=firstContentfulPaintEvents.length;let totalNavigations=navigationEvents.length;if(totalFcpEvents!==totalBrowserStarts||totalNavigations!==totalBrowserStarts||totalContentStartEvents!==totalBrowserStarts||totalBrowserStarts===0){messageLoopStartEvents=[];contentStartEvents=[];navigationEvents=[];firstContentfulPaintEvents=[];for(const proc of Object.values(model.processes)){for(const ev of proc.getDescendantEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartEvents.push(ev);}else if(ev.title===NAVIGATION_EVENT_NAME){navigationEvents.push(ev);}else if(ev.title===CONTENT_START_EVENT_NAME){contentStartEvents.push(ev);}}
for(const ev of proc.getDescendantEvents()){if(ev.title===FIRST_CONTENTFUL_PAINT_EVENT_NAME){firstContentfulPaintEvents.push(ev);break;}}}
@@ -8304,6 +8122,7 @@ function isNotForcedSubGarbageCollectionEvent(event){return tr.metrics.v8.utils.
function isFullMarkCompactorEvent(event){return event.title==='V8.GCCompactor';}
function isMarkCompactorSummaryEvent(event){return event.title==='V8.GCMarkCompactorSummary';}
function isMarkCompactorMarkingSummaryEvent(event){return event.title==='V8.GCMarkCompactorMarkingSummary';}
+function isScavengerStackScanningEvent(event){return event.title==='V8.GCScavengerStackScanning';}
function isIncrementalMarkingEvent(event){return event.title.startsWith('V8.GCIncrementalMarking');}
function isLatencyMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMC';}
function isMemoryMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMCReduceMemory';}
@@ -8318,11 +8137,18 @@ function isNotForcedMarkCompactorEvent(event){return!isForcedGarbageCollectionEv
function forcedGCEventName(){return LOW_MEMORY_EVENT;}
function topGarbageCollectionEventName(event){if(event.title===FULL_GC_EVENT){if(findParent(event,isLowMemoryEvent)){return LOW_MEMORY_MARK_COMPACTOR;}}
return TOP_GC_EVENTS[event.title];}
+function topGarbageCollectionEventNames(){return Object.values(TOP_GC_EVENTS);}
function subGarbageCollectionEventName(event){const topEvent=findParent(event,isTopGarbageCollectionEvent);const prefix=topEvent?topGarbageCollectionEventName(topEvent):'unknown';const name=event.title.replace('V8.GC_MC_','').replace('V8.GC_SCAVENGER_','').replace('V8.GC_','').replace(/_/g,'-').toLowerCase();return prefix+'-'+name;}
function jsExecutionThreads(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let threads=[];for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(rendererHelper.isChromeTracingUI)continue;threads.push(rendererHelper.mainThread);threads=threads.concat(rendererHelper.dedicatedWorkerThreads);threads=threads.concat(rendererHelper.foregroundWorkerThreads);}
return threads;}
-function groupAndProcessEvents(model,filterCallback,groupCallback,processCallback){const groupToEvents={};const threads=jsExecutionThreads(model);for(const thread of threads){for(const event of thread.sliceGroup.childEvents()){if(!filterCallback(event))continue;const group=groupCallback(event);groupToEvents[group]=groupToEvents[group]||[];groupToEvents[group].push(event);}}
+function groupAndProcessEvents(model,filterCallback,groupCallback,processCallback,groups){const groupToEvents={};if(groups){for(const group of groups){groupToEvents[group]=[];}}
+const threads=jsExecutionThreads(model);for(const thread of threads){for(const event of thread.sliceGroup.childEvents()){if(!filterCallback(event))continue;const group=groupCallback(event);if(groups&&!(group in groupToEvents)){continue;}
+groupToEvents[group]=groupToEvents[group]||[];groupToEvents[group].push(event);}}
for(const[group,events]of Object.entries(groupToEvents)){processCallback(group,events);}}
+function filterEvents(model,filterCallback){const threads=jsExecutionThreads(model);const events=[];for(const thread of threads){for(const event of thread.sliceGroup.childEvents()){if(!filterCallback(event))continue;events.push(event);}}
+return events;}
+function filterAndOrderEvents(model,filterCallback,keyCallback){const threads=jsExecutionThreads(model);const events={};for(const thread of threads){for(const event of thread.sliceGroup.childEvents()){if(!filterCallback(event))continue;const key=keyCallback(event);if(events[key]){events[key].push(event);}else{events[key]=[event];}}}
+return events;}
function unionOfIntervals(intervals){if(intervals.length===0)return[];return tr.b.math.mergeRanges(intervals.map(x=>{return{min:x.start,max:x.end};}),1e-6,function(ranges){return{start:ranges.reduce((acc,x)=>Math.min(acc,x.min),ranges[0].min),end:ranges.reduce((acc,x)=>Math.max(acc,x.max),ranges[0].max)};});}
function hasV8Stats(globalMemoryDump){let v8stats=undefined;globalMemoryDump.iterateContainerDumps(function(dump){v8stats=v8stats||dump.getMemoryAllocatorDumpByFullName('v8');});return!!v8stats;}
function rangeForMemoryDumps(model){const startOfFirstDumpWithV8=model.globalMemoryDumps.filter(hasV8Stats).reduce((start,dump)=>Math.min(start,dump.start),Infinity);if(startOfFirstDumpWithV8===Infinity)return new tr.b.math.Range();return tr.b.math.Range.fromExplicitRange(startOfFirstDumpWithV8,Infinity);}
@@ -8335,31 +8161,52 @@ points.sort((a,b)=>a.position-b.position);points.push({position:end,delta:0});co
while(right.lastIndex<points.length){const distanceUntilNextPoint=Math.min(left.distanceUntilNextPoint,right.distanceUntilNextPoint);const position1=left.position;const value1=right.cummulativePause-left.cummulativePause;left.advance(distanceUntilNextPoint);right.advance(distanceUntilNextPoint);if(distanceUntilNextPoint>0){const position2=left.position;const value2=right.cummulativePause-left.cummulativePause;mu.push(position1,1.0-value1/timeWindow,position2,1.0-value2/timeWindow);}}
return mu;}
function addMutatorUtilization(metricName,eventFilter,timeWindows,rendererHelpers,histograms){const histogramMap=new Map();for(const timeWindow of timeWindows){const summaryOptions={avg:false,count:false,max:false,min:true,std:false,sum:false};const description=`The minimum mutator utilization in ${timeWindow}ms time window`;const histogram=histograms.createHistogram(`${metricName}-${timeWindow}ms_window`,tr.b.Unit.byName.normalizedPercentage_biggerIsBetter,[],{summaryOptions,description});histogramMap.set(timeWindow,histogram);}
-for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;const pauses=[];for(const event of rendererHelper.mainThread.sliceGroup.childEvents()){if(eventFilter(event)&&event.end>event.start){pauses.push({start:event.start,end:event.end});}}
+for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread===undefined)continue;const pauses=[];for(const event of rendererHelper.mainThread.sliceGroup.childEvents()){if(eventFilter(event)&&event.end>event.start){pauses.push({start:event.start,end:event.end});}}
pauses.sort((a,b)=>a.start-b.start);const start=rendererHelper.mainThread.bounds.min;const end=rendererHelper.mainThread.bounds.max;for(const timeWindow of timeWindows){const mu=mutatorUtilization(start,end,timeWindow,pauses);histogramMap.get(timeWindow).addSample(mu.min);}}}
-return{addMutatorUtilization,findParent,forcedGCEventName,groupAndProcessEvents,isForcedGarbageCollectionEvent,isFullMarkCompactorEvent,isGarbageCollectionEvent,isIdleTask,isIncrementalMarkingEvent,isLatencyMarkCompactorEvent,isLowMemoryEvent,isMarkCompactorSummaryEvent,isMarkCompactorMarkingSummaryEvent,isMemoryMarkCompactorEvent,isNotForcedMarkCompactorEvent,isNotForcedTopGarbageCollectionEvent,isNotForcedSubGarbageCollectionEvent,isScavengerEvent,isSubGarbageCollectionEvent,isTopGarbageCollectionEvent,isTopV8ExecuteEvent,isV8Event,isV8ExecuteEvent,isV8RCSEvent,isCompileRCSCategory,isCompileOptimizeRCSCategory,isCompileUnoptimizeRCSCategory,isCompileParseRCSCategory,mutatorUtilization,rangeForMemoryDumps,subGarbageCollectionEventName,topGarbageCollectionEventName,unionOfIntervals,};});'use strict';tr.exportTo('tr.metrics.blink',function(){const BLINK_TOP_GC_FOREGROUND_SWEEPING_EVENTS={'BlinkGC.CompleteSweep':'blink-gc-complete-sweep','BlinkGC.LazySweepInIdle':'blink-gc-sweep-task-foreground','BlinkGC.LazySweepOnAllocation':'blink-gc-sweep-allocation'};const BLINK_TOP_GC_BACKGROUND_SWEEPING_EVENTS={'BlinkGC.ConcurrentSweep':'blink-gc-sweep-task-background'};const BLINK_TOP_GC_EVENTS=Object.assign({'BlinkGC.AtomicPauseMarkEpilogue':'blink-gc-atomic-pause-mark-epilogue','BlinkGC.AtomicPauseMarkPrologue':'blink-gc-atomic-pause-mark-prologue','BlinkGC.AtomicPauseMarkRoots':'blink-gc-atomic-pause-mark-roots','BlinkGC.AtomicPauseMarkTransitiveClosure':'blink-gc-atomic-pause-mark-transitive-closure','BlinkGC.AtomicPauseSweepAndCompact':'blink-gc-atomic-pause-sweep-and-compact','BlinkGC.IncrementalMarkingStartMarking':'blink-gc-incremental-start','BlinkGC.IncrementalMarkingStep':'blink-gc-incremental-step'},BLINK_TOP_GC_FOREGROUND_SWEEPING_EVENTS);const ATOMIC_PAUSE_EVENTS=['BlinkGC.AtomicPauseMarkEpilogue','BlinkGC.AtomicPauseMarkPrologue','BlinkGC.AtomicPauseMarkRoots','BlinkGC.AtomicPauseMarkTransitiveClosure','BlinkGC.AtomicPauseSweepAndCompact'];function blinkGarbageCollectionEventName(event){return BLINK_TOP_GC_EVENTS[event.title];}
+return{addMutatorUtilization,findParent,forcedGCEventName,filterEvents,filterAndOrderEvents,groupAndProcessEvents,isForcedGarbageCollectionEvent,isFullMarkCompactorEvent,isGarbageCollectionEvent,isIdleTask,isIncrementalMarkingEvent,isLatencyMarkCompactorEvent,isLowMemoryEvent,isMarkCompactorSummaryEvent,isMarkCompactorMarkingSummaryEvent,isMemoryMarkCompactorEvent,isNotForcedMarkCompactorEvent,isNotForcedTopGarbageCollectionEvent,isNotForcedSubGarbageCollectionEvent,isScavengerEvent,isScavengerStackScanningEvent,isSubGarbageCollectionEvent,isTopGarbageCollectionEvent,isTopV8ExecuteEvent,isV8Event,isV8ExecuteEvent,isV8RCSEvent,isCompileRCSCategory,isCompileOptimizeRCSCategory,isCompileUnoptimizeRCSCategory,isCompileParseRCSCategory,mutatorUtilization,rangeForMemoryDumps,subGarbageCollectionEventName,topGarbageCollectionEventName,topGarbageCollectionEventNames,unionOfIntervals,};});'use strict';tr.exportTo('tr.metrics.blink',function(){const BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP={'BlinkGC.AtomicPauseMarkEpilogue':'blink-gc-atomic-pause-mark-epilogue','BlinkGC.AtomicPauseMarkPrologue':'blink-gc-atomic-pause-mark-prologue','BlinkGC.AtomicPauseMarkRoots':'blink-gc-atomic-pause-mark-roots','BlinkGC.IncrementalMarkingStartMarking':'blink-gc-incremental-start','BlinkGC.IncrementalMarkingStep':'blink-gc-incremental-step','BlinkGC.UnifiedMarkingStep':'blink-gc-unified-marking-by-v8','BlinkGC.CompleteSweep':'blink-gc-complete-sweep','BlinkGC.LazySweepInIdle':'blink-gc-sweep-task-foreground','BlinkGC.LazySweepOnAllocation':'blink-gc-sweep-allocation','BlinkGC.AtomicPauseSweepAndCompact':'blink-gc-atomic-pause-sweep-and-compact'};const BLINK_TOP_GC_ROOTS_MARKING_EVENTS=['BlinkGC.VisitRoots'];const BLINK_GC_ATOMIC_PAUSE_TRANSITIVE_CLOSURE_EVENTS=['BlinkGC.AtomicPauseMarkTransitiveClosure'];const BLINK_GC_FOREGROUND_MARKING_TRANSITIVE_CLOSURE_EVENTS=['BlinkGC.AtomicPauseMarkTransitiveClosure','BlinkGC.IncrementalMarkingStep','BlinkGC.UnifiedMarkingStep'];const BLINK_TOP_GC_FOREGROUND_MARKING_EVENTS=['BlinkGC.AtomicPauseMarkEpilogue','BlinkGC.AtomicPauseMarkPrologue','BlinkGC.AtomicPauseMarkRoots','BlinkGC.IncrementalMarkingStartMarking',].concat(BLINK_GC_FOREGROUND_MARKING_TRANSITIVE_CLOSURE_EVENTS);const BLINK_GC_FORCED_FOREGROUND_MARKING_EVENTS=['BlinkGC.AtomicPauseMarkEpilogue','BlinkGC.AtomicPauseMarkPrologue','BlinkGC.AtomicPauseMarkRoots','BlinkGC.IncrementalMarkingStartMarking','BlinkGC.MarkBailOutObjects','BlinkGC.MarkFlushV8References','BlinkGC.MarkFlushEphemeronPairs',];const BLINK_TOP_GC_BACKGROUND_MARKING_EVENTS=['BlinkGC.ConcurrentMarkingStep'];const BLINK_TOP_GC_FOREGROUND_SWEEPING_EVENTS=['BlinkGC.CompleteSweep','BlinkGC.LazySweepInIdle','BlinkGC.LazySweepOnAllocation'];const BLINK_TOP_GC_BACKGROUND_SWEEPING_EVENTS=['BlinkGC.ConcurrentSweepingStep'];const BLINK_TOP_GC_EVENTS=Object.keys(BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP).concat(BLINK_GC_ATOMIC_PAUSE_TRANSITIVE_CLOSURE_EVENTS);const ATOMIC_PAUSE_EVENTS=['BlinkGC.AtomicPauseMarkEpilogue','BlinkGC.AtomicPauseMarkPrologue','BlinkGC.AtomicPauseMarkRoots','BlinkGC.AtomicPauseMarkTransitiveClosure','BlinkGC.AtomicPauseSweepAndCompact'];function blinkGarbageCollectionEventName(event){return BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP[event.title];}
+function blinkGarbageCollectionEventNames(){return Object.values(BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP);}
function isNonForcedEvent(event){return(!event.args||!event.args.forced)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);}
-function isNonForcedBlinkGarbageCollectionEvent(event){return event.title in BLINK_TOP_GC_EVENTS&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionEvent(event){return BLINK_TOP_GC_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedNonAggregatedBlinkGarbageCollectionEvent(event){return event.title in BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP&&isNonForcedEvent(event);}
function isNonForcedBlinkGarbageCollectionAtomicPauseEvent(event){return ATOMIC_PAUSE_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
-function isNonForcedBlinkGarbageCollectionForegroundSweepingEvent(event){return event.title in BLINK_TOP_GC_FOREGROUND_SWEEPING_EVENTS&&isNonForcedEvent(event);}
-function isNonForcedBlinkGarbageCollectionBackgroundSweepingEvent(event){return event.title in BLINK_TOP_GC_BACKGROUND_SWEEPING_EVENTS&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionRootsMarkingEvent(event){return BLINK_TOP_GC_ROOTS_MARKING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function
+isNonForcedBlinkGarbageCollectionMarkingTransitiveColsureEvent(event){return BLINK_GC_FOREGROUND_MARKING_TRANSITIVE_CLOSURE_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function
+isNonForcedBlinkGarbageCollectionAtomicPauseTransitiveColsureEvent(event){return BLINK_GC_ATOMIC_PAUSE_TRANSITIVE_CLOSURE_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionForegroundMarkingEvent(event){return BLINK_TOP_GC_FOREGROUND_MARKING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionForcedForegroundMarkEvent(event){return BLINK_GC_FORCED_FOREGROUND_MARKING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionBackgroundMarkingEvent(event){return BLINK_TOP_GC_BACKGROUND_MARKING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionForegroundSweepingEvent(event){return BLINK_TOP_GC_FOREGROUND_SWEEPING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionBackgroundSweepingEvent(event){return BLINK_TOP_GC_BACKGROUND_SWEEPING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
function isNonNestedNonForcedBlinkGarbageCollectionEvent(event){return isNonForcedBlinkGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isGarbageCollectionEvent);}
-function blinkGcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addDurationOfAtomicPause(histograms,model);addTotalDurationOfTopEvents(histograms,model);addTotalDurationOfBlinkAndV8TopEvents(histograms,model);addTotalDurationOfForegroundSweeping(histograms,model);addTotalDurationOfBackgroundSweeping(histograms,model);}
+function blinkGcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addDurationOfAtomicPause(histograms,model);addDurationOfAtomicPauseTransitiveClosure(histograms,model);addTotalDurationOfTopEvents(histograms,model);addTotalDurationOfBlinkAndV8TopEvents(histograms,model);addTotalDurationOfRootsMarking(histograms,model);addTotalDurationOfMarkingTransitiveClosure(histograms,model);addTotalDurationOfForegroundMarking(histograms,model);addTotalDurationOfForcedForegroundMarking(histograms,model);addTotalDurationOfBackgroundMarking(histograms,model);addTotalDurationOfForegroundSweeping(histograms,model);addTotalDurationOfBackgroundSweeping(histograms,model);}
tr.metrics.MetricRegistry.register(blinkGcMetric);const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;}
function createNumericForTotalEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:false,count:true,max:false,min:false,std:false,sum:true,percentile:[0.90]});return n;}
function createNumericForUnifiedEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:false,count:true,max:true,min:false,std:false,sum:true,percentile:[0.90]});return n;}
-function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
-histograms.addHistogram(cpuDuration);});}
+function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedNonAggregatedBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},blinkGarbageCollectionEventNames());}
function addDurationOfAtomicPause(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionAtomicPauseEvent,event=>event.args.epoch,function(group,events){const cpuDuration=createNumericForTopEventTime('blink-gc-atomic-pause');cpuDuration.addSample(events.reduce((acc,current)=>acc+current.cpuDuration,0));histograms.addHistogram(cpuDuration);});}
+function addDurationOfAtomicPauseTransitiveClosure(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionAtomicPauseTransitiveColsureEvent,event=>event.args.epoch,function(group,events){const cpuDuration=createNumericForTopEventTime('blink-gc-atomic-pause-mark-transitive-closure');cpuDuration.addSample(events.reduce((acc,current)=>acc+current.cpuDuration,0));histograms.addHistogram(cpuDuration);});}
function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
-histograms.addHistogram(cpuDuration);});}
+histograms.addHistogram(cpuDuration);},['blink-gc-total']);}
+function addTotalDurationOfRootsMarking(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionRootsMarkingEvent,event=>'blink-gc-mark-roots',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-roots']);}
+function addTotalDurationOfMarkingTransitiveClosure(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionMarkingTransitiveColsureEvent,event=>'blink-gc-mark-transitive-closure',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-transitive-closure']);}
+function addTotalDurationOfForegroundMarking(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionForegroundMarkingEvent,event=>'blink-gc-mark-foreground',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-foreground']);}
+function addTotalDurationOfForcedForegroundMarking(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionForcedForegroundMarkEvent,event=>'blink-gc-mark-foreground-forced',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-foreground-forced']);}
+function addTotalDurationOfBackgroundMarking(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionBackgroundMarkingEvent,event=>'blink-gc-mark-background',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-background']);}
function addTotalDurationOfForegroundSweeping(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionForegroundSweepingEvent,event=>'blink-gc-sweep-foreground',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
-histograms.addHistogram(cpuDuration);});}
+histograms.addHistogram(cpuDuration);},['blink-gc-sweep-foreground']);}
function addTotalDurationOfBackgroundSweeping(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionBackgroundSweepingEvent,event=>'blink-gc-sweep-background',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
-histograms.addHistogram(cpuDuration);});}
+histograms.addHistogram(cpuDuration);},['blink-gc-sweep-background']);}
function isV8OrBlinkTopLevelGarbageCollectionEvent(event){return tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent(event)||isNonNestedNonForcedBlinkGarbageCollectionEvent(event);}
function addTotalDurationOfBlinkAndV8TopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8OrBlinkTopLevelGarbageCollectionEvent,event=>'unified-gc-total',function(name,events){const cpuDuration=createNumericForUnifiedEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
-histograms.addHistogram(cpuDuration);});}
+histograms.addHistogram(cpuDuration);},['unified-gc-total']);}
return{blinkGcMetric,};});'use strict';tr.exportTo('tr.metrics.blink',function(){function leakDetectionMetric(histograms,model){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper===undefined){throw new Error('Chrome is not present.');}
const rendererHelpers=modelHelper.rendererHelpers;if(Object.keys(rendererHelpers).length===0){throw new Error('Renderer process is not present.');}
const pids=Object.keys(rendererHelpers);const chromeDumps=tr.metrics.sh.splitGlobalDumpsByBrowserName(model,undefined).get('chrome');const sumCounter=new Map();for(const pid of pids){for(const[key,count]of countLeakedBlinkObjects(chromeDumps,pid)){sumCounter.set(key,(sumCounter.get(key)||0)+count);}}
@@ -8370,10 +8217,11 @@ const firstCounter=countBlinkObjects(dumps[0],pid);const lastCounter=countBlinkO
return diffCounter;}
function countBlinkObjects(dump,pid){const counter=new Map();const processesMemoryDumps=dump.processMemoryDumps;if(processesMemoryDumps[pid]===undefined)return counter;const blinkObjectsDump=processesMemoryDumps[pid].memoryAllocatorDumps.find(dump=>dump.fullName==='blink_objects');for(const v of blinkObjectsDump.children){counter.set(v.name,v.numerics.object_count.value);}
return counter;}
-return{leakDetectionMetric,};});'use strict';tr.exportTo('tr.metrics.console',function(){const COUNT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e4,30);const SUMMARY_OPTIONS=tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS;const SOURCES=['all','js','network'];function consoleErrorMetric(histograms,model){const counts={};for(const source of SOURCES){counts[source]=0;}
-for(const slice of model.getDescendantEvents()){if(slice.category==='blink.console'&&slice.title==='ConsoleMessage::Error'){const source=slice.args.source.toLowerCase();counts.all++;if(source in counts){counts[source]++;}}
-if(slice.category==='v8.console'&&(slice.title==='V8ConsoleMessage::Exception'||slice.title==='V8ConsoleMessage::Error'||slice.title==='V8ConsoleMessage::Assert')){counts.all++;counts.js++;}}
-for(const source of SOURCES){histograms.createHistogram(`console:error:${source}`,tr.b.Unit.byName.count_smallerIsBetter,counts[source],{description:`Number of ${source} console error messages`,summaryOptions:SUMMARY_OPTIONS});}}
+return{leakDetectionMetric,};});'use strict';tr.exportTo('tr.metrics.console',function(){const COUNT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e4,30);const SUMMARY_OPTIONS=tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS;const SOURCES=['all','js','network'];function consoleErrorMetric(histograms,model){const counts={};for(const source of SOURCES){counts[source]={count:0,details:[]};}
+for(const slice of model.getDescendantEvents()){if(slice.category==='blink.console'&&slice.title==='ConsoleMessage::Error'){const source=slice.args.source.toLowerCase();counts.all.count++;if(slice.args.message){counts.all.details.push({pid:slice.getProcess().pid,...slice.args.message});}
+if(source in counts){counts[source].count++;if(slice.args.message){counts[source].details.push({pid:slice.getProcess().pid,...slice.args.message});}}}
+if(slice.category==='v8.console'&&(slice.title==='V8ConsoleMessage::Exception'||slice.title==='V8ConsoleMessage::Error'||slice.title==='V8ConsoleMessage::Assert')){counts.all.count++;counts.js.count++;}}
+for(const source of SOURCES){const hist=histograms.createHistogram(`console:error:${source}`,tr.b.Unit.byName.count_smallerIsBetter,{value:counts[source].count,diagnostics:{details:new tr.v.d.GenericSet(counts[source].details)}},{description:`Number of ${source} console error messages`,summaryOptions:SUMMARY_OPTIONS,});}}
tr.metrics.MetricRegistry.register(consoleErrorMetric);return{consoleErrorMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getCpuSnapshotsFromModel(model){const snapshots=[];for(const pid in model.processes){const snapshotInstances=model.processes[pid].objects.getAllInstancesNamed('CPUSnapshots');if(!snapshotInstances)continue;for(const object of snapshotInstances[0].snapshots){snapshots.push(object.args.processes);}}
return snapshots;}
function getProcessSumsFromSnapshot(snapshot){const processSums=new Map();for(const processData of snapshot){const processName=processData.name;if(!(processSums.has(processName))){processSums.set(processName,{sum:0.0,paths:new Set()});}
@@ -8423,19 +8271,14 @@ processOnEnded(playEndTime,duration){if(this.playStart_===undefined)return;if(th
addMetricToHistograms(histograms){this.addSample_(histograms,'time_to_video_play',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.timeToVideoPlay);this.addSample_(histograms,'time_to_audio_play',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.timeToAudioPlay);this.addSample_(histograms,'dropped_frame_count',tr.b.Unit.byName.count_smallerIsBetter,this.droppedFrameCount);for(const[key,value]of this.seekTimes.entries()){const keyString=key.toString().replace('.','_');this.addSample_(histograms,'pipeline_seek_time_'+keyString,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,value.pipelineSeekTime);this.addSample_(histograms,'seek_time_'+keyString,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,value.seekTime);}
this.addSample_(histograms,'buffering_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.bufferingTime);}
addSample_(histograms,name,unit,sample){if(sample===undefined)return;const histogram=histograms.getHistogramNamed(name);if(histogram===undefined){histograms.createHistogram(name,unit,sample);}else{histogram.addSample(sample);}}}
-tr.metrics.MetricRegistry.register(mediaMetric);return{mediaMetric,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const UNKNOWN_THREAD_NAME='Unknown';const CATEGORY_THREAD_MAP=new Map();CATEGORY_THREAD_MAP.set('total_all',[/.*/]);CATEGORY_THREAD_MAP.set('browser',[/^Browser Compositor$/,/^CrBrowserMain$/]);CATEGORY_THREAD_MAP.set('display_compositor',[/^VizCompositorThread$/]);CATEGORY_THREAD_MAP.set('GPU',[/^Chrome_InProcGpuThread$/,/^CrGpuMain$/]);CATEGORY_THREAD_MAP.set('IO',[/IOThread/]);CATEGORY_THREAD_MAP.set('raster',[/CompositorTileWorker/]);CATEGORY_THREAD_MAP.set('renderer_compositor',[/^Compositor$/]);CATEGORY_THREAD_MAP.set('renderer_main',[/^CrRendererMain$/]);CATEGORY_THREAD_MAP.set('total_rendering',[/^Browser Compositor$/,/^Chrome_InProcGpuThread$/,/^Compositor$/,/CompositorTileWorker/,/^CrBrowserMain$/,/^CrGpuMain$/,/^CrRendererMain$/,/IOThread/,/^VizCompositorThread$/]);const ALL_CATEGORIES=[...CATEGORY_THREAD_MAP.keys(),'other'];function addValueToMap_(map,key,value){const oldValue=map.get(key)||0;map.set(key,oldValue+value);}
-function categoryShouldHaveBreakdown(category){return category==='total_all'||category==='total_rendering';}
+tr.metrics.MetricRegistry.register(mediaMetric);return{mediaMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function memoryAblationMetric(histograms,model){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper.gpuHelper)return;const gpuProcess=modelHelper.gpuHelper.process;const events=[...gpuProcess.findTopmostSlicesNamed('Memory.GPU.PeakMemoryUsage.AblationTimes')];const allocHistogram=histograms.createHistogram('Ablation Alloc',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,10000,20),description:'The amount of time spent allocating the ablation '+'memory',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const deallocHistogram=histograms.createHistogram('Ablation Dealloc',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,10000,20),description:'The amount of time spent deallocating the ablation '+'memory',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});for(let i=0;i<events.length;i++){allocHistogram.addSample(events[i].args.alloc);deallocHistogram.addSample(events[i].args.dealloc);}}
+tr.metrics.MetricRegistry.register(memoryAblationMetric,{requiredCategories:['gpu.memory'],});return{memoryAblationMetric,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const UNKNOWN_THREAD_NAME='Unknown';const CATEGORY_THREAD_MAP=new Map();CATEGORY_THREAD_MAP.set('total_all',[/.*/]);CATEGORY_THREAD_MAP.set('browser',[/^Browser Compositor$/,/^CrBrowserMain$/]);CATEGORY_THREAD_MAP.set('display_compositor',[/^VizCompositorThread$/]);CATEGORY_THREAD_MAP.set('GPU',[/^Chrome_InProcGpuThread$/,/^CrGpuMain$/]);CATEGORY_THREAD_MAP.set('IO',[/IOThread/]);CATEGORY_THREAD_MAP.set('raster',[/CompositorTileWorker/]);CATEGORY_THREAD_MAP.set('renderer_compositor',[/^Compositor$/]);CATEGORY_THREAD_MAP.set('renderer_main',[/^CrRendererMain$/]);CATEGORY_THREAD_MAP.set('total_rendering',[/^Browser Compositor$/,/^Chrome_InProcGpuThread$/,/^Compositor$/,/CompositorTileWorker/,/^CrBrowserMain$/,/^CrGpuMain$/,/^CrRendererMain$/,/IOThread/,/^VizCompositorThread$/]);const ALL_CATEGORIES=[...CATEGORY_THREAD_MAP.keys(),'other'];function addValueToMap_(map,key,value){const oldValue=map.get(key)||0;map.set(key,oldValue+value);}
+function addToArrayInMap_(map,key,value){const arr=map.get(key)||[];arr.push(value);map.set(key,arr);}
function*getCategories_(threadName){let isOther=true;for(const[category,regexps]of CATEGORY_THREAD_MAP){for(const regexp of regexps){if(regexp.test(threadName)){if(category!=='total_all')isOther=false;yield category;break;}}}
if(isOther)yield'other';}
-function isSubset_(regexps1,regexps2){for(const r1 of regexps1){if(regexps2.find(r2=>r2.toString()===r1.toString())===undefined){return false;}}
-return true;}
-function addCpuUtilizationHistograms(histograms,model,segments,shouldNormalize,segmentCostFunc,histogramNameFunc,description,unit){if(!unit)unit=tr.b.Unit.byName.unitlessNumber;const histogramMap=new Map();for(const category of ALL_CATEGORIES){const histogram=histograms.createHistogram(histogramNameFunc(category),unit,[],{binBoundaries:tr.v.HistogramBinBoundaries.createExponential(1,50,20),description,summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histogramMap.set(category,histogram);}
-for(const[category,regexps]of CATEGORY_THREAD_MAP){const relatedCategories=new tr.v.d.RelatedNameMap();const histogram=histogramMap.get(category);for(const[otherCategory,otherRegexps]of CATEGORY_THREAD_MAP){if(otherCategory===category)continue;if(category!=='all'&&!isSubset_(otherRegexps,regexps))continue;const otherHistogram=histogramMap.get(otherCategory);relatedCategories.set(otherCategory,otherHistogram.name);}
-if([...relatedCategories.values()].length>0){histogram.diagnostics.set('breakdown',relatedCategories);}}
-for(const segment of segments){const threadValues=new Map();for(const thread of model.getAllThreads()){addValueToMap_(threadValues,thread.name||UNKNOWN_THREAD_NAME,segmentCostFunc(thread,segment));}
-const categoryValues=new Map();const breakdowns=new Map();for(const[threadName,coresPerSec]of threadValues){for(const category of getCategories_(threadName)){addValueToMap_(categoryValues,category,coresPerSec);if(!categoryShouldHaveBreakdown(category))continue;if(!breakdowns.has(category)){breakdowns.set(category,new tr.v.d.Breakdown());}
-breakdowns.get(category).set(threadName,coresPerSec);}}
-for(const category of ALL_CATEGORIES){let value=categoryValues.get(category)||0;if(shouldNormalize)value/=segment.duration;const diagnostics=new tr.v.d.DiagnosticMap();const breakdown=breakdowns.get(category);if(breakdown)diagnostics.set('breakdown',breakdown);const histogram=histogramMap.get(category);histogram.addSample(value,diagnostics);}}}
+function addCpuUtilizationHistograms(histograms,model,segments,segmentCostFunc,histogramNameFunc,description){const categoryValues=new Map();for(const segment of segments){const threadValues=new Map();for(const thread of model.getAllThreads()){addValueToMap_(threadValues,thread.name||UNKNOWN_THREAD_NAME,segmentCostFunc(thread,segment));}
+for(const[threadName,coresPerSec]of threadValues){for(const category of getCategories_(threadName)){addToArrayInMap_(categoryValues,category,coresPerSec);}}}
+const unit=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;for(const category of ALL_CATEGORIES){const values=categoryValues.get(category)||0;if(!values)continue;const avg=values.reduce((sum,e)=>sum+e,0)/segments.length;histograms.createHistogram(histogramNameFunc(category),unit,avg,{description,summaryOptions:{},});}}
const SUMMARY_OPTIONS={percentile:[0.90,0.95],ci:[0.95],};return{addCpuUtilizationHistograms,SUMMARY_OPTIONS,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const PRESENT_EVENT='Display::FrameDisplayed';const DISPLAY_EVENT='BenchmarkInstrumentation::DisplayRenderingStats';const DRM_EVENT='DrmEventFlipComplete';const SURFACE_FLINGER_EVENT='vsync_before';const COMPOSITOR_FRAME_PRESENTED_EVENT='FramePresented';const MIN_FRAME_LENGTH=0.5;const MIN_FRAME_COUNT=10;const PAUSE_THRESHOLD=20;const ASH_ENVIRONMENT='ash';const BROWSER_ENVIRONMENT='browser';class FrameEvent{constructor(event){this.event_=event;}
get eventStart(){return this.event_.start;}
get frameStart(){if(this.event_.title!==DRM_EVENT)return this.event_.start;const data=this.event_.args.data;const TIME=tr.b.UnitScale.TIME;return tr.b.convertUnit(data['vblank.tv_sec'],TIME.SEC,TIME.MILLI_SEC)+
@@ -8462,8 +8305,9 @@ return legacyEvents;}
function computeFrameSegments_(events,segments,opt_minFrameCount){const minFrameCount=opt_minFrameCount||MIN_FRAME_COUNT;const frameEvents=events.map(e=>new FrameEvent(e));const frameSegments=[];for(const segment of segments){const filtered=segment.boundsRange.filterArray(frameEvents,x=>x.eventStart);if(filtered.length<minFrameCount)continue;for(let i=1;i<filtered.length;i++){const duration=filtered[i].frameStart-filtered[i-1].frameStart;frameSegments.push(new FrameSegment(filtered[i-1],duration));}}
return frameSegments;}
function addBasicFrameTimeHistograms_(histograms,frameSegments,prefix){const frameTimes=(frameSegments.length===0)?[0]:frameSegments.map(x=>x.duration);histograms.createHistogram(`${prefix}frame_times`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,frameTimes,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,20),description:'Raw frame times.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram(`${prefix}percentage_smooth`,tr.b.Unit.byName.unitlessNumber_biggerIsBetter,100*tr.b.math.Statistics.sum(frameTimes,(x=>(x<17?1:0)))/frameTimes.length,{description:'Percentage of frames that were hitting 60 FPS.',summaryOptions:{},});}
-function addFrameTimeHistograms(histograms,model,segments,opt_minFrameCount){const minFrameCount=opt_minFrameCount||MIN_FRAME_COUNT;const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const events=getDisplayCompositorPresentationEvents_(modelHelper);if(!events)return;addFrameTimeHistogramsHelper(histograms,model,segments,events,'',true,minFrameCount);const eventsExp=getDisplayCompositorPresentationEventsExp_(modelHelper);if(eventsExp&&eventsExp.length>0){addFrameTimeHistogramsHelper(histograms,model,segments,eventsExp,'exp_',minFrameCount);}}
-function addFrameTimeHistogramsHelper(histograms,model,segments,events,prefix,addCpuMetrics,minFrameCount){const frameSegments=computeFrameSegments_(events,segments,minFrameCount);addBasicFrameTimeHistograms_(histograms,frameSegments,prefix+'');if(addCpuMetrics){tr.metrics.rendering.addCpuUtilizationHistograms(histograms,model,frameSegments,false,(thread,segment)=>thread.getCpuTimeForRange(segment.boundsRange),category=>`thread_${category}_cpu_time_per_frame`,'CPU cores of a thread group per frame',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);tr.metrics.rendering.addCpuUtilizationHistograms(histograms,model,frameSegments,false,(thread,segment)=>thread.getNumToplevelSlicesForRange(segment.boundsRange),category=>`tasks_per_frame_${category}`,'Number of tasks of a thread group per frame',tr.b.Unit.byName.unitlessNumber_smallerIsBetter);}
+function addFrameTimeHistograms(histograms,model,segments,opt_minFrameCount){const minFrameCount=opt_minFrameCount||MIN_FRAME_COUNT;const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const events=getDisplayCompositorPresentationEvents_(modelHelper);if(!events)return;addFrameTimeHistogramsHelper(histograms,model,segments,events,'',true,minFrameCount);const eventsExp=getDisplayCompositorPresentationEventsExp_(modelHelper);if(eventsExp&&eventsExp.length>0){addFrameTimeHistogramsHelper(histograms,model,segments,eventsExp,'exp_',false,minFrameCount);}}
+function addFrameTimeHistogramsHelper(histograms,model,segments,events,prefix,addCpuMetrics,minFrameCount){const frameSegments=computeFrameSegments_(events,segments,minFrameCount);addBasicFrameTimeHistograms_(histograms,frameSegments,prefix+'');if(addCpuMetrics){tr.metrics.rendering.addCpuUtilizationHistograms(histograms,model,frameSegments,(thread,segment)=>thread.getCpuTimeForRange(segment.boundsRange),category=>`thread_${category}_cpu_time_per_frame`,'CPU cores of a thread group per frame');tr.metrics.rendering.addCpuUtilizationHistograms(histograms,model,frameSegments,(thread,segment)=>thread.getNumToplevelSlicesForRange(segment.boundsRange),category=>`tasks_per_frame_${category}`,'Number of tasks of a thread group per frame');let totalWallTime=0;let totalCpuTime=0;for(const segment of frameSegments){for(const thread of model.getAllThreads()){totalCpuTime+=thread.getCpuTimeForRange(segment.boundsRange);totalWallTime+=thread.getWallTimeForRange(segment.boundsRange);}}
+histograms.createHistogram('cpu_wall_time_ratio',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,totalCpuTime/totalWallTime,{description:'Ratio of total cpu-time vs. wall-time.',summaryOptions:{},});}
const refreshPeriod=getRefreshPeriod(model,frameSegments.map(fs=>fs.boundsRange));frameSegments.forEach(fs=>fs.updateLength(refreshPeriod));const validFrames=frameSegments.filter(fs=>fs.length>=MIN_FRAME_LENGTH);const totalFrameDuration=tr.b.math.Statistics.sum(frameSegments,fs=>fs.duration);addJankCountHistograms(histograms,validFrames,prefix);const frameLengths=validFrames.map(frame=>frame.length);histograms.createHistogram(prefix+'frame_lengths',tr.b.Unit.byName.unitlessNumber_smallerIsBetter,frameLengths,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,5,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Frame times in vsyncs.'});histograms.createHistogram(prefix+'avg_surface_fps',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,frameLengths.length/tr.b.convertUnit(totalFrameDuration,tr.b.UnitScale.TIME.MILLI_SEC,tr.b.UnitScale.TIME.SEC),{description:'Average frames per second.',summaryOptions:{},});}
function addUIFrameTimeHistograms(histograms,model,segments,opt_minFrameCount){const minFrameCount=opt_minFrameCount||MIN_FRAME_COUNT;const events=getUIPresentationEvents_(model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper));if(events.length===0)return;const frameSegments=computeFrameSegments_(events,segments,minFrameCount);addBasicFrameTimeHistograms_(histograms,frameSegments,'ui_');}
function addJankCountHistograms(histograms,validFrames,prefix){const jankEvents=[];for(let i=1;i<validFrames.length;i++){const change=Math.round((validFrames[i].length-validFrames[i-1].length));if(change>0&&change<PAUSE_THRESHOLD){jankEvents.push(validFrames[i].event);}}
@@ -8475,34 +8319,7 @@ return FRAME_LENGTH;}
return{addFrameTimeHistograms,addUIFrameTimeHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const RGB_DECODE_EVENT='ImageFrameGenerator::decode';const YUV_DECODE_EVENT='ImageFrameGenerator::decodeToYUV';const BLINK_GPU_RASTER_DECODE_EVENT='GpuImageDecodeCache::DecodeImage';const BLINK_SOFTWARE_RASTER_DECODE_EVENT='SoftwareImageDecodeCache::'+'DecodeImageInTask';function getImageDecodingEvents_(modelHelper,ranges){if(!modelHelper||!modelHelper.rendererHelpers)return[];const events=[];for(const renderer of Object.values(modelHelper.rendererHelpers)){for(const thread of renderer.rasterWorkerThreads){const slices=thread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title===RGB_DECODE_EVENT||slice.title===YUV_DECODE_EVENT||slice.title===BLINK_GPU_RASTER_DECODE_EVENT||slice.title===BLINK_SOFTWARE_RASTER_DECODE_EVENT){events.push(slice);}}}}
return events;}
function addImageDecodeTimeHistograms(histograms,model,segments){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const decodeEvents=getImageDecodingEvents_(modelHelper,segments.map(s=>s.boundsRange));if(!decodeEvents)return;histograms.createHistogram('rgb_decode_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===RGB_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of the Blink RGB decoding path for a chunk '+'of image data (possibly the whole image).',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram('yuv_decode_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===YUV_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of the Blink YUV decoding path for a '+'chunk of image data (possibly the whole image).',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram('blink_decode_time_gpu_rasterization',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===BLINK_GPU_RASTER_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of decoding and scaling within the '+'GpuImageDecodeCache for a chunk of image data '+'(possibly the whole image)',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram('blink_decode_time_software_rasterization',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===BLINK_SOFTWARE_RASTER_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of decoding and scaling within the '+'SoftwareImageDecodeCache for a chunk of image data '+'(possibly the whole image)',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});}
-return{addImageDecodeTimeHistograms};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const BEGIN_SCROLL_UPDATE_COMP_NAME='LATENCY_BEGIN_SCROLL_LISTENER_UPDATE_MAIN_COMPONENT';const END_COMP_NAME='INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT';function*iterAsyncEvents_(processHelpers,ranges,processEventFn){for(const processHelper of processHelpers){const process=processHelper.process;for(const event of process.getDescendantEventsInSortedRanges(ranges,container=>container instanceof tr.model.AsyncSliceGroup)){yield*processEventFn(event);}}}
-function*processLatencyEvent(event){if(event.title!=='Latency::ScrollUpdate'||!('data'in event.args)||!(END_COMP_NAME in event.args.data)){return;}
-const data=event.args.data;const endTime=data[END_COMP_NAME].time;if(BEGIN_SCROLL_UPDATE_COMP_NAME in data){yield tr.b.Unit.timestampFromUs(endTime-data[BEGIN_SCROLL_UPDATE_COMP_NAME].time);}else{throw new Error('LatencyInfo has no begin component');}}
-function addLatencyHistograms(histograms,model,segments){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper)return;const ranges=segments.map(s=>s.boundsRange);const mainThreadScrollLatencies=[...iterAsyncEvents_(Object.values(modelHelper.rendererHelpers),ranges,processLatencyEvent)];histograms.createHistogram('main_thread_scroll_latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,mainThreadScrollLatencies,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,50),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Main thread scroll latencies.'});}
-return{addLatencyHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){function eventIsValidGraphicsEvent_(event,eventMap){if(event.title!=='Graphics.Pipeline'||!event.bindId||!event.args||!event.args.step){return false;}
-const bindId=event.bindId;if(eventMap.has(bindId)&&event.args.step in eventMap.get(bindId)){if(event.args.step==='IssueBeginFrame'||event.args.step==='ReceiveBeginFrame'){throw new Error('Unexpected duplicate step: '+event.args.step);}
-return false;}
-return true;}
-function generateBreakdownForCompositorPipelineInClient_(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('time before GenerateRenderPass',flow.GenerateRenderPass.start-flow.ReceiveBeginFrame.start);breakdown.set('GenerateRenderPass duration',flow.GenerateRenderPass.duration);breakdown.set('GenerateCompositorFrame duration',flow.GenerateCompositorFrame.duration);breakdown.set('SubmitCompositorFrame duration',flow.SubmitCompositorFrame.duration);return breakdown;}
-function generateBreakdownForCompositorPipelineInService_(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('Processing CompositorFrame on reception',flow.ReceiveCompositorFrame.duration);breakdown.set('Delay before SurfaceAggregation',flow.SurfaceAggregation.start-flow.ReceiveCompositorFrame.end);breakdown.set('SurfaceAggregation duration',flow.SurfaceAggregation.duration);return breakdown;}
-function generateBreakdownForDraw_(drawEvent){const breakdown=new tr.v.d.Breakdown();for(const slice of drawEvent.subSlices){breakdown.set(slice.title,slice.duration);}
-return breakdown;}
-function getDisplayCompositorThread_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const gpuHelper=chromeHelper.gpuHelper;if(gpuHelper){const thread=gpuHelper.process.findAtMostOneThreadNamed('VizCompositorThread');if(thread){return thread;}}
-if(!chromeHelper.browserProcess)return null;return chromeHelper.browserProcess.findAtMostOneThreadNamed('CrBrowserMain');}
-function getRasterTaskTimes(sourceFrameNumber,model){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const renderers=modelHelper.telemetryHelper.renderersWithIR;if(renderers.length===0)return;const rasterThreads=renderers[0].rasterWorkerThreads;let earliestStart=undefined;let lastEnd=undefined;for(const rasterThread of rasterThreads){for(const slice of[...rasterThread.findTopmostSlicesNamed('TaskGraphRunner::RunTask')]){if(slice.args&&slice.args.source_frame_number_&&slice.args.source_frame_number_===sourceFrameNumber){if(earliestStart===undefined||slice.start<earliestStart){earliestStart=slice.start;}
-if(lastEnd===undefined||slice.end>lastEnd){lastEnd=slice.end;}}}}
-return{start:earliestStart,end:lastEnd};}
-function addPipelineHistograms(histograms,model,segments){const ranges=segments.map(s=>s.boundsRange);const bindEvents=new Map();for(const thread of model.getAllThreads()){for(const event of thread.sliceGroup.childEvents()){if(!eventIsValidGraphicsEvent_(event,bindEvents))continue;for(const range of ranges){if(range.containsExplicitRangeInclusive(event.start,event.end)){if(!bindEvents.has(event.bindId))bindEvents.set(event.bindId,{});break;}}
-if(bindEvents.has(event.bindId)){bindEvents.get(event.bindId)[event.args.step]=event;}}}
-const dcThread=getDisplayCompositorThread_(model);const drawEvents={};if(dcThread){const events=[...dcThread.findTopmostSlicesNamed('Graphics.Pipeline.DrawAndSwap')];for(const segment of segments){const filteredEvents=segment.boundsRange.filterArray(events,evt=>evt.start);for(const event of filteredEvents){if((event.args&&event.args.status==='canceled')||!event.id.startsWith(':ptr:')){continue;}
-const id=parseInt(event.id.substring(5),16);if(id in drawEvents){throw new Error('Duplicate draw events: '+id);}
-drawEvents[id]=event;}}}
-const issueToReceipt=histograms.createHistogram('pipeline:begin_frame_transport',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency of begin-frame message from the display '+'compositor to the client, including the IPC latency and task-'+'queue time in the client.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const issueToRasterStart=histograms.createHistogram('pipeline:begin_frame_to_raster_start',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame message and '+'the beginning of the first CompositorTask run in the compositor.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const issueToRasterEnd=histograms.createHistogram('pipeline:begin_frame_to_raster_end',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame message and '+'the end of the last CompositorTask run in the compositor.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const receiptToSubmit=histograms.createHistogram('pipeline:begin_frame_to_frame_submission',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame reception and '+'CompositorFrame submission in the renderer.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const submitToAggregate=histograms.createHistogram('pipeline:frame_submission_to_display',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between CompositorFrame submission in the '+'renderer to display in the display-compositor, including IPC '+'latency, task-queue time in the display-compositor, and '+'additional processing (e.g. surface-sync etc.)',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const aggregateToDraw=histograms.createHistogram('pipeline:draw',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'How long it takes for the gpu-swap step.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});for(const flow of bindEvents.values()){if(!flow.IssueBeginFrame||!flow.ReceiveBeginFrame||!flow.SubmitCompositorFrame||!flow.SurfaceAggregation){continue;}
-issueToReceipt.addSample(flow.ReceiveBeginFrame.start-
-flow.IssueBeginFrame.start);receiptToSubmit.addSample(flow.SubmitCompositorFrame.end-flow.ReceiveBeginFrame.start,{breakdown:generateBreakdownForCompositorPipelineInClient_(flow)});submitToAggregate.addSample(flow.SurfaceAggregation.end-flow.SubmitCompositorFrame.end,{breakdown:generateBreakdownForCompositorPipelineInService_(flow)});if(flow.SubmitCompositorFrame.parentSlice){const sourceFrameNumber=flow.SubmitCompositorFrame.parentSlice.args.source_frame_number_;const rasterDuration=getRasterTaskTimes(sourceFrameNumber,model);if(rasterDuration&&rasterDuration.start&&rasterDuration.end){const receiveToStart=rasterDuration.start-
-flow.ReceiveBeginFrame.start;const receiveToEnd=rasterDuration.end-flow.ReceiveBeginFrame.end;if(receiveToEnd>0){issueToRasterStart.addSample(receiveToStart>0?receiveToStart:0);issueToRasterEnd.addSample(receiveToEnd);}}}
-if(flow.SurfaceAggregation.args&&flow.SurfaceAggregation.args.display_trace){const displayTrace=flow.SurfaceAggregation.args.display_trace;if(!(displayTrace in drawEvents))continue;const drawEvent=drawEvents[displayTrace];aggregateToDraw.addSample(drawEvent.duration,{breakdown:generateBreakdownForDraw_(drawEvent)});}}}
-return{addPipelineHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const IMPL_THREAD_RENDERING_STATS_EVENT='BenchmarkInstrumentation::ImplThreadRenderingStats';const VISIBLE_CONTENT_DATA='visible_content_area';const APPROXIMATED_VISIBLE_CONTENT_DATA='approximated_visible_content_area';const CHECKERBOARDED_VISIBLE_CONTENT_DATA='checkerboarded_visible_content_area';function addPixelsHistograms(histograms,model,segments){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;const approximatedPixelPercentages=[];const checkerboardedPixelPercentages=[];const ranges=segments.map(s=>s.boundsRange);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(rendererHelper.compositorThread===undefined)continue;const slices=rendererHelper.compositorThread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title!==IMPL_THREAD_RENDERING_STATS_EVENT)continue;const data=slice.args.data;if(!(VISIBLE_CONTENT_DATA in data)){throw new Error(`${VISIBLE_CONTENT_DATA} is missing`);}
+return{addImageDecodeTimeHistograms};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const IMPL_THREAD_RENDERING_STATS_EVENT='BenchmarkInstrumentation::ImplThreadRenderingStats';const VISIBLE_CONTENT_DATA='visible_content_area';const APPROXIMATED_VISIBLE_CONTENT_DATA='approximated_visible_content_area';const CHECKERBOARDED_VISIBLE_CONTENT_DATA='checkerboarded_visible_content_area';function addPixelsHistograms(histograms,model,segments){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;const approximatedPixelPercentages=[];const checkerboardedPixelPercentages=[];const ranges=segments.map(s=>s.boundsRange);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(rendererHelper.compositorThread===undefined)continue;const slices=rendererHelper.compositorThread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title!==IMPL_THREAD_RENDERING_STATS_EVENT)continue;const data=slice.args.data;if(!(VISIBLE_CONTENT_DATA in data)){throw new Error(`${VISIBLE_CONTENT_DATA} is missing`);}
const visibleContentArea=data[VISIBLE_CONTENT_DATA];if(visibleContentArea===0){continue;}
if(APPROXIMATED_VISIBLE_CONTENT_DATA in data){approximatedPixelPercentages.push(data[APPROXIMATED_VISIBLE_CONTENT_DATA]/visibleContentArea);}
if(CHECKERBOARDED_VISIBLE_CONTENT_DATA in data){checkerboardedPixelPercentages.push(data[CHECKERBOARDED_VISIBLE_CONTENT_DATA]/visibleContentArea);}}}
@@ -8513,9 +8330,14 @@ function addQueueingDurationHistograms(histograms,model,segments){const chromeHe
const queueingDurations=[];const ranges=segments.map(s=>s.boundsRange);for(const rendererHelper of targetRenderers){const mainThread=rendererHelper.mainThread;const compositorThread=rendererHelper.compositorThread;if(mainThread===undefined||compositorThread===undefined)continue;const beginMainFrameTimes=getEventTimesByBeginFrameId_(mainThread,BEGIN_MAIN_FRAME_EVENT,ranges);const sendBeginFrameTimes=getEventTimesByBeginFrameId_(compositorThread,SEND_BEGIN_FRAME_EVENT,ranges);for(const[id,time]of sendBeginFrameTimes){queueingDurations.push(beginMainFrameTimes.get(id)-time);}}
histograms.createHistogram('queueing_durations',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,queueingDurations,{binBoundaries:tr.v.HistogramBinBoundaries.createExponential(0.01,2,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Time between ScheduledActionSendBeginMainFrame in '+'the compositor thread and the corresponding '+'BeginMainFrame in the main thread.'});}
return{addQueueingDurationHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const GESTURE_EVENT='SyntheticGestureController::running';function renderingMetric(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;let segments=chromeHelper.telemetryHelper.irSegments;if(segments.length===0){segments=chromeHelper.telemetryHelper.animationSegments;}
-if(segments.length>0){tr.metrics.rendering.addFrameTimeHistograms(histograms,model,segments);tr.metrics.rendering.addImageDecodeTimeHistograms(histograms,model,segments);tr.metrics.rendering.addLatencyHistograms(histograms,model,segments);tr.metrics.rendering.addPipelineHistograms(histograms,model,segments);tr.metrics.rendering.addPixelsHistograms(histograms,model,segments);tr.metrics.rendering.addQueueingDurationHistograms(histograms,model,segments);}
+if(segments.length>0){tr.metrics.rendering.addFrameTimeHistograms(histograms,model,segments);tr.metrics.rendering.addImageDecodeTimeHistograms(histograms,model,segments);tr.metrics.rendering.addPixelsHistograms(histograms,model,segments);tr.metrics.rendering.addQueueingDurationHistograms(histograms,model,segments);}
const uiSegments=chromeHelper.telemetryHelper.uiSegments;if(uiSegments.length>0){tr.metrics.rendering.addUIFrameTimeHistograms(histograms,model,chromeHelper.telemetryHelper.uiSegments);}}
-tr.metrics.MetricRegistry.register(renderingMetric,{requiredCategories:['benchmark','toplevel'],});return{renderingMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function sampleExceptionMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}}
+tr.metrics.MetricRegistry.register(renderingMetric,{requiredCategories:['benchmark','toplevel'],});return{renderingMetric,};});'use strict';tr.exportTo('tr.metrics',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const unitlessNumber_smallerIsBetter=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;const EventFinderUtils=tr.e.chrome.EventFinderUtils;const METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(80e3,30);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function reportedByPageMetric(histograms,model){const timeToViewable=histograms.createHistogram('reported_by_page:time_to_viewable',timeDurationInMs_smallerIsBetter,[],{binBoundaries:METRIC_BOUNDARIES,description:'Time from navigation start'+'to telemetry:reported_by_page:viewable',summaryOptions:SUMMARY_OPTIONS,});const timeToInteractive=histograms.createHistogram('reported_by_page:time_to_interactive',timeDurationInMs_smallerIsBetter,[],{binBoundaries:METRIC_BOUNDARIES,description:'Time from navigation start '+'to telemetry:reported_by_page:interactive',summaryOptions:SUMMARY_OPTIONS,});const benchmarkTime=histograms.createHistogram('reported_by_page:benchmark_time',timeDurationInMs_smallerIsBetter,[],{binBoundaries:METRIC_BOUNDARIES,description:'Time from telemetry:reported_by_page:benchmark_begin '+'to telemetry:reported_by_page:benchmark_end',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread===undefined)continue;measureUserTime(rendererHelper,'navigationStart','telemetry:reported_by_page:viewable',timeToViewable);measureUserTime(rendererHelper,'navigationStart','telemetry:reported_by_page:interactive',timeToInteractive);measureUserTime(rendererHelper,'telemetry:reported_by_page:benchmark_begin','telemetry:reported_by_page:benchmark_end',benchmarkTime);}}
+function measureUserTime(rendererHelper,startName,endName,histogram){const startEventByNavId=new Map();for(const event of rendererHelper.mainThread.sliceGroup.childEvents()){const navId=getNavigationId(event);if(!navId)continue;if(EventFinderUtils.hasCategoryAndName(event,'blink.user_timing',startName)){startEventByNavId.set(navId,event);}
+if(EventFinderUtils.hasCategoryAndName(event,'blink.user_timing',endName)){if(!startEventByNavId.has(navId)){throw Error(`Missing ${startName} for ${endName} at {event.start}`);}
+const range=tr.b.math.Range.fromExplicitRange(startEventByNavId.get(navId).start,event.start);histogram.addSample(range.duration);startEventByNavId.delete(navId);}}}
+function getNavigationId(event){return event.args.data&&event.args.data.navigationId;}
+tr.metrics.MetricRegistry.register(reportedByPageMetric);return{reportedByPageMetric};});'use strict';tr.exportTo('tr.metrics',function(){function sampleExceptionMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}}
const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const[pid,process]of Object.entries(model.processes)){}
histograms.addHistogram(hist);throw new Error('There was an error');}
tr.metrics.MetricRegistry.register(sampleExceptionMetric);return{sampleExceptionMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function sampleMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}}
@@ -8566,11 +8388,51 @@ static isLcpInvalidateEvent(event){return event.title===LCP_INVALIDATE_EVENT_TIT
class LargestContentfulPaint{constructor(allBrowserEvents){this.allBrowserEvents=allBrowserEvents;}
findCandidates(){const finalLcpEvents=this.findFinalLcpEventOfEachNavigation(this.allBrowserEvents);const finalCandidates=finalLcpEvents.filter(finalLcpEvent=>!LcpInvalidateEvent.isLcpInvalidateEvent(finalLcpEvent));return finalCandidates;}
findFinalLcpEventOfEachNavigation(allBrowserEvents){const lcpEvents=[];for(const lcpEvent of allBrowserEvents){if(LcpCandidateEvent.isLcpCandidateEvent(lcpEvent)){lcpEvents.push(new LcpCandidateEvent(lcpEvent));}else if(LcpInvalidateEvent.isLcpInvalidateEvent(lcpEvent)){lcpEvents.push(new LcpInvalidateEvent(lcpEvent));}}
-const lcpEventsGroupedByNavigation=new Map();for(const e of lcpEvents){const key=e.mainFrameTreeNodeId;if(!lcpEventsGroupedByNavigation.has(key)){lcpEventsGroupedByNavigation[key]=[];}
-lcpEventsGroupedByNavigation[key].push(e);}
-const finalLcpEventOfEachNaivgation=[];for(const lcpEventList of Object.values(lcpEventsGroupedByNavigation)){lcpEventList.sort((a,b)=>a.start-b.start);finalLcpEventOfEachNaivgation.push(lcpEventList[lcpEventList.length-1]);}
-return finalLcpEventOfEachNaivgation;}}
-return{LCP_CANDIDATE_EVENT_TITLE,LCP_INVALIDATE_EVENT_TITLE,LargestContentfulPaint,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const LONG_TASK_THRESHOLD_MS=50;const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const unitlessNumber_smallerIsBetter=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;const RelatedEventSet=tr.v.d.RelatedEventSet;const hasCategoryAndName=tr.metrics.sh.hasCategoryAndName;const EventFinderUtils=tr.e.chrome.EventFinderUtils;function createBreakdownDiagnostic(breakdownTree){const breakdownDiagnostic=new tr.v.d.Breakdown();breakdownDiagnostic.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(const label in breakdownTree){breakdownDiagnostic.set(label,breakdownTree[label].total);}
+const lcpEventsGroupedByNavigation=new Map();for(const e of lcpEvents){const key=e.mainFrameTreeNodeId;if(!lcpEventsGroupedByNavigation.has(key)){lcpEventsGroupedByNavigation.set(key,[]);}
+lcpEventsGroupedByNavigation.get(key).push(e);}
+const finalLcpEventOfEachNavigation=[];for(const lcpEventList of lcpEventsGroupedByNavigation.values()){lcpEventList.sort((a,b)=>a.start-b.start);finalLcpEventOfEachNavigation.push(lcpEventList[lcpEventList.length-1]);}
+return finalLcpEventOfEachNavigation;}}
+return{LCP_CANDIDATE_EVENT_TITLE,LCP_INVALIDATE_EVENT_TITLE,LargestContentfulPaint,};});'use strict';tr.exportTo('tr.b.math',function(){function earthMoversDistance(firstHistogram,secondHistogram){const buckets=firstHistogram.length;if(secondHistogram.length!==buckets){throw new Error('Histograms have a different number of bins.');}
+const arrSum=arr=>arr.reduce((a,b)=>a+b,0);if(arrSum(firstHistogram)!==arrSum(secondHistogram)){throw new Error('The histograms\' sizes don\'t match.');}
+let total=0;let remainder=0;for(let bucket=0;bucket<buckets;bucket++){remainder+=secondHistogram[bucket]-
+firstHistogram[bucket];total+=Math.abs(remainder);}
+return total;}
+return{earthMoversDistance,};});'use strict';tr.exportTo('tr.e.chrome',function(){const earthMoversDistance=tr.b.math.earthMoversDistance;class SpeedIndex{static getSnapshotsProgress_(timestampedColorHistograms){const numberOfScreenshots=timestampedColorHistograms.length;const firstHistogram=timestampedColorHistograms[0].colorHistogram;const lastHistogram=timestampedColorHistograms[numberOfScreenshots-1].colorHistogram;const totalDistance=earthMoversDistance(firstHistogram[0],lastHistogram[0])+
+earthMoversDistance(firstHistogram[1],lastHistogram[1])+
+earthMoversDistance(firstHistogram[2],lastHistogram[2]);if(totalDistance===0){return[{value:1,ts:timestampedColorHistograms[0].ts}];}
+const snapshotsProgress=new Array(numberOfScreenshots);for(let i=0;i<numberOfScreenshots;i++){const histogram=timestampedColorHistograms[i].colorHistogram;const distance=earthMoversDistance(histogram[0],lastHistogram[0])+
+earthMoversDistance(histogram[1],lastHistogram[1])+
+earthMoversDistance(histogram[2],lastHistogram[2]);const moved=Math.max(totalDistance-distance,0);snapshotsProgress[i]={value:(moved/totalDistance),ts:timestampedColorHistograms[i].ts};}
+return snapshotsProgress;}
+static speedIndexFromSnapshotsProgress_(snapshotsProgress){if(snapshotsProgress.length===0){throw new Error('No snapshots were provided.');}
+let prevSnapshotTimeTaken=0;let prevSnapshotProgress=0;let speedIndex=0;const numberOfScreenshots=snapshotsProgress.length;for(let i=0;i<numberOfScreenshots;i++){const elapsed=snapshotsProgress[i].ts-prevSnapshotTimeTaken;speedIndex+=elapsed*(1.0-prevSnapshotProgress);prevSnapshotTimeTaken=snapshotsProgress[i].ts;prevSnapshotProgress=snapshotsProgress[i].value;}
+return Math.round(speedIndex);}
+static createColorHistogram(imagePixelValues){const n=imagePixelValues.length;const histogram=new Array(3);for(let j=0;j<3;j++){histogram[j]=new Array(256).fill(0);}
+for(let i=0;i<n;i+=4){const r=imagePixelValues[i];const g=imagePixelValues[i+1];const b=imagePixelValues[i+2];histogram[0][r]++;histogram[1][g]++;histogram[2][b]++;}
+return histogram;}
+static calculateSpeedIndex(timestampedColorHistograms){const snapshotsProgress=SpeedIndex.getSnapshotsProgress_(timestampedColorHistograms);return SpeedIndex.speedIndexFromSnapshotsProgress_(snapshotsProgress);}
+static lineSweep(lineSweepRects,viewport){const verticalSweepEdges=[];const horizontalSweepEdges=[];for(let i=0;i<lineSweepRects.length;i++){const rect=lineSweepRects[i];let left=rect.left;let right=rect.right;let top=rect.top;let bottom=rect.bottom;if(left>viewport.x+viewport.width)continue;if(right<viewport.x)continue;if(top>viewport.y+viewport.height)continue;if(bottom<viewport.y)continue;left=Math.max(left,viewport.y);right=Math.min(right,viewport.y+viewport.width);top=Math.max(top,viewport.y);bottom=Math.min(bottom,viewport.y+viewport.height);verticalSweepEdges.push({id:i,value:left,type:'left'},{id:i,value:right,type:'right'});horizontalSweepEdges.push({id:i,value:top,type:'top'},{id:i,value:bottom,type:'bottom'});}
+if(verticalSweepEdges.length===0||horizontalSweepEdges.length===0){return 0;}
+verticalSweepEdges.sort((a,b)=>a.value-b.value);horizontalSweepEdges.sort((a,b)=>a.value-b.value);const active=new Array(lineSweepRects.length).fill(false);let area=0;active[verticalSweepEdges[0].id]=true;for(let i=1;i<verticalSweepEdges.length;i++){const currentLine=verticalSweepEdges[i];const previousLine=verticalSweepEdges[i-1];const deltaX=currentLine.value-previousLine.value;if(deltaX===0)continue;let count=0;let firstRect;for(let j=0;j<horizontalSweepEdges.length;j++){if(active[horizontalSweepEdges[j].id]===true){if(horizontalSweepEdges[j].type==='top'){if(count===0){firstRect=j;}
+count++;}else{if(count===1){const deltaY=horizontalSweepEdges[j].value-
+horizontalSweepEdges[firstRect].value;area+=deltaX*deltaY;}
+count--;}}}
+active[currentLine.id]=(currentLine.type==='left');}
+return area;}
+static quadToRect(quad){const left=Math.min(quad[0],quad[2],quad[4]);const right=Math.max(quad[0],quad[2],quad[4]);const top=Math.min(quad[1],quad[3],quad[5]);const bottom=Math.max(quad[1],quad[3],quad[5]);return{left,right,top,bottom};}
+static calculateRectsBasedSpeedIndex(timestampedPaintRects,viewport){const numberOfRects=timestampedPaintRects.length;if(numberOfRects===0){throw new Error('Can\'t calculate speed index without any paint '+'rectangles.');}
+const areaAddedAtTimestamp=new Array(numberOfRects);const rects=[];let previousAreaOfUnion=0;let totalAreaOfUnion=0;for(let i=numberOfRects-1;i>=0;i--){rects.push(timestampedPaintRects[i].rect);const currentAreaOfUnion=SpeedIndex.lineSweep(rects,viewport);areaAddedAtTimestamp[i]={value:currentAreaOfUnion-previousAreaOfUnion,ts:timestampedPaintRects[i].ts};totalAreaOfUnion+=areaAddedAtTimestamp[i].value;previousAreaOfUnion=currentAreaOfUnion;}
+const paintProgressAtTimestamp=new Array(numberOfRects);let lastProgressRecorded=0;for(let i=0;i<numberOfRects;i++){paintProgressAtTimestamp[i]={value:areaAddedAtTimestamp[i].value/totalAreaOfUnion+
+lastProgressRecorded,ts:areaAddedAtTimestamp[i].ts};lastProgressRecorded=paintProgressAtTimestamp[i].value;}
+return SpeedIndex.speedIndexFromSnapshotsProgress_(paintProgressAtTimestamp);}}
+return{SpeedIndex,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const SpeedIndex=tr.e.chrome.SpeedIndex;const EventFinderUtils=tr.e.chrome.EventFinderUtils;const BIN_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function addRectsBasedSpeedIndexSample(samples,rendererHelper,navigationStart,loadDuration,frameID){let viewport;for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'viewport','loading')){if(event.args.data.frameID===frameID&&event.start<(navigationStart+loadDuration)){viewport=event.args.data;}}
+if(!viewport)return;const timestampedPaintRects=[];for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'PaintTimingVisualizer::LayoutObjectPainted','loading')){if(event.start>=navigationStart&&event.start<navigationStart+loadDuration){const paintRect=event.args.data.rect;if(!paintRect)continue;timestampedPaintRects.push({rect:SpeedIndex.quadToRect(paintRect),ts:event.start});}}
+const numberOfRects=timestampedPaintRects.length;if(numberOfRects===0)return;samples.push({value:SpeedIndex.calculateRectsBasedSpeedIndex(timestampedPaintRects,viewport)-navigationStart});}
+function collectRectsBasedSpeedIndexSamplesFromLoadExpectations(model,chromeHelper){const rectsBasedSpeedIndexSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];addRectsBasedSpeedIndexSample(rectsBasedSpeedIndexSamples,rendererHelper,expectation.navigationStart.start,expectation.duration,expectation.navigationStart.args.frame);}
+return rectsBasedSpeedIndexSamples;}
+function rectsBasedSpeedIndexMetric(histograms,model){const rectsBasedSpeedIndexHistogram=histograms.createHistogram('rectsBasedSpeedIndex',timeDurationInMs_smallerIsBetter,[],{binBoundaries:BIN_BOUNDARIES,description:' the average time at which visible parts of the'+' page are displayed (in ms).',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const samples=collectRectsBasedSpeedIndexSamplesFromLoadExpectations(model,chromeHelper);for(const sample of samples){rectsBasedSpeedIndexHistogram.addSample(sample.value);}}
+tr.metrics.MetricRegistry.register(rectsBasedSpeedIndexMetric);return{rectsBasedSpeedIndexMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){const LONG_TASK_THRESHOLD_MS=50;const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const unitlessNumber_smallerIsBetter=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;const RelatedEventSet=tr.v.d.RelatedEventSet;const hasCategoryAndName=tr.metrics.sh.hasCategoryAndName;const EventFinderUtils=tr.e.chrome.EventFinderUtils;function createBreakdownDiagnostic(breakdownTree){const breakdownDiagnostic=new tr.v.d.Breakdown();breakdownDiagnostic.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(const label in breakdownTree){breakdownDiagnostic.set(label,breakdownTree[label].total);}
return breakdownDiagnostic;}
const LOADING_METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const TIME_TO_INTERACTIVE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,40e3,35).addExponentialBins(80e3,15);const LAYOUT_SHIFT_SCORE_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,50,25);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ts){const objects=rendererHelper.process.objects;const frameLoaderInstances=objects.instancesByTypeName_.FrameLoader;if(frameLoaderInstances===undefined)return undefined;let snapshot;for(const instance of frameLoaderInstances){if(!instance.isAliveAt(ts))continue;const maybeSnapshot=instance.getSnapshotAt(ts);if(frameIdRef!==maybeSnapshot.args.frame.id_ref)continue;snapshot=maybeSnapshot;}
return snapshot;}
@@ -8578,7 +8440,7 @@ function findAllEvents(rendererHelper,category,title){const targetEvents=[];for(
return targetEvents;}
function getMostRecentValidEvent(rendererHelper,category,title){const targetEvents=findAllEvents(rendererHelper,category,title);let validEvent;for(const targetEvent of targetEvents){if(rendererHelper.isTelemetryInternalEvent(targetEvent))continue;if(validEvent===undefined){validEvent=targetEvent;}else{if(validEvent.start<targetEvent.start){validEvent=targetEvent;}}}
return validEvent;}
-function getFirstViewportReadySamples(rendererHelper,navIdToNavStartEvents){const samples=[];const pcEvent=getMostRecentValidEvent(rendererHelper,'blink.user_timing','pc');if(pcEvent===undefined)return samples;if(rendererHelper.isTelemetryInternalEvent(pcEvent))return samples;const navigationStartEvent=navIdToNavStartEvents.get(pcEvent.args.data.navigationId);if(navigationStartEvent===undefined)return samples;const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,pcEvent.start);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToEventRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventRange);samples.push({value:navStartToEventRange.duration,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),Start:new RelatedEventSet(navigationStartEvent),End:new RelatedEventSet(pcEvent)}});return samples;}
+function getFirstViewportReadySamples(rendererHelper,navIdToNavStartEvents){const samples=[];const pcEvent=getMostRecentValidEvent(rendererHelper,'blink.user_timing','pc');if(pcEvent===undefined)return samples;if(rendererHelper.isTelemetryInternalEvent(pcEvent))return samples;const navigationStartEvent=navIdToNavStartEvents.get(pcEvent.args.data.navigationId);if(navigationStartEvent===undefined)return samples;const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,pcEvent.start);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToEventRange);if(rendererHelper.mainThread===undefined)return samples;const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventRange);samples.push({value:navStartToEventRange.duration,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),Start:new RelatedEventSet(navigationStartEvent),End:new RelatedEventSet(pcEvent)}});return samples;}
function getAboveTheFoldLoadedToVisibleSamples(rendererHelper){const samples=[];const pcEvent=getMostRecentValidEvent(rendererHelper,'blink.user_timing','pc');const visibleEvent=getMostRecentValidEvent(rendererHelper,'blink.user_timing','visible');if(pcEvent!==undefined&&visibleEvent!==undefined){samples.push({value:Math.max(0.0,pcEvent.start-visibleEvent.start),diagnostics:{Start:new RelatedEventSet(visibleEvent),End:new RelatedEventSet(pcEvent)}});}
return samples;}
function findTimeToXEntries(category,eventName,rendererHelper,frameToNavStartEvents,navIdToNavStartEvents){const targetEvents=findAllEvents(rendererHelper,category,eventName);const entries=[];for(const targetEvent of targetEvents){if(rendererHelper.isTelemetryInternalEvent(targetEvent))continue;const frameIdRef=targetEvent.args.frame;const snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,targetEvent.start);if(snapshot===undefined||!snapshot.args.isLoadingMainFrame)continue;const url=snapshot.args.documentLoaderURL;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(url))continue;let navigationStartEvent;if(targetEvent.args.data===undefined||targetEvent.args.data.navigationId===undefined){navigationStartEvent=EventFinderUtils.findLastEventStartingOnOrBeforeTimestamp(frameToNavStartEvents.get(frameIdRef)||[],targetEvent.start);}else{navigationStartEvent=navIdToNavStartEvents.get(targetEvent.args.data.navigationId);}
@@ -8589,25 +8451,9 @@ return samples;}
function collectTimeToEventInCpuTime(rendererHelper,timeToXEntries){const samples=[];for(const{targetEvent,navigationStartEvent,url}of timeToXEntries){const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,targetEvent.start);const mainThreadCpuTime=rendererHelper.mainThread.getCpuTimeForRange(navStartToEventRange);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,navStartToEventRange);samples.push({value:mainThreadCpuTime,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStartEvent),end:new RelatedEventSet(targetEvent),infos:new tr.v.d.GenericSet([{pid:rendererHelper.pid,start:navigationStartEvent.start,event:targetEvent.start,}]),}});}
return samples;}
function findLayoutShiftSamples(rendererHelper){let sample;EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'LayoutShift','loading').forEach((events)=>{const evData=events.pop().args.data;if(evData.is_main_frame){sample={value:evData.cumulative_score};}});return sample?[sample]:[];}
-function lineSweep(lineSweepRects,viewport){const verticalSweepRects=[];const horizontalSweepRects=[];for(let i=0;i<lineSweepRects.length;i++){const rect=lineSweepRects[i];let left=rect.left;let right=rect.right;let top=rect.top;let bottom=rect.bottom;if(left>viewport.x+viewport.width)continue;if(right<viewport.x)continue;if(top>viewport.y+viewport.height)continue;if(bottom<viewport.y)continue;left=Math.max(left,viewport.y);right=Math.min(right,viewport.y+viewport.width);top=Math.max(top,viewport.y);bottom=Math.min(bottom,viewport.y+viewport.height);verticalSweepRects.push({id:i,value:left,type:'left'},{id:i,value:right,type:'right'});horizontalSweepRects.push({id:i,value:top,type:'top'},{id:i,value:bottom,type:'bottom'});}
-verticalSweepRects.sort((a,b)=>a.value-b.value);horizontalSweepRects.sort((a,b)=>a.value-b.value);const active=new Array(lineSweepRects.length).fill(0);let area=0;active[verticalSweepRects[0].id]=1;for(let i=1;i<verticalSweepRects.length;i++){const currentLine=verticalSweepRects[i];const previousLine=verticalSweepRects[i-1];const deltaX=currentLine.value-previousLine.value;if(deltaX===0)continue;let count=0;let firstRect;for(let j=0;j<horizontalSweepRects.length;j++){if(active[horizontalSweepRects[j].id]===1){if(horizontalSweepRects[j].type==='top'){if(count===0){firstRect=j;count++;}}else{if(count===1){const deltaY=horizontalSweepRects[j].value-
-horizontalSweepRects[firstRect].value;area+=deltaX*deltaY;count--;}}}}
-active[currentLine.id]=(currentLine.type==='left');}
-return area;}
-function addVisuallyCompleteBeforeSomeTimeSample(samples,rendererHelper,navigationStart,loadDuration,frameID){const someTime=2000;if(loadDuration<someTime)return;let viewport;for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'viewport','loading')){if(event.args.data.frameID===frameID&&event.start>navigationStart&&event.start<navigationStart+loadDuration){viewport=event.args.data;break;}}
-if(!viewport)return;const ccDisplayItemListObjects=rendererHelper.process.objects.getAllInstancesNamed('cc::DisplayItemList');if(!ccDisplayItemListObjects||!ccDisplayItemListObjects.length)return;const RectsUpdatedAfterSomeTime=[];for(let i=0;i<ccDisplayItemListObjects.length;i++){const displayItemListSnapshots=ccDisplayItemListObjects[i].snapshots;for(let j=0;j<displayItemListSnapshots.length;j++){const snapshot=displayItemListSnapshots[j];const timestamp=snapshot.ts;if(timestamp<navigationStart||timestamp>navigationStart+loadDuration){continue;}
-if(timestamp-navigationStart>someTime){RectsUpdatedAfterSomeTime.push(snapshot.args.params.layerRect);}}}
-const areaUpdatedAfterSomeTime=RectsUpdatedAfterSomeTime.length?lineSweep(RectsUpdatedAfterSomeTime,viewport):0;const pixelsLastUpdatedBeforeSomeTime=1-
-areaUpdatedAfterSomeTime/(viewport.width*viewport.height);samples.push({value:pixelsLastUpdatedBeforeSomeTime});}
-function addSpeedIndexSample(rendererHelper){const rects=[];let totalArea=0;for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'PaintTracker::LayoutObjectPainted','loading')){const rect=event.args.data.visible_new_visual_rect;if(!rect)continue;const area=areaOfQuad(rect);if(!area)continue;rects.push({rect,ts:event.start});totalArea+=area;}
-const sample=[];if(!totalArea)return sample;let areaSoFar=0;const progress=new Array(rects.length);for(let i=0;i<rects.length;i++){const area=areaOfQuad(rects[i].rect);areaSoFar+=(area/totalArea);progress[i]={value:areaSoFar,ts:rects[i].ts};}
-sample.push({value:calculateSpeedIndex(progress)});return sample;}
-function areaOfQuad(quad){const width=Math.max(Math.abs(quad[0]-quad[2]),Math.abs(quad[2]-quad[4]));const height=Math.max(Math.abs(quad[1]-quad[3]),Math.abs(quad[3]-quad[5]));return width*height;}
-function calculateSpeedIndex(progress){let lastMs=0;let lastProgress=0;let speedIndex=0;for(let i=0;i<progress.length;i++){const elapsed=progress[i].ts-lastMs;speedIndex+=elapsed*(1.0-lastProgress);lastMs=progress[i].ts;lastProgress=progress[i].value;}
-return speedIndex;}
function addFirstMeaningfulPaintSample(samples,rendererHelper,navigationStart,fmpMarkerEvent,url){const navStartToFMPRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,fmpMarkerEvent.start);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToFMPRange);const timeToFirstMeaningfulPaint=navStartToFMPRange.duration;const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToFMPRange);samples.push({value:timeToFirstMeaningfulPaint,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStart),end:new RelatedEventSet(fmpMarkerEvent),infos:new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start,}]),}});}
function addFirstMeaningfulPaintCpuTimeSample(samples,rendererHelper,navigationStart,fmpMarkerEvent,url){const navStartToFMPRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,fmpMarkerEvent.start);const mainThreadCpuTime=rendererHelper.mainThread.getCpuTimeForRange(navStartToFMPRange);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,navStartToFMPRange);samples.push({value:mainThreadCpuTime,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStart),end:new RelatedEventSet(fmpMarkerEvent),infos:new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start,}]),}});}
-function decorateInteractivitySampleWithDiagnostics_(rendererHelper,eventTimestamp,navigationStartEvent,firstMeaningfulPaintTime,domContentLoadedEndTime,url){if(eventTimestamp===undefined)return undefined;const navigationStartTime=navigationStartEvent.start;const navStartToEventTimeRange=tr.b.math.Range.fromExplicitRange(navigationStartTime,eventTimestamp);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToEventTimeRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventTimeRange);const breakdownDiagnostic=createBreakdownDiagnostic(breakdownTree);return{value:navStartToEventTimeRange.duration,diagnostics:tr.v.d.DiagnosticMap.fromObject({'Start':new RelatedEventSet(navigationStartEvent),'Navigation infos':new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,navigationStartTime,firstMeaningfulPaintTime,domContentLoadedEndTime,eventTimestamp,}]),'Breakdown of [navStart, eventTimestamp]':breakdownDiagnostic,}),};}
+function decorateInteractivitySampleWithDiagnostics_(rendererHelper,eventTimestamp,navigationStartEvent,firstContentfulPaintTime,domContentLoadedEndTime,url){if(eventTimestamp===undefined)return undefined;const navigationStartTime=navigationStartEvent.start;const navStartToEventTimeRange=tr.b.math.Range.fromExplicitRange(navigationStartTime,eventTimestamp);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToEventTimeRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventTimeRange);const breakdownDiagnostic=createBreakdownDiagnostic(breakdownTree);return{value:navStartToEventTimeRange.duration,diagnostics:tr.v.d.DiagnosticMap.fromObject({'Start':new RelatedEventSet(navigationStartEvent),'Navigation infos':new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,navigationStartTime,firstContentfulPaintTime,domContentLoadedEndTime,eventTimestamp,}]),'Breakdown of [navStart, eventTimestamp]':breakdownDiagnostic,}),};}
function getCandidateIndex(entry){return entry.targetEvent.args.data.candidateIndex;}
function findLastCandidateForEachNavigation(timeToXEntries){const entryMap=new Map();for(const e of timeToXEntries){const navStartEvent=e.navigationStartEvent;if(!entryMap.has(navStartEvent)){entryMap.set(navStartEvent,[]);}
entryMap.get(navStartEvent).push(e);}
@@ -8617,17 +8463,18 @@ return lastCandidates;}
function findLargestTextPaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents){const timeToPaintEntries=findTimeToXEntries('loading','LargestTextPaint::Candidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const timeToPaintBlockingEntries=findTimeToXEntries('loading','LargestTextPaint::NoCandidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const lastCandidateEvents=findLastCandidateForEachNavigation(timeToPaintEntries.concat(timeToPaintBlockingEntries)).filter(event=>event.targetEvent.title!=='LargestTextPaint::NoCandidate');return collectTimeToEvent(rendererHelper,lastCandidateEvents);}
function findLargestImagePaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents){const timeToPaintEntries=findTimeToXEntries('loading','LargestImagePaint::Candidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const timeToPaintBlockingEntries=findTimeToXEntries('loading','LargestImagePaint::NoCandidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const lastCandidateEvents=findLastCandidateForEachNavigation(timeToPaintEntries.concat(timeToPaintBlockingEntries)).filter(event=>event.targetEvent.title!=='LargestImagePaint::NoCandidate');return collectTimeToEvent(rendererHelper,lastCandidateEvents);}
function findLargestContentfulPaintHistogramSamples(allBrowserEvents){const lcp=new tr.e.chrome.LargestContentfulPaint(allBrowserEvents);const lcpSamples=lcp.findCandidates().map(candidate=>{const{durationInMilliseconds,size,type,inMainFrame,mainFrameTreeNodeId}=candidate;return{value:durationInMilliseconds,diagnostics:{size:new tr.v.d.GenericSet([size]),type:new tr.v.d.GenericSet([type]),inMainFrame:new tr.v.d.GenericSet([inMainFrame]),mainFrameTreeNodeId:new tr.v.d.GenericSet([mainFrameTreeNodeId]),},};});return lcpSamples;}
-function collectLoadingMetricsForRenderer(rendererHelper){const frameToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const navIdToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByNavId(rendererHelper,'navigationStart','blink.user_timing');const firstPaintSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('loading','firstPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));const timeToFCPEntries=findTimeToXEntries('loading','firstContentfulPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const firstContentfulPaintSamples=collectTimeToEvent(rendererHelper,timeToFCPEntries);const firstContentfulPaintCpuTimeSamples=collectTimeToEventInCpuTime(rendererHelper,timeToFCPEntries);const onLoadSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('blink.user_timing','loadEventStart',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));const aboveTheFoldLoadedToVisibleSamples=getAboveTheFoldLoadedToVisibleSamples(rendererHelper);const firstViewportReadySamples=getFirstViewportReadySamples(rendererHelper,navIdToNavStartEvents);const largestImagePaintSamples=findLargestImagePaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const largestTextPaintSamples=findLargestTextPaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const layoutShiftSamples=findLayoutShiftSamples(rendererHelper);const speedIndexSamples=addSpeedIndexSample(rendererHelper);return{frameToNavStartEvents,firstPaintSamples,firstContentfulPaintSamples,firstContentfulPaintCpuTimeSamples,onLoadSamples,aboveTheFoldLoadedToVisibleSamples,firstViewportReadySamples,largestImagePaintSamples,largestTextPaintSamples,layoutShiftSamples,speedIndexSamples};}
-function collectMetricsFromLoadExpectations(model,chromeHelper){const interactiveSamples=[];const firstCpuIdleSamples=[];const firstMeaningfulPaintSamples=[];const firstMeaningfulPaintCpuTimeSamples=[];const visuallyCompleteBeforeSomeTimeSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
-const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];addVisuallyCompleteBeforeSomeTimeSample(visuallyCompleteBeforeSomeTimeSamples,rendererHelper,expectation.navigationStart.start,expectation.duration,expectation.navigationStart.args.frame);if(expectation.fmpEvent!==undefined){addFirstMeaningfulPaintSample(firstMeaningfulPaintSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);addFirstMeaningfulPaintCpuTimeSample(firstMeaningfulPaintCpuTimeSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);}
-if(expectation.firstCpuIdleTime!==undefined){firstCpuIdleSamples.push(decorateInteractivitySampleWithDiagnostics_(rendererHelper,expectation.firstCpuIdleTime,expectation.navigationStart,expectation.fmpEvent.start,expectation.domContentLoadedEndEvent.start,expectation.url));}
-if(expectation.timeToInteractive!==undefined){interactiveSamples.push(decorateInteractivitySampleWithDiagnostics_(rendererHelper,expectation.timeToInteractive,expectation.navigationStart,expectation.fmpEvent.start,expectation.domContentLoadedEndEvent.start,expectation.url));}}
-return{firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstCpuIdleSamples,interactiveSamples,visuallyCompleteBeforeSomeTimeSamples,};}
+function collectLoadingMetricsForRenderer(rendererHelper){const frameToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const navIdToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByNavId(rendererHelper,'navigationStart','blink.user_timing');const firstPaintSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('loading','firstPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));const timeToFCPEntries=findTimeToXEntries('loading','firstContentfulPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const firstContentfulPaintSamples=collectTimeToEvent(rendererHelper,timeToFCPEntries);const firstContentfulPaintCpuTimeSamples=collectTimeToEventInCpuTime(rendererHelper,timeToFCPEntries);const onLoadSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('blink.user_timing','loadEventStart',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));const aboveTheFoldLoadedToVisibleSamples=getAboveTheFoldLoadedToVisibleSamples(rendererHelper);const firstViewportReadySamples=getFirstViewportReadySamples(rendererHelper,navIdToNavStartEvents);const largestImagePaintSamples=findLargestImagePaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const largestTextPaintSamples=findLargestTextPaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const layoutShiftSamples=findLayoutShiftSamples(rendererHelper);const navigationStartSamples=timeToFCPEntries.map(entry=>{return{value:entry.navigationStartEvent.start};});return{frameToNavStartEvents,firstPaintSamples,firstContentfulPaintSamples,firstContentfulPaintCpuTimeSamples,onLoadSamples,aboveTheFoldLoadedToVisibleSamples,firstViewportReadySamples,largestImagePaintSamples,largestTextPaintSamples,layoutShiftSamples,navigationStartSamples,};}
+function collectMetricsFromLoadExpectations(model,chromeHelper){const interactiveSamples=[];const firstCpuIdleSamples=[];const firstMeaningfulPaintSamples=[];const firstMeaningfulPaintCpuTimeSamples=[];const totalBlockingTimeSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];if(expectation.fmpEvent!==undefined){addFirstMeaningfulPaintSample(firstMeaningfulPaintSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);addFirstMeaningfulPaintCpuTimeSample(firstMeaningfulPaintCpuTimeSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);}
+if(expectation.firstCpuIdleTime!==undefined){firstCpuIdleSamples.push(decorateInteractivitySampleWithDiagnostics_(rendererHelper,expectation.firstCpuIdleTime,expectation.navigationStart,expectation.fcpEvent.start,expectation.domContentLoadedEndEvent.start,expectation.url));}
+if(expectation.timeToInteractive!==undefined){interactiveSamples.push(decorateInteractivitySampleWithDiagnostics_(rendererHelper,expectation.timeToInteractive,expectation.navigationStart,expectation.fcpEvent.start,expectation.domContentLoadedEndEvent.start,expectation.url));}
+if(expectation.totalBlockingTime!==undefined){totalBlockingTimeSamples.push({value:expectation.totalBlockingTime,diagnostics:{url:new tr.v.d.GenericSet([expectation.url]),navigationStart:new RelatedEventSet(expectation.navigationStart),firstContentfulPaint:new RelatedEventSet(expectation.fcpEvent),interactiveTime:new tr.v.d.GenericSet([expectation.timeToInteractive]),}});}}
+return{firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstCpuIdleSamples,interactiveSamples,totalBlockingTimeSamples,};}
function addSamplesToHistogram(samples,histogram,histograms){for(const sample of samples){histogram.addSample(sample.value,sample.diagnostics);if(histogram.name!=='timeToFirstContentfulPaint')continue;if(!sample.breakdownTree)continue;for(const[category,breakdown]of Object.entries(sample.breakdownTree)){const relatedName=`${histogram.name}:${category}`;let relatedHist=histograms.getHistogramsNamed(relatedName)[0];if(!relatedHist){relatedHist=histograms.createHistogram(relatedName,histogram.unit,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,summaryOptions:{count:false,max:false,min:false,sum:false,},});let relatedNames=histogram.diagnostics.get('breakdown');if(!relatedNames){relatedNames=new tr.v.d.RelatedNameMap();histogram.diagnostics.set('breakdown',relatedNames);}
relatedNames.set(category,relatedName);}
relatedHist.addSample(breakdown.total,{breakdown:tr.v.d.Breakdown.fromEntries(Object.entries(breakdown.events)),});}}}
-function loadingMetric(histograms,model){const firstPaintHistogram=histograms.createHistogram('timeToFirstPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintHistogram=histograms.createHistogram('timeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const onLoadHistogram=histograms.createHistogram('timeToOnload',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to onload. '+'This is temporary metric used for PCv1/v2 sanity checking',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintHistogram=histograms.createHistogram('timeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const timeToInteractiveHistogram=histograms.createHistogram('timeToInteractive',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to Interactive',summaryOptions:SUMMARY_OPTIONS,});const timeToFirstCpuIdleHistogram=histograms.createHistogram('timeToFirstCpuIdle',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to First CPU Idle',summaryOptions:SUMMARY_OPTIONS,});const aboveTheFoldLoadedToVisibleHistogram=histograms.createHistogram('aboveTheFoldLoadedToVisible',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time from first visible to load for AMP pages only.',summaryOptions:SUMMARY_OPTIONS,});const firstViewportReadyHistogram=histograms.createHistogram('timeToFirstViewportReady',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time from navigation to load for AMP pages only. ',summaryOptions:SUMMARY_OPTIONS,});const largestImagePaintHistogram=histograms.createHistogram('largestImagePaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Image Paint',summaryOptions:SUMMARY_OPTIONS,});const largestTextPaintHistogram=histograms.createHistogram('largestTextPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Text Paint',summaryOptions:SUMMARY_OPTIONS,});const largestContentfulPaintHistogram=histograms.createHistogram('largestContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Contentful Paint',summaryOptions:SUMMARY_OPTIONS,});const layoutShiftHistogram=histograms.createHistogram('mainFrameCumulativeLayoutShift',unitlessNumber_smallerIsBetter,[],{binBoundaries:LAYOUT_SHIFT_SCORE_BOUNDARIES,description:'Main Frame Document Cumulative Layout Shift Score',summaryOptions:SUMMARY_OPTIONS,});const visuallyCompleteBeforeSomeTimeHistogram=histograms.createHistogram('visuallyCompleteBeforeSomeTime',unitlessNumber_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Fraction of pixels in the initial viewport reaching '+'final value within some time interval after navigation start.',summaryOptions:SUMMARY_OPTIONS,});const speedIndexHistogram=histograms.createHistogram('speedIndex',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:' the average time at which visible parts of the'+' page are displayed (in ms).',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;const samplesSet=collectLoadingMetricsForRenderer(rendererHelper);const lcpSamples=findLargestContentfulPaintHistogramSamples(chromeHelper.browserHelper.mainThread.sliceGroup.slices);addSamplesToHistogram(lcpSamples,largestContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstPaintSamples,firstPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintSamples,firstContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintCpuTimeSamples,firstContentfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.onLoadSamples,onLoadHistogram,histograms);addSamplesToHistogram(samplesSet.aboveTheFoldLoadedToVisibleSamples,aboveTheFoldLoadedToVisibleHistogram,histograms);addSamplesToHistogram(samplesSet.firstViewportReadySamples,firstViewportReadyHistogram,histograms);addSamplesToHistogram(samplesSet.largestImagePaintSamples,largestImagePaintHistogram,histograms);addSamplesToHistogram(samplesSet.largestTextPaintSamples,largestTextPaintHistogram,histograms);addSamplesToHistogram(samplesSet.layoutShiftSamples,layoutShiftHistogram,histograms);addSamplesToHistogram(samplesSet.speedIndexSamples,speedIndexHistogram,histograms);}
-const samplesSet=collectMetricsFromLoadExpectations(model,chromeHelper);addSamplesToHistogram(samplesSet.firstMeaningfulPaintSamples,firstMeaningfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstMeaningfulPaintCpuTimeSamples,firstMeaningfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.interactiveSamples,timeToInteractiveHistogram,histograms);addSamplesToHistogram(samplesSet.firstCpuIdleSamples,timeToFirstCpuIdleHistogram,histograms);addSamplesToHistogram(samplesSet.visuallyCompleteBeforeSomeTimeSamples,visuallyCompleteBeforeSomeTimeHistogram,histograms);}
+function loadingMetric(histograms,model){const firstPaintHistogram=histograms.createHistogram('timeToFirstPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintHistogram=histograms.createHistogram('timeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const onLoadHistogram=histograms.createHistogram('timeToOnload',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to onload. '+'This is temporary metric used for PCv1/v2 sanity checking',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintHistogram=histograms.createHistogram('timeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const timeToInteractiveHistogram=histograms.createHistogram('timeToInteractive',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to Interactive',summaryOptions:SUMMARY_OPTIONS,});const totalBlockingTimeHistogram=histograms.createHistogram('totalBlockingTime',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Total Blocking Time',summaryOptions:SUMMARY_OPTIONS,});const timeToFirstCpuIdleHistogram=histograms.createHistogram('timeToFirstCpuIdle',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to First CPU Idle',summaryOptions:SUMMARY_OPTIONS,});const aboveTheFoldLoadedToVisibleHistogram=histograms.createHistogram('aboveTheFoldLoadedToVisible',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time from first visible to load for AMP pages only.',summaryOptions:SUMMARY_OPTIONS,});const firstViewportReadyHistogram=histograms.createHistogram('timeToFirstViewportReady',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time from navigation to load for AMP pages only. ',summaryOptions:SUMMARY_OPTIONS,});const largestImagePaintHistogram=histograms.createHistogram('largestImagePaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Image Paint',summaryOptions:SUMMARY_OPTIONS,});const largestTextPaintHistogram=histograms.createHistogram('largestTextPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Text Paint',summaryOptions:SUMMARY_OPTIONS,});const largestContentfulPaintHistogram=histograms.createHistogram('largestContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Contentful Paint',summaryOptions:SUMMARY_OPTIONS,});const layoutShiftHistogram=histograms.createHistogram('mainFrameCumulativeLayoutShift',unitlessNumber_smallerIsBetter,[],{binBoundaries:LAYOUT_SHIFT_SCORE_BOUNDARIES,description:'Main Frame Document Cumulative Layout Shift Score',summaryOptions:SUMMARY_OPTIONS,});const navigationStartHistogram=histograms.createHistogram('navigationStart',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'navigationStart',summaryOptions:SUMMARY_OPTIONS,});tr.metrics.sh.rectsBasedSpeedIndexMetric(histograms,model);const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;const samplesSet=collectLoadingMetricsForRenderer(rendererHelper);const lcpSamples=findLargestContentfulPaintHistogramSamples(chromeHelper.browserHelper.mainThread.sliceGroup.slices);addSamplesToHistogram(lcpSamples,largestContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstPaintSamples,firstPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintSamples,firstContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintCpuTimeSamples,firstContentfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.onLoadSamples,onLoadHistogram,histograms);addSamplesToHistogram(samplesSet.aboveTheFoldLoadedToVisibleSamples,aboveTheFoldLoadedToVisibleHistogram,histograms);addSamplesToHistogram(samplesSet.firstViewportReadySamples,firstViewportReadyHistogram,histograms);addSamplesToHistogram(samplesSet.largestImagePaintSamples,largestImagePaintHistogram,histograms);addSamplesToHistogram(samplesSet.largestTextPaintSamples,largestTextPaintHistogram,histograms);addSamplesToHistogram(samplesSet.layoutShiftSamples,layoutShiftHistogram,histograms);addSamplesToHistogram(samplesSet.navigationStartSamples,navigationStartHistogram,histograms);}
+const samplesSet=collectMetricsFromLoadExpectations(model,chromeHelper);addSamplesToHistogram(samplesSet.firstMeaningfulPaintSamples,firstMeaningfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstMeaningfulPaintCpuTimeSamples,firstMeaningfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.interactiveSamples,timeToInteractiveHistogram,histograms);addSamplesToHistogram(samplesSet.firstCpuIdleSamples,timeToFirstCpuIdleHistogram,histograms);addSamplesToHistogram(samplesSet.totalBlockingTimeSamples,totalBlockingTimeHistogram,histograms);}
tr.metrics.MetricRegistry.register(loadingMetric);return{loadingMetric,createBreakdownDiagnostic};});'use strict';tr.exportTo('tr.metrics',function(){const SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY=tr.v.HistogramBinBoundaries.createExponential(1,1000,50);function spaNavigationMetric(histograms,model){const histogram=new tr.v.Histogram('spaNavigationStartToFpDuration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY);histogram.description='Latency between the input event causing'+' a SPA navigation and the first paint event after it';histogram.customizeSummaryOptions({count:false,sum:false,});const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper){return;}
const rendererHelpers=modelHelper.rendererHelpers;if(!rendererHelpers){return;}
const browserHelper=modelHelper.browserHelper;for(const rendererHelper of Object.values(rendererHelpers)){const spaNavigations=tr.metrics.findSpaNavigationsOnRenderer(rendererHelper,browserHelper);for(const spaNav of spaNavigations){let beginTs=0;if(spaNav.navStartCandidates.inputLatencyAsyncSlice){const beginData=spaNav.navStartCandidates.inputLatencyAsyncSlice.args.data;beginTs=model.convertTimestampToModelTime('traceEventClock',beginData.INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT.time);}else{beginTs=spaNav.navStartCandidates.goToIndexSlice.start;}
@@ -8747,7 +8594,7 @@ function expectedQueueingTimeMetric(histograms,model){const chromeHelper=model.g
function addExpectedQueueingTimeMetric_(eqtName,getEventTimes,isCpuTime,rendererHelpers,histograms,model){function getTasks(rendererHelper){const tasks=[];for(const slice of
tr.e.chrome.EventFinderUtils.findToplevelSchedulerTasks(rendererHelper.mainThread)){const times=getEventTimes(slice);if(times.duration>0&&!containsForcedGC_(slice)){tasks.push({start:times.start,end:times.start+times.duration});}}
return tasks;}
-const totalHistogram=getOrCreateHistogram_(histograms,`total:${WINDOW_SIZE_MS}ms_window:${eqtName}`,`The maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window`+' for a given renderer');for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread.bounds.duration<WINDOW_SIZE_MS)continue;const tasks=getTasks(rendererHelper);const totalBreakdown=getV8Contribution_(eqtName,getEventTimes,isCpuTime,totalHistogram,histograms,rendererHelper,model);totalHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks),{v8:totalBreakdown});}}
+const totalHistogram=getOrCreateHistogram_(histograms,`total:${WINDOW_SIZE_MS}ms_window:${eqtName}`,`The maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window`+' for a given renderer');for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread===undefined)continue;if(rendererHelper.mainThread.bounds.duration<WINDOW_SIZE_MS)continue;const tasks=getTasks(rendererHelper);const totalBreakdown=getV8Contribution_(eqtName,getEventTimes,isCpuTime,totalHistogram,histograms,rendererHelper,model);totalHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks),{v8:totalBreakdown});}}
function getV8Contribution_(eqtName,getEventTimes,isCpuTime,totalEqtHistogram,histograms,rendererHelper,model){if(!model.categories.includes('v8'))return null;const totalBreakdown=new tr.v.d.Breakdown();const eventNamesWithTaskExtractors=getV8EventNamesWithTaskExtractors_(getEventTimes);if(!isCpuTime){const taskExtractorsUsingRCS=getV8EventNamesWithTaskExtractorsUsingRCS_(getEventTimes);for(const[eventName,getTasks]of taskExtractorsUsingRCS){eventNamesWithTaskExtractors.set(eventName,getTasks);}}
let totalNames=totalEqtHistogram.diagnostics.get('v8');if(!totalNames){totalNames=new tr.v.d.RelatedNameMap();totalEqtHistogram.diagnostics.set('v8',totalNames);}
for(const[eventName,getTasks]of eventNamesWithTaskExtractors){const totalHistogram=getOrCreateHistogram_(histograms,`total:${WINDOW_SIZE_MS}ms_window:${eqtName}:${eventName}`,`Contribution to the expected queueing time by ${eventName}`+' for a given renderer. It is computed as the maximum EQT in'+` a ${WINDOW_SIZE_MS}ms sliding window after shrinking top-level`+` tasks to contain only ${eventName} subevents`);const tasks=getTasks(rendererHelper);const totalSample=tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks);totalHistogram.addSample(totalSample);totalBreakdown.set(eventName,totalSample);totalNames.set(eventName,totalHistogram.name);}
@@ -8840,7 +8687,8 @@ if(!typeToSize[entry.objectTypeName]){typeToSize[entry.objectTypeName]=0;}
typeToSize[entry.objectTypeName]+=entry.size;}
let largestValue=0;let largestType='';for(const key in typeToSize){if(largestValue<typeToSize[key]){largestValue=typeToSize[key];largestType=key;}}
addProcessScalar({source:'reported_by_chrome',component:[allocatorName,largestType],property:HEAP_CATEGORY_SIZE,value:largestValue});}}
-function addV8MemoryDumpValues(processDump,addProcessScalar){const v8Dump=processDump.getMemoryAllocatorDumpByFullName('v8');if(v8Dump===undefined)return;v8Dump.children.forEach(function(isolateDump){const mallocDump=isolateDump.getDescendantDumpByFullName('malloc');if(mallocDump!==undefined){addV8ComponentValues(mallocDump,['v8','allocated_by_malloc'],addProcessScalar);}
+function addV8MemoryDumpValues(processDump,addProcessScalar){const v8Dump=processDump.getMemoryAllocatorDumpByFullName('v8');if(v8Dump===undefined)return;const sharedDump=v8Dump.getDescendantDumpByFullName('shared');if(sharedDump!==undefined){addV8ComponentValues(sharedDump,['v8','shared'],addProcessScalar);sharedDump.children.forEach(function(subDump){addV8ComponentValues(subDump,['v8','shared',subDump.name],addProcessScalar);});}
+v8Dump.children.forEach(function(isolateDump){const mallocDump=isolateDump.getDescendantDumpByFullName('malloc');if(mallocDump!==undefined){addV8ComponentValues(mallocDump,['v8','allocated_by_malloc'],addProcessScalar);}
let heapDump=isolateDump.getDescendantDumpByFullName('heap');if(heapDump===undefined){heapDump=isolateDump.getDescendantDumpByFullName('heap_spaces');}
if(heapDump!==undefined){addV8ComponentValues(heapDump,['v8','heap'],addProcessScalar);heapDump.children.forEach(function(spaceDump){if(spaceDump.name==='other_spaces')return;addV8ComponentValues(spaceDump,['v8','heap',spaceDump.name],addProcessScalar);});}});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.code_and_metadata_size});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.bytecode_and_metadata_size});}
function addV8ComponentValues(componentDump,componentPath,addProcessScalar){CHROME_VALUE_PROPERTIES.forEach(function(property){addProcessScalar({source:'reported_by_chrome',component:componentPath,property,value:componentDump.numerics[property.name]});});}
@@ -8918,13 +8766,11 @@ relatedNames.set(category,relatedName);relatedHist.addSample(breakdown.total,{br
function splitOneRangeIntoPerSecondRanges(startTime,endTime){const results=[];for(let i=0;startTime+(i+1)*1000<=endTime;i+=1){const start=i*1000;const end=(i+1)*1000;results.push({start,end,});}
return results;}
function getNavigationInfos(model){const navigationInfos=[];const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
-const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];navigationInfos.push({navigationStart:expectation.navigationStart,rendererHelper,url:expectation.url});}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];if(rendererHelper.mainThread===undefined)continue;navigationInfos.push({navigationStart:expectation.navigationStart,rendererHelper,url:expectation.url});}
navigationInfos.forEach((navInfo,i)=>{if(i===navigationInfos.length-1){navInfo.navigationEndTime=model.bounds.max;}else{navInfo.navigationEndTime=navigationInfos[i+1].navigationStart.start;}});return navigationInfos;}
-function getRendererHelpers(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const rendererHelpers=[];for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;rendererHelpers.push(rendererHelper);}
-return rendererHelpers;}
function getWallTimeBreakdownTree(rendererHelper,start,end){const startEndRange=tr.b.math.Range.fromExplicitRange(start,end);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,startEndRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,startEndRange);return breakdownTree;}
function getCpuTimeBreakdownTree(rendererHelper,start,end){const startEndRange=tr.b.math.Range.fromExplicitRange(start,end);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,startEndRange);return breakdownTree;}
-function persecondMetric(histograms,model){const rendererHelpers=getRendererHelpers(model);const navigationInfos=getNavigationInfos(model);if(navigationInfos.length===0){return;}
+function persecondMetric(histograms,model){const navigationInfos=getNavigationInfos(model);if(navigationInfos.length===0){return;}
navigationInfos.forEach(navInfo=>{const navigationStart=navInfo.navigationStart.start;const navigationEnd=navInfo.navigationEndTime;const startEndPairs=splitOneRangeIntoPerSecondRanges(navigationStart,navigationEnd);const breakdownList=startEndPairs.map(p=>{const wallHistogramName=`wall_${p.start}_to_${p.end}`;const wallHistogramDescription=`Wall-clock time ${p.start} to ${p.end} breakdown`;const cpuHistogramName=`cpu_${p.start}_to_${p.end}`;const cpuHistogramDescription=`CPU time ${p.start} to ${p.end} breakdown`;const pid=navInfo.rendererHelper.pid;const breakdownTree=getWallTimeBreakdownTree(navInfo.rendererHelper,navigationStart+p.start,navigationStart+p.end);const cpuBreakdownTree=getCpuTimeBreakdownTree(navInfo.rendererHelper,navigationStart+p.start,navigationStart+p.end);const diagnostics={'Navigation infos':new tr.v.d.GenericSet([{url:navInfo.url,pid:navInfo.rendererHelper.pid,navStart:navigationStart,frameIdRef:navInfo.navigationStart.args.frame}]),'breakdown':tr.metrics.sh.createBreakdownDiagnostic(breakdownTree),};return Object.assign(p,{breakdownTree,cpuBreakdownTree,wallHistogramName,wallHistogramDescription,cpuHistogramName,cpuHistogramDescription,diagnostics,});});breakdownList.forEach(p=>{if(!histograms.getHistogramNamed(p.wallHistogramName)){histograms.createHistogram(p.wallHistogramName,timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:p.wallHistogramDescription,summaryOptions:SUMMARY_OPTIONS,});}
const wallHistogram=histograms.getHistogramNamed(p.wallHistogramName);addSamplesToHistogram(p,p.breakdownTree,wallHistogram,histograms,p.diagnostics);if(!histograms.getHistogramNamed(p.cpuHistogramName)){histograms.createHistogram(p.cpuHistogramName,timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:p.cpuHistogramDescription,summaryOptions:SUMMARY_OPTIONS,});}
const cpuHistogram=histograms.getHistogramNamed(p.cpuHistogramName);addSamplesToHistogram(p,p.cpuBreakdownTree,cpuHistogram,histograms,p.diagnostics);});});}
@@ -8945,46 +8791,7 @@ function computeChromeBounds_(model){const chromeBounds=new tr.b.math.Range();co
for(const pid in chromeHelper.rendererHelpers){if(chromeHelper.rendererHelpers[pid].mainThread){chromeBounds.addRange(chromeHelper.rendererHelpers[pid].mainThread.bounds);}}
return chromeBounds;}
function powerMetric(histograms,model){const data={model,histograms:{}};for(const interval of computeTimeIntervals_(model)){createHistograms_(data,interval,histograms);}}
-tr.metrics.MetricRegistry.register(powerMetric);return{powerMetric};});'use strict';tr.exportTo('tr.b.math',function(){function earthMoversDistance(firstHistogram,secondHistogram){const buckets=firstHistogram.length;if(secondHistogram.length!==buckets){throw new Error('Histograms have a different number of bins.');}
-const arrSum=arr=>arr.reduce((a,b)=>a+b,0);if(arrSum(firstHistogram)!==arrSum(secondHistogram)){throw new Error('The histograms\' sizes don\'t match.');}
-let total=0;let remainder=0;for(let bucket=0;bucket<buckets;bucket++){remainder+=secondHistogram[bucket]-
-firstHistogram[bucket];total+=Math.abs(remainder);}
-return total;}
-return{earthMoversDistance,};});'use strict';tr.exportTo('tr.e.chrome',function(){const earthMoversDistance=tr.b.math.earthMoversDistance;class SpeedIndex{static getSnapshotsProgress_(timestampedColorHistograms){const numberOfScreenshots=timestampedColorHistograms.length;const firstHistogram=timestampedColorHistograms[0].colorHistogram;const lastHistogram=timestampedColorHistograms[numberOfScreenshots-1].colorHistogram;const totalDistance=earthMoversDistance(firstHistogram[0],lastHistogram[0])+
-earthMoversDistance(firstHistogram[1],lastHistogram[1])+
-earthMoversDistance(firstHistogram[2],lastHistogram[2]);if(totalDistance===0){return[{value:1,ts:timestampedColorHistograms[0].ts}];}
-const snapshotsProgress=new Array(numberOfScreenshots);for(let i=0;i<numberOfScreenshots;i++){const histogram=timestampedColorHistograms[i].colorHistogram;const distance=earthMoversDistance(histogram[0],lastHistogram[0])+
-earthMoversDistance(histogram[1],lastHistogram[1])+
-earthMoversDistance(histogram[2],lastHistogram[2]);const moved=Math.max(totalDistance-distance,0);snapshotsProgress[i]={value:(moved/totalDistance),ts:timestampedColorHistograms[i].ts};}
-return snapshotsProgress;}
-static speedIndexFromSnapshotsProgress_(snapshotsProgress){if(snapshotsProgress.length===0){throw new Error('No snapshots were provided.');}
-let prevSnapshotTimeTaken=0;let prevSnapshotProgress=0;let speedIndex=0;const numberOfScreenshots=snapshotsProgress.length;for(let i=0;i<numberOfScreenshots;i++){const elapsed=snapshotsProgress[i].ts-prevSnapshotTimeTaken;speedIndex+=elapsed*(1.0-prevSnapshotProgress);prevSnapshotTimeTaken=snapshotsProgress[i].ts;prevSnapshotProgress=snapshotsProgress[i].value;}
-return Math.round(speedIndex);}
-static createColorHistogram(imagePixelValues){const n=imagePixelValues.length;const histogram=new Array(3);for(let j=0;j<3;j++){histogram[j]=new Array(256).fill(0);}
-for(let i=0;i<n;i+=4){const r=imagePixelValues[i];const g=imagePixelValues[i+1];const b=imagePixelValues[i+2];histogram[0][r]++;histogram[1][g]++;histogram[2][b]++;}
-return histogram;}
-static calculateSpeedIndex(timestampedColorHistograms){const snapshotsProgress=SpeedIndex.getSnapshotsProgress_(timestampedColorHistograms);return SpeedIndex.speedIndexFromSnapshotsProgress_(snapshotsProgress);}
-static lineSweep(lineSweepRects,viewport){const verticalSweepEdges=[];const horizontalSweepEdges=[];for(let i=0;i<lineSweepRects.length;i++){const rect=lineSweepRects[i];let left=rect.left;let right=rect.right;let top=rect.top;let bottom=rect.bottom;if(left>viewport.x+viewport.width)continue;if(right<viewport.x)continue;if(top>viewport.y+viewport.height)continue;if(bottom<viewport.y)continue;left=Math.max(left,viewport.y);right=Math.min(right,viewport.y+viewport.width);top=Math.max(top,viewport.y);bottom=Math.min(bottom,viewport.y+viewport.height);verticalSweepEdges.push({id:i,value:left,type:'left'},{id:i,value:right,type:'right'});horizontalSweepEdges.push({id:i,value:top,type:'top'},{id:i,value:bottom,type:'bottom'});}
-verticalSweepEdges.sort((a,b)=>a.value-b.value);horizontalSweepEdges.sort((a,b)=>a.value-b.value);const active=new Array(lineSweepRects.length).fill(false);let area=0;active[verticalSweepEdges[0].id]=true;for(let i=1;i<verticalSweepEdges.length;i++){const currentLine=verticalSweepEdges[i];const previousLine=verticalSweepEdges[i-1];const deltaX=currentLine.value-previousLine.value;if(deltaX===0)continue;let count=0;let firstRect;for(let j=0;j<horizontalSweepEdges.length;j++){if(active[horizontalSweepEdges[j].id]===true){if(horizontalSweepEdges[j].type==='top'){if(count===0){firstRect=j;}
-count++;}else{if(count===1){const deltaY=horizontalSweepEdges[j].value-
-horizontalSweepEdges[firstRect].value;area+=deltaX*deltaY;}
-count--;}}}
-active[currentLine.id]=(currentLine.type==='left');}
-return area;}
-static quadToRect(quad){const left=Math.min(quad[0],quad[2],quad[4]);const right=Math.max(quad[0],quad[2],quad[4]);const top=Math.min(quad[1],quad[3],quad[5]);const bottom=Math.max(quad[1],quad[3],quad[5]);return{left,right,top,bottom};}
-static calculateRectsBasedSpeedIndex(timestampedPaintRects,viewport){const numberOfRects=timestampedPaintRects.length;if(numberOfRects===0){throw new Error('Can\'t calculate speed index without any paint '+'rectangles.');}
-const areaAddedAtTimestamp=new Array(numberOfRects);const rects=[];let previousAreaOfUnion=0;let totalAreaOfUnion=0;for(let i=numberOfRects-1;i>=0;i--){rects.push(timestampedPaintRects[i].rect);const currentAreaOfUnion=SpeedIndex.lineSweep(rects,viewport);areaAddedAtTimestamp[numberOfRects-i-1]={value:currentAreaOfUnion-previousAreaOfUnion,ts:timestampedPaintRects[i].ts};totalAreaOfUnion+=areaAddedAtTimestamp[numberOfRects-i-1].value;previousAreaOfUnion=currentAreaOfUnion;}
-const paintProgressAtTimestamp=new Array(numberOfRects);let lastProgressRecorded=0;for(let i=0;i<numberOfRects;i++){paintProgressAtTimestamp[i]={value:areaAddedAtTimestamp[i].value/totalAreaOfUnion+
-lastProgressRecorded,ts:areaAddedAtTimestamp[i].ts};lastProgressRecorded=paintProgressAtTimestamp[i].value;}
-return SpeedIndex.speedIndexFromSnapshotsProgress_(paintProgressAtTimestamp);}}
-return{SpeedIndex,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const SpeedIndex=tr.e.chrome.SpeedIndex;const EventFinderUtils=tr.e.chrome.EventFinderUtils;const BIN_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function addRectsBasedSpeedIndexSample(samples,rendererHelper,navigationStart,loadDuration,frameID){let viewport;for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'viewport','loading')){if(event.args.data.frameID===frameID&&event.start<(navigationStart+loadDuration)){viewport=event.args.data;break;}}
-if(!viewport)return;const timestampedPaintRects=[];for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'PaintTracker::LayoutObjectPainted','loading')){if(event.start>=navigationStart&&event.start<navigationStart+loadDuration){const paintRect=event.args.data.visible_new_visual_rect;if(!paintRect)continue;timestampedPaintRects.push({rect:SpeedIndex.quadToRect(paintRect),ts:event.start});}}
-const numberOfRects=timestampedPaintRects.length;if(numberOfRects===0)return;samples.push({value:SpeedIndex.calculateRectsBasedSpeedIndex(timestampedPaintRects,viewport)-navigationStart});}
-function collectRectsBasedSpeedIndexSamplesFromLoadExpectations(model,chromeHelper){const rectsBasedSpeedIndexSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
-const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];addRectsBasedSpeedIndexSample(rectsBasedSpeedIndexSamples,rendererHelper,expectation.navigationStart.start,expectation.duration,expectation.navigationStart.args.frame);}
-return rectsBasedSpeedIndexSamples;}
-function rectsBasedSpeedIndexMetric(histograms,model){const rectsBasedSpeedIndexHistogram=histograms.createHistogram('rectsBasedSpeedIndex',timeDurationInMs_smallerIsBetter,[],{binBoundaries:BIN_BOUNDARIES,description:' the average time at which visible parts of the'+' page are displayed (in ms).',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const samples=collectRectsBasedSpeedIndexSamplesFromLoadExpectations(model,chromeHelper);for(const sample of samples){rectsBasedSpeedIndexHistogram.addSample(sample.value);}}
-tr.metrics.MetricRegistry.register(rectsBasedSpeedIndexMetric);return{rectsBasedSpeedIndexMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function computeAnimationThroughput(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0){throw new Error('Animation missing frameEvents '+
+tr.metrics.MetricRegistry.register(powerMetric);return{powerMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function computeAnimationThroughput(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0){throw new Error('Animation missing frameEvents '+
animationExpectation.stableId);}
const durationInS=tr.b.convertUnit(animationExpectation.duration,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);return animationExpectation.frameEvents.length/durationInS;}
function computeAnimationframeTimeDiscrepancy(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0){throw new Error('Animation missing frameEvents '+
@@ -9110,11 +8917,11 @@ return data;},copyToImageData:function copyToImageData(imageData){var width=imag
break;case 3:for(y=0;y<height;y++){for(x=0;x<width;x++){R=data[i++];G=data[i++];B=data[i++];imageDataArray[j++]=R;imageDataArray[j++]=G;imageDataArray[j++]=B;imageDataArray[j++]=255;}}
break;case 4:for(y=0;y<height;y++){for(x=0;x<width;x++){C=data[i++];M=data[i++];Y=data[i++];K=data[i++];R=255-clampTo8bit(C*(1-K/255)+K);G=255-clampTo8bit(M*(1-K/255)+K);B=255-clampTo8bit(Y*(1-K/255)+K);imageDataArray[j++]=R;imageDataArray[j++]=G;imageDataArray[j++]=B;imageDataArray[j++]=255;}}
break;default:throw new Error('Unsupported color mode');}}};return constructor;})();global.jpegDecode=decode;function decode(jpegData,opts){var defaultOpts={useTArray:false,colorTransform:true};if(opts){if(typeof opts==='object'){opts={useTArray:(typeof opts.useTArray==='undefined'?defaultOpts.useTArray:opts.useTArray),colorTransform:(typeof opts.colorTransform==='undefined'?defaultOpts.colorTransform:opts.colorTransform)};}else{opts=defaultOpts;opts.useTArray=true;}}else{opts=defaultOpts;}
-var arr=new Uint8Array(jpegData);var decoder=new JpegImage();decoder.parse(arr);decoder.colorTransform=opts.colorTransform;var image={width:decoder.width,height:decoder.height,data:opts.useTArray?new Uint8Array(decoder.width*decoder.height*4):new Buffer(decoder.width*decoder.height*4)};decoder.copyToImageData(image);return image;}'use strict';tr.exportTo('tr.metrics.sh',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const SpeedIndex=tr.e.chrome.SpeedIndex;const LOADING_METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function addSpeedIndexScreenshotsBasedSample(samples,navigationStart,browserHelper){const screenshotObjects=browserHelper.process.objects.getAllInstancesNamed('Screenshot');if(!screenshotObjects)return;for(let i=0;i<screenshotObjects.length;i++){const snapshots=screenshotObjects[i].snapshots;const timestampedColorHistograms=[];snapshots.map(snapshot=>{if(snapshot.ts>=navigationStart.start){timestampedColorHistograms.push({colorHistogram:SpeedIndex.createColorHistogram(getPixelData(snapshot.args)),ts:snapshot.ts});}});samples.push({value:SpeedIndex.calculateSpeedIndex(timestampedColorHistograms)-
+var arr=new Uint8Array(jpegData);var decoder=new JpegImage();decoder.parse(arr);decoder.colorTransform=opts.colorTransform;var image={width:decoder.width,height:decoder.height,data:opts.useTArray?new Uint8Array(decoder.width*decoder.height*4):new Buffer(decoder.width*decoder.height*4)};decoder.copyToImageData(image);return image;}'use strict';tr.exportTo('tr.metrics.sh',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const SpeedIndex=tr.e.chrome.SpeedIndex;const LOADING_METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function addSpeedIndexScreenshotsBasedSample(samples,navigationStart,loadDuration,browserHelper){const screenshotObjects=browserHelper.process.objects.getAllInstancesNamed('Screenshot');if(!screenshotObjects)return;for(let i=0;i<screenshotObjects.length;i++){const snapshots=screenshotObjects[i].snapshots;const timestampedColorHistograms=[];snapshots.map(snapshot=>{if(snapshot.ts>=navigationStart.start&&snapshot.ts<navigationStart.start+loadDuration){timestampedColorHistograms.push({colorHistogram:SpeedIndex.createColorHistogram(getPixelData(snapshot.args)),ts:snapshot.ts});}});samples.push({value:SpeedIndex.calculateSpeedIndex(timestampedColorHistograms)-
navigationStart.start});}}
function getPixelData(base64JpegImage){const binaryString=atob(base64JpegImage);const bytes=new DataView(new ArrayBuffer(base64JpegImage.length));tr.b.Base64.DecodeToTypedArray(base64JpegImage,bytes);const rawImageData=jpegDecode(bytes.buffer,{useTArray:true});return rawImageData.data;}
function collectSpeedIndexSamplesFromLoadExpectations(model,chromeHelper){const speedIndexScreenshotsBasedSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
-const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];addSpeedIndexScreenshotsBasedSample(speedIndexScreenshotsBasedSamples,expectation.navigationStart,chromeHelper.browserHelper);}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];addSpeedIndexScreenshotsBasedSample(speedIndexScreenshotsBasedSamples,expectation.navigationStart,expectation.duration,chromeHelper.browserHelper);}
return speedIndexScreenshotsBasedSamples;}
function screenshotsBasedSpeedIndexMetric(histograms,model){const speedIndexScreenshotsBasedHistogram=histograms.createHistogram('speedIndexScreenshotsBased',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'The average time at which visible parts of the'+' page are displayed.',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const samples=collectSpeedIndexSamplesFromLoadExpectations(model,chromeHelper);for(const sample of samples){speedIndexScreenshotsBasedHistogram.addSample(sample.value);}}
tr.metrics.MetricRegistry.register(screenshotsBasedSpeedIndexMetric);return{screenshotsBasedSpeedIndexMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function webviewStartupMetric(histograms,model){const startupWallHist=new tr.v.Histogram('webview_startup_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupWallHist.description='WebView startup wall time';const startupCPUHist=new tr.v.Histogram('webview_startup_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupCPUHist.description='WebView startup CPU time';const loadWallHist=new tr.v.Histogram('webview_url_load_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadWallHist.description='WebView blank URL load wall time';const loadCPUHist=new tr.v.Histogram('webview_url_load_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadCPUHist.description='WebView blank URL load CPU time';for(const slice of model.getDescendantEvents()){if(!(slice instanceof tr.model.ThreadSlice))continue;if(slice.title==='WebViewStartupInterval'){startupWallHist.addSample(slice.duration);startupCPUHist.addSample(slice.cpuDuration);}
@@ -9149,6 +8956,7 @@ const processName=tr.b.getOnlyElement(x.bins[p1].processes)[0];x.bins[p1].proces
function getHistogramUnit_(name){return tr.b.Unit.byName.unitlessNumber_smallerIsBetter;}
function getHistogramBoundaries_(name){if(name.startsWith('Event.Latency.Scroll')){return tr.v.HistogramBinBoundaries.createExponential(1e3,1e5,50);}
if(name.startsWith('Graphics.Smoothness.Throughput')){return tr.v.HistogramBinBoundaries.createLinear(0,100,101);}
+if(name.startsWith('Memory.Memory.GPU.PeakMemoryUsage')){return tr.v.HistogramBinBoundaries.createLinear(0,1e6,100);}
return tr.v.HistogramBinBoundaries.createExponential(1e-3,1e3,50);}
function umaMetric(histograms,model){const histogramValues=new Map();const nameCounts=new Map();for(const process of model.getAllProcesses()){const histogramEvents=new Map();for(const event of process.instantEvents){if(event.title!=='UMAHistogramSamples')continue;const name=event.args.name;const events=histogramEvents.get(name)||[];if(!histogramEvents.has(name))histogramEvents.set(name,events);events.push(event);}
let processName=tr.e.chrome.chrome_processes.canonicalizeProcessName(process.name);nameCounts.set(processName,(nameCounts.get(processName)||0)+1);processName=`${processName}_${nameCounts.get(processName)}`;for(const[name,events]of histogramEvents){const values=histogramValues.get(name)||{sum:0,bins:[]};if(!histogramValues.has(name))histogramValues.set(name,values);const endValues=parseBuckets_(events[events.length-1],processName);if(events.length===1){mergeBins_(values,endValues);}else if(events.length===2){subtractBins_(endValues,parseBuckets_(events[0],processName));mergeBins_(values,endValues);}else{throw new Error('There should be at most two snapshots of an UMA '+'histogram in each process');}}}
@@ -9173,60 +8981,47 @@ histograms.addHistogram(cpuTotalOptimizeCode);histograms.addHistogram(wallTotalO
function computeDeoptimizeCodeMetrics(histograms,model){const cpuTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalDeoptimizeCode.description='cpu total time spent in code deoptimization';const wallTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalDeoptimizeCode.description='wall total time spent in code deoptimization';for(const e of model.findTopmostSlicesNamed('V8.DeoptimizeCode')){cpuTotalDeoptimizeCode.addSample(e.cpuDuration);wallTotalDeoptimizeCode.addSample(e.duration);}
histograms.addHistogram(cpuTotalDeoptimizeCode);histograms.addHistogram(wallTotalDeoptimizeCode);}
function executionMetric(histograms,model){computeExecuteMetrics(histograms,model);computeParseLazyMetrics(histograms,model);computeCompileIgnitionMetrics(histograms,model);computeCompileFullCodeMetrics(histograms,model);computeRecompileMetrics(histograms,model);computeOptimizeCodeMetrics(histograms,model);computeDeoptimizeCodeMetrics(histograms,model);}
-tr.metrics.MetricRegistry.register(executionMetric);return{executionMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const TARGET_FPS=60;const MS_PER_SECOND=1000;const WINDOW_SIZE_MS=MS_PER_SECOND/TARGET_FPS;function gcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);addDurationOfSubEvents(histograms,model);addPercentageInV8ExecuteOfTopEvents(histograms,model);addTotalPercentageInV8Execute(histograms,model);addMarkCompactorMutatorUtilization(histograms,model);addTotalMarkCompactorTime(histograms,model);addTotalMarkCompactorMarkingTime(histograms,model);}
-tr.metrics.MetricRegistry.register(gcMetric);const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;const percentage_smallerIsBetter=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;}
+tr.metrics.MetricRegistry.register(executionMetric);return{executionMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const TARGET_FPS=60;const MS_PER_SECOND=1000;const WINDOW_SIZE_MS=MS_PER_SECOND/TARGET_FPS;function gcMetric(histograms,model,options){options=options||{};addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);if(options.include_sub_events){addDurationOfSubEvents(histograms,model);}
+addPercentageInV8ExecuteOfTopEvents(histograms,model);addTotalPercentageInV8Execute(histograms,model);addMarkCompactorMutatorUtilization(histograms,model);addTotalMarkCompactorTime(histograms,model);addTotalMarkCompactorMarkingTime(histograms,model);addScavengerSurvivedFromStackEvents(histograms,model);}
+tr.metrics.MetricRegistry.register(gcMetric);const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;const percentage_smallerIsBetter=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const bytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;}
function createNumericForSubEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:false,percentile:[0.90]});return n;}
function createNumericForIdleTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:true,percentile:[]});return n;}
function createPercentage(name,numerator,denominator,unit){const hist=new tr.v.Histogram(name,unit);if(denominator===0){hist.addSample(0);}else{hist.addSample(numerator/denominator);}
hist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false,percentile:[]});return hist;}
-function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
-function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
+function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);},tr.metrics.v8.utils.topGarbageCollectionEventNames());}
+function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);},['v8-gc-total']);}
function isV8MarkCompactorSummary(event){return!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event)&&tr.metrics.v8.utils.isMarkCompactorSummaryEvent(event);}
function isV8MarkCompactorMarkingSummary(event){return!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event)&&tr.metrics.v8.utils.isMarkCompactorMarkingSummaryEvent(event);}
function createHistogramFromSummary(histograms,name,events){const foregroundDuration=createNumericForTopEventTime(name+'-foreground');const backgroundDuration=createNumericForTopEventTime(name+'-background');const totalDuration=createNumericForTopEventTime(name+'-total');const relatedNames=new tr.v.d.RelatedNameMap();relatedNames.set('foreground',foregroundDuration.name);relatedNames.set('background',backgroundDuration.name);for(const event of events){foregroundDuration.addSample(event.args.duration);backgroundDuration.addSample(event.args.background_duration);const breakdownForTotal=new tr.v.d.Breakdown();breakdownForTotal.set('foreground',event.args.duration);breakdownForTotal.set('background',event.args.background_duration);totalDuration.addSample(event.args.duration+event.args.background_duration,{breakdown:breakdownForTotal});}
histograms.addHistogram(foregroundDuration);histograms.addHistogram(backgroundDuration);histograms.addHistogram(totalDuration,{breakdown:relatedNames});}
-function addTotalMarkCompactorTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorSummary,event=>'v8-gc-mark-compactor',(name,events)=>createHistogramFromSummary(histograms,name,events));}
-function addTotalMarkCompactorMarkingTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorMarkingSummary,event=>'v8-gc-mark-compactor-marking',(name,events)=>createHistogramFromSummary(histograms,name,events));}
+function addTotalMarkCompactorTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorSummary,event=>'v8-gc-mark-compactor',(name,events)=>createHistogramFromSummary(histograms,name,events),['v8-gc-mark-compactor']);}
+function addTotalMarkCompactorMarkingTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorMarkingSummary,event=>'v8-gc-mark-compactor-marking',(name,events)=>createHistogramFromSummary(histograms,name,events),['v8-gc-mark-compactor-marking']);}
+function createNumericForTotalBytes(name){const n=new tr.v.Histogram(name,bytes_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:false,count:false,max:false,min:false,std:false,sum:true,percentile:[]});return n;}
+function createNumericForSampledPercent(name){const n=new tr.v.Histogram(name,percentage_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:true,std:true,sum:false,percentile:[]});return n;}
+function addScavengerSurvivedFromStackEvents(histograms,model){const baseName='v8-gc-scavenger-survived';tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isScavengerStackScanningEvent,event=>baseName,function(name,events){const sampledPercentage=createNumericForSampledPercent(baseName+'-percentage-from-stack');let survivedWithoutStack=0;let survivedWithStack=0;events.forEach(function(event){const bytesBefore=event.args.survived_bytes_before;const bytesAfter=event.args.survived_bytes_after;sampledPercentage.addSample((bytesAfter>0)?(bytesAfter-bytesBefore)/bytesAfter:0);survivedWithoutStack+=bytesBefore;survivedWithStack+=bytesAfter;});histograms.addHistogram(sampledPercentage);const totalBytesSurvivedWithoutStack=createNumericForTotalBytes(baseName+'-total-bytes-without-stack');totalBytesSurvivedWithoutStack.addSample(survivedWithoutStack);histograms.addHistogram(totalBytesSurvivedWithoutStack);const totalBytesSurvivedWithStack=createNumericForTotalBytes(baseName+'-total-bytes-with-stack');totalBytesSurvivedWithStack.addSample(survivedWithStack);histograms.addHistogram(totalBytesSurvivedWithStack);const overallPercentage=createPercentage(baseName+'-total-percentage-from-stack',survivedWithStack-survivedWithoutStack,survivedWithStack,percentage_smallerIsBetter);histograms.addHistogram(overallPercentage);},[baseName]);}
function addDurationOfSubEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedSubGarbageCollectionEvent,tr.metrics.v8.utils.subGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForSubEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
-function addPercentageInV8ExecuteOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addPercentageInV8Execute(histograms,model,name,events);});}
-function addTotalPercentageInV8Execute(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addPercentageInV8Execute(histograms,model,name,events);});}
+function addPercentageInV8ExecuteOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addPercentageInV8Execute(histograms,model,name,events);},tr.metrics.v8.utils.topGarbageCollectionEventNames());}
+function addTotalPercentageInV8Execute(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addPercentageInV8Execute(histograms,model,name,events);},['v8-gc-total']);}
function addPercentageInV8Execute(histograms,model,name,events){let cpuDurationInV8Execute=0;let cpuDurationTotal=0;events.forEach(function(event){const v8Execute=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isV8ExecuteEvent);if(v8Execute){cpuDurationInV8Execute+=event.cpuDuration;}
cpuDurationTotal+=event.cpuDuration;});const percentage=createPercentage(name+'_percentage_in_v8_execute',cpuDurationInV8Execute,cpuDurationTotal,percentage_smallerIsBetter);histograms.addHistogram(percentage);}
function addMarkCompactorMutatorUtilization(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const rendererHelpers=Object.values(chromeHelper.rendererHelpers);tr.metrics.v8.utils.addMutatorUtilization('v8-gc-mark-compactor-mmu',tr.metrics.v8.utils.isNotForcedMarkCompactorEvent,[100],rendererHelpers,histograms);}
-return{gcMetric,WINDOW_SIZE_MS,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const COUNT_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1000000,50);const DURATION_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.1,10000,50);const SUMMARY_OPTIONS={std:false,count:false,sum:false,min:false,max:false,};function computeDomContentLoadedTime_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let domContentLoadedTime=0;for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(ev.title==='domContentLoadedEventEnd'&&ev.start>domContentLoadedTime){domContentLoadedTime=ev.start;}}}
-return domContentLoadedTime;}
-function computeInteractiveTime_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let interactiveTime=0;for(const expectation of model.userModel.expectations){if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
-if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(expectation.timeToInteractive===undefined)continue;if(interactiveTime!==0)throw new Error('Too many navigations');interactiveTime=expectation.timeToInteractive;}
-return interactiveTime;}
-function convertMicroToMilli_(time){return tr.b.convertUnit(time,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);}
-function computeRuntimeStats(histograms,slices){const runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slices);function addHistogramsForRuntimeGroup(runtimeGroup,optRelatedNameMaps){histograms.createHistogram(`${runtimeGroup.name}:duration`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,{value:convertMicroToMilli_(runtimeGroup.time),diagnostics:optRelatedNameMaps?{samples:optRelatedNameMaps.durationBreakdown}:{}},{binBoundaries:DURATION_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,diagnostics:optRelatedNameMaps?{samples:optRelatedNameMaps.durationNames}:{}});histograms.createHistogram(`${runtimeGroup.name}:count`,tr.b.Unit.byName.count_smallerIsBetter,{value:runtimeGroup.count,diagnostics:optRelatedNameMaps?{samples:optRelatedNameMaps.countBreakdown}:{}},{binBoundaries:COUNT_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,diagnostics:optRelatedNameMaps?{samples:optRelatedNameMaps.countNames}:{}});}
-function addDetailedHistogramsForRuntimeGroup(runtimeGroup){const durationNames=new tr.v.d.RelatedNameMap();const durationBreakdown=new tr.v.d.Breakdown();const countNames=new tr.v.d.RelatedNameMap();const countBreakdown=new tr.v.d.Breakdown();for(const entry of runtimeGroup.values){const durationSampleHistogram=histograms.createHistogram(`${entry.name}:duration`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,convertMicroToMilli_(entry.time),{binBoundaries:DURATION_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,});durationNames.set(entry.name,durationSampleHistogram.name);durationBreakdown.set(entry.name,convertMicroToMilli_(entry.time));const countSampleHistogram=histograms.createHistogram(`${entry.name}:count`,tr.b.Unit.byName.count_smallerIsBetter,entry.count,{binBoundaries:COUNT_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,});countNames.set(entry.name,countSampleHistogram.name);countBreakdown.set(entry.name,entry.count);}
-addHistogramsForRuntimeGroup(runtimeGroup,{durationNames,durationBreakdown,countNames,countBreakdown});}
-for(const runtimeGroup of runtimeGroupCollection.runtimeGroups){addHistogramsForRuntimeGroup(runtimeGroup);}
-const blinkGroupCollection=runtimeGroupCollection.blinkRCSGroupCollection;if(blinkGroupCollection.totalTime>0){blinkGroupCollection.runtimeGroups.forEach(addDetailedHistogramsForRuntimeGroup);}}
-function runtimeStatsMetric(histograms,model){const interactiveTime=computeInteractiveTime_(model);const domContentLoadedTime=computeDomContentLoadedTime_(model);const endTime=Math.max(interactiveTime,domContentLoadedTime);const slices=[...model.getDescendantEvents()].filter(event=>event instanceof tr.e.v8.V8ThreadSlice&&event.start<=endTime);computeRuntimeStats(histograms,slices);}
-function addDurationHistogram(railStageName,runtimeGroupName,sampleValue,histograms,durationNamesByGroupName){const histName=`${railStageName}_${runtimeGroupName}:duration`;histograms.createHistogram(histName,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,convertMicroToMilli_(sampleValue),{binBoundaries:DURATION_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,});let relatedNames=durationNamesByGroupName.get(runtimeGroupName);if(!relatedNames){relatedNames=new tr.v.d.RelatedNameMap();durationNamesByGroupName.set(runtimeGroupName,relatedNames);}
-relatedNames.set(railStageName,histName);}
-function addCountHistogram(railStageName,runtimeGroupName,sampleValue,histograms,countNamesByGroupName){const histName=`${railStageName}_${runtimeGroupName}:count`;histograms.createHistogram(histName,tr.b.Unit.byName.count_smallerIsBetter,sampleValue,{binBoundaries:COUNT_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,});let relatedNames=countNamesByGroupName.get(runtimeGroupName);if(!relatedNames){relatedNames=new tr.v.d.RelatedNameMap();countNamesByGroupName.set(runtimeGroupName,relatedNames);}
-relatedNames.set(railStageName,histName);}
-function addTotalDurationHistogram(histogramName,time,histograms,relatedNames){const value=convertMicroToMilli_(time);const breakdown=new tr.v.d.Breakdown();if(relatedNames){for(const[cat,histName]of relatedNames){breakdown.set(cat,histograms.getHistogramNamed(histName).average);}}
-histograms.createHistogram(`${histogramName}:duration`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,{value,diagnostics:{'RAIL stages':breakdown}},{binBoundaries:DURATION_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,diagnostics:{'RAIL stages':relatedNames},});}
-function addTotalCountHistogram(histogramName,value,histograms,relatedNames){const breakdown=new tr.v.d.Breakdown();if(relatedNames){for(const[cat,histName]of relatedNames){breakdown.set(cat,histograms.getHistogramNamed(histName).average);}}
-histograms.createHistogram(`${histogramName}:count`,tr.b.Unit.byName.count_smallerIsBetter,{value,diagnostics:{'RAIL stages':breakdown}},{binBoundaries:COUNT_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,diagnostics:{'RAIL stages':relatedNames},});}
-function computeRuntimeStatsBucketOnUE(histograms,slices,v8SlicesBucketOnUEMap){const durationNamesByGroupName=new Map();const countNamesByGroupName=new Map();for(const[name,slicesUE]of v8SlicesBucketOnUEMap){const runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slicesUE);let overallV8Time=runtimeGroupCollection.totalTime;let overallV8Count=runtimeGroupCollection.totalCount;for(const runtimeGroup of runtimeGroupCollection.runtimeGroups){addDurationHistogram(name,runtimeGroup.name,runtimeGroup.time,histograms,durationNamesByGroupName);if(runtimeGroup.name==='Blink C++'){overallV8Time-=runtimeGroup.time;}
-addCountHistogram(name,runtimeGroup.name,runtimeGroup.count,histograms,countNamesByGroupName);if(runtimeGroup.name==='Blink C++'){overallV8Count-=runtimeGroup.count;}}
-if(runtimeGroupCollection.blinkRCSGroupCollection.totalTime>0){const blinkRCSGroupCollection=runtimeGroupCollection.blinkRCSGroupCollection;for(const group of blinkRCSGroupCollection.runtimeGroups){addDurationHistogram(name,group.name,group.time,histograms,durationNamesByGroupName);addCountHistogram(name,group.name,group.count,histograms,countNamesByGroupName);}}
-addDurationHistogram(name,'V8-Only',overallV8Time,histograms,durationNamesByGroupName);addCountHistogram(name,'V8-Only',overallV8Count,histograms,countNamesByGroupName);}
-const runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slices);let overallV8Time=runtimeGroupCollection.totalTime;let overallV8Count=runtimeGroupCollection.totalCount;for(const runtimeGroup of runtimeGroupCollection.runtimeGroups){addTotalDurationHistogram(runtimeGroup.name,runtimeGroup.time,histograms,durationNamesByGroupName.get(runtimeGroup.name));if(runtimeGroup.name==='Blink C++'){overallV8Time-=runtimeGroup.time;}
-addTotalCountHistogram(runtimeGroup.name,runtimeGroup.count,histograms,countNamesByGroupName.get(runtimeGroup.name));if(runtimeGroup.name==='Blink C++'){overallV8Count-=runtimeGroup.count;}}
-if(runtimeGroupCollection.blinkRCSGroupCollection.totalTime>0){const blinkRCSGroupCollection=runtimeGroupCollection.blinkRCSGroupCollection;for(const group of blinkRCSGroupCollection.runtimeGroups){addTotalDurationHistogram(group.name,group.time,histograms,durationNamesByGroupName.get(group.name));addTotalCountHistogram(group.name,group.count,histograms,countNamesByGroupName.get(group.name));}}
-addTotalDurationHistogram('V8-Only',overallV8Time,histograms,durationNamesByGroupName.get('V8-Only'));addTotalCountHistogram('V8-Only',overallV8Count,histograms,countNamesByGroupName.get('V8-Only'));}
-function runtimeStatsTotalMetric(histograms,model){const v8ThreadSlices=[...model.getDescendantEvents()].filter(event=>event instanceof tr.e.v8.V8ThreadSlice).sort((e1,e2)=>e1.start-e2.start);const v8SlicesBucketOnUEMap=new Map();for(const expectation of model.userModel.expectations){if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
-const slices=expectation.range.filterArray(v8ThreadSlices,event=>event.start);if(slices.length===0)continue;const lastSlice=slices[slices.length-1];if(!expectation.range.intersectsRangeExclusive(lastSlice.range)){slices.pop();}
-if(v8SlicesBucketOnUEMap.get(expectation.stageTitle)===undefined){v8SlicesBucketOnUEMap.set(expectation.stageTitle,slices);}else{const totalSlices=v8SlicesBucketOnUEMap.get(expectation.stageTitle).concat(slices);v8SlicesBucketOnUEMap.set(expectation.stageTitle,totalSlices);}}
-computeRuntimeStatsBucketOnUE(histograms,v8ThreadSlices,v8SlicesBucketOnUEMap);}
-tr.metrics.MetricRegistry.register(runtimeStatsTotalMetric);tr.metrics.MetricRegistry.register(runtimeStatsMetric);return{runtimeStatsMetric,runtimeStatsTotalMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){function v8AndMemoryMetrics(histograms,model){tr.metrics.v8.executionMetric(histograms,model);tr.metrics.v8.gcMetric(histograms,model);tr.metrics.sh.memoryMetric(histograms,model,{rangeOfInterest:tr.metrics.v8.utils.rangeForMemoryDumps(model)});}
-tr.metrics.MetricRegistry.register(v8AndMemoryMetrics);return{v8AndMemoryMetrics,};});'use strict';tr.exportTo('tr.metrics.vr',function(){const VR_GL_THREAD_NAME='VrShellGL';function createHistograms(histograms,name,options,hasCpuTime){const createdHistograms={wall:histograms.createHistogram(name+'_wall',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options)};if(hasCpuTime){createdHistograms.cpu=histograms.createHistogram(name+'_cpu',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options);}
+return{gcMetric,WINDOW_SIZE_MS,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const COUNT_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1000000,50);const DURATION_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.1,10000,50);const SUMMARY_OPTIONS={std:false,count:false,sum:false,min:false,max:false,};function convertMicroToMilli_(time){return tr.b.convertUnit(time,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);}
+function addDurationHistogram(histogramName,time,histograms){const value=convertMicroToMilli_(time);histograms.createHistogram(`${histogramName}:duration`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,{value},{binBoundaries:DURATION_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,});}
+function addCountHistogram(histogramName,value,histograms){histograms.createHistogram(`${histogramName}:count`,tr.b.Unit.byName.count_smallerIsBetter,{value},{binBoundaries:COUNT_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS});}
+function runtimeStatsTotalMetric(histograms,model){const v8Slices=tr.metrics.v8.utils.filterEvents(model,ev=>ev instanceof tr.e.v8.V8ThreadSlice);const runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(v8Slices);let overallV8Time=runtimeGroupCollection.totalTime;let overallV8Count=runtimeGroupCollection.totalCount;let mainThreadTime=runtimeGroupCollection.totalTime;let mainThreadCount=runtimeGroupCollection.totalCount;let mainThreadV8Time=runtimeGroupCollection.totalTime;let mainThreadV8Count=runtimeGroupCollection.totalCount;for(const runtimeGroup of runtimeGroupCollection.runtimeGroups){addDurationHistogram(runtimeGroup.name,runtimeGroup.time,histograms);if(runtimeGroup.name==='Blink C++'){overallV8Time-=runtimeGroup.time;mainThreadV8Time-=runtimeGroup.time;}else if(runtimeGroup.name.includes('Background')){mainThreadTime-=runtimeGroup.time;mainThreadV8Time-=runtimeGroup.time;}
+addCountHistogram(runtimeGroup.name,runtimeGroup.count,histograms);if(runtimeGroup.name==='Blink C++'){overallV8Count-=runtimeGroup.count;mainThreadV8Count-=runtimeGroup.count;}else if(runtimeGroup.name.includes('Background')){mainThreadCount-=runtimeGroup.count;mainThreadV8Count-=runtimeGroup.count;}}
+if(runtimeGroupCollection.blinkRCSGroupCollection.totalTime>0){const blinkRCSGroupCollection=runtimeGroupCollection.blinkRCSGroupCollection;for(const group of blinkRCSGroupCollection.runtimeGroups){addDurationHistogram(group.name,group.time,histograms);addCountHistogram(group.name,group.count,histograms);}}
+addDurationHistogram('V8-Only',overallV8Time,histograms);addCountHistogram('V8-Only',overallV8Count,histograms);addDurationHistogram('Total-Main-Thread',mainThreadTime,histograms);addCountHistogram('Total-Main-Thread',mainThreadCount,histograms);addDurationHistogram('V8-Only-Main-Thread',mainThreadV8Time,histograms);addCountHistogram('V8-Only-Main-Thread',mainThreadV8Count,histograms);}
+tr.metrics.MetricRegistry.register(runtimeStatsTotalMetric);return{runtimeStatsTotalMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){function v8AndMemoryMetrics(histograms,model){tr.metrics.v8.executionMetric(histograms,model);tr.metrics.v8.gcMetric(histograms,model);tr.metrics.sh.memoryMetric(histograms,model,{rangeOfInterest:tr.metrics.v8.utils.rangeForMemoryDumps(model)});}
+tr.metrics.MetricRegistry.register(v8AndMemoryMetrics);return{v8AndMemoryMetrics,};});'use strict';tr.exportTo('tr.metrics.v8',function(){function computeSyncInstantiationTimeMetric(histograms,wasmEvents){if(!wasmEvents.hasOwnProperty('wasm.SyncInstantiate'))return;const wasmSyncInstantiationTimeCPU=new tr.v.Histogram('v8:wasm:sync_instantiate:cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);wasmSyncInstantiationTimeCPU.description='cpu time spent instantiating a WebAssembly module';const wasmSyncInstantiationTimeWall=new tr.v.Histogram('v8:wasm:sync_instantiate:wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);wasmSyncInstantiationTimeWall.description='wall time spent instantiating a WebAssembly module';for(const e of wasmEvents['wasm.SyncInstantiate']){wasmSyncInstantiationTimeCPU.addSample(e.cpuDuration);wasmSyncInstantiationTimeWall.addSample(e.duration);}
+histograms.addHistogram(wasmSyncInstantiationTimeCPU);histograms.addHistogram(wasmSyncInstantiationTimeWall);}
+function computeStreamingBaselineCompileTimeMetric(histograms,wasmEvents){if(!wasmEvents.hasOwnProperty('wasm.StartStreamingCompilation')||!wasmEvents.hasOwnProperty('wasm.BaselineFinished')){return;}
+const histogram=new tr.v.Histogram('v8:wasm:streaming_baseline_compilation:wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);const compilationStart=wasmEvents['wasm.StartStreamingCompilation'][0].start;const compilationEnd=wasmEvents['wasm.BaselineFinished'][0].end;histogram.addSample(compilationEnd-compilationStart);histograms.addHistogram(histogram);}
+function computeCompilationTierupWallTimeMetric(histograms,wasmEvents){if(!wasmEvents.hasOwnProperty('wasm.BaselineFinished')||!wasmEvents.hasOwnProperty('wasm.TopTierFinished')){return;}
+const histogram=new tr.v.Histogram('v8:wasm:compilation_tierup:wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);const tierupStart=wasmEvents['wasm.BaselineFinished'][0].start;const tierupEnd=wasmEvents['wasm.TopTierFinished'][0].end;histogram.addSample(tierupEnd-tierupStart);histograms.addHistogram(histogram);}
+function collectWasmEvents(model){const wasmEvents=tr.metrics.v8.utils.filterAndOrderEvents(model,event=>event.title.startsWith('wasm.'),event=>event.title);return wasmEvents;}
+function wasmMetric(histograms,model){const wasmEvents=collectWasmEvents(model);computeSyncInstantiationTimeMetric(histograms,wasmEvents);computeStreamingBaselineCompileTimeMetric(histograms,wasmEvents);computeCompilationTierupWallTimeMetric(histograms,wasmEvents);}
+tr.metrics.MetricRegistry.register(wasmMetric);return{wasmMetric,};});'use strict';tr.exportTo('tr.metrics.vr',function(){const VR_GL_THREAD_NAME='VrShellGL';function createHistograms(histograms,name,options,hasCpuTime){const createdHistograms={wall:histograms.createHistogram(name+'_wall',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options)};if(hasCpuTime){createdHistograms.cpu=histograms.createHistogram(name+'_cpu',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options);}
return createdHistograms;}
function frameCycleDurationMetric(histograms,model,opt_options){const histogramsByEventTitle=new Map();const expectationEvents=tr.importer.VR_EXPECTATION_EVENTS;for(const eventName in expectationEvents){const extraInfo=expectationEvents[eventName];histogramsByEventTitle.set(eventName,createHistograms(histograms,extraInfo.histogramName,{description:extraInfo.description},extraInfo.hasCpuTime));}
histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateAnimationsAndOpacity',createHistograms(histograms,'update_animations_and_opacity',{description:'Duration to apply animation and opacity changes'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateBindings',createHistograms(histograms,'update_bindings',{description:'Duration to push binding values'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateLayout',createHistograms(histograms,'update_layout',{description:'Duration to compute element sizes, layout and textures'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateWorldSpaceTransform',createHistograms(histograms,'update_world_space_transforms',{description:'Duration to calculate element transforms in world space'},true));histogramsByEventTitle.set('UiRenderer::DrawUiView',createHistograms(histograms,'draw_ui',{description:'Duration to draw the UI'},true));histogramsByEventTitle.set('UiElementRenderer::DrawTexturedQuad',createHistograms(histograms,'draw_textured_quad',{description:'Duration to draw a textured element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientQuad',createHistograms(histograms,'draw_gradient_quad',{description:'Duration to draw a gradient element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientGridQuad',createHistograms(histograms,'draw_gradient_grid_quad',{description:'Duration to draw a gradient grid element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawController',createHistograms(histograms,'draw_controller',{description:'Duration to draw the controller'},true));histogramsByEventTitle.set('UiElementRenderer::DrawLaser',createHistograms(histograms,'draw_laser',{description:'Duration to draw the laser'},true));histogramsByEventTitle.set('UiElementRenderer::DrawReticle',createHistograms(histograms,'draw_reticle',{description:'Duration to draw the reticle'},true));histogramsByEventTitle.set('UiElementRenderer::DrawShadow',createHistograms(histograms,'draw_shadow',{description:'Duration to draw a shadow element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawStars',createHistograms(histograms,'draw_stars',{description:'Duration to draw the stars'},true));histogramsByEventTitle.set('UiElementRenderer::DrawBackground',createHistograms(histograms,'draw_background',{description:'Duration to draw the textured background'},true));histogramsByEventTitle.set('UiElementRenderer::DrawKeyboard',createHistograms(histograms,'draw_keyboard',{description:'Duration to draw the keyboard'},true));const drawUiSubSlicesMap=new Map();const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let rangeOfInterest=model.bounds;const userExpectationsOfInterest=[tr.model.um.AnimationExpectation];if(opt_options&&opt_options.rangeOfInterest){rangeOfInterest=opt_options.rangeOfInterest;userExpectationsOfInterest.push(tr.model.um.ResponseExpectation);}
@@ -9488,10 +9283,10 @@ const listener=function(event){this.setPaneBuilder(pane.childPaneBuilder,pane);}
this.listeners_.set(pane,listener);pane.addEventListener('request-child-pane-change',listener);Polymer.dom(paneContainer).appendChild(pane);pane.appended();},rebuild(){let currentPane=Polymer.dom(this.$.pane_container).firstElementChild;while(currentPane){currentPane.rebuild();currentPane=currentPane.nextElementSibling;}},get panesForTesting(){const panes=[];let currentChild=Polymer.dom(this.$.pane_container).firstElementChild;while(currentChild){panes.push(currentChild);currentChild=currentChild.nextElementSibling;}
return panes;}});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-container-memory-dump-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],set selection(selection){if(selection===undefined){this.currentSelection_=undefined;this.dumpsByContainerName_=undefined;this.updateContents_();return;}
selection.forEach(function(event){if(!(event instanceof tr.model.ContainerMemoryDump)){throw new Error('Memory dump sub-view only supports container memory dumps');}});this.currentSelection_=selection;this.dumpsByContainerName_=tr.b.groupIntoMap(this.currentSelection_.toArray(),dump=>dump.containerName);for(const dumps of this.dumpsByContainerName_.values()){dumps.sort((a,b)=>a.start-b.start);}
-this.updateContents_();},get selection(){return this.currentSelection_;},get requiresTallView(){return true;},updateContents_(){Polymer.dom(this.$.content).textContent='';if(this.dumpsByContainerName_===undefined)return;const containerNames=Array.from(this.dumpsByContainerName_.keys());if(containerNames.length===0)return;if(containerNames.length>1){this.buildViewForMultipleContainerNames_();}else{this.buildViewForSingleContainerName_();}},buildViewForSingleContainerName_(){const containerMemoryDumps=tr.b.getFirstElement(this.dumpsByContainerName_.values());const dumpView=this.ownerDocument.createElement('tr-ui-a-stacked-pane-view');Polymer.dom(this.$.content).appendChild(dumpView);dumpView.setPaneBuilder(function(){const headerPane=document.createElement('tr-ui-a-memory-dump-header-pane');headerPane.containerMemoryDumps=containerMemoryDumps;return headerPane;});},buildViewForMultipleContainerNames_(){const ownerDocument=this.ownerDocument;const rows=[];for(const[containerName,dumps]of this.dumpsByContainerName_){rows.push({containerName,subRows:dumps,isExpanded:true,});}
+this.updateContents_();},get selection(){return this.currentSelection_;},get requiresTallView(){return true;},updateContents_(){Polymer.dom(this.$.content).textContent='';if(this.dumpsByContainerName_===undefined)return;const containerNames=Array.from(this.dumpsByContainerName_.keys());if(containerNames.length===0)return;if(containerNames.length>1){this.buildViewForMultipleContainerNames_();}else{this.buildViewForSingleContainerName_();}},buildViewForSingleContainerName_(){const containerMemoryDumps=tr.b.getFirstElement(this.dumpsByContainerName_.values());const dumpView=unwrap(this.ownerDocument).createElement('tr-ui-a-stacked-pane-view');Polymer.dom(this.$.content).appendChild(dumpView);dumpView.setPaneBuilder(function(){const headerPane=document.createElement('tr-ui-a-memory-dump-header-pane');headerPane.containerMemoryDumps=containerMemoryDumps;return headerPane;});},buildViewForMultipleContainerNames_(){const ownerDocument=this.ownerDocument;const rows=[];for(const[containerName,dumps]of this.dumpsByContainerName_){rows.push({containerName,subRows:dumps,isExpanded:true,});}
rows.sort(function(a,b){return a.containerName.localeCompare(b.containerName);});const columns=[{title:'Dump',value(row){if(row.subRows===undefined){return this.singleDumpValue_(row);}
-return this.groupedDumpValue_(row);},singleDumpValue_(row){const linkEl=ownerDocument.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet([row]));Polymer.dom(linkEl).appendChild(tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument}));return linkEl;},groupedDumpValue_(row){const linkEl=ownerDocument.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(row.subRows));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument,textContent:row.subRows.length+' memory dump'+
-(row.subRows.length===1?'':'s')+' in '}));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument,textContent:row.containerName,bold:true}));return linkEl;}}];const table=this.ownerDocument.createElement('tr-ui-b-table');table.tableColumns=columns;table.tableRows=rows;table.showHeader=false;table.rebuild();Polymer.dom(this.$.content).appendChild(table);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:false,title:'Global Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:true,title:'Global Memory Dumps',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:false,title:'Process Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:true,title:'Process Memory Dumps',});return{};});'use strict';(function(){const COUNTER_SAMPLE_TABLE_COLUMNS=[{title:'Counter',width:'150px',value(row){return row.counter;}},{title:'Series',width:'150px',value(row){return row.series;}},{title:'Time',width:'150px',value(row){return row.start;}},{title:'Value',width:'100%',value(row){return row.value;}}];Polymer({is:'tr-ui-a-counter-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.currentSelection_=undefined;this.$.table.tableColumns=COUNTER_SAMPLE_TABLE_COLUMNS;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_(){this.$.table.tableRows=this.selection?this.getRows_(this.selection.toArray()):[];this.$.table.rebuild();},getRows_(samples){const samplesByCounter=tr.b.groupIntoMap(samples,sample=>sample.series.counter.guid);const rows=[];for(const counterSamples of samplesByCounter.values()){const samplesBySeries=tr.b.groupIntoMap(counterSamples,sample=>sample.series.guid);for(const seriesSamples of samplesBySeries.values()){const seriesRows=this.getRowsForSamples_(seriesSamples);seriesRows[0].counter=seriesSamples[0].series.counter.name;seriesRows[0].series=seriesSamples[0].series.name;if(seriesRows.length>1){seriesRows[0].subRows=seriesRows.slice(1);seriesRows[0].isExpanded=true;}
+return this.groupedDumpValue_(row);},singleDumpValue_(row){const linkEl=unwrap(ownerDocument).createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet([row]));Polymer.dom(linkEl).appendChild(tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument}));return linkEl;},groupedDumpValue_(row){const linkEl=unwrap(ownerDocument).createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(row.subRows));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument,textContent:row.subRows.length+' memory dump'+
+(row.subRows.length===1?'':'s')+' in '}));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument,textContent:row.containerName,bold:true}));return linkEl;}}];const table=unwrap(this.ownerDocument).createElement('tr-ui-b-table');table.tableColumns=columns;table.tableRows=rows;table.showHeader=false;table.rebuild();Polymer.dom(this.$.content).appendChild(table);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:false,title:'Global Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:true,title:'Global Memory Dumps',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:false,title:'Process Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:true,title:'Process Memory Dumps',});return{};});'use strict';(function(){const COUNTER_SAMPLE_TABLE_COLUMNS=[{title:'Counter',width:'150px',value(row){return row.counter;}},{title:'Series',width:'150px',value(row){return row.series;}},{title:'Time',width:'150px',value(row){return row.start;}},{title:'Value',width:'100%',value(row){return row.value;}}];Polymer({is:'tr-ui-a-counter-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.currentSelection_=undefined;this.$.table.tableColumns=COUNTER_SAMPLE_TABLE_COLUMNS;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_(){this.$.table.tableRows=this.selection?this.getRows_(this.selection.toArray()):[];this.$.table.rebuild();},getRows_(samples){const samplesByCounter=tr.b.groupIntoMap(samples,sample=>sample.series.counter.guid);const rows=[];for(const counterSamples of samplesByCounter.values()){const samplesBySeries=tr.b.groupIntoMap(counterSamples,sample=>sample.series.guid);for(const seriesSamples of samplesBySeries.values()){const seriesRows=this.getRowsForSamples_(seriesSamples);seriesRows[0].counter=seriesSamples[0].series.counter.name;seriesRows[0].series=seriesSamples[0].series.name;if(seriesRows.length>1){seriesRows[0].subRows=seriesRows.slice(1);seriesRows[0].isExpanded=true;}
rows.push(seriesRows[0]);}}
return rows;},getRowsForSamples_(samples){return samples.map(function(sample){return{start:sample.timestamp,value:sample.value};});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:false,title:'Counter Sample',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:true,title:'Counter Samples',});})();'use strict';tr.exportTo('tr.ui.analysis',function(){function MultiEventSummary(title,events){this.title=title;this.duration_=undefined;this.selfTime_=undefined;this.events_=events;this.cpuTimesComputed_=false;this.cpuSelfTime_=undefined;this.cpuDuration_=undefined;this.maxDuration_=undefined;this.maxCpuDuration_=undefined;this.maxSelfTime_=undefined;this.maxCpuSelfTime_=undefined;this.untotallableArgs_=[];this.totalledArgs_=undefined;}
MultiEventSummary.prototype={set title(title){if(title==='Totals'){this.totalsRow=true;}
@@ -9759,7 +9554,7 @@ this.brushingStateController_=brushingStateController;if(this.brushingStateContr
this.onSelectionChanged_();},get selection(){return this.brushingStateController_.selection;},onSelectionChanged_(e){if(this.lastSelection_&&this.selection.equals(this.lastSelection_)){return;}
this.lastSelection_=this.selection;this.tallMode=false;this.tabView_.label=getTabStripLabel(this.selection.length);const eventsByBaseTypeName=this.selection.getEventsOrganizedByBaseType(true);const ASV=tr.ui.analysis.AnalysisSubView;const eventsByTagName=ASV.getEventsOrganizedByTypeInfo(this.selection);const newSubViews=[];eventsByTagName.forEach(function(events,typeInfo){newSubViews.push(createSubView(typeInfo,events));});this.tabView_.resetSubViews(newSubViews);},onSelectedSubViewChanged_(){const selectedSubView=this.tabView_.selectedSubView;if(!selectedSubView){this.tallMode=false;this.maybeChangeRelatedEvents_(undefined);return;}
this.tallMode=selectedSubView.requiresTallView;this.maybeChangeRelatedEvents_(selectedSubView.relatedEventsToHighlight);},maybeChangeRelatedEvents_(events){if(this.brushingStateController){this.brushingStateController.changeAnalysisViewRelatedEvents(events);}}});})();'use strict';tr.exportTo('tr.ui.b',function(){Polymer({is:'tr-ui-b-dropdown',properties:{label:{type:String,value:'',},},open(){if(this.isOpen)return;Polymer.dom(this.$.button).classList.add('open');const buttonRect=this.$.button.getBoundingClientRect();this.$.dialog.style.top=buttonRect.bottom-1+'px';this.$.dialog.style.left=buttonRect.left+'px';this.$.dialog.showModal();const dialogRect=this.$.dialog.getBoundingClientRect();if(dialogRect.right>window.innerWidth){this.$.dialog.style.left=Math.max(0,buttonRect.right-
-dialogRect.width)+'px';}},onDialogTap_(event){if(event.detail.sourceEvent.srcElement!==this.$.dialog)return;const dialogRect=this.$.dialog.getBoundingClientRect();let inside=true;inside&=event.detail.x>=dialogRect.left;inside&=event.detail.x<dialogRect.right;inside&=event.detail.y>=dialogRect.top;inside&=event.detail.y<dialogRect.bottom;if(inside)return;event.preventDefault();this.close();},close(){if(!this.isOpen)return;this.$.dialog.close();Polymer.dom(this.$.button).classList.remove('open');this.$.button.focus();},get isOpen(){return this.$.button.classList.contains('open');}});return{};});'use strict';tr.exportTo('tr.ui.b',function(){const FaviconsByHue={blue:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAlrklEQVR4Ae2dCXwdVb3H5265yc3SpEk3ukEXCqVUBLT4Wm19oFKtaN0fKijy9CMguPBarIJsIiA8qsjTh7SllAoFeVBaEARkLV1ooXtL0yRdkqZp9u3uy/v/5uY/OZm75y659+acdnLOnP385zv/+58zZ2YMinTplIAhzsoDceaT2RKUQLwHIMFqh0V2ll0kn4XA6byv9/Vw834kX19e7keRQCzhRyk6bJJYRvD1YTXuhRdeqDj77LPPtNls400mU7HRaCzFFggEVJ/iSqhsicFgKIXUKL6bvB6fz9fj9/u7Kb4bPjaK67Xb7Q0HDhw49IUvfKEd2XUb7WpxHIYvXRgJ8AELkzRso1gmKrwkBfjG7373u5Zly5ZNKS8vn2G1Ws80m83YphPI0wnQUemQFp0IzQR9tdfrxXbI5XId6ujo+PCuu+6qXbNmjYfa9NMmngDoBmt+hIe944M53AUhwqwCvXTp0qJrr732opKSkk8XFhZ+imC+gIAryAZB0QnlJuB3OJ3Ot3p6el5/6KGHttxzzz0O6pse+GEP+3AGnKE2EhgG0tAFt99++4WkoT9tsVgW0DaH4guzAeg4+uD0eDxbaXuDNPzrt9xyy3bS8G4qB8BF6OOoKr+yDDfAB0B91VVXFf72t7+9lLT05QUFBZfQoYWtnA+ux+12v0ra/W+/+tWvXlq5cqWTBjUsYR8OgDPU8KGtjR9++OHHx4wZ8+2ioqKv0X4lbfnsWh0Ox9+bmprWzpgxYxsNFBpd1Op5bcbkM+AMtgr11q1bTz/zzDP/gy4Qv02zGtPzmehIY6MZmmq6UF176NChJ+bMmXOkD3QR9khFczY+HwEXwTbV1NTMI229FCYIXSTm43gTho8uUgMwYUir3zN16tR3qAIfbXkJej4dcIxF1dbkm44ePfqZqqqqpTT7MZf2pYsgAZqN2dTS0nLP5MmTX6EsDDrDHqFU7kTnA+Aa2BMmTDBv2bLliyNHjlxCZsgFuXMYhr6nZL7saGtru/eiiy7aUF9f76UeAfKcBz2XAUffVbgJbAuB/Y3KysoldONl5tDjkrs9oBtL+1tbWwH6UwS6/mZSzg0sVwHXTJG9e/deOGXKlOWksS/MOelncYdJo2+vra396axZs7ZTN0XTJYt7Hdq1XANc1dg0DNOqVatGLl68+DZa/3E1XTwCeOn6JLCly6ncU9+mNLnBZRLOYPAHHI5H2l5/8TdHbl3SRjUx6DkztZgrgKOfDLf5xIkT36moqLiLzJG0rAFJAomsKDp1W51S74IZnSIX8DcrXV3LlK/Oe5xqZPsckGc96LkAOPpowrZ79+5ZNK31BzkzQtKI4qxvV0dJTSLJ592kHKu7QfnPxXupFmhzbFkNeTb/tGsae/bs2Va6wr/lrLPO2izhTgLQZIuaaMp1yvTNyvNbb1HomFB1ZtrAUNYqymztGMNt2rhx44T58+evohs1n0r2+AyX8mnT4KIAvZ63lA82f1/55TX1FJ21tnk2As4zJObq6urP0BTgCmlri2TFDmcEcHQDtnlz4w+Uyz+Hm0Rsm2PuPGtcNpkomtZesGBBYXNz8210d+05CXfWsBLaEQNd5I+e8JyyYettCh0zyoBrpawyWbJFg2twv/jiixPnzZu3mhZFzQ2VqIyJRwIZ0+BiZzyeTcqebVcqS350nKKzxmTJBsDRB3WWZN++fXPpps060tpVouxkODEJDAng6GIg0KI0Hv+mcsXnN9FeVsyyDLWJwnCbadXfomnTpm2UcCcGc1blNhiqlNMmblT+9soi6hdmWKC4hlSJDiXgaBsCsNDKvysnTpz4JIWLaJMupyVgKFLGjHtSefrNK2kYFtpwjIeMs6FqWIOb7kr+Yty4cX+m2+0446XLBwkESHuPrPqz8uymX9BwhhTyoQBchZseQiigdcj30grAO+SDCPlAtW4MeLikdMQdyvqt9yp0rCl1SDR5pgFX4V64cGERvdhmRWlp6XU6scjdfJNAcfF1ysqNK5Q5C2F+ZhzyTF4AqHCPGjXKSjdwHqUHfr+ab8cyW8YzZLMo0QTgcj2jfO/S7ynNzS7KxtOI0UqkJC1TGlyFm3pccPDgwfsk3Ck5drlVidX6VWXFxvvAAG0Z0+SZAJzhtjQ2Ni6ld5D8KLeOjOxtyiRgK/6R8uy7S6m+jF14phtwmEBow3L8+PGr6FnJm1MmLFlRbkqgtOxm5am3rgITtIGNtJrJ6QQcHcdPkYUuKL9MsybLKSydlICijKxcrjz+0pdJFKzJ0wZ5ugBnuM27du2aT7ffV9JUIGCXTkqAJEAsjJ2wQlm1fj7tpPWOZzoAB9yo1/zSSy/NoLdJraMwFsdLJyUgSqBQGX/GOuX+FTMoEpCDmZRr8nQBbqIHgovnzp27mtaWlImjkmEpAU0CYGPmR1crF19cTHH4hU854KmuECcMOmo9derUAyNGjLiawtJlWAJZOQ8eTQb27keUyz7xM8qS8jnyVGpwNk0s+/fv/4qEO9oRlWkDJGArvVpZ89JXKC7lMyupApzhNm/YsGH6GWec8eCAAcgdKYFYEhhz2oPK3X+ZTtlSao+nEnDzxWRL0eNmj0q7O9bRlOkhEoA9ft6cR5WPq/Y4IE+J+ZyKSjS7m56jvK+srEzeqQw5epmNyDkbXBRPT8//Kl++6EaKSok9nqwG10yTHTt2fJpWB0q4xYMlw4lLoJhu5z/y3KepYEpMlWQBV7U3mSXFNN99H71YPfEByRJSAqIEwND4yfcpFyzgqcOkGE2mMGtvy2OPPXY9vZjnTLGfMiwlMGgJWCxnKktv/QmVT3pWZbCAM9zmxx9//IzRo0fj0STppARSJ4HykTcqN//3GVRhUqZKMoCrC6no6Zy7yTSxpW5ksiYpAZKA0WhTPj73dxRKakHWYABn7W3Zs2cPvjH5eXlApATSIoGi4i8oK56/tA9ysAr2EnKDARxlzJdddlkJ3dC5N6HWZGYpgUQlMH7SvbRWpYSKsamSUA2JAs7a2/ynP/3pOvrc9eSEWpOZpQQSlYDZPFn54a/xcDoDnpAWTxRw5DfRJ7DL6HUPP060rzK/lMCgJFA+8sfKZd/CqlRc9yXEbCKZWXtbli1b9gN6EX3loDorC0kJJCoBk6lS+ebVP6BiCU8bJgI48ppxU2fs2LHXJNpHmV9KICkJVFZdo3zsY7j5w6ZKXNXFCzhrb/PDDz/8HbK9x8ZVu8wkJZAqCZjNY5Wf3vkdqo4Bj8sWjxdw5DPRt3KKTjvtNNxhkk5KIPMSqBz1E2Xq7ITekBUP4Ky9LevWrfsGae9JmR+ZbFFKgCRgLpik3HL3NygUty0eD+Cq9h4/fnwBbTdIQUsJDKkERo+9QSkr47ubMfmNlQHaG5v56aef/ndaUDVtSAcnG5cSMFumKXc/fDGYpI35jCiXeADH3KOZ7lp+Sy6HjShHmZApCWA57dgJ3wKTtIFNQB7RxQIc6abLL7+cniEesTBiLTJBSiCTEiguWah8/isjqEkAHpXhaIk4M5BuXrp06ZfoOUtcvUonJTD0EjCaipSvff9L1JGYU4bRAEeaCjh9P+fr0jwZ+uMqe9AnAZgpo0Z/nfYY8IgcR0qA9sZmeuCBBybZbLZ/66taelIC2SEBKzF5zTJMWbMdDl5DXDTAVe29aNGib5D2jpQvpEIZISWQEQkYicm5C0QtnjDg6uwJPY72tYx0WDYiJZCoBMorGXDW4iE1hNPMOBMQb1qzZs0MmvueHlJKRmS1BCZYYZoOA2exTFd+dT/eTsuzKSFaPJwkNMDPO++8+fLiMvdA+Z8JJcqPN+9RGnocoZ0PBELjFF2cbjdYIEykvq4wWehd4APb05dBari4gaWCe/p8AT+uFOdT4j7aoJTB7oAGowFurqqqmicBV5QPmgLKX3b7lVbHANmRLLPVVSjnGT6hzFRa44dHHEqIHhQThXC8+YQiqQ66K9rnvakoD1O9DPiAJvSAo8vYjMXFxWZ6U9VFA3IP052fv+5VGntzBW4+SCYl4KtQ/L3tpCBJ0+WpC/hKLgKrvb29DDj41Q4WIvUOcaZHH310lslkGqlPHI77uQd38CgZTBbSVBVKXk+CGYwjS758/ywwS1sIz/oI1uCmmTNnflKaJ7l/OmuQG3migQ9xnvg0W2gaN/2TfYDzoLQDFw5wVYOT/T1XAq7JKacDKuS2csVg1B/unB6W2nkwaiiumEs7rMEBueZEG5zpN9Gt+QKyv+douWQg5yXAkPvtHYO78MxiCZisJXNsVRML7C3HndRN5li1w/WnNPaNDz744Ll0ZpRm8Zhk1wYhAYacjPJBlM7eIgHFUFryxZvPpR6q/Io9DavBJ0yYcJY0T0Qx5U84CDnNrtjb82dQZHqZysefRQPaRltEDc4JRlr7PS1/Ri9HopeAwWRWjLYKQiF/NLnBWgpmocGZY3XYoomCBOybaPXgNKnBVfnk7R8V8qLyvIAcrBoLiqaCXdoYcvXYMeB8KmPfSIBPUVPln7yWQD/kjEEOD7fABsBVfvtGoTIdYoOPHDnSXFhYODmHhyq7noAEgpCPUPyOTiql3QBMoIbsyGo0F04uInYdbW3RTZRbb711AnXZmh3dlr3IhAQYcpooz0RzaWmDTk1r0YLrwS4GwRaJuoMGmXrjOeecI5fHQiLDzKmQF9ILXFXIGYfc8q2jZ4JdBlyFnE9ZHolx1KhR8gJzmMHNw9Ugz8U7nrijWToyZCZFtMEBu7GoqGgiD1j6w08CKuTWUsXv6s65O56GApVdlWM+cnoNbqB3D+JzEdINYwkw5DlnkxvNYJetEdVEETU4Ioy0RLZEzoEPY7r7hh6EvIQ0eQ/FZP/sCpilPgNwKG0VbgyFdzTqCXC8ZFw6KQEAoxgLS3NoPbkR7GosIyxqcBxSgwQcYpCOJWDAOnIrKUbS5AH9M5GcKUt8OiEZcK1HbIMjQiVfAq7JRgb6JADIDQR5tpuuAaMGuGaisAbXIiTgkutwEujX5L2UnJ02uSEIOHdfZVpqcBaH9GNKIKjJQ6yAmOUylYHsa+6cprBZg3MfpA3OkpB+WAkENXmxEnDbs2+e3KABrvU9RINTih56LbMMSAlAAqomL7BRQFOU2SGYgMouOqV1jGHWIrxer50+8iofV8uOQ5a1vVA1OUEecOPtWdlhkxsUH/2saE5lmufBtVifz4erCOmkBGJKIKjJ8V0ETT/GLJPODAG/X8+uOg+O0087BaHB09kJWXd+SSCoyYuUgIceaB/qeXL/AA2uci3a4JB8QGrw/AIwE6NRNbmlcMht8oBftT40ZY2xsw2OsJogAYcopEtUAqomt5Am9w6dJg8ENPNagzysBs/2W7KJCl/mz4wE8OYsg3loNLnKbNAG1+DGqFmDI1LdpA2eGRjytRX19XAEecDr6kMqcyM1BNTrR41ltCxqcAYc6yOlkxIYtASCmhyP9WZ2doVmUXhtL1hWHWtw3lccDkcb1H22L6zROiwDWSmBoCa39mnyDHSRmPV7nG36lliDs1r3t7e31+kzyX0pgcFIQNPkGbrj6be3gV287Z95Vk0U7MCpkdXV1bXyIjMoEPk3eQmokJsKglOIAD1tm6J4Wo7UMsd9PQ+wBse+CvgzzzwjAe+TjvRSIwGGnB4qS02F4WohE8W58zk94CGzKP6XX3652+VyNdN6lFHh6pFxUgKDkQAgDygWxeDzDKZ47DJeV3PvvtfpVQChJgoKs80C+8Xf09NzRJopEIt0qZQAIFfou0GpXoUIVv0uxxHqq8ov+cxzyDShmsFut9elcmCyLikBloAKuZEm71Jsi/vdKrMi4GqTbIMz8cjgw0yK1OB8SKSfagkMgDwVlZMGDzg6oJR9tIFh5lmzwdEMR/pPnjxZiwjppATSJQHVJg/QRaffm3wT9Gvg624GswPgRsXhNLh//fr1u2nRFYCXTkogbRJQbybCXEl2diXgCzh2bthNFQHwAZAz4BgEgEaijz4C29zZ2VkjzRSIRbp0SiAIOT7MgCnExDeyThS/s7uma+vaZqpANFHUbusBZ8i9ra2tWyXgqozknzRLQIMcF56JOiLc19O6lYrB1hmgvVGVCDj2VQ1Ovq+mpmaLBBwikS4TElAhx7vJE55dIWhb6rZQH6G9WYNrXRYBh/ZmDe5buXLlVj85LacMSAmkWQIa5Im0Q4x2bXkUGpzhZo7VWsIBrp4JGzZsaCc7/KDU4olIW+ZNVgL9kMe2x4P2d+dB+86X8NFP1uARAUffWIPDnvHSdOE2CTjEIl0mJRCEPA57nAj3dzXj468qr+SzDa51V9TgiGTAcTZ4yQ7fLAHXZCUDGZSABnlUm5wgba3dDFZpE00Uraf6Bx5YveNM8C5fvnzbJZdc4iwuLqYH7Yavq+ytURq70rRIKIvEGlmZAYswDjZCRBchLUJ0ULeGqYzaQL8AfEj/PA5nz8u/Zw3O2ntAC+EAR0bVnnn33Xe7Gxsb35gyZcqlxhR9mGj/oU7liWfrlPZOd5jRZGfUbK9bmUnPGIYIeEB3B8i1PyUKBHTo+vPFEYrcfpR6orYfR6NZmiUQ8Cs9XU1vbDiyEysI2f5myLVe6wFHAqSlanDyPTt37nz+9NNPTxngv/3DHqW5lV4tkGPO67ErPi+9pgw/mYAGfjyO8zJo+vL6dH2dmc6vb1/fP31/9Pn1+7HK69P15fXt9eUP+LxKR/OB5yk7flrFOfABNehtcCSKgHuvu+66t2n5bGtk7TGgvpg7uQg3BmW22BSTGa8pIwehx+s4L3wxzOXFOM4j+sjHecSwmEcMi3nEsJhHDIt5ENY75IXjMhxWI+P4E6u8Pp3bYV/fHsWDRb/f1Vq3b9XblBzxAhNFowEOte+hlYWO+vr6f6QKcDSaq06F3FQYdeUEow9fDGfLmMU+ieFI/RPziOFU5Y9UT/T4gOJ2tP/D7e7Bmz+hwcNeYKKOcIAjHiaKZqa8+uqr6+l9KYgf9g6QG/sgxwHXbxAQgyCG9fmGal/skxiO1B8xjxhOVf5I9USLV8j+7mjd/Rz1RzRPwGuIiwQ4zBScFaDas3Tp0r0dHR2HpRYPyo8hD+7Jv5mUABj0eeyHjx58Yh+1y4CDVTAb4qIBzpCjEjfNiW+Qd+775dcPeTRdI9NCf+OSlQl98M3RvAFM0sbmCVhNCHAcSah8TYuvXr16PT2MjAql65OAapPjXXzRnP4iCnk5Llw5ToMvhsPlzYU4cQxiOFLfxTxiuC+/3+/xNB9/cz3tito7rHmCIpE0ONJwRrAd7l61alXjkSNHXpBaHKLpd5hZMfELJ3FA9Buy8oESw/p8vC/mEcOcnmu+OAYxHGkcYh4xTPlx38DtaHnhZP3rjZQEDR5xehBF4eIFXDVT1q5d+whp8YhnS7DK4fdXhdyEd/FJl04J+ANef3PDpkeoDTZPkgIcfR2gxe+7776aY8eO/VNq8dDD2A95sjamLE8/eSTggRsuLj2Otn821D5fQ4lxaW8cpWgaHOnQ1pqZQmHXU0899VePxxPWoEeB4ewYchwadhzmw4V4jhPDnJ6oL9YhhuOtRywjhuMtr88n1iGGOZ8YJ4Y5PZKv+H2BthOb/0pl8F5mEfCoFkUswNEHVICLTdVMufPOOw+QFn9TanGIJtTBHjeSucIHCjkQZsfhSOmcL14/2fqSLa/vZ6z6YqXr68M+1p24nK1vHq3++wHaZfMETEaFG2XjARzaWgOcwq4XX3zxYdLiKC9dGAkw5Pqf2czso0OMkRhGXG5u9N5vpb3p/YdpAKy9AR+YjGlJxAs4a3GcPa4lS5bsOnHixGapxUkaEVwQcnqrasYdw80wowMcl/HOJN0gtLfb1bH5yMHHd1FlDDhr75QAjk6yFsdVKyB3bty48UE5owLRRHYa5JgSY8dhniZDPMeJYU5P1BfrEMOR6hHzIBzLcV8j1aePR31cRgxzPjFODPel+xWvv6N5x4OUhCWoYA8MxqW9KV9cJgryAXBocQbcdeONN+6kd4k/J9eoQDyRnQq5se+Fk3yg2UcxDvcdULUmjotcbeQULhtvffr8XC6Sj5a5TORe9KdwXq5PXz5KOn0WUHH2nnyudt/qnVSMtXfMqcH+xuMHHGVYi6sXm2jwpptuWk4PJrfLNSqiSEPDGuShSTImggTUNSdee/uxA2uXUxaGO27bm6uNxwbnvKzF8fOABp2vvfZa89atW/8oLzhZRJF9zVyJnEWmCBKgb14qPZ01f2xv3o03VsE8AXNx295cVSKAo4yoxVXIFy9e/Aw91rZLXnCySCP7Jpo+NNLnPMQvHXAYfjz/UDuXEcNcVowTw5yeal9sQwxHakfMI4bF/HhiyuPq2LV/293PUB6GO2HtjfoHA7g4o4LGnWvWrLnL6XT6pKkCkUZ3gNxAL4HnA4rcCMfrOG+k8rHS420n3nyJthcrPxgK+D2+5oa37qI+qHyRj4vLhLU3xpAo4CjDgOOMUrX4HXfcse/AgQPr6I20SJcuhgQYcvVijS++pN938RpQHD0n1h378Cms99Zrb7CXkBsM4GiAIVenDGnfccMNNzzU0tLSKE2V+OSvmiuYXZFOkwDmvD2e7saa/X99iCLxOBoAF7W3ljfewGABZ1ucpw2d7733XusTTzxxE33+xCNNlfjED3vcqELON2WGr0+WCS03cXtaTmy6qbutppUkyHAnNO+tl/xgAUc9DLmmxWnacAeB/hDdANK3I/cjSCAIebi3d0QokKfRZHcrvZ01D9XtW72DhqjX3mBtUC5ZwGGqaFqcws5LL7109dGjR9+WN4DiPx7DHXLc0HE5Wt7es/m21WCob4PiTOimTjiJJwM46gPg2PiCE2ee/Wc/+9lvyB5vkvY4SSNO12+uxFkgT7LB7vZ6uptq9678DQ3JThsYggkAppgvCg7OJQs4WkUnMH2CMw6dc9ANoJNPPvnkL8ke90p7nCQSpzPS9CFscryHbzhsEEvA7/a2NLzzy46WXSdpV+WH/KQuLFEvu1QAzrY4mypqJ+lVE9u3bdv2Z9jjEnIWd2wfkBsM+W+T9813093K6j/X7l+9nSQjwp3UhaUo5VQAjvoY8gGmysKFC1fSgqxX3G6ckNLFKwEVcu3rY/k5swK729Hb9Mqed29fSXLRmyawCAZ9YSnKOVWAo06GHDTjQgGdti9atOjXdNH5noScpJGAU00VI74+ln+OXv2gOJ0t7x3cduevaXQqJ+TztGDK4IbkUg24aI+rkNNXIrquuOKKG+kBiYNyURZEHr/LR8j99OFXt6v94KH377/R4WjtImkAcBFuMJQS7Q1Jp0NFoHNiBw0Eube2tnbT/PnzFzz3UtMIA76mJV1cEjAYcIhInLgTkuMOZonH3XW8dvdff9zZur+JhtNLGwMO8zal2hviSgfgqBduAOhki7u6u7u3NHWO+yxNidkk5EEhxfM3CHmfSHN0zQq98Fjxunta6w+v+9GphneO0Wj0cKdUc7Nc0wW4qG608AcffNBrMlvfLx0x5XMGo7lAQs6HIbbfLytNnLELZUkOrO2mF2b2nDz64rX1hzccpG7p4YbmBuApd+kCHB3lI8G+2vnOlr0dBYVV+4tKxl1MswWW/gOX8rHlXYUsq+C8ChaeZv8/vOqYvo5hb2l48+d1+9fiNrwId8rmuyMd7HQCLrYJyDXQ20/tOGUxF+6wlU1aYDQWFPGBEwvIcHgJ9MtKE2f4jFkQq9rcnu72xrp//OTIgccx181wY8477XBDBJkGXAO9o2VPm+JzbioZMXWewVRQ2n/g0C3poklgoKyyc57cTxeUXnfHCVrXfU1D7fr9NJ4e2gA4w530OpNoMuK0TAGO9ljlaJB3tVd3u1yNb5ZVzPy40Wyt7L+Y4u5JP5IE+iFnsUbKmfl4zHN7nG3VdXtWXNvU8GYd9QBgZxxujDyTgKM9OAZc9e1dDY6ejoOvl1fNnm0yFY1TaApR/QhoMK/8G0UCGuQGEmUWKHK83jhA89z0gvoPDu1cfn1b864T1H29WZIRzc1iyzTgA+CmTqj7Lkeru6156xsVoy+cQk+fn44DJyHnQxTd1yBXRRk9bzpTsSrQ7/MoLvvJN/a/d9uSno5jLdQew40bOVghmFG4Md5MA4424UJA97rtvub6f71VPupcq9lSNttgNBLj8oZQUFzR/w6UU+ZVOeD2eV2B3u7ax/a9e/PvXI7OTuqxCDcuKDMON6Q2VICjbYacJ/jpHYte/8mjr35gtVUdLCwaPYfmyunDlFKbQ1ixXBByiDRzTl0RGPBiPXd7S8Pbyw68d+/TdAz5YlK8QzkkcEMSQwk42mfI4Wugt53c3uB0nHyttHz6THo4dywOnjRZIK7ojiHPxOw4lg4EYJI4mnfW7V95ff3h9bupd9DarLlhkohTgZk9+/pElS2AA27eVOjt3fW9p4699kr5qFkmc0HZR6TJ0nfEYngDzZUYmQeZrN6ZhEnSeXj1nk2/vr2nsw5vn4LGZrj1i6cG2VLyxYYacIyAz2zW4hro9HPnO3nstZ2FhZX7Cm1j5tCDAEWkyqU2j3HctV+7FJvjWE+CWRKvt6utpeGtX+7f/vv/6zNJGG7McfPFZNpuv8cY/oDkbAAcHRIhF0FXw21N2084HfWv2UonjaHPhEwJaikJ+oAjqdvRINfFD2ZXfSILF5I+Fz2kUP/akT0rlhyv2bCX6mKNDcD1N3CgqIbc4RzPJof+YOoEJx7eioNPl+FDlHSxqdgQnj77h5+oGPeJXxQUlE3Cg7qZ+EmmdnPWYYYjGRec/nMrbnfnsbaT2+6v2f3wZqoPJghDzVOAvNwVDbLCSqbplJTNNsAxKP5hBeR4OBGfSQDkDHpRYWFFyYzzf/Gd4oqpV5JGt+IZxlRqLGorr1zwmdjEmOMZEp/X4erpqFld/f4Djzud7ZghgabGBrDZ1sYsCa/lTqwhKphOly0min6MLCT42KAV2Kbzeb1Ob9Pxf+32utteLSqZOJ4++jRJmi16Efbv95/8rDsi++pzFX3mCM1kvXPkw7X/Vbd31eskc3H6D9pbhBvHJ7mfiv7upjSUjRpcHCD6xyYLa3PW6DBbVM0+4/yffKq88iPXmq0jJuOdf/J2vyjC/nBQk/fviyHRzva6u462N+96qHrng29RHtbUrLUx9cc3bljpsEISq8yKcLYDzkIC5Aw6bHNAzva5CrnZbC6c/pHrLykbefYVZmv5NAk6iy66PwBsV8fhrrYDj1Xv+uOr9GYyBpt9ntcWbe2s1NriiHMFcPSZtTlAhzZn0AE4ww7fOuP86z45ovLcKyzWkecEL0RN0kYnwYguaGP78MJLetl8277O1j2Pffj+n96mPAAZG8BmHxobYPMdSYCdtVqb+qa5XAKcO40+49qBQYc2Z42uAk77qj919tUfqxh1wZXWosrz6cEKslxQbPhOLwZNFKz4I7D9broL2fp+e/OO1TW7H3mPBMNgi75ojgBqvpCkYG64XAQckkW/sYlmCzQ6Ty2KoBeccc53Z5eP/uiXrIWjFpjNRTaD+no0FM1/2DWo6cIRb3D1eh12l7P5jY5TH6yv27cGt9cBsQg1wtDWvIl2dk5obeq75nIVcB4AQ86gs+nCoLNmV7V8YcnY4ikzvr3ANuKMz1mLqi4k0E3q+7nVu6OoIn+cOv9NUyJ4+ACfBKG3t263d9a9XPvh2jecPSdxg4a1M4BmyBlqnvaD1s4ZcyTc0ct1wHlMetBhi7CNziYM+6qmrzrtwtHjJi/6rK1k/OfoiblpAJ1hz0XNzpoai6AANTafu/uwvafh5cajG//ZcmL7KZIJA8xwiz7SoK1ZY+c02DQO1eUL4OJ4grZH0E6HRmetDsAZetE3T5q6eHr5mPPmWQurzjcXls8i0K20VFcx4iWYeA9JFpoyA4CmJatYI0JQu7zOjr0uZ8v7HU073zlW82w1dR7aGPAC5nA+0llj8z2HnDNFaAxhXb4BzoMMUtlvo0Ojs1bXA69qdEqHby4sLLeOm7p4Vln5tAsshRXnFxSMOJseirbgAhXPjAZvmrDYgn7/jRRuPjV+EGLUxbzRBSKWqdJ7RnChGKBPftAt9AMeZ/v7XR2HdzTWPLvX6eyAycFQA2jeGHBOY23NGhuNcEMUzA/HRyo/RhM6ChF0aHbRVhe1O0POceybiovH28ZNW/SR4pJJ55oLiieZzLZJJottPFY2BoHHWnWAT1Wr0owkUn18JJYoHv9xUQiQNd/roJfnNPi89mNed++x3p5jexoPb9zV29uAu4qAlDUx+ww2fI6Dz0CL9nWkzlD23HZ6qef2aKL3HmNl84VBZ83OQEfyOR98lDWOnjB3dFnFOZOttjGTLIWlk81m20RaMlBpUEw2Ay2QoRPARg1SffQXF7F9vtpFaOEgxbSrhuhDAV57gBZ+BBSf3e9ztXq99uMeZ/dRl73pWFf7vqOn6jfBhmYoRe0rwhsuLOZlu5p9tTv5/Gc4Ac7HEWMWN4ZW9AE6Q83Q8z6fHKKvQq+r10DmjrmoZEKx1Ta6yGItK7aYy7AiUvF4u+weV1evy37K4eip7yWzAmBCi4obwwyfta7oI8xAM8TYF/NwWbHevNXWNPYQNxwBF4Uggo4wg8q+CL0IuAg350Ec18H1oi0xjH3RMXiI4zBrVwZcDyxDy1DzPudnn+tjX2x32IQhfOmCEmBZMJDwGXQxLMYBbqSxz5AjDg4+b7wPH9DBMXz6fUCKOEAs+gwv+0gTw9jHBsd+cG+Y/uUDMUyHH3XYLBsGNJIvQq3PgwbEesQGGUDRR1i/Mez6eHEf9WJfOp0EWPi6aLkbQQIsLwYZ2aLFiekRqhwAJkPK8KJMtLhIdcr4PgnwwZECSU4Cejnq91G7Po7BFVvWx+n3xbwyHIcE/h9VLWRYHWXC/QAAAABJRU5ErkJggg==',green:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAltklEQVR4Ae2dCXQcxZnHR3NoNDp8SD7kU7bxFXCchBhMYoLNmhCcOBBykGw2gYTkPV6AhGXD2sTZJQcJG3jsgw3hscuCsTEsOAQW1sbY+MAHxpYtHzI+5EOy5UMStnWPZkZzab9/j75WTWt6NKO5Z6r82lVdXV1d9e/ffPq6uro7zyBDIhXIi7DyngjLyWJRKhDpCYiy2pwoztrpxSwCb+d1bayFm9f1Yu3+cj2MAgOJH2bXnNnEGiHWppW8d999d/inPvWp6YWFheNMJlOR0WgswdLT06PElFdM+xbn5eWVQDXK76TI7vP57H6/v5PyOxFjobwuh8Nx4dixYye+9rWvtaK4ZqFVNY/TiGUIoQCfsBCbcjaLNVHgJRUQG3/4wx9ali1bNmXYsGEzrFbrdLPZjGUagTyNAB2ZCLXoh3CJoD/p9XqxnOju7j7R1tZ2/LHHHqtbtWqVh47pp0X8AaAZbPmRzvnAJzPXhRBhVoBeunSp7b777ruuuLj4xoKCghsI5s8TcPnpIBT9oNwE/D6Xy7Xdbrd/8Oyzz+5+/PHHndQ2LfA5D3suA85QGwmMPLLQ+b///e/nkIW+0WKxLKBlLuUXpAPQEbTB5fF4KmnZShb+g0ceeaSKLLyb9gPgIvQRVJVdRXIN8CCo77777oI//vGPt5CV/n5+fv5NdGrhK2dDsLvd7k1k3f/n17/+9frly5e7qFM5CXsuAM5QI4a1Nh4/fvza0aNH/4PNZvs2rZfRks2h2el0/u2TTz55dcaMGXuoo7DoolXPajcmmwFnsBWoKysrJ02fPv3v6QLxH2hUY1o2E63XNxqhOUkXqq+eOHHitblz557pBV2EXW/XjM3PRsBFsE21tbXXk7VeCheELhKzsb9Rw0cXqT1wYciqP37FFVd8SBX4aMlK0LPphKMvirWm2FRfX//lESNGLKXRj3m0LoOOAjQas/Py5cuPV1RUbKQiDDrDrrNX5mRnA+Aq2OPHjzfv3r3766WlpUvIDfl85pyG1LeU3Jd9LS0tT1x33XVrzp8/76UWAfKMBz2TAUfbFbgJbAuBfUdZWdkSuvFyZepxydwW0I2lo83NzQD9rwS69mZSxnUsUwFXXZHDhw/PmTJlytNksedknPpp3GCy6FV1dXX/OGvWrCpqpui6pHGr+zct0wBXLDZ1w/TSSy+V3n777b+j+R8/pYtHAC9DrwIO9xHD5c5XDF5fS0ya0MWo3+nwvrBx47nfLLlvKypj0DNmaDFTAEc7GW5zQ0PDD4YPH/4YuSMJmQMSExVpsPPxhjsMHt/FuLWkp8dwqb3dt2zhnD2vUKXsnwPytAc9EwBHG01YDh06NIuGtf5DjoyQGmHC4XMLwmwd/Caft2fnmXr3A3d8Zf9hqgXWHEtaQ57Of9pViz179mwrXeE/MnPmzF0S7sEDGuueJnPevCuusO76sPq6R2bPHm2l+sy0gKG0NZTp2jCG27R27drx8+fPf4lu1NwQ6wnKlf0TZcFF/bwe//Z9uxw/vvfuj89Tftr65ukIOI+QmE+ePPllGgJ8UfraIloDp5MBOFoB37zxQvdPvr5gP24SsW+OsfO0CenkoqhWe8GCBQWXLl36Hd1de1vCnTas9GtIXp5h5LgJ1re3H7z2dwsWjMTUYlwrpZXLki4WXIV73bp1E66//vqVNClK3mLvh1RkGcmy4GJr3B7/zkOVXXfd86PD5yg/bVyWdAAcbVBGSY4cOTKPbtqsJqs9QhRPpqNTIBWAo4U0l+1yw1nXd29duH8nrabFKEuqXRSG20yz/hZPnTp1rYQ7OpjTqTRNUhwxtsK69t3tcxZTuzDCAsOVUiOaSsBxbAhgoZl/d02YMOF1SttokSGDFSCabeVj819/v3LOXdQNCy04xynjLFUHVuGmu5K/HDNmzHN0ux2/eBmyQoEe84gRluc2V13zS+pOSiFPBeAK3PQQQj7NQ36CZgA+Kh9EyAqqgzpBQ4h5w4aZH6URlidwrmljSix5sgFX4F60aJGNXmzzYklJyf1BqsiVrFOgqNh0/5ubJr24aFEp3M+kQ57MCwAF7pEjR1rpBs4KeuD3W1l3NtOkQ6kaRQnXfZfL/+Y3bqz7Ed3f6KZyPIwYbpe4bEuWBVfgphbn19TUPCnhjsu5y6hKCgqM33pr4+QnwQAtSbPkyQCc4bY0NjYupXeQ3JNRZ0Y2Nm4KFBab7tlSdc1SqjBpF56JBhwuEI5hOXfu3N30rOS/xk0tWVFGKjB0mPlfN1bOuRtM0AI2EuomJxJwNBx/iix0QfkNupJ+mtIySAUMpSPyn16z5fPfICnYkicM8kQBznCbq6ur59Pt9+U0FAjYZZAK4J6+aczE/BffWn/1fJIjoXc8EwE44Ea95vXr18+gt0mtpjQmx8sgFVAVIEgKJkzJX/2fq66aQZmAHMzE3ZInCnATPRBcNG/evJU0t2SI2iuZkAoIChiNeUM+O6d45cLbxxVRNv7Cxx3weFeIHwwaar148eJTQ4cO/SmlZUiyAuk4Dh5Ogs5O3wsLPrfnQSoT9zHyeFpw/FhQn+Xo0aPflHCHO6Vym6hASYnpp29v+dw3wQ4tYChuhjdegDPc5jVr1kybPHnyM2IHZFoqMJAC48Zbn/nzi1dNo3Jx9cfjCbh54cKFRfS42Qrpdw90OuV2rQLwx6/9QvGKhQsVfxyQx8WKx6MS/EgUv5vmGTw5ZMgQeadSe/aSvJ5pPrgoj73D91/zr97zEOXFxR+P1YKrrsm+fftupNmBEm7xbMl01AoUlRjvWb1u9o20Y1xclVgBV6w3uSVFNN79JL3LLuoOyR2kAqICYKhisu3JBQvG8tBhTIzGsjNbb8vLL7/8C3oxz3SxoTItFRisAhaLcfqyP435Oe0f86jKYAFnuM2vvPLK5FGjRuHRJBmkAnFToLTM8tCfnpk5mSqMyVWJBXBcWFro6Zw/0Z+Vwrj1TFYkFSAFwNQX5w/5N0rGNCFrMICz9bZ8/PHH+MbkV+UZkQokQoGiQtPX/rb+M7f0Qg5WwV5UYTCAYx/zrbfeWkw3dJ6I6miysFQgSgXGV9ieWHjrqGLajV2VqGqIFnC23ua//OUv99PnriuiOposLBWIUgGLJa9iya8q8HA6Ax6VFY8WcJQ30Sewh9DrHn4WZVtlcanAoBQYXmr62fe+NwGzUnHdFxWz0RRm621ZtmzZT+hF9GWDaq3cSSoQpQImU17ZnfeO+gntFvWwYTSAo6wZN3XKy8vvjbKNsrhUICYFykZa7r1mwUjc/GFXJaL6IgWcrbf5+eef/wH53uUR1S4LSQXipIDZklf+m99N/AFVx4BH5ItHCjjKmehbObaxY8fiDpMMUoGkK0BW/OezZxdH9YasSABn621ZvXr1HWS9Jya9Z/KAUgFSID8/b+KjT02/g5IR++KRAK5Y73HjxuXT8oBUWiqQSgVGlVseoCnZfHdzQH4HKgDrjcX8xhtv/B1NqJqays7JY0sFLPl5U59bVbEQTNLCfOoKEwngGHs0013L78npsLo6yg1JUgAMjhlb8D0wSQvYBOS6YSDAsd30/e9/n54hHrpItxa5QSqQRAWKh5gWffWbY4bSIQF4WIbDbcQvA9vNS5cuvY2es8TVqwxSgZQrYDQabHffU34bNWTAIcNwgGObAjh9P+c70j1J+XmVDehVACyOLs//Dq0y4Loc621g59301FNPTSwsLPyiVFcqkE4K2ArzvvjPv52GIWv2w0P64uEAV6z34sWL76BfjF65dOqzbEsOKQAm5/9diWjFowZcGT2hx9G+nUO6ya5mkAL0WBsDzla8X+tDWWa+uDStWrVqBo19T+u3l8xIawUsplFp3b54NY7mik/703/MxNtpeTSlnxWHk64NKuCf/exn58uLS6086b8+3Pqg4WDNHw0O5yf9Gkuf9+sX6N3twXmaVWwMkUWv+Q7eLVShHk1mv310Kg9Vrt/h/PStQoN/PlVxhBYYa7AbVCwc4PQxzxHXS8ANhkZ7jaGq8W8Gh6ed9MuM4C2ebrD7Jhp6CIJsDr481/UGw4nnqY8MeFB3tYDjF6BY8KKiIjO9qeq6oNI5urL+1L8bOt2XM673PrPf4OjwZDXk/p6e68BqV1cXAx5kxUP54MgzrVixYpbJZCrNuLOagAZnItyQwWQ2GgppXlKeEec8OwON75V+/YErZlHv2A8P6qieBTddeeWVX5LuSZBWGbnCkDs7PQa/PyO7EL7RZI5HTCj+EhXaTwt7IKpfprXgintCBU3kf8+TgIfXNlO2AnJbicVAt7izLoBR2xDLPOoYW/CgP1eiBWf6TXRrPp/877lZp0YOd4ghhyUPNUKRydJYbaa5IyYU5l8+53BRP5hjxYprf9NYNz7zzDOfpl9GSSZ3Wra9vwIMORm9rArUn5Kbfzzt09QphV+xcyEt+Pjx42dK90SUKXvSDDksedYEwnrYyIKZ1J89tOhacN5gpLnfU7Om87Ij/RRgyLPJiFlsZjALC84cK/1GBgdswLqJZg9OzabOcwdl3KcAIC8oNuMtrn2ZGZpCHyxW0xXU/H4Xmgw49xLrRgJ8Sob2VTY7CgVUyLNgnLwXcIXfXgkUpvv54KWlpeaCgoKKKHSSRTNYAQXyIrPB1eXVzOLIrE5ZrcaK0lKbuaXFCbDZYCsuCfcEmcbf/va34ym2cqaMs18BhjyTZ/3TmKB17ncngV1Y8X6AM/XGq65SPsaZ/WdV9jBIAUBuLSSfnPFgIjIoHj2pCFO7xR6oFpy7YRw5cqS8wAw69bmz0gc5cMiwQE0uKrH0G0kRfXDFQbfZbBMyrGuyuXFUQIGc3p/Q7fSRT65O6YjjERJXVX6hCewqHPNRsILAFjyP3j2Iz0XIkMMKBCA3ZdwQosloBLsqyziFogXHBiNNkS3OhrFRdE6GwSsAyPPJkrvJkmeCHVeYNeUBcPbBlc7ziko9AY6XjMsgFVDmkysXnqAjAwIN54NdlWWkRQuOLuRJwCGDDKyA0ZRnsNrM5JOn/zi5yZzHgHPz1VEUZCjkS8BVbWSiVwGGXCEkjVUxGlXA1b85bMHVDAl4Gp/BFDaNIXe7vGk7uEL+iOheK0zzKAqkkxY8hQBlwqEBeX4BJmilZ2uNRuX6UeGYW8gWnNelD85KyDikAgy5uzv9xslNRvUiU217PwtOW7TQq4VlQioABRTIrTQzNc1MeU9eD9gNacHVPzper9dBH3mVj6tJlsMqwJB7yJKnyzg5vTXAITRaYZrHwdV8n8/Xpa7IhFQgjAKAnOZhp83gSo/foGVXGQfHD1D9EcKCh+mT3CQVCFKAIfe6yZKrFAUVSdqK39cjsqtwLfrgaEiPtOBJOx9ZcyBAbs7H3JUUd8mnWPCgn5l4QalskICn+CRl6OEVyMld8brp9VkpMuU9fj+7KCrkIS14v9fpZqjostnJVYDuJJIlJ6RSYMrBrK9HAVyFG71nC45MZZE+eHKhyLajMeQ+jz/phtzvy4MPrrIMbUULzoDbs0102Z/kKgDITRZj0g253+8Huwy40mm24KoCTqezBeZezglXJZGJQSgAyA0EOSx5MgLcfp+7p0V7LLbgTL2/tbX1tLaQXJcKDEYBtuSD2Xcw+zg6u8EuflHMs+KiYAVByTx58mSdvMgMCCL/j12BpEFO9Laed9Yxx70t72ELjnUF8DfffFMC3quOjOKjAEOeyMEVfOyqevtFLeD9RlH8GzZs6Ozu7r5E81FGxqd7shapAI1mwCen5zz93sT45H5Pz6UTey52ktb9XBTor1jv3o1+u91+RropkEWGeCoAyI0EebyHV8Bqt8t7htoKuEMCjn4AcqWAw+E4jQwZpALxVkCBnG7tK5DDZ4nT4nb5wawIuNJ09sFFC+7DSIq04PE+tbI+VoAhj5dPjiHCbrsXgNNTGMEWXBwHVyFvamqq48bIWCqQCAUAeQ8ZcJoBGHP1+KF0NHvALCw4c6zUG8qC+995551DNOkq9iPH3HRZQTYrgJuJmKQVa6CvOffUfNhwiOoRXRSFXwYcx0AGCvjoI7CX2tvba6WbAllkSKQCsUKuXGB2eWsr37twidopuihKs7WAM+Te5ubmSgl4Ik+trJsVYMgHMz0E/ndXm6eS6qI3E+m7KHwsxYLTiq+2tna3BJxlkXGiFQDceDe5EiMd6UIPzLU0OneD2d4FDKtBz4L7li9fXkmzs4IKq3vJhFQgAQow5NFUTYT696w5DwsuuieK/416QgGu/BLWrFnTSn54jbTi0cgty8aqgAo5rj0HWHB7vtvhqTnyUVMrlWYLDrhDAo62YQOsNvwZLw0X7pGAkxIyJFWBgHsy8CHhf9tb3Pj4q8IrxWBXhRs1iBYc6ww4fg1e8sN3ScAhiwzJVoAhJ1dc/2YnNaq5oWsXRQBcdFHU5oo3epAJwBly79NPP73npptuchUVFRWoe+RgwnXRZmh3YBQqu4OuMQuyiX0a6GQHCuhs1D1GX7VBKVhp7APgtfvSS4dcm1bUsQVn6x105FCAo6Diz3z00UedjY2NW6dMmXKL0ag19kHtiHil9nyj4b2dVYaOLvEVFhHvnpKCXs9XDUa3m44dpF1QW7TiB23UWdHdR+cw8DlDBlCgE/S30A5h9tOpLubsaG/r6JWnJ+gNrtbmrRdO7sYMQva/GXK1nVrAsQGaoCDMvufgwYP/N2nSpLgB/sJb6w0tHWhTZgV3t4teidBNjYbkkEhPem2/uCyjpt1fu127f7LLa4+vbZ+2Pdry2vWB9tdu1+6vPV6gvN/vMzTUHv8/Ku2hBaz2gxs1hTLLqIEB995///07aPpss661QS1RhEyEG93LtxbQKxH4+7gQPdLAZRGLad5fzOMyYoxyXEZMi2XEtFhGTItlxLRYBmltQFkE3ofTSmYE/w20v3Y7H4dj7fECrorP42mu2rZhB23VvcDEnuEAh9n30MxC5/nz59+LF+A4aKaGAOT5wbxpO6M9X9jOedqyqVjntujxo21Tostrjxfheldnx3tuu91JxWHBQ15goqpQgCMfFpytuGfTpk3v0PtSkJ/zAZBbLL2QMyRiDIUYCjEtlkllWmyTmNZrk1hGTMervF49YfL99JbNpvrat6k5onsCXvsFPcDhpuBXofjhS5cuPdzW1nZKWvGAfhaGvJ+cMiPRCoBBj8t16tCOTUfoWAw4WAWz/UKoi0wUQmGGHJW4aUx8TVlZ2YP0DR9sz/kAyBG8HsgjQ7IUAOD2jvY1dDwMa0F8hjsk4HoWHO2FyVet+MqVK9+hh5Hl2YQyvQGQm/PJXQkXcKcCge9YcFrJDPFftOVDVJFWWdH2Z4Dy9PpjT92R/e9QH8EiPAwwGtI9oXxdHxzb8ItgP9z90ksvNZ45c+ZdOf8K0vQFC42sKJAzwNoYRfmkiWltOV4Xy4hp3p5psdgHMa3XD7GMmKbyALKrs/3dMx8faKQkLDgAB6MhrTflRww4fi3uV1999QWy4rq/FlSYi0GB3GLJxa4ntc9+r9d/5tjHL9BB2T2JCXA0PsiKP/nkk7Vnz559X1rx/ueVIQ9z8a+OJMsygYGmaHTAXVdnZ+f7x/bsqO0FfEC4cZbC+eDYDmutuimU7v7rX//63x6PR/dPAnbK1QDITcoQYq/fDSHwp5hjMR3I7b9d70+3Xj7XPdj6Yt1f266B6htou7a+3nW6c9lTf/Lwf9PuuJ0suidhPYqBAEdzUAEcecVN+cMf/nCMrPg2acUhTf9goYtOk5ncFT5RKII0B07rbedykcax1hfr/tp2DlTfQNu19dE6Rk4c9o5th3d+cIxW2T0Je3HJ1UQCOKy1Cjilu9etW/c8WXGuQ8YaBVTINflydXAK+H007+TUyedpb7begA9MDuhJRAo4W3H8erqXLFlS3dDQsEtacVJDJ0jIdYSJMhvW29nVuevAtvXVtCsDztY7LoCjSWzF4dgDctfatWufkSMqkEY/AHIzja5gLjMHTgcm9AfyOQ9lOM3bo43FOsS0Xj1iGaQHCtG2D/XxPmKa2yPmiWne3uP3+Zvqjj9D21y0gD0wGJH1pnIDXmSiDAIAD7rYfOihhw7Su8TflnNUFH10/zPTRafJbFZOMp9ojrETp/mEinm6lYbZEG192vLcDr042vZp69fuH247psR2tDS/XbVl/UHaj613RKMnLFEkLgqXZSuuXGzigA8//PDT9GByK/6MyKCvAEOuX0Ju0SoAprzd3a3VO9Y/TdsY7oh9b64vWsDZF8cBXZs3b75UWVn5Z3nByXLqxwHI5c0gfYWCt8B6Nzde+HPj6dN4VhDuCZiL2Pfm2qIBHPuIVlyB/Pbbb3+THmurlhecLKl+DH9cHULkYuyfI45kwX68j5jmfcU8Mc3b4x2LxxDTescRy4hpoTwezXN1dVVvfeuVN6kIwx219Ub1gwGcrbhysYkGrFq16jGXy+WTrgokDR8UyE00iZNPKIojHWngsnr7D7Q90uNEWi7a4w1QHgz5vF5f3ZEDj1ETADdfXEZtvdGFaAHHPgw4flGKFX/00UePHDt2bDW9kRbbZRhAAYYcWMslWAMDPcxgb768mm7qYL631nqDvajCYADHARhytuLOBx544NnLly83SlclMv0BuZFGV2ToU6CH4HY7nI37Nr/3LOXicTSt9e4rHGFqsICzL66Oi+/du7f5tddee5g+f+KRrkpk6pvplr4CObsbORwDKBpy9pyuqX74YkN9M60y3FGNe2uVHyzgqIchV604DRvuI9CfpRtA2uPIdR0FFMjlU1L0pQcvjZo0PHvggw37SCqt9QZrgwqxAg5XRbXilHbdcsstK+vr63fIG0CRnw+GPFf9cbpbaejqaNuxZfXylWCod+G7lmAsJYDjDOLgWPiCE788x4MPPvgb8sc/kf44qRFhCECeez45/O5up/OTqo3v/oakwuvOwBBcADDFfFFycCEWC85HRCMwfIJfHBrnpBtATa+//vqvyB/3Sn+cFIkw4Ja+URxCzHKfHGaZ/tJ76SmdX9FrIJpoVeGHYrDEw4KUHHyIB+BoJxrDrorSSHrVRNWePXuegz8uIY/8BCmQG7P/zQVgAn735aYLz+3fsq6KFBLhjunCUlQ7HoCjPoY8yFVZtGjRcpqQtdGtvLhSPKxMh1MgYMkBefZ65TRJ0NDZ1rpxy2vLl1NHta4JDCaYijnEC3A0hCHnURU02rF48eJ/oYvOvRLy6M6ViVwVoymepye64yeytI8sd1dH+94tb6z4FzDSu/CwYNzgRh/iqSAAF/1xNNhBX4nouPPOOx+iByRq5KQsSB55YMizyRXHiEm3vbNm99o3HnJ2dHSQGgBchBsMxcV6Q+lEOHtonNjAPILcW1dXt3P+/PkLPth/eGgePqclQ0QK4L3synvBs2BKMmYIuhz2c/s2rf1ZY33tJyRAFy0MONzbuFpvCJwIwFEvQhDo5It3d3Z27naYCm6mGXWFPNE9UFT+H04B/vhAgPHM9Mv9fvpglNPZfGjnpntOHzl0lvqrhTuulpv1TBTgogVX0wcOHOiix7j2Dx899is0HJYvIefTMHCc1/uFjUwckcL9EHphpv34gY/uq9nzUU0IuGG5AXjcQ6IAR0MZbI6VxjfV17UVlQw5OqR0xEKah2GRkEd+TlXIIW2GGHK86tjtcjnqjx74pwNb38dteNFyx228W0/FRAIuHhOQq6BfqD1+0WIp2Dds1KgFNCRmkz65KFX4tAp5Bvjk8LndDkfriQN7fn5g6waMdTPcGPNOONxQMtmAq6DTnasWn8e1s7R8wvVkyEv4xKFRMoRXQDUIiiGnz16n4b8eGud2d9kbqnd+cC+9bu0o9chOCwBnuHEzJyF+N9WrhmQBjgOyBVchv9xwobOro3XbqPGTrjVZLGV8MaW2TiZ0FQhATlKyqrolk78B49z0HsGT+zatua/uyMHT1AKAnXS40fNkAo7jITDgStx++aKz+cLZD8onTZ1NryEeA59c+uUBoQb6X4UcBdPAJ8dwJt5CRTMDD+xY88YvGs+caqCWad2SpFhu1i7ZgAfBTY1Q1umdz+7zp45uHXfFjCn0AstJeUYJOZ+ggWLVXUmxKcesQHqWkm6/t2zd+saKJW0Xmy5T2xlu3MjBDMGkwg3tkg04jonQD3S60vbVVh/cPmbyFGu+rXA2+eRkyGGWZBhIAdYpYMST75H30Bg3fcqlp62p4eWNry7/N3rVWju1WYQbF5RJhxu6pQpwHJsh5wsN+nit13+quupA4ZChNSVDh881mkw0wiKtOcQaKEAnCJrMoMwIpJESj6Orlaa8Ltv2v6++QeeQLybFO5QpgRtapBJwHJ8hR6yCfuFUzQX6U7d5RPn4K8kvL5cuC6QaOKiQJ8EfJ2/bgItJR3vbwb1b1v3iaOX2Q9RCWG223HBJxKHAZP/+FMHSBXDAzYsCPV18dp06eGBjecVkk7Ww6DPSZVHO14D/sbsyYMEYCuDOpNfj7mlpOL9yw6oXf996sQFvn4LFZri1k6diOFpsu6YacLSef9lsxVXQ6c+d79ShqoN05/NI0TByWYxwWWjAQPrmYc96nz7xNeWBhxRofNvpbDl7rPpX2/73f97qdUkYboxx88Vkwm6/h+28ZmM6AI4miZCLoCvp86eON9ibWzYPHVk+mlyWKXBZMC7WdyI1vZKrvdqwrLEJArAxSoJvgna0XNpctXntkqOVHx6mWtliA3DtDRwYqpQHkJJOAe3BXFr88PCmSist+OKqjZZCpK+55bYvVEy78pcFRcUT8eRL3zAZbZWhnwIAM5bAw3/dXfaz9SeO/vve99fsovrggjDUPATI011xwPj8smJpeO++6QY4mhUwzwHI8Zg5vrQKyBl0W0FJSfENt/39D0pHj73LYrVayXWR1pwE0guBGYjRMaeOkNBDtc1NDSs/XLP6FVdnJ0ZIYKmxAGz2tTFKwnO5ozsQ7ZjIkC4uiraPLBJiLLAK7NP5vG63t/bQvkMOR8emoWWjx9Fr0CZKt0UrYd96nyvHtkM/xhwudkfsra0fHtz6/j/v2/zuB6S5OPwH6y3CjfMT25+KvubGNZWOFlzsINrHLgtbc7bocFsUyz7vq9+6oXzK9PsKCgsraE6L4rb0nVSxutxOByx5aA3Yz/aRn+1yOOobT598dte6N7dTabbUbLUx9Mc3btjosEEKXXkKc9MdcJYGkDPo8M0BOfvnCuRms7lg7uJv31Q+ruJOa1HxVLzcEv65BJ0lDB0z2LiAJD/7VNOF+pcr1/5tE72uhMHmmMe1RV87La222NNMARxtZmsO0GHNGXQAzrAjtn5x0Te/VD556p0FxSVX4Y1RmIorQSdlhKCAjfFsL1lse+eRptOnXv7ovbd2UBGAjAVgcwyLDbD5jiTATlurTW1TQyYBzo1Gm3HtwKDDmrNFVwCndSW+5uavXzNu8oy7CocMuRpfVgi8hiF3hxcDLgpm/GFilMfg6OjYf+H08ZU0MrKXNGOwxVh0RwA1X0hSMjNCJgIOZdFuLKLbAovOQ4si6PlXz7959tipM28rKhm2wGzNL8TrGHLlopShxoQo3Fr3drsdXZ1tWxtO1byzf9v7uL0OiEWokYa15kX0szPCalPb1ZCpgHMHGHIGnV0XBp0tu2Lli4eNKPrc/C8vKC0v/0phybA5NI5uogldivuSbePpGAkB3JifjU+CODrbqlqamjYc2LZxq73tMm7QsHUG0Aw5Q83DfrDaGeOOMBRinOmAc1+0oPONInZfxFix9BOmXjVq+py5Nw8rG/kVmp47lV+XFvDVM8+NUS11H9R0S91xqq350oYTVZXvnzt15CKJxQAz3GKMbbDWbLEzGmwRDE5nQ8ygIwbksOhs1QE54NbG5qu+cMO0cZOmXW8bMvTqgsLiWQS7FW95hc+ersAHA+1XXmRJlrqbXqxz2NnRvv/CmZMfHtm1/ST1F9YY8ALmUDG2s8WGC5IVYFM/lJAtFpz7wzH6xbADdF4AuBZ4xaJTvrKtoLjYOuvaL80qGzPx8wVDSq622Yo/ZTSbLLhbqjwzqsxPp9JKCMiXqBGaAMQ4UMD1xU0Y8jsMmM2HJ9ZpLprH6bQfc3V07m9uPLvv8J4dh112O1wOhhpA88KA8za21myxldqpfFaFbAWcT5IIOvx00VcXrTtDznkcm4aWlRXOuGbeZ4aXjfm0xVYwMT/fOtFsLRhnwsMYyvCjUQG/76KVD62NtVIHoNWWUiAmoHFRCJAVX5pi+oKdk+zzBbe7+6zH6Trb2tz48fG9O6vbm5txVxGQsiXmmMFGzHmIGWjRvw7dGCqc6UGreqb3J1z70VcAzjFbddGVYbC1sVhW+aFUzPzMqNETJ1YUDyubaLUVVeRbrRNMFnOZyWguzAvAj9fToZ6AmwPLjxUKCk1EMltoir30OJOjhyD2+b0On8fb7O7uPtft7Kq3tzWf/eTs2fr6mmr40AylaH1FeEOlxbLsfnCstCeb/2PNs7mP2r6hz+ICeNmycwwwGWqGnde5jBgjjUWsN4/cHfPQ0lFF9PidzVpUWFRgK8KMSIPL2eXo7qLRuvZWZ3vLxS5yKwAmuwgcM8yI2eqKMdIMNEOMdbEM78t1ckzFciPkIuDimQ0CkjYwqByL8IuAY7u4jcujPqS5XkoGpbEuBhE4TrN1ZcC1wDK0DDWvc3mOuT6OxePmTDrXARdPNGvBcCLWgsvrDDEgRzmOOT9UXTgW5wM6BIZPuw5IkQeIxZjh5RjbxDTWsSBwHFjL0f9Z8BztfthuszaIwy0i1NpyOIBYj3hABlCMkdYuDLs2X1xHvViXQaMAi6/Jlqs6CrBeDDKKhcsTt+tUGQQmQ8rwYp9weXp1yvxeBfjkSEFiU0Cro3YdtWvzGFzxyNo87bpYVqYjUOD/AZrbm7Ts1rpFAAAAAElFTkSuQmCC',red:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAk/0lEQVR4Ae2dCZxUxZ3Hq8/pnhkGmOEQuQS5VCTxWHEDBlyNkciakMMkxujGuOvHO24IKCae0UQlKwmyroocoqtozGpA4oFiVAQU5IaRcchwDsPczNF39/5/b+bfVL/p7ume6bur+DyqXt31r2//5//q1XvPIJRLpgQMMVYeiDGfyhanBGKdgDirzYvsLLtIPguB0/lc7+vh5vNIvr68Oo8ige6EH6Vo3iSxjODrw1rcm2++2f+MM84YV1hYONRkMhUZjcY+OAKBgOZTXDGVLTYYDH0gNYpvIa/V5/O1+v3+FopvgY+D4tra29uP7N27d98VV1zRiOy6g06DcRyGr1wYCfCEhUnK2yiWiQYvSQG+8ac//all3rx5o/v16ze+oKBgnNlsxjGWQB5LgA5MhrToh1BL0Fd4vV4c+1wu176mpqYvHnnkkf0rVqzwUJt+OuQfALrBmh/hvHc8mfkuCBlmDei5c+fab7nllguLi4svttlsXyeYzyPgrJkgKPpBuQn4LU6n88PW1tZ1ixYt2vjoo486qG964PMe9nwGnKE2EhgG0tDWBx988HzS0BdbLJbpdEymeFsmAB1DH5wej2cTHR+Qhl937733biYN76ZyAFyGPoaqcitLvgEeAvX1119ve/jhhy8nLX211Wq9lKYWtnIuuFa3272WtPv/3nPPPW8tWbLESYPKS9jzAXCGGj60tfGLL764YPDgwT+x2+3fp/MyOnLZ1Tscjj/X1NS8OH78+E9poNDoslbPaTMmlwFnsDWoN23adNq4ceN+TBeIP6FVjbG5THSksdEKTQVdqL64b9++lyZPnlzVCboMe6SiWRufi4DLYJsqKyunkraeCxOELhJzcbxxw0cXqQGYMKTVHz399NM/pgp8dOQk6Lk04RiLpq3JNx04cOAbAwYMmEurH1PoXLkIEqDVmPV1dXWPjhw58l3KwqAz7BFKZU90LgAeBHvYsGHmjRs3/mtpaekcMkPOy55pSH9PyXzZ0tDQ8NiFF1646vDhw17qESDPetCzGXD0XYObwLYQ2FeVlZXNoRsvZ6Yfl+ztAd1Y2lNfXw/QXyHQ9TeTsm5g2Qp40BTZtWvX+aNHj15AGvv8rJN+BneYNPrm/fv3/2LixImbqZuy6ZLBve7atWwDXNPYNAzT0qVLS2fNmvUA7f+4gS4eAbxynRIwHN8ozDseFQZHTW9l4m/3BBa/8nnDfT97vKqBKmPQs2ZpMVsARz8ZbvPRo0ev6d+//yNkjiRlD0hvqUh3ecsrpwtD2+GEdcMfELUNbWLewNniBaqU7XNAnvGgZwPg6KMJx44dOybSstYf1coISSOKsy4tiJLa8ySPX6wvrxN3TLpX7KJaoM1xZDTkmfynPaixJ02aVEBX+PdOmDBhg4K754D2tqTFKKacPVhsOPEnce+kSQK/IjMdYChjFWWmdozhNq1evXrYtGnTltKNmq/3doLypXyyNLgsP49XfPhOpfjZzCcEbKGMtc0zEXBeITFXVFR8g5YAn1O2toxW9+FUAI5ewDY/UC9+PvrXAjeJ2DbH2nnGuEwyUYJae/r06bba2toH6O7a6wrujGGlS0eMBjFw1ADxetMT4oHpZ2lbi3GtlFEmS6Zo8CDca9asGT516tTltClK3WLvglRsEanS4HJvXF6x/v0vxHXfWigOUXzGmCyZADj6oK2S7N69ewrdtFlJWnuALDwVjk8C6QAcPSSTpa6iTvxwwm/EejrNiFWWdJsoDLeZdv3NHDNmzGoFd3wwZ1JuMlkGjBsoVlf9TsykfmGFBYorrUo0nYCjbQjAQjv/rhs+fPjLFLbToVwWS4Boto/sL14++ri4joZhoQNznDbO0tVwEG66K/nLIUOGPEW32/GLVy43JGAeUiKeqvsv8UsaTlohTwfgGtz0EIKV9iE/RjsAH1IPIuQG1SGjCAhDWaF4qHmBeGzwYIG3EaRFk6cacA3uGTNm2OnFNs/16dPn1hChqJOck0CJTdxaeY94bsZkzfxMOeSpvADQ4B44cGAB3cBZRg/8fi/nZjNDBpSuVZRow3d4xGsjHxT/VlsrXJSPlxGjFUlIWqo0uAY39dhaXl4+X8GdkLnLqkrsFvE90uTzwQAdKdPkqQCc4bZUV1fPpXeQ3JhVM6M6mzAJ9LGJG+v/IOZShSm78Ew24DCB0Ibl0KFD19Ozkr9JmLRURVkpgdIi8Zvqx8X1YIIOsJFUMzmZgKPj+FNkoQvK79CqyQIKK6ckIE4pEQsqHxbfIVGwJk8a5MkCnOE2b9++fRrdfl9CS4GAXTklATwiYRpVJp7bfb+YRuJI6h3PZAAOuFGv+a233hpPb5NaSeHkPGJCFSuXnRIgSGwTBomVb/2nGE8jAORgJuGaPFmAm+iB4KIpU6Ysp70lJdk5BarXyZaA0ShKLh4tls+6QBRRW/gLn3DAE10hfjDoaMHx48ef6Nu37w0UVi7FEsjEdfBoImh2iMX97hR3Up6Er5EnUoPjx4L6LHv27PmugjvalKo0WQJ97eKGLx8U3wU7dIChhCneRAHOcJtXrVo1dtSoUQvlAaiwkkB3EqAngxauuk2MpXwJtccTCbj5kksuKaLHzZYpu7u76VTpegnAHr9svFh2yQTNHgfkCdHiiagEPxLN7qbnKOeXlJSoO5X62UvxebbZ4LJ4yB5/muzx2RSXEHu8txo8aJps2bLlYtodqOCWZ0uF45YA7T68cfu94mIqmBBTpbeAa9qbzJIiWu+eTy9Wj3tAqoCSgCwBIETr4/OnjwsuHfaK0d4UZu1tef7552+nF/OMkzuqwkoCPZWA1SzGvXS9uI3K93pVpaeAM9zmF154YdSgQYPwaJJySgIJk8DgvmL2C/8hRlGFvTJVegM4Liwt9HTO78k0KUzYyFRFSgIkATJVCq88S/yOgr3akNUTwFl7W3bu3IlvTH5LzYiSQDIk0KdAXEEbsi7vhBysgr24XE8ARxnzlVdeWUw3dB6LqzWVWUkgTgmMHSgeu3Ky9oFeNlXiqiFewFl7m5988slb6XPXI+NqTWVWEohTAhaTGPnMLIGH0xnwuLR4vIAjv4k+gV1Cr3u4Kc6+quxKAj2SwIA+4qbrpwjsSsV1X1zMxpOZtbdl3rx5P6cX0Zf1qLeqkJJAnBIwmUTZ/TPFz6lY3MuG8QCOvGbc1DnllFNujrOPKruSQK8kQG/Kuple0Yx942yqxFRfrICz9jY/88wz15DtfUpMtatMSgIJkoDZJE5Z9mNxDVXHgMdki8cKOPKZ6Fs59lNPPRV3mJRTEki5BIb0FbdNOj2+N2TFAjhrb8vKlSuvIu09IuUjUw0qCZAErBYx4i/XiasoGLMtHgvgmvYeOnSolY47lKSVBNIpgWH9xR0lJcG7m93y210GaG8c5ldfffVfaEPVmHQOTrWtJEAbsca8f7O4BEzSwXxGFEwsgGPt0Ux3LX+ktsNGlKNKSJEEsJ121CDxIzBJB9gE5BFdd4Aj3XT11VfTM8R9Z0SsRSUoCaRQAn0LxIyrvyb6UpMAPCrD0RLxy0C6ee7cud+m5yzV50VIGMqlXwL0/Kb9nsvEt6kn3S4ZRgMcaRrg9P2cHyjzJP0Tq3rQIQGYKSP6iR/QGQMekeNICdDeOExPPPHEiMLCwq91VK3+VxLIDAkUWcXXnrhaYMma7XDw2sVFA1zT3jNnzryKtHekfF0qVBFKAqmQABFpnDUxRIvHDbi2ekKPo30/FR1WbSgJxCuBwcVBwFmLd6kinGbGLwHxphUrVoynte+xXUqpiIyWQKBoWEb3L1GdozXxsS/9u/Z2Wl5N6aLFYaTrXRDwr371q9PUxaVePJl/3nzef4uaN28S7hNHunQ2EOgSRa/r1rkuEXild1enr6unecJVHktd9OlwaOJp1LPddEApg92QotEANw8YMGCqApwktmen8K9cIURTI8kv810BdXGI72JR73LR9+ND5jvzOx9nD80u11QhVj1DxRjwkBr0gOMXoGnwoqIiM72p6sKQ3Hl64nv0fhGoPZ5Vo8ff7P5+v2jw+Eil5S7kfQKBC8FqW1sbAx6ixRGpd4gzLVu2bKLJZCrVJ+bjebbBzXNkoTsipfRQo0HTWRybWz7BWvqHkYMn0qjYDg8ZoB5w1uCmM8888yJlnoTIKitPGHIj3R3hyc0lHwCPLbRdRB4A56EF5yoc4Igzkf09RQEelFNWBwB5f3okJhfnE2MqNZumgFk6wC4gDzoZcKbfRLfmrWR/Tw7mUoGsl0Ao5DzVueEXmUyThxcW8heUeVDanMmAIwLnxoULF55Nv4w+Wg71X85IgCE3AoEccjScPr8ZderZNCSNX3lo8ioKk28aNmzYhFz8cyYPPF/DHZAbRKPXmzNrK6B6qM0ygbxP6WCOtaUjWYNzgpH2fo/JVwDyYdxmUuH9zWZN3eXKePuYjGAWPDPH2tD0GhwZTLR7cIzS4Jp8cvY/QN6PIG/KAU0OVouMxtPBLh0MuTZ3rMFBPRzOjQT4aO1M/ZfTEmDIc8Emt5s0wDV+OydNY5oBR5ym2ktLS802m21kTs+sGlxQAoC8r4nMFZp9DQAGIct8m9EwstRuh0XCw9DGqAfceP/992MrGrYzKJcnEjgJOdjIUhcQBbcPHQx2wXRwIGyDM/XGs846S22PzdI57k23AXkJmbAnfNm5dwUAn1mkbe3+ohNwRAVYgwcBHzhwoLrA7A0pWVxWg5xe5Wo8qQCzZjQAuNRs7rKSwhocAwHsRrvdPhwnyuWnBAB5H9LkLZomzy4ZFJmNYFfjmHuu1+AGevdgMScqPz8loEGuafLsGr/ZYAC7bI3A1x6751EgwkhbZIvVGjiLJH99QF5Mmrw1SzQ5mKVFcAAOpa3BjdnjkyD1BDheMq6ckoDQNDntQsQSYjY4ghzsBllGWLbBMQaDAhxiUI4lYCLNWEzmiqbJM/zBIKvByIBz9zUNzica+QpwFofyWQIMObGe0c4kAgx4sKeswYMRCvCMnsO0dY4hb/P5M/YZT7NJ0+AsI41pXkVBJCKUicLiUX4XCQDyIhNWyYP6sEuedEZE0+DcLwU4S0L5YSXAkLdrmjxslrRFGmOxwal3bLakraOq4cyWACAv1DR5ZvWTVlHArmaJcM/YRAn+zfF6ve2cqHwlgUgSYMi7rDNTASYs1b7PH5DZ1Zjm/gXH4fP52oInKqAkEEUCgJz2YWeMRU6Xv3p2NZWO1c3gCqfS4FFmVCV1kQBD7qS3aKX7LXE+v1/W4BrXbKJwxwNKg7MolB+rBAC5jd69Ql5anS8goMGDyhqdkS8otQQFeFrnKGsb1zQ5Qa5p8jSNwm8ImihByMNq8EC6/9akSUCq2d5JAK+H0zR576rpUWkwSyuXETW4Zq9QzQFlg/dIvqpQpwQYche9vDvVb7X1BgRs8CDL6JKswbUEAry1s6/KUxLokQQAeQFtQUz1HU96FzrYZcC1vss2uBbhcDgaoO7VnvAeza0q1CmBDsiFcPlTIxJQ7aTXoetbYw3O1PsbGxv/oc+kzpUEeiKBk5q8J6XjL9Pk9YBd/KSYZ81EwQmcFllRUbFfXWR2CET933sJAHKrZq4k9w4nelrldOwnLwg3wqzBka4lvPbaawpwSEO5hEkgCHkS18kB72v1zXrAg+vgTL3/7bffbnG5XLVms3lgwkaoKsp7CQByC0nBo+nRxIvD7ffXrjve1EI1dzFR0FoQcGRobW2tUmYKxKJcIiWgQU6gJ1qRg9U2X6CK+gq4wwKOcQByLUN7e/s/EKGckkCiJQDI6fUOCd9x2O7zgVkZcK3rbIPLGtyHlRSlwRM9tao+loAMOcf1xge8TT4vAPfREaLB5XXwIOTHjh3b35sGVVklge4kAMhhqngTsC0E9dR6fGA2BG70IZwG97/xxhs7aNMVgFdOSSBpEsDNxA5zpXdWuY/MjVW1zTuoowA8BHIGHIMA0Ej00Udga5ubmyuVmQKxKJdMCQByE/ENfd6Tf6C2xR+ofPFITS31UzZRtG7rAWfIvfX19ZsU4MmcWlU3SyAIeQ8UOYCt93g3keelI0R7o34ZcJxrGpx8X2Vl5UYFOESiXCokAMgBI3lxHTDkqxyujVQU2ps1eLDLMuD4MbAG9y1ZsmSTn1wwpwooCSRZAgx5PM3Qg3L+JTX10OAMN3OsVRMOcO2XsGrVqkayw8uVFo9H3CpvbyXAkMNa6e7AQ6DNXl/5W8fqGyk7a/CIgKNvrMFhz3hpufBTBTjEolwqJQDIAXd3DrDWuj34+KvGK/lsgweLyhockQw4fg1essM3KMCDslKBFEqAIY+mxdGdynbPBvIAuGyiIElz8o0eRLB6xy/Bu2DBgk8vvfRSZ1FRkU3Lnaf/VRaVCM/xmpwffSRlBijCuUjxyBsxLUJCpMfbkB39AvD6/jn8fufjh46wBmftHdJCOMCRUbNnPvnkk5bq6uoPRo8efbmRnphOhGvbWiGO/c9fhaeuORHVpaQOt+8rwlmCb7uHyC6k7UgpUctEKBStTEijnSf6iZfzRGhCyxJvO3K96Q7T42mi2nnig21N5dhByPY3Qx7snh5wJEAmmgYn37Nt27a/nnbaaQkDfP+dTwp3dT3aySrn9HtEu9+r2YYQUCw2IgbIeRk0lOO4cOmIk12q88ttI8x9jdR/fX79eXfl9en68pHG7w34xW5nzV8pv4cOeQ08pIpwahltMuDeW2+99SPaPlsfTUuE1NjNSTbCjSEVGS2i0NihD2KFG+U4L3w5jDQ4OY7zyL6cRw7LeeSwnEcOy3nksJwHYb1DXjguw2EtMob/uiuvT+d22Ne3h3iw6Az46he37PyITiNeYKJsNMCh9j20s9Bx+PDhvyUKcDSarQ6Q2wnyaNf4nMa3nTFWjsuEcXNfYu1fsvP3RCbQwLU+598a3W4HBaHBw15gou5wgCMeGpy1uGft2rVv0OskEJ/3DpDbjCYNWoZE9iEghkIOy3nSGZb7JIcj9UnOI4cTlT9SPdHiAeZ2Z93r5MnmCaK7uEiA40eCXwWo9sydO3dXU1PTl0qLd8iPIe84U/+nUgJgsC3g+XJJ8+7d1C4DDlbBbBcX7iITmZCZIUclbloTX1VWVnYnfcMH6XnvADmcKwDZKpcqCUBN13jbVpHnpoPNE+a1SzciaXBkRF1BLb58+fI36GFkVKhcpwQ0Td7lS4xKPMmUgFv4PG+3HXmD2pC1d1jzBP2IBjh+FSgIM8W9dOnS6qqqqjfV/iuShuSwsmJTkEsSSV4Qa9+1Pseb77ZWVVMr0OBgE4yC1bAuVsA1M+XFF19cTFo84q8lbAt5EKkgT80kuwMB/7q2I4upNTZPegU4eh2ixefPn1958ODBd5QW7zqhDHm0q3+V1nMJkPIW9f72d149UVHZCXi3cGOWomlwpENbB80UCrteeeWVZz0eT8Q/CSiUr64DciwhnnQcjnbjArk5PV6fy3KL+va6q6+35fX1d1dfd+n6+vjcL/yBjx3Vz1J5Fx2yeRLVougOcPQHFeBiUzNTfvvb3+4lLf53pcUhmq4ON4IKDB2QY3Lg2JfDPHFyHMLxOq67p/X1try+v93V1126vj6cd9jezr+vaCrfS6dsnoDJqHCjbCyAQ1sHAaewa82aNc+QFkd55cJIQA85w5cKH91hiORwKtpOVhs+4nij89gzNB7W3oAPTHZrScQKOGtx/Hpcc+bM2X706NENSouTNCI4QG4lTZ5qx3AzbGif41Ldl0S0B+1d73dtWNy4ezvVx4Cz9k4I4Ogna3EY9oDcuXr16oVqRQWiiexOavKTiOEyC44vtzisRXbGcxznicfnsrHWp8/P5SL5+v531zd9/fry3aV7aOVkk+P4QsrnpAPsgcGYtDfli8lEQT4ADi3OgLtmz569jd4l/rraowLxRHY2TZPjY6kd/5CTJ1kOR0qPXHP4FK471vr0+blcJF/uc/gehMbq69eXj5buoy2xR31trz/duGMblWPtHdPqCfciFhOF87IW1y420eBdd921gB5MblR7VFhE4X2GPHyqig0ngY49J97GxU27FlA6wx2z7c11xgs42+Jo0Pnee+/Vbtq06U/qgpPFGdkH5FhdUS42CeD5qb2exj997qzFG6tgnoC5mG1vbiUewFFG1uIa5LNmzXqNHmvbri44WaSR/QLaZstLiJyLrXP4sRwox2XkMJeV4+Qwpyfal9uQw5HakfPIYTk/tHej37X9vuMbX6M8DHfc2hv19wRw1uLaxSY6sGLFikecTifegYg6lYsiAUCO1RWeUGRFOFbHeSOV7y491nZizRdve93lB0Nu+qD8O22HH6E+AG6+uIxbe2MM8QKOMgw4flGaFn/ooYd27927dyW9kRbpynUjgSDkeP+HOkJkEKBfwCF/68oXmvdgv7dee4O9uFxPAEcDDDlrcccdd9yxqK6urlqZKrHJH5BbeqRfYqs/G3NhzftEwF39ZNPORdR/PI6m195xD6ungLMtzsuGzs8++6z+pZdeuos+f0JLl8pUiWUmGHL82c73A69hcwm/5/3WQ3eVOxrw2gWGO651b73cewo46mHIg1qclg23EOiL6AaQvh11HkECgNysNDltdPKLfe6GRU837d5CotJr7x5rzN4CDlMlqMUp7Lz88suXHzhw4CN1AygC0WGi8x1y3NCp8To++lXN+uVgqPPgu5ZgLC2AY6rQOA6+4MQvr/3OO++8j+zxGmWPkzRidJq5YuiNvomxoQzLBru72e+pWdS46z7qWjsdYAgmAJhivijYM5cIiaITWD7BLw6dc9ANoGMvv/zy3WSP0zeGevzjo6ryy2H50EKQR7pNnmvx0MvugN/7vuPw3Vucx47RbGv8kA+WeFmwVxAkAnAQjM6wqaJ1kl41sfnTTz99Cva4gjz2OQLk+DBTrjswAbt7r6fhqacbdm6m8cpw9+rCUpZdIgBHfQx5iKkyY8aMJbQh6123Gz9I5WKVwElNnrurK16C+4i39d05NeuXkFz0pgkUZkL+9CcKcMwdQw6acaGATrfPnDnz13TR+ZmCnKQRh4OpYs5Rm9yjXVS2f3ZX3YZfk0g0TsjnZcGEwQ1xJxpw2R7XIKevRJy49tprZ9MDEuVqUxZEHrtjyHNpjRwrJvU+R/nDjZtn13scJ0gaAFyGGwwlRHtD0snY3obOyR00EOTe/fv3r582bdr0pmXv9MVXbpWLTQImklWHQGWRxlY203IB7kaf69CC5p037XDU4osCbXQw4DBvE6q9Mf5kAI564UJAJ1vc1dLSsnFUZctltKOuUEHeIaRY/gfkcBBotq6k+KnzJwKe+mUnym9c13roIA1FD3dCNTfkBZcswGV1Ewxv3bq1rcBk+Xycpd836c+vVUHeMQmx/M+yCgozlkIZkoe2mYrWgKf19ROVt/y55cty6pYebmhuAJ5wlyzA0VGeC/a1zm9z1jaVme17hluKL6HVAgtPXMJHloMVsqxCBJrh4+yA292+tv3Ifz7btAu34WW4E7beHUkMyQRcbhNzEpyXTY5jx+kJly2jrSXTSZPbeeLkAiocXgIsq6Aww2fLiFjY3Cf8nsbX2/bf9mzjLqx1M9xY80463BBCqgEPgr7VWdvQbvCuH28tnUo2eR+eOHRKuegSCMqKTHOY55l44F0mDQHn0eXNX9z8yomKPTSiVjoAOMONmzlJsbup3qBLFeBokJVOEPJyV2PLUW/738+2DbjAZjCV8cVUsHcqEFECgDwoyIi50pOAde46n6NiYePuW9a2HfgH9QJgpxxujD6VgKM9OJ4XzT/gOeHY7W5Yd65t0CS70TRE24nRuWrQkV39H0kCDHmmrK1gZnH7/ZjXsfWRhs23b3HUHKW+682SlGhullmqAQ+Bmzqhndd6He5PHDUfTC48ZXShwXyagpynp3ufzRUIMp0OuwLpWUq6/d72wd21G+fsdzfVUX8YbtzIwQ7BlMINeaQacLQJ1wX0Fr/b9zfnwQ/PKxhUUGKyTjIJo4Enr6OI+j+SBGQ5YcU81Qfgdga8gQpP0/O/qP/4d41eB77yK8ONC8qUww15pQtwtM2Q84VGwEsbyN9srdo60FRYPsRin2wxmOzYS4AHc5WLLoGT5kr0fIlMxY5AvL+k2e9ufK/98Lz7aje9SnPIF5PyHcq0wI2xphNwtM+Qww+CvsFRfaTa2/beuILSM+0G0ynKZIGounephJxNkhpf+7aFjTtvp5WSHdRDaG3W3DBJ5KVAzHHKXaYADrj50KCv8rS0rXFUvXtOwSBTX5P1K8pkiY0NNleSSRNu3sAkKfc0L7+j9sMH97ua8fYpaGyGW795KrbOJyFXugHHkHgu4DPkmjanP3e+Na1V2waa7buHmAsn0/ZRu7YXQ5ksUVE4adIlducKcU0mCW7euBvWOo7c/UDtxr90miQMN9a4+WIyabffow5el5gJgKNLMuQy6Fp4g+PY0cNksoyylgymz4SM7nioS9nmurkMOT0JeUh0j05ga/toiuj78OKQr/W9RY3b57x64stdVBlrbACuv4EDJZV2l2lXb+gPrivxw8OXVgvosNFhp6MQ4TvKzvnni+yn/rLUaB2BJ1/4TzKlKRdGArCVe+PY1m70uw9+7Kz+wx/rt26g+mCCMNS8BMjbXbW/vr1pM5FlMw1wjA19wgHI8SVmKx2AnEG39zfbiu8vu+CasdZ+19HHWAvM2ESqzBYSUXgHDRwv5rxC0ub3uCrI1n6w/tMXGr1OrJBAU+MA2GxrY5WE93LH2xQVTZ7LFBNFP0IWEnwc0Aps0/mcfq/3rbYDO+r9zrUjLMVDaePWCGW26EV48px//Kw5ovl4wxSbI/Ty+Y+fa97zq0WNO9aRzOXlP2hvGW7MT0aYJCdH3RHCWDPZoX9ssrA2Z40Os0XT7HMGnP/1C2yDbulrtI7E64nx7lae1EweXKr7Bq0cybGd7SI7m9a1D3zmqln0WN3nH1J+1tSstbH0xzduWOlErjhSgymKz3TAWQyAnEGHbQ7I2T7XIDebzba7+p1z6STbgGv7GwvGKNBZdNF9GWx6J/eXO5x1z/++aetaejMZg80+r2vLtnZGam15xNkCOPrM2hygQ5sz6ACcYYdf8Kuy8y86zz7g2jKj7SwFOkkkjJPBJlNv9xZH3fOP12/+iLICZBwAm31obIDNdyQBdsZqbepb0GUT4Nxp9BnXDgw6tDlrdA1wOtf828rO+afJtkHXDTLZz7XiNQxUBIXz1XwB1KASa9n0Rilx3Of4fJPz+PKF9Vs/o2gGW/ZlcwRQ84UkBbPDZSPgkKzGKfmy2QKNzkuLMujWG0rPmnRhwZBvDzbbp9sN5kLAni8XpQy1n9AG1I6At51edPnBRlf1G4sbduP2OiCWoUYY2poP2c7OCq1NfQ+6bAWcB4D+A3IGnU0XBp01u6blh5qLi27od8b0Mdb+3xxosp9PoJvwch3Anmvr6Vi/BtRegprA9tX6HJu/dDe+vbhp7wf0RincoGHtDKAZcoaal/2gtbPGHKG+dnHZDjgPSA86TBi20dmEYV/T9FMKTx00q3j0ZSOsfb5ZYrCO0UyYLNbssqbuhBpfS/jyoLvl7f9r3f/O+vajx0kmDDDDLftIg7ZmjZ3VYNM4NJcrgMvjgTbHuAA5NDprdQDO0Mu++Yf9xo2dXDB4Kmn1c/uZCibShWkBPi+CR+gy1ZSRgcbmJzxJQ0t8riafaxdp6883uWo+Xtm0r4LGDG0MeAFzOB/prLFhguQE2DQOzeUa4PK4WKsDdD4Ath54TaNTvJbWz2wr+FHfsRMnWErPG2iyndvfVHAGwW7BBSqA7/jX0QwLL1kXrYAYjg1f+LhMBNC4UCSoPfSmqL21Pufn5Z6GLS83V+xq8jphcjDUAJoPBpzTWFuzxu6ongrkkuM5yqUxyWPB+Bh0va0ua3eGnOPYNw21FRX+oHDcV06zlpxdQvtfCg2mEYVGy1CrMNpZw7Mvwy93AmG9oBlafT6GGPYzQGbfLfyOdr/nSHvAd5B28x2scp/Y+Wr7vu1HnG24qwhIWROzz2DD5zj4DLRsX0fqDmXPbqeXe3aPJnrvGXT2WavLpgyDrfflvNoP5eLiYYMmWctGDjEVj+hrtowsMliG01cayugppEK6k2qnbWCFlNGMxhh81vRsXkAbgywizUuvWWinW+QOT8DX7vL76tsCnkPNXs+Bal/rwR3u+gPrWg/DhmYoZe0rwxsuLOdl84P96BLLgdR8Apynq4O5DqWKMOAFtLIPwBlqhp3P9Xk14DvrCKmbzB3zSGtx0RBjob2fuaCoj8GKHZGiJeBub/K62qr97Y4D7tY2MisAZofyPukzzPBZ68o+wgw0Q4xzOQ+X1ddN2fLD5SPg8syGAEkJMqx6kGXA9WlcDvUhzPWiLTmMc9kxeIjjMGtXBlwPLEPLUPM552ef62NfbjdvwhC+ch0SYFkwkPD14PI5QwzokY99jg9XF1rheEAHx/DpzwEp4gCx7DO87CNNDuMcBxz7HWd5+j8LPE+HH3XYLBv40Q4Zan0+NCDXIzfIAMo+wvqDYdfHy+eoF+fK6STAwtdFq9MIEmB5McjIFi1OTo9QZQiYDCnDizLR4iLVqeI7JcCTowTSOwno5ag/R+36OAZXblkfpz+X86pwDBL4fwN/IZwMBwH5AAAAAElFTkSuQmCC',yellow:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAC4CAYAAAChOH1KAAAlaElEQVR4Ae2dCZhUxbXHTy+zL8ywDDsSVhEVJQoCkoSIIr4kvohLxO2ZfC8an0mQrCQm+uJ7qHkv5hE/xSQaNokBogkxigaUuLDIpsiOMA4MOwyz7zPd7/yLOZfqnu7p7umeXut83+2qW7du3apTv3v63Lr31rWRkUhowOZViPc6Nutpbq/8WPVO8173sYtJ6kgDusI7yme2nYdTdIZQj0NH1vrgwYNtc+bMyb344otzCgsL87KysnLT0tKym5ub6+rr62vKy8urd+7cWfv444/XlJSUAGSB2VfoKw3HM9KBBqQzOsiSspsEXgmhCMTtDGT2TTfdNDwvL28kQzvC6XSOcDgcQ2w2Wzfenme323M5nuN2uwPql/O5OF+dy+Wq4X2rOV7Z2tpa3NLSsp9Pgv3V1dX7XnnllU/4RKnj7S5edNARl4WjRnQNBFS+njnJ4wKxHtp37NgxpG/fvlPY6l7CAI/kZTgv/YMBN1L64hPAzcAf5eUTXvax9d9x/PjxtZdcckkxH0OAF8gljNThE7acVIdbQLZzDyJuX7du3YChQ4dOycnJ+QJb5M+zFR4Qr73L1v4IW/h3amtr/3nw4MG1kyZNOsJ1FdglBOwpKakItwfQTz31VN4dd9wxnd2LL7J1/hzDPCxRSWDYD7BVf5fdmbeXLl26avbs2dXcFsCdkqCnCtwCNEL7gAEDnBs2bPh8QUHBnenp6TdyWi4vySY1TU1NKysqKl6cMGHCO0eOHGnhBuqQJ71FT3a4FczcqQgdu3btGtWvX787MzMzv8YWun+y0eyvPWzRjzY0NPzp2LFjL44ePXoP52vlRbfo/nZN6PRkhdvyoX/7299245GNe9iHvoMvBC9P6N6KQOX5gvRD9tGX8gjMovvuu6+SixRrjjCpJJngRluwAGz78uXLu0+dOvXbDPW3eL2QFyOeGihnyOevWbPm6VtvvfUsbwLcAnpSuCzJALcH1KtXr+4zduzYWbm5uf/OnZWMvrQnouGv1bD8ftu2bf937bXXnmgDPCkgT3S4lZXmDrGvX79+0EUXXTSbRz3u5fXM8Ps85Upo4FGWBbt3735q4sSJh7n1YskRJqQkKtyoN8B2vPzyyz2uueaaX2RnZ9/NN1bSE7IX4qjSfMOoqa6ubvFbb7318xkzZpRx1XDxKZY8jmoauCqJBjfqi8WB4bzNmzf/W/fu3R/j9R6Bm5o6OWyuErK1fMiKwuhfJ8VNZWUVtT/77MTHFrYNI8oIS8L444kEt7ggju3bt182bNiweXwHcXwnuy5pdwPYzrofMdiR8SaaW9wffFLc+N3RE/7xEStNrHhkCu/iXkgEuFFHBfb8+fMLb7vttkf4YvGb7II4u1g3CVm8vXEpORtfiGzdbbaWmpqW3724ou4/v/WDj8u5cMAd965KvMMt1tp56NCh24qKip7gmy99IttzyVWao3ERYekKcbnpxKmy5h/3HbVpGZcvdzzj1ooDnngUnHQOLI8++mhBZWXlC3369FlowI5tV9lt1KdPz7SFdaUTXnj00REF0kccxqWRjMdKWWBv2rTpUn7YfwnfWRwZ225NnKN3peXWtdDion0799TedfkXPvqY0+GLywWnni2m8Xiz3KgPLLbz6NGj3xgzZsw7BuyY8uH34E47jRxzUc47J/eN/wb6ixf0W1zxFC+VEWvtnDt3biE/ybagZ8+ez7CysngxEqcasNkoq6i785m6w1ctmDt3GB5xEMjjwiOIh0qgDjjJMG49hp9ae5Gt9fA47c+4r1a03BJvRbS43J/s3FN3R5ubIhebMR0Tj7XlFoudtm/fvmsuvfTSNQZsb2wSY91ptw0fMzrnrYObr7iGa5zGC9yUmBrPWMItYGOY7xZ+W/wvrIw8XowkqAa4Q/M+MzjjL0d2jL+FmxBzFyVWcOO4OLPT+AH6b/ELuAs5bp4LYSUkujDg6f37Ohee3ncVHjUWCx4TzmJxUAE7/eTJk4/06NHjKb7bGIt6JDpH8Vt/N9l7dnc8dfaTcY9wJWG0YjKSEm2oFNh80ZhRVlb2NL/D+KP47SFTs3A1UFiQ9qOakglPjx7dKyMWgEcTbgX2+PHjs3j6hCX8fMjXw1We2T/+NZCTY//65jeGLRk/vjuGdaNqwaN1NavA5salnz179jl+9evO+O+WxKxhrIYCA2mrtq71xdwLNt7P+Zp4kacLA+0W1vZoWG4BO4197McM2GH1V8LunJPtuLP84FWPcQOidpHZ1XCjfCxppaWl32Ef+6GE7R1T8bA1UJDveOjUnvHfAQ+8CBthl+uvgK6EGy6PAru4uPj23r17z/VXCZOeOhro1cs5t3T7uNu5xQJ4l7nGXQU3KqzGsfmF0+v79+8/P5oTR6YOKgnYUjfZ+vdLm7/vg7HXtwEOTroE8K6AWyy2kx9ZHTdkyJAlbY1IwJ4wVe4KDTAgacMHZy3Z9vbl47h83MkEhxEHvCvgRpnOefPmFfF49lLMU83rRowGPDRgs1POpRdlLZ33xJAi3iCAe+QJdyXSZ4sCmyuVwY+truA5RKaFW0Gzf2gaiNehQH+tqKt3vZkzaAOeRWnkRZ4m9Jc9pPRIWm6cKMrPPnz48HcN2CH1Q8pmzs6yTzux+8rvsgJkiDBiBjdScAvY8LOv4hd58UyBEaOBoDRQ1DPtEfa/r+LMEX2SMFJwoxzHE0880Yv97AXsZ6OSRowGgtIAeLl0VNaCJx4d0ot3wL9/RLiMxF8AKgKY4WcvY3dkOseNxEgDieZz62qqq29dlTNo422cFhH/O9wzBCeHgptfOHjAgK13lYmHqoHsLMf0ozvHPcD7yehJWMY3HLgtsBcvXnwB34F8ONTGmPxGA94a6FuU9vDiZy6+gNPDBjxcuNXoyA033PA4+01mLmzvnjLrIWuA36jP/dcb8h7nHcMePeks3JbV3rp167X8sVF8NMmI0UBENJCXa7/xo3fGXsuFhWW9Ows39nNed911uRdeeOEvI9IiU4jRgKaBi0Zk/vK663rDGxDAta3BRTsDt2W1n3/++Vk8jfDQ4A5lchkNBK+BNKdt6OJfD5rFewjcIV9chgq3BTZ/UGkY36wxz2cH318mZ4ga4Js7Dy1fMHpYZwHvDNzqIpI/1fEkX0Sab8+E2GEme/AasNltmdO/kP8k79Gpi8tQ4Las9rvvvjuBXxe7LvhqmpxGA53TQE6O7bp1r4+RW/PgNWj3JFS4ldXmW+zfY6vdudqavYwGQtAAOONb89/nXUK23sHCbVnt119//TKelmFqCPUzWY0GwtJAbq596j9eueQyLiSki8tQ4IbVdl555ZWzOTRmO6zuMjuHpgGbbfxlOeAOcIPDoPgLBm7LavM3H0fl5+d/KbSKmdxGA+FrID/P/qWXXxw1iksK2noHC7ey2pMnT8bQXzD7hN8aU4LRgIcGbPYpV3UDf0Fb70CgitV2LFq0aAhb7RkexzMrRgNR1ADPezJj0fyLhvAhYWzBbofuSbBwO6dOnfogX7miUCNGAzHRAA+cOP5lSt6DfPCgXJNg4HawO5LDs0XdFJMWmYMaDWgaKChw3DR5ck/MqBDwwrIjuMUlwTQN0/lzHvjuoBGjgZhqwG6ngmfnDsTbXgGtd0dwY5u6kBwwYMCt5qZNTPvUHLxNA+BwYP/0W3lVLiz9MuxvA6w2FsecOXN68fPaX2wr2wRGAzHXQF6O44tzZlsvEwur7erVEdzY5rz77rtv5s9S49anEaOBuNCA3W5L+/rt3W/mynTomnQEt3JJ+JvrmA3IiNFAXGmgX1FawC+m+YJbzLxjyZIlI/mNdtzTN2I0EFcayMqyXfbS7y4cyZWSURNw6yH+4MYOjokTJ95iLiQ99BX3K271Tx331Qy7guBy0vg8WG/FKoft4IbP4i3IpPztwsLCz3tvNOvxrYEW23iqKPs9VxKfnUlc4fncPSrvtUpYb2lygU+/frc33JZLcs899xSwS3KpxxFSdMVWv4dsle+Qzd0c9xqAGevm/AJVVBSTy+ViCDwhQQN8JLVLc1P7/bz39VXOuTye+/rK5zvNcz+U1ZG4XO5Lb5teWbBs1QHMUCXsWoV4w42yYLUd99133yQ2/dBVSoutbhc5997MmkscS4hOK2hxU1mlb7h9daj3f7r3uq99Yp3GJ67jnqktk5atopVcF3Dr0UnecKNNCu5+/fpNNv42m4PyN8jWdDTW/Rjy8TF22yPLTWeriFyWLQu5mLjeAbD26eaezMHfeQG3SLJaiwRdsK7g5icAJ+kbUjVuc+OziYkpPD0Cdc/nDk0EM9xJFedn2ybyrvizEnatknS4oQIsjlmzZvXMzs6+0MplIgmrAQHcwT0tHZxMYXaGe9QDX03vCW55kaap/vIFt33mzJlXt2VUmcxPYmsAgBfmsWkD4Nz9SbbYvnq1G7yC5Q7hRgYH35W82vjbiQ20d+11wL23JfI6OO1TSIBbXBMArsTbciu4eU4Sc1dSNJREoQKcZ9+DBY+U4F8AIv8GEleJUfrJyiTw2g5uGS0R2hXcPL79mSjVyxwmyhpwwkXJdVNFTeRGUQRwNEXiEkajedkZBF4FblUN/nHr5zDi9p/85Cd92NSzh2YkWTUAwAtgwcWkJXhD+UTKm3VLWh9uhmJYmiNwo5lY7Pw8yXDZaMLk1YAArkZRuOdhaRN5mXSxDdyCZ2FZrUgPKrj55s1QczEpKknuEIB347cRYcGFiEQMUf++3V1DubcEbtVx7Sw3v3UzLLm71LRO14AADqudyJKbaQO3ArdqjQ434naeB3CIsdyJ3M2h110Aj+QoSui16Pwe4DUnm4ZwCYphDhXcGC2Rcxahg0dKkMlIimkAgOdnu6mqzvdTg/Gujqx0G7jFiInFM0iHIME+atSo9MzMzAEqxfyknAbOAc4gMBWJdnGZke4eMOozmengmBcFuA63bdq0aYV4jDDletU02NIAAM/LOge3lZgYEceUMa2FXFWAbcEtKzaen4RHP42kugYE8M6Mg8uFqVh+6FLSfOlVtnU2v+yHcFCRG/xaPOt3KG29evUyN2989UAKpgHwXH4evKZee0A6SD0IsMgucQl9FaFvk7iEgfLr27vnucGvwK38E9lu42FAY7lFGyYkZcGz2YkFLgkg+TkOsdyqtjJaomjnZ7gN3AnQidGsosOhWXDrHZdo1iC4YwHgzEzfbglKsBm4g1NkquUSwGsb4neYEG5MTjp5WG6P0RIeBswxN3BSDd3g2gvAc/irox35wsGV1DW5UK/0DDemNlZeCI5ijQkikT91jY1GjAZ8asAX4AI7Qj3us4BOJOpl6nFfRaU77AI3Ntv00RK+gDBw+1KaSTuvgXOAu6mOZwqRuUcEOuSSuITn9+x8TC9L4hLqpTqdynIjCdbbc+6t1tbWFiQaMRroSAMAnF/MpXoA3lHGKG9rddk8+BWfG9WwNTU11fqaoSjKdTSHSwANAPCsjDYTGQf1xb9IYzPVclWU1UaVdLipoaEBG40YDQSlAR1wuYrzDlGQRZuPUmWb937+1r3L0/fnuQM9+NXhdhu4fWjfJHWoAQtwocwrtyQHC6vX7u1WvctDBkmrb7YBbstTErhVQl1dHb82asRoIDQNAHA8j+frIi+0ksLLzRe5wq/iGaMlQrq7oqLCw6yHdyizdypp4JwFd1MDzz4noyjRbD9OrMpaD8vtlqFAAO4uLy8X8qNZL3OsJNEAf6uGLfg5wKPdJMBdXuMCv4plHF/cElWX06dPV5vREqUK89NJDZwDPPouCv4tTpVTtV5tgVvRvnv37hoDt64eE++MBgB4Bs+hDGvqvaA8pIlIXPIhXdIkjx7KNskvIa4q9xyyA24Py40ViHvlypXVPNbNMzobMRoITwMW4F7FeMOJzZKmxwVa71DPg7hIczNVvba+SdwSJFszTgntbh4xKTHWW1RmwnA0AMDTYcG5kK5ccAXLIyUlfBiLY9Rb3BLEscFVW1tbghUjRgOR0IAFuOaKRKJc7zJqG2wlnObiRTwRBbfQjg0uHg781Fhu1oSRiGkAgKfxuJy3ixGpdVS0qtb9KQeKYQ4V02K5BXA3j5gUI7MRo4FIakAAj2SZelmnKuggr1scYxvg1hNaecTkoLHcUI2RSGtAAI+UxZZyUM89h1wwyviamcWzWG5sVyZ94cKFn/L3CxE3YjQQcQ0AcCfPjAMwIyVMq2vhasenXJ5iWMoVuIV2165duxp4xOSYZDCh0UCkNaADLtY3nLC+yXZs14EmfsPTuqAEz9ZoiQU3p7XW1NQY1wTaMdJlGsC7urDg4Qpc6JoGN/xtuCSw3MKyB9xi0l0nTpzYbPzucNVu9g+kAQE8lDFwlOnh0TDKJ8tsmznZ4pfjHpYb+yABGVq3bNmywbjdUImRrtYAAHeE4IML2HJC4OvIW/e3buB66pZbVbudz41MP/3pT3fziwvmNnxX96wpX2kAgHd2ZtnGFqr68QuO3eCWF59uCQ5iWe7q6uqms2fPbjWuCdRiJBoaEMBDORb4LKugLYwrvmGuw62KEcuNFQtujrccO3bsAwO30pH5iZIGBPBgR05QrWNltk0c4K33gHADcGRq2bBhw3rjd7MmjERVAwAccAcj8LfX7Wxdz3kFbvCLRYleDOIYnOEX9tWca93OnDmznmd+7aFypuiP48jjhMVIdDUQjNdQVecuK7iheSLXrJIXPO7KM6ko46wAl9fMOE0Rj0Q1YsJhC8O9mT+Vfb09Ub8EhFaFKc2taVReYRmDMEszu4eigY4Ad7HZPnyKMAQoVtvjYhLH0eHGusCNHVr27du3euDAgRGF21axm2zH1pLN3Yzjxb3YG89Qel02PzIM3bUXf9jjtSdf4i8def3s4veFW39l+StHHaODjaGW5zd/R8fw08pQy2ppddOuva2rGVPFKrdN4EYzlehuCRKwDuB5Pk/KGzlyZM/169e/z5/vi8gXFwC28x9fZrDh1ieOVNW7cBcscSqcAjWtbXRXf/l/K6/ed6rpDDcXr5fh9jtAtzrKl+XGRtDXzJa77siRI6tHjBhxUyRcE9vhV8lWe4SLTizJR3XZLtRiwMlIzDWAx/qOn6HV+04Rf1iQ4AKAV3Brgc1x6/Y74iIw71hwFjTxqMlKniBTtoUV2lyJS0c+f+GLJzc3EgcaYI+EthyilVwVAAVOhVmP2unj3LJBLLfyZe6///5NVVVVRzty7mXHZA8BeC6PJcF3M0tsdADbzF94OPq9P5OMb4NTsdweCPqDWwCHyW8uKSl5zcB9Tm95fDWSg8FSIzHRAC48SyvoNT64YpNDARvMeogvuJEBZh474axo5ikf/trM784bOacBATzYO2kmH1t5/quLxNLCCK/aSX8Fl7yI1Qav7cQf3DgLLL/7ySefLC4rK9turPd5/QHwbOODn1dIFGKw2uW1tP3/3qZiPpzub7ez2qhOMHDj7GjasWPHSy1qSBG7GYEGlAVnwI3/HR0dtLK53XWCXmLVC9hgE0Y4JLg5v9oBO8L8N82cOfM1nvah1FhvqOa85BoLfl4ZXRiD1a6sp9L7lil/G3CDS79goyr+LDe24Wyw/G5+9axh+/btf4jUsCAOkCwigEfCpzRl+PbN8ZDUzhP0h5oadbNG97d9Wm2wFQhuAVxZ729+85t/raysPGmsd/vTEoBn8dRhRiKvAWW1G+jk7OXqQlKstt9REqlBR3AjD8w+CgHcjUePHq3duXPnImO9WRs+xFhw31Y33H8jWO29J2jR0Qr1QSc8+QcewSX49CuB4IblRgHqopLDxm9/+9sr+E2dMmO9fesUY+DGgvvWTWdSYbVrmqjsxytpBfjjRS4mO/S3caxAcCOPWG8FOD9vUrVnz54XjfWGanyLAG5GUcIfRcFzJPtP0ov7jhPe6RWwA1pt9EwwcIv1Vn4379M4Z86cl9h6VxrrDRX6FgW4GQf3rZwgU2G1qxup8ud/V8N/YrXBYUCrjUMEAzfyifVWvvfGjRvLN2/e/LS5awnV+Bfc5MnCOHiE7s6lWjktTN22Unp6awmVs5aD9rWlR4KF29t6N8yYMWMFT96z07xnKar0HQLwTDOK4ls5HaTCHTlVTTvvWqR8bTyrLaMkQVltFB0s3MjrYb358yL1y5Ytm8vzm7iMewL1+BcB3PjgwfnguMPC85G4Xt5Gc3nShnrWbMhWG70RCtztrPfDDz/88f79+/9sLi79gy1bBHBZN6F/DeA2+4Ez9OdfvE4fc65OWW2UHgrcyC/WG38ROJsavv/97/+Gb8ufNdYb6ulY4H/DRUk13zmU9kKDlY109sd/pd9wFGDLhWRQIyTYXyRUuGG9cRAMC+Kg9e+9914ZX2D+mt0UXjUSSAMAPMP75b5AO6XQ9iama0sJ/XrjQSrjZotLAt7AHfgLWkKFGwUL4GrkhNfr+eJyJd+93Gbck+D0LoCHYtFSIS/uRJ6oom23v6BeIROwwVnIYKMnOgs33BPrriXHG+bNm/cIv45WY0ZPoNbAIoAHznk+By5IIXJhKnGV2JYuaXpe2R4o1PfR4/720/PocX/5O0rH6EhlA9U88096hPPp7gg4A28hWW0cqzNwYz/xvS3r/dxzzx1cvXr1L3j0hOfZCLkeKDPlBP43XBSAEcwCBQlEelz21dP0uGwPFOr76HF/++l59Li//P7S20ZHaO0++sXv31cfbvK22uAtZOks3DgQDijWG2da/V133fXm3r17l5ubO1BPcALA01PcB29mp4OnaVj+jcX0JmsNYMsIiVjt4JTplSscuGGeAbhYb1So7pZbbvnV8ePH9xr/20vTHaxaFpxNWyr41nob4WefrKG9dy6kX4EfXsQlAVfgq9NuQAS+SsKHPy829rt5gquWDydMmPCVjIyMdMzaKWI/8S5hMdJeA+r7MNyN6GwR0Zy/v/NIp+O4ckw9LsfR0/S4bA81xAx1VQ1U+8s19K1/7qVjXGYtL7Dc8oCUpg0cMTQJx3LjSDi4t3tSN3/+fPjfjxn/O7TOyGAXJQ2f0ODdsEAkPLfWtb9yLH/HD7Q9lNrhsqyBnY639tJjv39X+dlitQXssKw26hIu3ChDABf3BGdeHfvfq/jR2BVm/BsqCl4E8OD3SMycGM/ec4JW3LuYVnELADa4wb2TsN0RLkNJJOBGQTjLMBbpAfj06dOfLC4uft8ADhUFLwAcF5m6b5pMcVxAlpyl9298jp5krXiDDY7AU9gSKbhREVhwffSkjt+3rLn55pt/WFpa+rEZQQmtrwA3XJRkE4B9pJw+/trz9MPKOjVhvLc7Ao4iIpGGW/xv/L2o0ZMDBw5U8HyDD/HjscUteEDXSNAaEMCTxWrjgaiT1VT84Ev00IFTVMGKELDBiwz7RQzurrYNqqKHDh1q4tvzGyZfmHVNbsUHufz5byNBasDRZn7kvpiuOokj9LXgEJJHj/vKK/kk7Ex+7KOLlIUQdyBP19CJOa/Q/W/soSOchJERwB1RP5vLs6Qr4PZ55vHFZb2r1b1lXNHRa/muXKYB3OqDgBFfgOvg+CtAz6PHuyo/jqEvOA7WYbF5GrSKX71NDyzaQAc4SYb88O+O67SI+dlcliVdATcKB+CyyMFsH+w6WsnTAO+8pD9dx4CnGcBFNYFDAVwfBw+8V+xzAGyeKar+D+vpu798k7ZzjWCtxR3Rh/0iXtmugluvqECO0MbPD5zpmUs7RvamKQx4Rgp/S0rXUVBxAVwpkk1ivPviAPtsPVUv2Uizfvaq+jgToBarDbBhsdGcLpFowo0GqIas3kOnud0fjBlAk/nWc450Wpe0MMkKFV3BB9ddgHiLtzC27GOfmvcWPTj3DfqIuwFQY4ErolvshIab22KdnWiIasyGYqrkZwreu/ICmsivYBVIpyGzkY41IP92cpHZce7ob23icY/jVXToZ3+jB55fR59wDfCNSN1iR3xkxFcro2G55bhyhlqAf3yEanccp7WfG05j+XszRQAcf7VGAmsgHgHHyQawedqzXQ8up/949WNrVEQHO2J3IANpKZpwS10EbhV+eoYaV++lt68dRaPyM2mAAVzUFDgUwJEz1v436oBb6p+W0cZbX6CHNn1KpzkJUMPP1h+GYo/U+ifnaNdJtOHWwZZGus/UUMufPqS114+mfvkZNNxpLHjQPS6Ax9JFwRh2Hdtjnqzy9WnP0k9Ky9QNmpiCDQVGG27pNB1yxF31jeT63Xu07ooLqLx3N7oy3UFO6TjZyYS+NaAPqUbbgquhvkZq5FGwX13/ND3L/YgPnsLH9jXch76OmsQKbjRQQd0WWrAv30r7bXbaOLIPXZHppG7GTQmOBQ/AeZeuHj1B7/HEOXSikg4/vZZmzV5Ba/mwsNbiX8uoiNygQR9HVWIJtzQUjYaLYrkp6w5QOfvhb04aSn3YDx9m3BRRVcehAN7VFCk3hAfz9p6kN29fQD9Y+REd4poJ2GKx9TuPXV0ln4qJNdxotPeiQD9dTc3sprw39gI60zufxsFNkb9cny0xiUoDMtrUFTTBr1e30huo8e399D/TfkPPcj9V8oF1/1qeFRGLHbOeiTXcaLj0g1hvPXSv2EqfuG20bngRXc43fAod/H8rHRgzrcX5gS39tOlKjEI4IcDGmzPHKql43ts0i7/g+y6rQe44yoiIgC19GFNNwTWLF0FdcLJh4cf1Cd/p5Q9SqyW7ex7lLL2X7vjsILq3WyZ/vIBzWZ3ImYy01wDch3AFUOMZbJ5TpH7rYVrAs64uPVvtYan1N2hgrbGIwQr38GHtHw+WW28AlCKLnP0IW+ubyLX0A9pxqIzWXNib+vLk7oPlYtNArqvwfDwcvYgLUsu2eP9pemfOSvrBwyvpHe4HuCAyGgKwvZ/siwuwoYV4styojwieYsaCGT14dj1lxfl7YZYlz3j2drr6Xy6m2T3zqD+PqpAZNmTt+BGAiiVYgcWHC8L3H46+toOeenAZvc/7wuUAzAI01vVnRCLwP8ElRlDiFW40EXUTwAVyAG4tA3tR3oI76J4xA+mOvHTKgKtiIIfq2kswcANquCDVTdTIj0YsvXcpLSo9rcatYZ31RaDmU8Aa5Wp/0BinxDPcUA3qhwXuEwCHLw5LbgGO+MyraNCDk+nuEb1pOrsr6TyyYiBnxXiLP8ABNW6dswvStP8UrXrmPVr8x410mPfXgUYcUGOID1CLbx3CfwLvFUWJd7hFFagnrLhALq4KLjoF9IyvXkb9Zk+lmSOK6Cv8XfZMfl7cQC4a9BECatyIqW6gBob6b0+toT/+5SM1OQ5cDgEbcd0FAdRwQeIWaq6bkkSBG5VFXQVyWHFxVQC4QK7iU4ZTr4e/RLeN7kdfzcugXAU57xnOBRYfIykE1htv8yioG6lm1zH6y3/9nZat/UQ96CQgA2yJ+3JB4h5sdFYiwS1wCeDe/jjAFosOa57+2c9Q4X9/mW4Z3Zdm8J3OQszJJ3c7Uwl0AI0Fkw80sFPBU5iV7zpOL//0VVqx9VP1pTAALEAjrltq8asTwlpz3S1JRLil8gI5XBUs8MdlfFwgV8AX5lHW4zfSxCsG0vUDu9NEfnY8Xax5Ml+Awu0QK13bRE2lZ2n9llJ6g4f11pdXW4+h6hYacfjUcus8YVwQrnM7SWS40RjUXyAXn1wgB+ACucTTxw2mbj+cRlNH9aZp/QroYobcpmZ3QkFcUiJbdLHQ8Bnw0gC7Hu5jFbRzDz8Dwi/nrtlUom6VwzLLIhYa6zrUsNJiqRPCBeH6tpNEh1sa5AtyfXQFcAN6gVydAHdNoAF3j6PpQ3rQlIIcGoxRFoCubg5x5niHXYcZz3wAaIx6VNRSSXEZrV28iVYt2UBHuCkCrkCNdT0O10OsdMJDzW1Rkixw6+0R0MVd0V0WuQgV0MXKO798CRV9bRxdMbQHje3TjS7n0Za+cF0wdq7DjgPFwroDZIgCmkPAjDHpttGO4/zo6YcHy2jbnzbRlld30CnOAmB1qAVoPR1Ay4IjyMLRxJdkg1vvEbQNroq4KwI7ABeovUNsQz7nzHHU78YxdMWQnjSWn0q8LDuNemIObVyQAnbrwpQzQ3Tg9fi5rYF/BV7klLgijX9wIQiYEeKtcn7r5czJKvqo+AxtW7mdtvxxkxq+E+urwytwSyh5BGgu0XI/AlcywXIkM9zSFWLJBXSEFsQcB+BYF+glriBvS7dPGU6FUy+iwcOKaFBRPg3qlkUD89NpIFv4fmzdnQAez1OLK6POLK91bBPLq0IGFxd8AjHSsN5mlVt4/PlYVROV8qQ2paeq6DDPr3d4zW4q4WE7fAsdYAJWAVbiANk7DpiRJjAjVIflMGkFfZAqgrbqC+AF6Ahl8QU20mS7vo86WXIzyfm1K6jfZQNpQEEm5fP0w1lZaZTNw47Z7L9ns4XPZl8+i61+NsOdwQA3svWtY9+4ni1xHfvJdTw8V1fPS2Mz1Vc0UNVHpXTkT1voWE2DB5AAFFCK1RVgBWR9Xc8j+wjMEnJRyS2pBLfekzrkiCtQOdThFaB9wS0nhYRShne5so5jIy4CwCACmncolhWQCpwIJS7wAmyJ+8rrXS5nTx3RFZ46rfZsqQCohwK7Hgr4HaXpZUgcR9PjWBfo9LikIRSQBWZ93V8a0vUyJI5jpKRA6UY8NSAgeoeAGmmBQu/9UDrSvAXwQQRCPRRQA4X6PhI/V6r59al0oxZPDQisSPUVlzQ9lLx6iLi3AEiIHgqk3qHk886rCjA/7TWADjESugZ0vUncO5RSJV3W9VBAlTRZ9w6xXdIkrwkDaKAjxQfY1WwOoIFQdGvADaDMzmz+f6SMYEX4z7hMAAAAAElFTkSuQmCC'};return{FaviconsByHue,};});'use strict';Polymer({is:'tr-ui-b-info-bar-group',ready(){this.messages_=[];},clearMessages(){this.messages_=[];this.updateContents_();},addMessage(text,opt_buttons){opt_buttons=opt_buttons||[];for(let i=0;i<opt_buttons.length;i++){if(opt_buttons[i].buttonText===undefined){throw new Error('buttonText must be provided');}
+dialogRect.width)+'px';}},onDialogTap_(event){if(event.detail.sourceEvent.srcElement!==unwrap(this.$.dialog))return;const dialogRect=this.$.dialog.getBoundingClientRect();let inside=true;inside&=event.detail.x>=dialogRect.left;inside&=event.detail.x<dialogRect.right;inside&=event.detail.y>=dialogRect.top;inside&=event.detail.y<dialogRect.bottom;if(inside)return;event.preventDefault();this.close();},close(){if(!this.isOpen)return;this.$.dialog.close();Polymer.dom(this.$.button).classList.remove('open');this.$.button.focus();},get isOpen(){return this.$.button.classList.contains('open');}});return{};});'use strict';tr.exportTo('tr.ui.b',function(){const FaviconsByHue={blue:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAlrklEQVR4Ae2dCXwdVb3H5265yc3SpEk3ukEXCqVUBLT4Wm19oFKtaN0fKijy9CMguPBarIJsIiA8qsjTh7SllAoFeVBaEARkLV1ooXtL0yRdkqZp9u3uy/v/5uY/OZm75y659+acdnLOnP385zv/+58zZ2YMinTplIAhzsoDceaT2RKUQLwHIMFqh0V2ll0kn4XA6byv9/Vw834kX19e7keRQCzhRyk6bJJYRvD1YTXuhRdeqDj77LPPtNls400mU7HRaCzFFggEVJ/iSqhsicFgKIXUKL6bvB6fz9fj9/u7Kb4bPjaK67Xb7Q0HDhw49IUvfKEd2XUb7WpxHIYvXRgJ8AELkzRso1gmKrwkBfjG7373u5Zly5ZNKS8vn2G1Ws80m83YphPI0wnQUemQFp0IzQR9tdfrxXbI5XId6ujo+PCuu+6qXbNmjYfa9NMmngDoBmt+hIe944M53AUhwqwCvXTp0qJrr732opKSkk8XFhZ+imC+gIAryAZB0QnlJuB3OJ3Ot3p6el5/6KGHttxzzz0O6pse+GEP+3AGnKE2EhgG0tAFt99++4WkoT9tsVgW0DaH4guzAeg4+uD0eDxbaXuDNPzrt9xyy3bS8G4qB8BF6OOoKr+yDDfAB0B91VVXFf72t7+9lLT05QUFBZfQoYWtnA+ux+12v0ra/W+/+tWvXlq5cqWTBjUsYR8OgDPU8KGtjR9++OHHx4wZ8+2ioqKv0X4lbfnsWh0Ox9+bmprWzpgxYxsNFBpd1Op5bcbkM+AMtgr11q1bTz/zzDP/gy4Qv02zGtPzmehIY6MZmmq6UF176NChJ+bMmXOkD3QR9khFczY+HwEXwTbV1NTMI229FCYIXSTm43gTho8uUgMwYUir3zN16tR3qAIfbXkJej4dcIxF1dbkm44ePfqZqqqqpTT7MZf2pYsgAZqN2dTS0nLP5MmTX6EsDDrDHqFU7kTnA+Aa2BMmTDBv2bLliyNHjlxCZsgFuXMYhr6nZL7saGtru/eiiy7aUF9f76UeAfKcBz2XAUffVbgJbAuB/Y3KysoldONl5tDjkrs9oBtL+1tbWwH6UwS6/mZSzg0sVwHXTJG9e/deOGXKlOWksS/MOelncYdJo2+vra396axZs7ZTN0XTJYt7Hdq1XANc1dg0DNOqVatGLl68+DZa/3E1XTwCeOn6JLCly6ncU9+mNLnBZRLOYPAHHI5H2l5/8TdHbl3SRjUx6DkztZgrgKOfDLf5xIkT36moqLiLzJG0rAFJAomsKDp1W51S74IZnSIX8DcrXV3LlK/Oe5xqZPsckGc96LkAOPpowrZ79+5ZNK31BzkzQtKI4qxvV0dJTSLJ592kHKu7QfnPxXupFmhzbFkNeTb/tGsae/bs2Va6wr/lrLPO2izhTgLQZIuaaMp1yvTNyvNbb1HomFB1ZtrAUNYqymztGMNt2rhx44T58+evohs1n0r2+AyX8mnT4KIAvZ63lA82f1/55TX1FJ21tnk2As4zJObq6urP0BTgCmlri2TFDmcEcHQDtnlz4w+Uyz+Hm0Rsm2PuPGtcNpkomtZesGBBYXNz8210d+05CXfWsBLaEQNd5I+e8JyyYettCh0zyoBrpawyWbJFg2twv/jiixPnzZu3mhZFzQ2VqIyJRwIZ0+BiZzyeTcqebVcqS350nKKzxmTJBsDRB3WWZN++fXPpps060tpVouxkODEJDAng6GIg0KI0Hv+mcsXnN9FeVsyyDLWJwnCbadXfomnTpm2UcCcGc1blNhiqlNMmblT+9soi6hdmWKC4hlSJDiXgaBsCsNDKvysnTpz4JIWLaJMupyVgKFLGjHtSefrNK2kYFtpwjIeMs6FqWIOb7kr+Yty4cX+m2+0446XLBwkESHuPrPqz8uymX9BwhhTyoQBchZseQiigdcj30grAO+SDCPlAtW4MeLikdMQdyvqt9yp0rCl1SDR5pgFX4V64cGERvdhmRWlp6XU6scjdfJNAcfF1ysqNK5Q5C2F+ZhzyTF4AqHCPGjXKSjdwHqUHfr+ab8cyW8YzZLMo0QTgcj2jfO/S7ynNzS7KxtOI0UqkJC1TGlyFm3pccPDgwfsk3Ck5drlVidX6VWXFxvvAAG0Z0+SZAJzhtjQ2Ni6ld5D8KLeOjOxtyiRgK/6R8uy7S6m+jF14phtwmEBow3L8+PGr6FnJm1MmLFlRbkqgtOxm5am3rgITtIGNtJrJ6QQcHcdPkYUuKL9MsybLKSydlICijKxcrjz+0pdJFKzJ0wZ5ugBnuM27du2aT7ffV9JUIGCXTkqAJEAsjJ2wQlm1fj7tpPWOZzoAB9yo1/zSSy/NoLdJraMwFsdLJyUgSqBQGX/GOuX+FTMoEpCDmZRr8nQBbqIHgovnzp27mtaWlImjkmEpAU0CYGPmR1crF19cTHH4hU854KmuECcMOmo9derUAyNGjLiawtJlWAJZOQ8eTQb27keUyz7xM8qS8jnyVGpwNk0s+/fv/4qEO9oRlWkDJGArvVpZ89JXKC7lMyupApzhNm/YsGH6GWec8eCAAcgdKYFYEhhz2oPK3X+ZTtlSao+nEnDzxWRL0eNmj0q7O9bRlOkhEoA9ft6cR5WPq/Y4IE+J+ZyKSjS7m56jvK+srEzeqQw5epmNyDkbXBRPT8//Kl++6EaKSok9nqwG10yTHTt2fJpWB0q4xYMlw4lLoJhu5z/y3KepYEpMlWQBV7U3mSXFNN99H71YPfEByRJSAqIEwND4yfcpFyzgqcOkGE2mMGtvy2OPPXY9vZjnTLGfMiwlMGgJWCxnKktv/QmVT3pWZbCAM9zmxx9//IzRo0fj0STppARSJ4HykTcqN//3GVRhUqZKMoCrC6no6Zy7yTSxpW5ksiYpAZKA0WhTPj73dxRKakHWYABn7W3Zs2cPvjH5eXlApATSIoGi4i8oK56/tA9ysAr2EnKDARxlzJdddlkJ3dC5N6HWZGYpgUQlMH7SvbRWpYSKsamSUA2JAs7a2/ynP/3pOvrc9eSEWpOZpQQSlYDZPFn54a/xcDoDnpAWTxRw5DfRJ7DL6HUPP060rzK/lMCgJFA+8sfKZd/CqlRc9yXEbCKZWXtbli1b9gN6EX3loDorC0kJJCoBk6lS+ebVP6BiCU8bJgI48ppxU2fs2LHXJNpHmV9KICkJVFZdo3zsY7j5w6ZKXNXFCzhrb/PDDz/8HbK9x8ZVu8wkJZAqCZjNY5Wf3vkdqo4Bj8sWjxdw5DPRt3KKTjvtNNxhkk5KIPMSqBz1E2Xq7ITekBUP4Ky9LevWrfsGae9JmR+ZbFFKgCRgLpik3HL3NygUty0eD+Cq9h4/fnwBbTdIQUsJDKkERo+9QSkr47ubMfmNlQHaG5v56aef/ndaUDVtSAcnG5cSMFumKXc/fDGYpI35jCiXeADH3KOZ7lp+Sy6HjShHmZApCWA57dgJ3wKTtIFNQB7RxQIc6abLL7+cniEesTBiLTJBSiCTEiguWah8/isjqEkAHpXhaIk4M5BuXrp06ZfoOUtcvUonJTD0EjCaipSvff9L1JGYU4bRAEeaCjh9P+fr0jwZ+uMqe9AnAZgpo0Z/nfYY8IgcR0qA9sZmeuCBBybZbLZ/66taelIC2SEBKzF5zTJMWbMdDl5DXDTAVe29aNGib5D2jpQvpEIZISWQEQkYicm5C0QtnjDg6uwJPY72tYx0WDYiJZCoBMorGXDW4iE1hNPMOBMQb1qzZs0MmvueHlJKRmS1BCZYYZoOA2exTFd+dT/eTsuzKSFaPJwkNMDPO++8+fLiMvdA+Z8JJcqPN+9RGnocoZ0PBELjFF2cbjdYIEykvq4wWehd4APb05dBari4gaWCe/p8AT+uFOdT4j7aoJTB7oAGowFurqqqmicBV5QPmgLKX3b7lVbHANmRLLPVVSjnGT6hzFRa44dHHEqIHhQThXC8+YQiqQ66K9rnvakoD1O9DPiAJvSAo8vYjMXFxWZ6U9VFA3IP052fv+5VGntzBW4+SCYl4KtQ/L3tpCBJ0+WpC/hKLgKrvb29DDj41Q4WIvUOcaZHH310lslkGqlPHI77uQd38CgZTBbSVBVKXk+CGYwjS758/ywwS1sIz/oI1uCmmTNnflKaJ7l/OmuQG3migQ9xnvg0W2gaN/2TfYDzoLQDFw5wVYOT/T1XAq7JKacDKuS2csVg1B/unB6W2nkwaiiumEs7rMEBueZEG5zpN9Gt+QKyv+douWQg5yXAkPvtHYO78MxiCZisJXNsVRML7C3HndRN5li1w/WnNPaNDz744Ll0ZpRm8Zhk1wYhAYacjPJBlM7eIgHFUFryxZvPpR6q/Io9DavBJ0yYcJY0T0Qx5U84CDnNrtjb82dQZHqZysefRQPaRltEDc4JRlr7PS1/Ri9HopeAwWRWjLYKQiF/NLnBWgpmocGZY3XYoomCBOybaPXgNKnBVfnk7R8V8qLyvIAcrBoLiqaCXdoYcvXYMeB8KmPfSIBPUVPln7yWQD/kjEEOD7fABsBVfvtGoTIdYoOPHDnSXFhYODmHhyq7noAEgpCPUPyOTiql3QBMoIbsyGo0F04uInYdbW3RTZRbb711AnXZmh3dlr3IhAQYcpooz0RzaWmDTk1r0YLrwS4GwRaJuoMGmXrjOeecI5fHQiLDzKmQF9ILXFXIGYfc8q2jZ4JdBlyFnE9ZHolx1KhR8gJzmMHNw9Ugz8U7nrijWToyZCZFtMEBu7GoqGgiD1j6w08CKuTWUsXv6s65O56GApVdlWM+cnoNbqB3D+JzEdINYwkw5DlnkxvNYJetEdVEETU4Ioy0RLZEzoEPY7r7hh6EvIQ0eQ/FZP/sCpilPgNwKG0VbgyFdzTqCXC8ZFw6KQEAoxgLS3NoPbkR7GosIyxqcBxSgwQcYpCOJWDAOnIrKUbS5AH9M5GcKUt8OiEZcK1HbIMjQiVfAq7JRgb6JADIDQR5tpuuAaMGuGaisAbXIiTgkutwEujX5L2UnJ02uSEIOHdfZVpqcBaH9GNKIKjJQ6yAmOUylYHsa+6cprBZg3MfpA3OkpB+WAkENXmxEnDbs2+e3KABrvU9RINTih56LbMMSAlAAqomL7BRQFOU2SGYgMouOqV1jGHWIrxer50+8iofV8uOQ5a1vVA1OUEecOPtWdlhkxsUH/2saE5lmufBtVifz4erCOmkBGJKIKjJ8V0ETT/GLJPODAG/X8+uOg+O0087BaHB09kJWXd+SSCoyYuUgIceaB/qeXL/AA2uci3a4JB8QGrw/AIwE6NRNbmlcMht8oBftT40ZY2xsw2OsJogAYcopEtUAqomt5Am9w6dJg8ENPNagzysBs/2W7KJCl/mz4wE8OYsg3loNLnKbNAG1+DGqFmDI1LdpA2eGRjytRX19XAEecDr6kMqcyM1BNTrR41ltCxqcAYc6yOlkxIYtASCmhyP9WZ2doVmUXhtL1hWHWtw3lccDkcb1H22L6zROiwDWSmBoCa39mnyDHSRmPV7nG36lliDs1r3t7e31+kzyX0pgcFIQNPkGbrj6be3gV287Z95Vk0U7MCpkdXV1bXyIjMoEPk3eQmokJsKglOIAD1tm6J4Wo7UMsd9PQ+wBse+CvgzzzwjAe+TjvRSIwGGnB4qS02F4WohE8W58zk94CGzKP6XX3652+VyNdN6lFHh6pFxUgKDkQAgDygWxeDzDKZ47DJeV3PvvtfpVQChJgoKs80C+8Xf09NzRJopEIt0qZQAIFfou0GpXoUIVv0uxxHqq8ov+cxzyDShmsFut9elcmCyLikBloAKuZEm71Jsi/vdKrMi4GqTbIMz8cjgw0yK1OB8SKSfagkMgDwVlZMGDzg6oJR9tIFh5lmzwdEMR/pPnjxZiwjppATSJQHVJg/QRaffm3wT9Gvg624GswPgRsXhNLh//fr1u2nRFYCXTkogbRJQbybCXEl2diXgCzh2bthNFQHwAZAz4BgEgEaijz4C29zZ2VkjzRSIRbp0SiAIOT7MgCnExDeyThS/s7uma+vaZqpANFHUbusBZ8i9ra2tWyXgqozknzRLQIMcF56JOiLc19O6lYrB1hmgvVGVCDj2VQ1Ovq+mpmaLBBwikS4TElAhx7vJE55dIWhb6rZQH6G9WYNrXRYBh/ZmDe5buXLlVj85LacMSAmkWQIa5Im0Q4x2bXkUGpzhZo7VWsIBrp4JGzZsaCc7/KDU4olIW+ZNVgL9kMe2x4P2d+dB+86X8NFP1uARAUffWIPDnvHSdOE2CTjEIl0mJRCEPA57nAj3dzXj468qr+SzDa51V9TgiGTAcTZ4yQ7fLAHXZCUDGZSABnlUm5wgba3dDFZpE00Uraf6Bx5YveNM8C5fvnzbJZdc4iwuLqYH7Yavq+ytURq70rRIKIvEGlmZAYswDjZCRBchLUJ0ULeGqYzaQL8AfEj/PA5nz8u/Zw3O2ntAC+EAR0bVnnn33Xe7Gxsb35gyZcqlxhR9mGj/oU7liWfrlPZOd5jRZGfUbK9bmUnPGIYIeEB3B8i1PyUKBHTo+vPFEYrcfpR6orYfR6NZmiUQ8Cs9XU1vbDiyEysI2f5myLVe6wFHAqSlanDyPTt37nz+9NNPTxngv/3DHqW5lV4tkGPO67ErPi+9pgw/mYAGfjyO8zJo+vL6dH2dmc6vb1/fP31/9Pn1+7HK69P15fXt9eUP+LxKR/OB5yk7flrFOfABNehtcCSKgHuvu+66t2n5bGtk7TGgvpg7uQg3BmW22BSTGa8pIwehx+s4L3wxzOXFOM4j+sjHecSwmEcMi3nEsJhHDIt5ENY75IXjMhxWI+P4E6u8Pp3bYV/fHsWDRb/f1Vq3b9XblBzxAhNFowEOte+hlYWO+vr6f6QKcDSaq06F3FQYdeUEow9fDGfLmMU+ieFI/RPziOFU5Y9UT/T4gOJ2tP/D7e7Bmz+hwcNeYKKOcIAjHiaKZqa8+uqr6+l9KYgf9g6QG/sgxwHXbxAQgyCG9fmGal/skxiO1B8xjxhOVf5I9USLV8j+7mjd/Rz1RzRPwGuIiwQ4zBScFaDas3Tp0r0dHR2HpRYPyo8hD+7Jv5mUABj0eeyHjx58Yh+1y4CDVTAb4qIBzpCjEjfNiW+Qd+775dcPeTRdI9NCf+OSlQl98M3RvAFM0sbmCVhNCHAcSah8TYuvXr16PT2MjAql65OAapPjXXzRnP4iCnk5Llw5ToMvhsPlzYU4cQxiOFLfxTxiuC+/3+/xNB9/cz3tito7rHmCIpE0ONJwRrAd7l61alXjkSNHXpBaHKLpd5hZMfELJ3FA9Buy8oESw/p8vC/mEcOcnmu+OAYxHGkcYh4xTPlx38DtaHnhZP3rjZQEDR5xehBF4eIFXDVT1q5d+whp8YhnS7DK4fdXhdyEd/FJl04J+ANef3PDpkeoDTZPkgIcfR2gxe+7776aY8eO/VNq8dDD2A95sjamLE8/eSTggRsuLj2Otn821D5fQ4lxaW8cpWgaHOnQ1pqZQmHXU0899VePxxPWoEeB4ewYchwadhzmw4V4jhPDnJ6oL9YhhuOtRywjhuMtr88n1iGGOZ8YJ4Y5PZKv+H2BthOb/0pl8F5mEfCoFkUswNEHVICLTdVMufPOOw+QFn9TanGIJtTBHjeSucIHCjkQZsfhSOmcL14/2fqSLa/vZ6z6YqXr68M+1p24nK1vHq3++wHaZfMETEaFG2XjARzaWgOcwq4XX3zxYdLiKC9dGAkw5Pqf2czso0OMkRhGXG5u9N5vpb3p/YdpAKy9AR+YjGlJxAs4a3GcPa4lS5bsOnHixGapxUkaEVwQcnqrasYdw80wowMcl/HOJN0gtLfb1bH5yMHHd1FlDDhr75QAjk6yFsdVKyB3bty48UE5owLRRHYa5JgSY8dhniZDPMeJYU5P1BfrEMOR6hHzIBzLcV8j1aePR31cRgxzPjFODPel+xWvv6N5x4OUhCWoYA8MxqW9KV9cJgryAXBocQbcdeONN+6kd4k/J9eoQDyRnQq5se+Fk3yg2UcxDvcdULUmjotcbeQULhtvffr8XC6Sj5a5TORe9KdwXq5PXz5KOn0WUHH2nnyudt/qnVSMtXfMqcH+xuMHHGVYi6sXm2jwpptuWk4PJrfLNSqiSEPDGuShSTImggTUNSdee/uxA2uXUxaGO27bm6uNxwbnvKzF8fOABp2vvfZa89atW/8oLzhZRJF9zVyJnEWmCBKgb14qPZ01f2xv3o03VsE8AXNx295cVSKAo4yoxVXIFy9e/Aw91rZLXnCySCP7Jpo+NNLnPMQvHXAYfjz/UDuXEcNcVowTw5yeal9sQwxHakfMI4bF/HhiyuPq2LV/293PUB6GO2HtjfoHA7g4o4LGnWvWrLnL6XT6pKkCkUZ3gNxAL4HnA4rcCMfrOG+k8rHS420n3nyJthcrPxgK+D2+5oa37qI+qHyRj4vLhLU3xpAo4CjDgOOMUrX4HXfcse/AgQPr6I20SJcuhgQYcvVijS++pN938RpQHD0n1h378Cms99Zrb7CXkBsM4GiAIVenDGnfccMNNzzU0tLSKE2V+OSvmiuYXZFOkwDmvD2e7saa/X99iCLxOBoAF7W3ljfewGABZ1ucpw2d7733XusTTzxxE33+xCNNlfjED3vcqELON2WGr0+WCS03cXtaTmy6qbutppUkyHAnNO+tl/xgAUc9DLmmxWnacAeB/hDdANK3I/cjSCAIebi3d0QokKfRZHcrvZ01D9XtW72DhqjX3mBtUC5ZwGGqaFqcws5LL7109dGjR9+WN4DiPx7DHXLc0HE5Wt7es/m21WCob4PiTOimTjiJJwM46gPg2PiCE2ee/Wc/+9lvyB5vkvY4SSNO12+uxFkgT7LB7vZ6uptq9678DQ3JThsYggkAppgvCg7OJQs4WkUnMH2CMw6dc9ANoJNPPvnkL8ke90p7nCQSpzPS9CFscryHbzhsEEvA7/a2NLzzy46WXSdpV+WH/KQuLFEvu1QAzrY4mypqJ+lVE9u3bdv2Z9jjEnIWd2wfkBsM+W+T9813093K6j/X7l+9nSQjwp3UhaUo5VQAjvoY8gGmysKFC1fSgqxX3G6ckNLFKwEVcu3rY/k5swK729Hb9Mqed29fSXLRmyawCAZ9YSnKOVWAo06GHDTjQgGdti9atOjXdNH5noScpJGAU00VI74+ln+OXv2gOJ0t7x3cduevaXQqJ+TztGDK4IbkUg24aI+rkNNXIrquuOKKG+kBiYNyURZEHr/LR8j99OFXt6v94KH377/R4WjtImkAcBFuMJQS7Q1Jp0NFoHNiBw0Eube2tnbT/PnzFzz3UtMIA76mJV1cEjAYcIhInLgTkuMOZonH3XW8dvdff9zZur+JhtNLGwMO8zal2hviSgfgqBduAOhki7u6u7u3NHWO+yxNidkk5EEhxfM3CHmfSHN0zQq98Fjxunta6w+v+9GphneO0Wj0cKdUc7Nc0wW4qG608AcffNBrMlvfLx0x5XMGo7lAQs6HIbbfLytNnLELZUkOrO2mF2b2nDz64rX1hzccpG7p4YbmBuApd+kCHB3lI8G+2vnOlr0dBYVV+4tKxl1MswWW/gOX8rHlXYUsq+C8ChaeZv8/vOqYvo5hb2l48+d1+9fiNrwId8rmuyMd7HQCLrYJyDXQ20/tOGUxF+6wlU1aYDQWFPGBEwvIcHgJ9MtKE2f4jFkQq9rcnu72xrp//OTIgccx181wY8477XBDBJkGXAO9o2VPm+JzbioZMXWewVRQ2n/g0C3poklgoKyyc57cTxeUXnfHCVrXfU1D7fr9NJ4e2gA4w530OpNoMuK0TAGO9ljlaJB3tVd3u1yNb5ZVzPy40Wyt7L+Y4u5JP5IE+iFnsUbKmfl4zHN7nG3VdXtWXNvU8GYd9QBgZxxujDyTgKM9OAZc9e1dDY6ejoOvl1fNnm0yFY1TaApR/QhoMK/8G0UCGuQGEmUWKHK83jhA89z0gvoPDu1cfn1b864T1H29WZIRzc1iyzTgA+CmTqj7Lkeru6156xsVoy+cQk+fn44DJyHnQxTd1yBXRRk9bzpTsSrQ7/MoLvvJN/a/d9uSno5jLdQew40bOVghmFG4Md5MA4424UJA97rtvub6f71VPupcq9lSNttgNBLj8oZQUFzR/w6UU+ZVOeD2eV2B3u7ax/a9e/PvXI7OTuqxCDcuKDMON6Q2VICjbYacJ/jpHYte/8mjr35gtVUdLCwaPYfmyunDlFKbQ1ixXBByiDRzTl0RGPBiPXd7S8Pbyw68d+/TdAz5YlK8QzkkcEMSQwk42mfI4Wugt53c3uB0nHyttHz6THo4dywOnjRZIK7ojiHPxOw4lg4EYJI4mnfW7V95ff3h9bupd9DarLlhkohTgZk9+/pElS2AA27eVOjt3fW9p4699kr5qFkmc0HZR6TJ0nfEYngDzZUYmQeZrN6ZhEnSeXj1nk2/vr2nsw5vn4LGZrj1i6cG2VLyxYYacIyAz2zW4hro9HPnO3nstZ2FhZX7Cm1j5tCDAEWkyqU2j3HctV+7FJvjWE+CWRKvt6utpeGtX+7f/vv/6zNJGG7McfPFZNpuv8cY/oDkbAAcHRIhF0FXw21N2084HfWv2UonjaHPhEwJaikJ+oAjqdvRINfFD2ZXfSILF5I+Fz2kUP/akT0rlhyv2bCX6mKNDcD1N3CgqIbc4RzPJof+YOoEJx7eioNPl+FDlHSxqdgQnj77h5+oGPeJXxQUlE3Cg7qZ+EmmdnPWYYYjGRec/nMrbnfnsbaT2+6v2f3wZqoPJghDzVOAvNwVDbLCSqbplJTNNsAxKP5hBeR4OBGfSQDkDHpRYWFFyYzzf/Gd4oqpV5JGt+IZxlRqLGorr1zwmdjEmOMZEp/X4erpqFld/f4Djzud7ZghgabGBrDZ1sYsCa/lTqwhKphOly0min6MLCT42KAV2Kbzeb1Ob9Pxf+32utteLSqZOJ4++jRJmi16Efbv95/8rDsi++pzFX3mCM1kvXPkw7X/Vbd31eskc3H6D9pbhBvHJ7mfiv7upjSUjRpcHCD6xyYLa3PW6DBbVM0+4/yffKq88iPXmq0jJuOdf/J2vyjC/nBQk/fviyHRzva6u462N+96qHrng29RHtbUrLUx9cc3bljpsEISq8yKcLYDzkIC5Aw6bHNAzva5CrnZbC6c/pHrLykbefYVZmv5NAk6iy66PwBsV8fhrrYDj1Xv+uOr9GYyBpt9ntcWbe2s1NriiHMFcPSZtTlAhzZn0AE4ww7fOuP86z45ovLcKyzWkecEL0RN0kYnwYguaGP78MJLetl8277O1j2Pffj+n96mPAAZG8BmHxobYPMdSYCdtVqb+qa5XAKcO40+49qBQYc2Z42uAk77qj919tUfqxh1wZXWosrz6cEKslxQbPhOLwZNFKz4I7D9broL2fp+e/OO1TW7H3mPBMNgi75ojgBqvpCkYG64XAQckkW/sYlmCzQ6Ty2KoBeccc53Z5eP/uiXrIWjFpjNRTaD+no0FM1/2DWo6cIRb3D1eh12l7P5jY5TH6yv27cGt9cBsQg1wtDWvIl2dk5obeq75nIVcB4AQ86gs+nCoLNmV7V8YcnY4ikzvr3ANuKMz1mLqi4k0E3q+7nVu6OoIn+cOv9NUyJ4+ACfBKG3t263d9a9XPvh2jecPSdxg4a1M4BmyBlqnvaD1s4ZcyTc0ct1wHlMetBhi7CNziYM+6qmrzrtwtHjJi/6rK1k/OfoiblpAJ1hz0XNzpoai6AANTafu/uwvafh5cajG//ZcmL7KZIJA8xwiz7SoK1ZY+c02DQO1eUL4OJ4grZH0E6HRmetDsAZetE3T5q6eHr5mPPmWQurzjcXls8i0K20VFcx4iWYeA9JFpoyA4CmJatYI0JQu7zOjr0uZ8v7HU073zlW82w1dR7aGPAC5nA+0llj8z2HnDNFaAxhXb4BzoMMUtlvo0Ojs1bXA69qdEqHby4sLLeOm7p4Vln5tAsshRXnFxSMOJseirbgAhXPjAZvmrDYgn7/jRRuPjV+EGLUxbzRBSKWqdJ7RnChGKBPftAt9AMeZ/v7XR2HdzTWPLvX6eyAycFQA2jeGHBOY23NGhuNcEMUzA/HRyo/RhM6ChF0aHbRVhe1O0POceybiovH28ZNW/SR4pJJ55oLiieZzLZJJottPFY2BoHHWnWAT1Wr0owkUn18JJYoHv9xUQiQNd/roJfnNPi89mNed++x3p5jexoPb9zV29uAu4qAlDUx+ww2fI6Dz0CL9nWkzlD23HZ6qef2aKL3HmNl84VBZ83OQEfyOR98lDWOnjB3dFnFOZOttjGTLIWlk81m20RaMlBpUEw2Ay2QoRPARg1SffQXF7F9vtpFaOEgxbSrhuhDAV57gBZ+BBSf3e9ztXq99uMeZ/dRl73pWFf7vqOn6jfBhmYoRe0rwhsuLOZlu5p9tTv5/Gc4Ac7HEWMWN4ZW9AE6Q83Q8z6fHKKvQq+r10DmjrmoZEKx1Ta6yGItK7aYy7AiUvF4u+weV1evy37K4eip7yWzAmBCi4obwwyfta7oI8xAM8TYF/NwWbHevNXWNPYQNxwBF4Uggo4wg8q+CL0IuAg350Ec18H1oi0xjH3RMXiI4zBrVwZcDyxDy1DzPudnn+tjX2x32IQhfOmCEmBZMJDwGXQxLMYBbqSxz5AjDg4+b7wPH9DBMXz6fUCKOEAs+gwv+0gTw9jHBsd+cG+Y/uUDMUyHH3XYLBsGNJIvQq3PgwbEesQGGUDRR1i/Mez6eHEf9WJfOp0EWPi6aLkbQQIsLwYZ2aLFiekRqhwAJkPK8KJMtLhIdcr4PgnwwZECSU4Cejnq91G7Po7BFVvWx+n3xbwyHIcE/h9VLWRYHWXC/QAAAABJRU5ErkJggg==',green:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAltklEQVR4Ae2dCXQcxZnHR3NoNDp8SD7kU7bxFXCchBhMYoLNmhCcOBBykGw2gYTkPV6AhGXD2sTZJQcJG3jsgw3hscuCsTEsOAQW1sbY+MAHxpYtHzI+5EOy5UMStnWPZkZzab9/j75WTWt6NKO5Z6r82lVdXV1d9e/ffPq6uro7zyBDIhXIi7DyngjLyWJRKhDpCYiy2pwoztrpxSwCb+d1bayFm9f1Yu3+cj2MAgOJH2bXnNnEGiHWppW8d999d/inPvWp6YWFheNMJlOR0WgswdLT06PElFdM+xbn5eWVQDXK76TI7vP57H6/v5PyOxFjobwuh8Nx4dixYye+9rWvtaK4ZqFVNY/TiGUIoQCfsBCbcjaLNVHgJRUQG3/4wx9ali1bNmXYsGEzrFbrdLPZjGUagTyNAB2ZCLXoh3CJoD/p9XqxnOju7j7R1tZ2/LHHHqtbtWqVh47pp0X8AaAZbPmRzvnAJzPXhRBhVoBeunSp7b777ruuuLj4xoKCghsI5s8TcPnpIBT9oNwE/D6Xy7Xdbrd/8Oyzz+5+/PHHndQ2LfA5D3suA85QGwmMPLLQ+b///e/nkIW+0WKxLKBlLuUXpAPQEbTB5fF4KmnZShb+g0ceeaSKLLyb9gPgIvQRVJVdRXIN8CCo77777oI//vGPt5CV/n5+fv5NdGrhK2dDsLvd7k1k3f/n17/+9frly5e7qFM5CXsuAM5QI4a1Nh4/fvza0aNH/4PNZvs2rZfRks2h2el0/u2TTz55dcaMGXuoo7DoolXPajcmmwFnsBWoKysrJ02fPv3v6QLxH2hUY1o2E63XNxqhOUkXqq+eOHHitblz557pBV2EXW/XjM3PRsBFsE21tbXXk7VeCheELhKzsb9Rw0cXqT1wYciqP37FFVd8SBX4aMlK0LPphKMvirWm2FRfX//lESNGLKXRj3m0LoOOAjQas/Py5cuPV1RUbKQiDDrDrrNX5mRnA+Aq2OPHjzfv3r3766WlpUvIDfl85pyG1LeU3Jd9LS0tT1x33XVrzp8/76UWAfKMBz2TAUfbFbgJbAuBfUdZWdkSuvFyZepxydwW0I2lo83NzQD9rwS69mZSxnUsUwFXXZHDhw/PmTJlytNksedknPpp3GCy6FV1dXX/OGvWrCpqpui6pHGr+zct0wBXLDZ1w/TSSy+V3n777b+j+R8/pYtHAC9DrwIO9xHD5c5XDF5fS0ya0MWo3+nwvrBx47nfLLlvKypj0DNmaDFTAEc7GW5zQ0PDD4YPH/4YuSMJmQMSExVpsPPxhjsMHt/FuLWkp8dwqb3dt2zhnD2vUKXsnwPytAc9EwBHG01YDh06NIuGtf5DjoyQGmHC4XMLwmwd/Caft2fnmXr3A3d8Zf9hqgXWHEtaQ57Of9pViz179mwrXeE/MnPmzF0S7sEDGuueJnPevCuusO76sPq6R2bPHm2l+sy0gKG0NZTp2jCG27R27drx8+fPf4lu1NwQ6wnKlf0TZcFF/bwe//Z9uxw/vvfuj89Tftr65ukIOI+QmE+ePPllGgJ8UfraIloDp5MBOFoB37zxQvdPvr5gP24SsW+OsfO0CenkoqhWe8GCBQWXLl36Hd1de1vCnTas9GtIXp5h5LgJ1re3H7z2dwsWjMTUYlwrpZXLki4WXIV73bp1E66//vqVNClK3mLvh1RkGcmy4GJr3B7/zkOVXXfd86PD5yg/bVyWdAAcbVBGSY4cOTKPbtqsJqs9QhRPpqNTIBWAo4U0l+1yw1nXd29duH8nrabFKEuqXRSG20yz/hZPnTp1rYQ7OpjTqTRNUhwxtsK69t3tcxZTuzDCAsOVUiOaSsBxbAhgoZl/d02YMOF1SttokSGDFSCabeVj819/v3LOXdQNCy04xynjLFUHVuGmu5K/HDNmzHN0ux2/eBmyQoEe84gRluc2V13zS+pOSiFPBeAK3PQQQj7NQ36CZgA+Kh9EyAqqgzpBQ4h5w4aZH6URlidwrmljSix5sgFX4F60aJGNXmzzYklJyf1BqsiVrFOgqNh0/5ubJr24aFEp3M+kQ57MCwAF7pEjR1rpBs4KeuD3W1l3NtOkQ6kaRQnXfZfL/+Y3bqz7Ed3f6KZyPIwYbpe4bEuWBVfgphbn19TUPCnhjsu5y6hKCgqM33pr4+QnwQAtSbPkyQCc4bY0NjYupXeQ3JNRZ0Y2Nm4KFBab7tlSdc1SqjBpF56JBhwuEI5hOXfu3N30rOS/xk0tWVFGKjB0mPlfN1bOuRtM0AI2EuomJxJwNBx/iix0QfkNupJ+mtIySAUMpSPyn16z5fPfICnYkicM8kQBznCbq6ur59Pt9+U0FAjYZZAK4J6+aczE/BffWn/1fJIjoXc8EwE44Ea95vXr18+gt0mtpjQmx8sgFVAVIEgKJkzJX/2fq66aQZmAHMzE3ZInCnATPRBcNG/evJU0t2SI2iuZkAoIChiNeUM+O6d45cLbxxVRNv7Cxx3weFeIHwwaar148eJTQ4cO/SmlZUiyAuk4Dh5Ogs5O3wsLPrfnQSoT9zHyeFpw/FhQn+Xo0aPflHCHO6Vym6hASYnpp29v+dw3wQ4tYChuhjdegDPc5jVr1kybPHnyM2IHZFoqMJAC48Zbn/nzi1dNo3Jx9cfjCbh54cKFRfS42Qrpdw90OuV2rQLwx6/9QvGKhQsVfxyQx8WKx6MS/EgUv5vmGTw5ZMgQeadSe/aSvJ5pPrgoj73D91/zr97zEOXFxR+P1YKrrsm+fftupNmBEm7xbMl01AoUlRjvWb1u9o20Y1xclVgBV6w3uSVFNN79JL3LLuoOyR2kAqICYKhisu3JBQvG8tBhTIzGsjNbb8vLL7/8C3oxz3SxoTItFRisAhaLcfqyP435Oe0f86jKYAFnuM2vvPLK5FGjRuHRJBmkAnFToLTM8tCfnpk5mSqMyVWJBXBcWFro6Zw/0Z+Vwrj1TFYkFSAFwNQX5w/5N0rGNCFrMICz9bZ8/PHH+MbkV+UZkQokQoGiQtPX/rb+M7f0Qg5WwV5UYTCAYx/zrbfeWkw3dJ6I6miysFQgSgXGV9ieWHjrqGLajV2VqGqIFnC23ua//OUv99PnriuiOposLBWIUgGLJa9iya8q8HA6Ax6VFY8WcJQ30Sewh9DrHn4WZVtlcanAoBQYXmr62fe+NwGzUnHdFxWz0RRm621ZtmzZT+hF9GWDaq3cSSoQpQImU17ZnfeO+gntFvWwYTSAo6wZN3XKy8vvjbKNsrhUICYFykZa7r1mwUjc/GFXJaL6IgWcrbf5+eef/wH53uUR1S4LSQXipIDZklf+m99N/AFVx4BH5ItHCjjKmehbObaxY8fiDpMMUoGkK0BW/OezZxdH9YasSABn621ZvXr1HWS9Jya9Z/KAUgFSID8/b+KjT02/g5IR++KRAK5Y73HjxuXT8oBUWiqQSgVGlVseoCnZfHdzQH4HKgDrjcX8xhtv/B1NqJqays7JY0sFLPl5U59bVbEQTNLCfOoKEwngGHs0013L78npsLo6yg1JUgAMjhlb8D0wSQvYBOS6YSDAsd30/e9/n54hHrpItxa5QSqQRAWKh5gWffWbY4bSIQF4WIbDbcQvA9vNS5cuvY2es8TVqwxSgZQrYDQabHffU34bNWTAIcNwgGObAjh9P+c70j1J+XmVDehVACyOLs//Dq0y4Loc621g59301FNPTSwsLPyiVFcqkE4K2ArzvvjPv52GIWv2w0P64uEAV6z34sWL76BfjF65dOqzbEsOKQAm5/9diWjFowZcGT2hx9G+nUO6ya5mkAL0WBsDzla8X+tDWWa+uDStWrVqBo19T+u3l8xIawUsplFp3b54NY7mik/703/MxNtpeTSlnxWHk64NKuCf/exn58uLS6086b8+3Pqg4WDNHw0O5yf9Gkuf9+sX6N3twXmaVWwMkUWv+Q7eLVShHk1mv310Kg9Vrt/h/PStQoN/PlVxhBYYa7AbVCwc4PQxzxHXS8ANhkZ7jaGq8W8Gh6ed9MuM4C2ebrD7Jhp6CIJsDr481/UGw4nnqY8MeFB3tYDjF6BY8KKiIjO9qeq6oNI5urL+1L8bOt2XM673PrPf4OjwZDXk/p6e68BqV1cXAx5kxUP54MgzrVixYpbJZCrNuLOagAZnItyQwWQ2GgppXlKeEec8OwON75V+/YErZlHv2A8P6qieBTddeeWVX5LuSZBWGbnCkDs7PQa/PyO7EL7RZI5HTCj+EhXaTwt7IKpfprXgintCBU3kf8+TgIfXNlO2AnJbicVAt7izLoBR2xDLPOoYW/CgP1eiBWf6TXRrPp/877lZp0YOd4ghhyUPNUKRydJYbaa5IyYU5l8+53BRP5hjxYprf9NYNz7zzDOfpl9GSSZ3Wra9vwIMORm9rArUn5Kbfzzt09QphV+xcyEt+Pjx42dK90SUKXvSDDksedYEwnrYyIKZ1J89tOhacN5gpLnfU7Om87Ij/RRgyLPJiFlsZjALC84cK/1GBgdswLqJZg9OzabOcwdl3KcAIC8oNuMtrn2ZGZpCHyxW0xXU/H4Xmgw49xLrRgJ8Sob2VTY7CgVUyLNgnLwXcIXfXgkUpvv54KWlpeaCgoKKKHSSRTNYAQXyIrPB1eXVzOLIrE5ZrcaK0lKbuaXFCbDZYCsuCfcEmcbf/va34ym2cqaMs18BhjyTZ/3TmKB17ncngV1Y8X6AM/XGq65SPsaZ/WdV9jBIAUBuLSSfnPFgIjIoHj2pCFO7xR6oFpy7YRw5cqS8wAw69bmz0gc5cMiwQE0uKrH0G0kRfXDFQbfZbBMyrGuyuXFUQIGc3p/Q7fSRT65O6YjjERJXVX6hCewqHPNRsILAFjyP3j2Iz0XIkMMKBCA3ZdwQosloBLsqyziFogXHBiNNkS3OhrFRdE6GwSsAyPPJkrvJkmeCHVeYNeUBcPbBlc7ziko9AY6XjMsgFVDmkysXnqAjAwIN54NdlWWkRQuOLuRJwCGDDKyA0ZRnsNrM5JOn/zi5yZzHgHPz1VEUZCjkS8BVbWSiVwGGXCEkjVUxGlXA1b85bMHVDAl4Gp/BFDaNIXe7vGk7uEL+iOheK0zzKAqkkxY8hQBlwqEBeX4BJmilZ2uNRuX6UeGYW8gWnNelD85KyDikAgy5uzv9xslNRvUiU217PwtOW7TQq4VlQioABRTIrTQzNc1MeU9eD9gNacHVPzper9dBH3mVj6tJlsMqwJB7yJKnyzg5vTXAITRaYZrHwdV8n8/Xpa7IhFQgjAKAnOZhp83gSo/foGVXGQfHD1D9EcKCh+mT3CQVCFKAIfe6yZKrFAUVSdqK39cjsqtwLfrgaEiPtOBJOx9ZcyBAbs7H3JUUd8mnWPCgn5l4QalskICn+CRl6OEVyMld8brp9VkpMuU9fj+7KCrkIS14v9fpZqjostnJVYDuJJIlJ6RSYMrBrK9HAVyFG71nC45MZZE+eHKhyLajMeQ+jz/phtzvy4MPrrIMbUULzoDbs0102Z/kKgDITRZj0g253+8Huwy40mm24KoCTqezBeZezglXJZGJQSgAyA0EOSx5MgLcfp+7p0V7LLbgTL2/tbX1tLaQXJcKDEYBtuSD2Xcw+zg6u8EuflHMs+KiYAVByTx58mSdvMgMCCL/j12BpEFO9Laed9Yxx70t72ELjnUF8DfffFMC3quOjOKjAEOeyMEVfOyqevtFLeD9RlH8GzZs6Ozu7r5E81FGxqd7shapAI1mwCen5zz93sT45H5Pz6UTey52ktb9XBTor1jv3o1+u91+RropkEWGeCoAyI0EebyHV8Bqt8t7htoKuEMCjn4AcqWAw+E4jQwZpALxVkCBnG7tK5DDZ4nT4nb5wawIuNJ09sFFC+7DSIq04PE+tbI+VoAhj5dPjiHCbrsXgNNTGMEWXBwHVyFvamqq48bIWCqQCAUAeQ8ZcJoBGHP1+KF0NHvALCw4c6zUG8qC+995551DNOkq9iPH3HRZQTYrgJuJmKQVa6CvOffUfNhwiOoRXRSFXwYcx0AGCvjoI7CX2tvba6WbAllkSKQCsUKuXGB2eWsr37twidopuihKs7WAM+Te5ubmSgl4Ik+trJsVYMgHMz0E/ndXm6eS6qI3E+m7KHwsxYLTiq+2tna3BJxlkXGiFQDceDe5EiMd6UIPzLU0OneD2d4FDKtBz4L7li9fXkmzs4IKq3vJhFQgAQow5NFUTYT696w5DwsuuieK/416QgGu/BLWrFnTSn54jbTi0cgty8aqgAo5rj0HWHB7vtvhqTnyUVMrlWYLDrhDAo62YQOsNvwZLw0X7pGAkxIyJFWBgHsy8CHhf9tb3Pj4q8IrxWBXhRs1iBYc6ww4fg1e8sN3ScAhiwzJVoAhJ1dc/2YnNaq5oWsXRQBcdFHU5oo3epAJwBly79NPP73npptuchUVFRWoe+RgwnXRZmh3YBQqu4OuMQuyiX0a6GQHCuhs1D1GX7VBKVhp7APgtfvSS4dcm1bUsQVn6x105FCAo6Diz3z00UedjY2NW6dMmXKL0ag19kHtiHil9nyj4b2dVYaOLvEVFhHvnpKCXs9XDUa3m44dpF1QW7TiB23UWdHdR+cw8DlDBlCgE/S30A5h9tOpLubsaG/r6JWnJ+gNrtbmrRdO7sYMQva/GXK1nVrAsQGaoCDMvufgwYP/N2nSpLgB/sJb6w0tHWhTZgV3t4teidBNjYbkkEhPem2/uCyjpt1fu127f7LLa4+vbZ+2Pdry2vWB9tdu1+6vPV6gvN/vMzTUHv8/Ku2hBaz2gxs1hTLLqIEB995///07aPpss661QS1RhEyEG93LtxbQKxH4+7gQPdLAZRGLad5fzOMyYoxyXEZMi2XEtFhGTItlxLRYBmltQFkE3ofTSmYE/w20v3Y7H4dj7fECrorP42mu2rZhB23VvcDEnuEAh9n30MxC5/nz59+LF+A4aKaGAOT5wbxpO6M9X9jOedqyqVjntujxo21Tostrjxfheldnx3tuu91JxWHBQ15goqpQgCMfFpytuGfTpk3v0PtSkJ/zAZBbLL2QMyRiDIUYCjEtlkllWmyTmNZrk1hGTMervF49YfL99JbNpvrat6k5onsCXvsFPcDhpuBXofjhS5cuPdzW1nZKWvGAfhaGvJ+cMiPRCoBBj8t16tCOTUfoWAw4WAWz/UKoi0wUQmGGHJW4aUx8TVlZ2YP0DR9sz/kAyBG8HsgjQ7IUAOD2jvY1dDwMa0F8hjsk4HoWHO2FyVet+MqVK9+hh5Hl2YQyvQGQm/PJXQkXcKcCge9YcFrJDPFftOVDVJFWWdH2Z4Dy9PpjT92R/e9QH8EiPAwwGtI9oXxdHxzb8ItgP9z90ksvNZ45c+ZdOf8K0vQFC42sKJAzwNoYRfmkiWltOV4Xy4hp3p5psdgHMa3XD7GMmKbyALKrs/3dMx8faKQkLDgAB6MhrTflRww4fi3uV1999QWy4rq/FlSYi0GB3GLJxa4ntc9+r9d/5tjHL9BB2T2JCXA0PsiKP/nkk7Vnz559X1rx/ueVIQ9z8a+OJMsygYGmaHTAXVdnZ+f7x/bsqO0FfEC4cZbC+eDYDmutuimU7v7rX//63x6PR/dPAnbK1QDITcoQYq/fDSHwp5hjMR3I7b9d70+3Xj7XPdj6Yt1f266B6htou7a+3nW6c9lTf/Lwf9PuuJ0suidhPYqBAEdzUAEcecVN+cMf/nCMrPg2acUhTf9goYtOk5ncFT5RKII0B07rbedykcax1hfr/tp2DlTfQNu19dE6Rk4c9o5th3d+cIxW2T0Je3HJ1UQCOKy1Cjilu9etW/c8WXGuQ8YaBVTINflydXAK+H007+TUyedpb7begA9MDuhJRAo4W3H8erqXLFlS3dDQsEtacVJDJ0jIdYSJMhvW29nVuevAtvXVtCsDztY7LoCjSWzF4dgDctfatWufkSMqkEY/AHIzja5gLjMHTgcm9AfyOQ9lOM3bo43FOsS0Xj1iGaQHCtG2D/XxPmKa2yPmiWne3uP3+Zvqjj9D21y0gD0wGJH1pnIDXmSiDAIAD7rYfOihhw7Su8TflnNUFH10/zPTRafJbFZOMp9ojrETp/mEinm6lYbZEG192vLcDr042vZp69fuH247psR2tDS/XbVl/UHaj613RKMnLFEkLgqXZSuuXGzigA8//PDT9GByK/6MyKCvAEOuX0Ju0SoAprzd3a3VO9Y/TdsY7oh9b64vWsDZF8cBXZs3b75UWVn5Z3nByXLqxwHI5c0gfYWCt8B6Nzde+HPj6dN4VhDuCZiL2Pfm2qIBHPuIVlyB/Pbbb3+THmurlhecLKl+DH9cHULkYuyfI45kwX68j5jmfcU8Mc3b4x2LxxDTescRy4hpoTwezXN1dVVvfeuVN6kIwx219Ub1gwGcrbhysYkGrFq16jGXy+WTrgokDR8UyE00iZNPKIojHWngsnr7D7Q90uNEWi7a4w1QHgz5vF5f3ZEDj1ETADdfXEZtvdGFaAHHPgw4flGKFX/00UePHDt2bDW9kRbbZRhAAYYcWMslWAMDPcxgb768mm7qYL631nqDvajCYADHARhytuLOBx544NnLly83SlclMv0BuZFGV2ToU6CH4HY7nI37Nr/3LOXicTSt9e4rHGFqsICzL66Oi+/du7f5tddee5g+f+KRrkpk6pvplr4CObsbORwDKBpy9pyuqX74YkN9M60y3FGNe2uVHyzgqIchV604DRvuI9CfpRtA2uPIdR0FFMjlU1L0pQcvjZo0PHvggw37SCqt9QZrgwqxAg5XRbXilHbdcsstK+vr63fIG0CRnw+GPFf9cbpbaejqaNuxZfXylWCod+G7lmAsJYDjDOLgWPiCE788x4MPPvgb8sc/kf44qRFhCECeez45/O5up/OTqo3v/oakwuvOwBBcADDFfFFycCEWC85HRCMwfIJfHBrnpBtATa+//vqvyB/3Sn+cFIkw4Ja+URxCzHKfHGaZ/tJ76SmdX9FrIJpoVeGHYrDEw4KUHHyIB+BoJxrDrorSSHrVRNWePXuegz8uIY/8BCmQG7P/zQVgAn735aYLz+3fsq6KFBLhjunCUlQ7HoCjPoY8yFVZtGjRcpqQtdGtvLhSPKxMh1MgYMkBefZ65TRJ0NDZ1rpxy2vLl1NHta4JDCaYijnEC3A0hCHnURU02rF48eJ/oYvOvRLy6M6ViVwVoymepye64yeytI8sd1dH+94tb6z4FzDSu/CwYNzgRh/iqSAAF/1xNNhBX4nouPPOOx+iByRq5KQsSB55YMizyRXHiEm3vbNm99o3HnJ2dHSQGgBchBsMxcV6Q+lEOHtonNjAPILcW1dXt3P+/PkLPth/eGgePqclQ0QK4L3synvBs2BKMmYIuhz2c/s2rf1ZY33tJyRAFy0MONzbuFpvCJwIwFEvQhDo5It3d3Z27naYCm6mGXWFPNE9UFT+H04B/vhAgPHM9Mv9fvpglNPZfGjnpntOHzl0lvqrhTuulpv1TBTgogVX0wcOHOiix7j2Dx899is0HJYvIefTMHCc1/uFjUwckcL9EHphpv34gY/uq9nzUU0IuGG5AXjcQ6IAR0MZbI6VxjfV17UVlQw5OqR0xEKah2GRkEd+TlXIIW2GGHK86tjtcjnqjx74pwNb38dteNFyx228W0/FRAIuHhOQq6BfqD1+0WIp2Dds1KgFNCRmkz65KFX4tAp5Bvjk8LndDkfriQN7fn5g6waMdTPcGPNOONxQMtmAq6DTnasWn8e1s7R8wvVkyEv4xKFRMoRXQDUIiiGnz16n4b8eGud2d9kbqnd+cC+9bu0o9chOCwBnuHEzJyF+N9WrhmQBjgOyBVchv9xwobOro3XbqPGTrjVZLGV8MaW2TiZ0FQhATlKyqrolk78B49z0HsGT+zatua/uyMHT1AKAnXS40fNkAo7jITDgStx++aKz+cLZD8onTZ1NryEeA59c+uUBoQb6X4UcBdPAJ8dwJt5CRTMDD+xY88YvGs+caqCWad2SpFhu1i7ZgAfBTY1Q1umdz+7zp45uHXfFjCn0AstJeUYJOZ+ggWLVXUmxKcesQHqWkm6/t2zd+saKJW0Xmy5T2xlu3MjBDMGkwg3tkg04jonQD3S60vbVVh/cPmbyFGu+rXA2+eRkyGGWZBhIAdYpYMST75H30Bg3fcqlp62p4eWNry7/N3rVWju1WYQbF5RJhxu6pQpwHJsh5wsN+nit13+quupA4ZChNSVDh881mkw0wiKtOcQaKEAnCJrMoMwIpJESj6Orlaa8Ltv2v6++QeeQLybFO5QpgRtapBJwHJ8hR6yCfuFUzQX6U7d5RPn4K8kvL5cuC6QaOKiQJ8EfJ2/bgItJR3vbwb1b1v3iaOX2Q9RCWG223HBJxKHAZP/+FMHSBXDAzYsCPV18dp06eGBjecVkk7Ww6DPSZVHO14D/sbsyYMEYCuDOpNfj7mlpOL9yw6oXf996sQFvn4LFZri1k6diOFpsu6YacLSef9lsxVXQ6c+d79ShqoN05/NI0TByWYxwWWjAQPrmYc96nz7xNeWBhxRofNvpbDl7rPpX2/73f97qdUkYboxx88Vkwm6/h+28ZmM6AI4miZCLoCvp86eON9ibWzYPHVk+mlyWKXBZMC7WdyI1vZKrvdqwrLEJArAxSoJvgna0XNpctXntkqOVHx6mWtliA3DtDRwYqpQHkJJOAe3BXFr88PCmSist+OKqjZZCpK+55bYvVEy78pcFRcUT8eRL3zAZbZWhnwIAM5bAw3/dXfaz9SeO/vve99fsovrggjDUPATI011xwPj8smJpeO++6QY4mhUwzwHI8Zg5vrQKyBl0W0FJSfENt/39D0pHj73LYrVayXWR1pwE0guBGYjRMaeOkNBDtc1NDSs/XLP6FVdnJ0ZIYKmxAGz2tTFKwnO5ozsQ7ZjIkC4uiraPLBJiLLAK7NP5vG63t/bQvkMOR8emoWWjx9Fr0CZKt0UrYd96nyvHtkM/xhwudkfsra0fHtz6/j/v2/zuB6S5OPwH6y3CjfMT25+KvubGNZWOFlzsINrHLgtbc7bocFsUyz7vq9+6oXzK9PsKCgsraE6L4rb0nVSxutxOByx5aA3Yz/aRn+1yOOobT598dte6N7dTabbUbLUx9Mc3btjosEEKXXkKc9MdcJYGkDPo8M0BOfvnCuRms7lg7uJv31Q+ruJOa1HxVLzcEv65BJ0lDB0z2LiAJD/7VNOF+pcr1/5tE72uhMHmmMe1RV87La222NNMARxtZmsO0GHNGXQAzrAjtn5x0Te/VD556p0FxSVX4Y1RmIorQSdlhKCAjfFsL1lse+eRptOnXv7ovbd2UBGAjAVgcwyLDbD5jiTATlurTW1TQyYBzo1Gm3HtwKDDmrNFVwCndSW+5uavXzNu8oy7CocMuRpfVgi8hiF3hxcDLgpm/GFilMfg6OjYf+H08ZU0MrKXNGOwxVh0RwA1X0hSMjNCJgIOZdFuLKLbAovOQ4si6PlXz7959tipM28rKhm2wGzNL8TrGHLlopShxoQo3Fr3drsdXZ1tWxtO1byzf9v7uL0OiEWokYa15kX0szPCalPb1ZCpgHMHGHIGnV0XBp0tu2Lli4eNKPrc/C8vKC0v/0phybA5NI5uogldivuSbePpGAkB3JifjU+CODrbqlqamjYc2LZxq73tMm7QsHUG0Aw5Q83DfrDaGeOOMBRinOmAc1+0oPONInZfxFix9BOmXjVq+py5Nw8rG/kVmp47lV+XFvDVM8+NUS11H9R0S91xqq350oYTVZXvnzt15CKJxQAz3GKMbbDWbLEzGmwRDE5nQ8ygIwbksOhs1QE54NbG5qu+cMO0cZOmXW8bMvTqgsLiWQS7FW95hc+ersAHA+1XXmRJlrqbXqxz2NnRvv/CmZMfHtm1/ST1F9YY8ALmUDG2s8WGC5IVYFM/lJAtFpz7wzH6xbADdF4AuBZ4xaJTvrKtoLjYOuvaL80qGzPx8wVDSq622Yo/ZTSbLLhbqjwzqsxPp9JKCMiXqBGaAMQ4UMD1xU0Y8jsMmM2HJ9ZpLprH6bQfc3V07m9uPLvv8J4dh112O1wOhhpA88KA8za21myxldqpfFaFbAWcT5IIOvx00VcXrTtDznkcm4aWlRXOuGbeZ4aXjfm0xVYwMT/fOtFsLRhnwsMYyvCjUQG/76KVD62NtVIHoNWWUiAmoHFRCJAVX5pi+oKdk+zzBbe7+6zH6Trb2tz48fG9O6vbm5txVxGQsiXmmMFGzHmIGWjRvw7dGCqc6UGreqb3J1z70VcAzjFbddGVYbC1sVhW+aFUzPzMqNETJ1YUDyubaLUVVeRbrRNMFnOZyWguzAvAj9fToZ6AmwPLjxUKCk1EMltoir30OJOjhyD2+b0On8fb7O7uPtft7Kq3tzWf/eTs2fr6mmr40AylaH1FeEOlxbLsfnCstCeb/2PNs7mP2r6hz+ICeNmycwwwGWqGnde5jBgjjUWsN4/cHfPQ0lFF9PidzVpUWFRgK8KMSIPL2eXo7qLRuvZWZ3vLxS5yKwAmuwgcM8yI2eqKMdIMNEOMdbEM78t1ckzFciPkIuDimQ0CkjYwqByL8IuAY7u4jcujPqS5XkoGpbEuBhE4TrN1ZcC1wDK0DDWvc3mOuT6OxePmTDrXARdPNGvBcCLWgsvrDDEgRzmOOT9UXTgW5wM6BIZPuw5IkQeIxZjh5RjbxDTWsSBwHFjL0f9Z8BztfthuszaIwy0i1NpyOIBYj3hABlCMkdYuDLs2X1xHvViXQaMAi6/Jlqs6CrBeDDKKhcsTt+tUGQQmQ8rwYp9weXp1yvxeBfjkSEFiU0Cro3YdtWvzGFzxyNo87bpYVqYjUOD/AZrbm7Ts1rpFAAAAAElFTkSuQmCC',red:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAk/0lEQVR4Ae2dCZxUxZ3Hq8/pnhkGmOEQuQS5VCTxWHEDBlyNkciakMMkxujGuOvHO24IKCae0UQlKwmyroocoqtozGpA4oFiVAQU5IaRcchwDsPczNF39/5/b+bfVL/p7ume6bur+DyqXt31r2//5//q1XvPIJRLpgQMMVYeiDGfyhanBGKdgDirzYvsLLtIPguB0/lc7+vh5vNIvr68Oo8ige6EH6Vo3iSxjODrw1rcm2++2f+MM84YV1hYONRkMhUZjcY+OAKBgOZTXDGVLTYYDH0gNYpvIa/V5/O1+v3+FopvgY+D4tra29uP7N27d98VV1zRiOy6g06DcRyGr1wYCfCEhUnK2yiWiQYvSQG+8ac//all3rx5o/v16ze+oKBgnNlsxjGWQB5LgA5MhrToh1BL0Fd4vV4c+1wu176mpqYvHnnkkf0rVqzwUJt+OuQfALrBmh/hvHc8mfkuCBlmDei5c+fab7nllguLi4svttlsXyeYzyPgrJkgKPpBuQn4LU6n88PW1tZ1ixYt2vjoo486qG964PMe9nwGnKE2EhgG0tDWBx988HzS0BdbLJbpdEymeFsmAB1DH5wej2cTHR+Qhl937733biYN76ZyAFyGPoaqcitLvgEeAvX1119ve/jhhy8nLX211Wq9lKYWtnIuuFa3272WtPv/3nPPPW8tWbLESYPKS9jzAXCGGj60tfGLL764YPDgwT+x2+3fp/MyOnLZ1Tscjj/X1NS8OH78+E9poNDoslbPaTMmlwFnsDWoN23adNq4ceN+TBeIP6FVjbG5THSksdEKTQVdqL64b9++lyZPnlzVCboMe6SiWRufi4DLYJsqKyunkraeCxOELhJzcbxxw0cXqQGYMKTVHz399NM/pgp8dOQk6Lk04RiLpq3JNx04cOAbAwYMmEurH1PoXLkIEqDVmPV1dXWPjhw58l3KwqAz7BFKZU90LgAeBHvYsGHmjRs3/mtpaekcMkPOy55pSH9PyXzZ0tDQ8NiFF1646vDhw17qESDPetCzGXD0XYObwLYQ2FeVlZXNoRsvZ6Yfl+ztAd1Y2lNfXw/QXyHQ9TeTsm5g2Qp40BTZtWvX+aNHj15AGvv8rJN+BneYNPrm/fv3/2LixImbqZuy6ZLBve7atWwDXNPYNAzT0qVLS2fNmvUA7f+4gS4eAbxynRIwHN8ozDseFQZHTW9l4m/3BBa/8nnDfT97vKqBKmPQs2ZpMVsARz8ZbvPRo0ev6d+//yNkjiRlD0hvqUh3ecsrpwtD2+GEdcMfELUNbWLewNniBaqU7XNAnvGgZwPg6KMJx44dOybSstYf1coISSOKsy4tiJLa8ySPX6wvrxN3TLpX7KJaoM1xZDTkmfynPaixJ02aVEBX+PdOmDBhg4K754D2tqTFKKacPVhsOPEnce+kSQK/IjMdYChjFWWmdozhNq1evXrYtGnTltKNmq/3doLypXyyNLgsP49XfPhOpfjZzCcEbKGMtc0zEXBeITFXVFR8g5YAn1O2toxW9+FUAI5ewDY/UC9+PvrXAjeJ2DbH2nnGuEwyUYJae/r06bba2toH6O7a6wrujGGlS0eMBjFw1ADxetMT4oHpZ2lbi3GtlFEmS6Zo8CDca9asGT516tTltClK3WLvglRsEanS4HJvXF6x/v0vxHXfWigOUXzGmCyZADj6oK2S7N69ewrdtFlJWnuALDwVjk8C6QAcPSSTpa6iTvxwwm/EejrNiFWWdJsoDLeZdv3NHDNmzGoFd3wwZ1JuMlkGjBsoVlf9TsykfmGFBYorrUo0nYCjbQjAQjv/rhs+fPjLFLbToVwWS4Boto/sL14++ri4joZhoQNznDbO0tVwEG66K/nLIUOGPEW32/GLVy43JGAeUiKeqvsv8UsaTlohTwfgGtz0EIKV9iE/RjsAH1IPIuQG1SGjCAhDWaF4qHmBeGzwYIG3EaRFk6cacA3uGTNm2OnFNs/16dPn1hChqJOck0CJTdxaeY94bsZkzfxMOeSpvADQ4B44cGAB3cBZRg/8fi/nZjNDBpSuVZRow3d4xGsjHxT/VlsrXJSPlxGjFUlIWqo0uAY39dhaXl4+X8GdkLnLqkrsFvE90uTzwQAdKdPkqQCc4bZUV1fPpXeQ3JhVM6M6mzAJ9LGJG+v/IOZShSm78Ew24DCB0Ibl0KFD19Ozkr9JmLRURVkpgdIi8Zvqx8X1YIIOsJFUMzmZgKPj+FNkoQvK79CqyQIKK6ckIE4pEQsqHxbfIVGwJk8a5MkCnOE2b9++fRrdfl9CS4GAXTklATwiYRpVJp7bfb+YRuJI6h3PZAAOuFGv+a233hpPb5NaSeHkPGJCFSuXnRIgSGwTBomVb/2nGE8jAORgJuGaPFmAm+iB4KIpU6Ysp70lJdk5BarXyZaA0ShKLh4tls+6QBRRW/gLn3DAE10hfjDoaMHx48ef6Nu37w0UVi7FEsjEdfBoImh2iMX97hR3Up6Er5EnUoPjx4L6LHv27PmugjvalKo0WQJ97eKGLx8U3wU7dIChhCneRAHOcJtXrVo1dtSoUQvlAaiwkkB3EqAngxauuk2MpXwJtccTCbj5kksuKaLHzZYpu7u76VTpegnAHr9svFh2yQTNHgfkCdHiiagEPxLN7qbnKOeXlJSoO5X62UvxebbZ4LJ4yB5/muzx2RSXEHu8txo8aJps2bLlYtodqOCWZ0uF45YA7T68cfu94mIqmBBTpbeAa9qbzJIiWu+eTy9Wj3tAqoCSgCwBIETr4/OnjwsuHfaK0d4UZu1tef7552+nF/OMkzuqwkoCPZWA1SzGvXS9uI3K93pVpaeAM9zmF154YdSgQYPwaJJySgIJk8DgvmL2C/8hRlGFvTJVegM4Liwt9HTO78k0KUzYyFRFSgIkATJVCq88S/yOgr3akNUTwFl7W3bu3IlvTH5LzYiSQDIk0KdAXEEbsi7vhBysgr24XE8ARxnzlVdeWUw3dB6LqzWVWUkgTgmMHSgeu3Ky9oFeNlXiqiFewFl7m5988slb6XPXI+NqTWVWEohTAhaTGPnMLIGH0xnwuLR4vIAjv4k+gV1Cr3u4Kc6+quxKAj2SwIA+4qbrpwjsSsV1X1zMxpOZtbdl3rx5P6cX0Zf1qLeqkJJAnBIwmUTZ/TPFz6lY3MuG8QCOvGbc1DnllFNujrOPKruSQK8kQG/Kuple0Yx942yqxFRfrICz9jY/88wz15DtfUpMtatMSgIJkoDZJE5Z9mNxDVXHgMdki8cKOPKZ6Fs59lNPPRV3mJRTEki5BIb0FbdNOj2+N2TFAjhrb8vKlSuvIu09IuUjUw0qCZAErBYx4i/XiasoGLMtHgvgmvYeOnSolY47lKSVBNIpgWH9xR0lJcG7m93y210GaG8c5ldfffVfaEPVmHQOTrWtJEAbsca8f7O4BEzSwXxGFEwsgGPt0Ux3LX+ktsNGlKNKSJEEsJ121CDxIzBJB9gE5BFdd4Aj3XT11VfTM8R9Z0SsRSUoCaRQAn0LxIyrvyb6UpMAPCrD0RLxy0C6ee7cud+m5yzV50VIGMqlXwL0/Kb9nsvEt6kn3S4ZRgMcaRrg9P2cHyjzJP0Tq3rQIQGYKSP6iR/QGQMekeNICdDeOExPPPHEiMLCwq91VK3+VxLIDAkUWcXXnrhaYMma7XDw2sVFA1zT3jNnzryKtHekfF0qVBFKAqmQABFpnDUxRIvHDbi2ekKPo30/FR1WbSgJxCuBwcVBwFmLd6kinGbGLwHxphUrVoynte+xXUqpiIyWQKBoWEb3L1GdozXxsS/9u/Z2Wl5N6aLFYaTrXRDwr371q9PUxaVePJl/3nzef4uaN28S7hNHunQ2EOgSRa/r1rkuEXild1enr6unecJVHktd9OlwaOJp1LPddEApg92QotEANw8YMGCqApwktmen8K9cIURTI8kv810BdXGI72JR73LR9+ND5jvzOx9nD80u11QhVj1DxRjwkBr0gOMXoGnwoqIiM72p6sKQ3Hl64nv0fhGoPZ5Vo8ff7P5+v2jw+Eil5S7kfQKBC8FqW1sbAx6ixRGpd4gzLVu2bKLJZCrVJ+bjebbBzXNkoTsipfRQo0HTWRybWz7BWvqHkYMn0qjYDg8ZoB5w1uCmM8888yJlnoTIKitPGHIj3R3hyc0lHwCPLbRdRB4A56EF5yoc4Igzkf09RQEelFNWBwB5f3okJhfnE2MqNZumgFk6wC4gDzoZcKbfRLfmrWR/Tw7mUoGsl0Ao5DzVueEXmUyThxcW8heUeVDanMmAIwLnxoULF55Nv4w+Wg71X85IgCE3AoEccjScPr8ZderZNCSNX3lo8ioKk28aNmzYhFz8cyYPPF/DHZAbRKPXmzNrK6B6qM0ygbxP6WCOtaUjWYNzgpH2fo/JVwDyYdxmUuH9zWZN3eXKePuYjGAWPDPH2tD0GhwZTLR7cIzS4Jp8cvY/QN6PIG/KAU0OVouMxtPBLh0MuTZ3rMFBPRzOjQT4aO1M/ZfTEmDIc8Emt5s0wDV+OydNY5oBR5ym2ktLS802m21kTs+sGlxQAoC8r4nMFZp9DQAGIct8m9EwstRuh0XCw9DGqAfceP/992MrGrYzKJcnEjgJOdjIUhcQBbcPHQx2wXRwIGyDM/XGs846S22PzdI57k23AXkJmbAnfNm5dwUAn1mkbe3+ohNwRAVYgwcBHzhwoLrA7A0pWVxWg5xe5Wo8qQCzZjQAuNRs7rKSwhocAwHsRrvdPhwnyuWnBAB5H9LkLZomzy4ZFJmNYFfjmHuu1+AGevdgMScqPz8loEGuafLsGr/ZYAC7bI3A1x6751EgwkhbZIvVGjiLJH99QF5Mmrw1SzQ5mKVFcAAOpa3BjdnjkyD1BDheMq6ckoDQNDntQsQSYjY4ghzsBllGWLbBMQaDAhxiUI4lYCLNWEzmiqbJM/zBIKvByIBz9zUNzica+QpwFofyWQIMObGe0c4kAgx4sKeswYMRCvCMnsO0dY4hb/P5M/YZT7NJ0+AsI41pXkVBJCKUicLiUX4XCQDyIhNWyYP6sEuedEZE0+DcLwU4S0L5YSXAkLdrmjxslrRFGmOxwal3bLakraOq4cyWACAv1DR5ZvWTVlHArmaJcM/YRAn+zfF6ve2cqHwlgUgSYMi7rDNTASYs1b7PH5DZ1Zjm/gXH4fP52oInKqAkEEUCgJz2YWeMRU6Xv3p2NZWO1c3gCqfS4FFmVCV1kQBD7qS3aKX7LXE+v1/W4BrXbKJwxwNKg7MolB+rBAC5jd69Ql5anS8goMGDyhqdkS8otQQFeFrnKGsb1zQ5Qa5p8jSNwm8ImihByMNq8EC6/9akSUCq2d5JAK+H0zR576rpUWkwSyuXETW4Zq9QzQFlg/dIvqpQpwQYche9vDvVb7X1BgRs8CDL6JKswbUEAry1s6/KUxLokQQAeQFtQUz1HU96FzrYZcC1vss2uBbhcDgaoO7VnvAeza0q1CmBDsiFcPlTIxJQ7aTXoetbYw3O1PsbGxv/oc+kzpUEeiKBk5q8J6XjL9Pk9YBd/KSYZ81EwQmcFllRUbFfXWR2CET933sJAHKrZq4k9w4nelrldOwnLwg3wqzBka4lvPbaawpwSEO5hEkgCHkS18kB72v1zXrAg+vgTL3/7bffbnG5XLVms3lgwkaoKsp7CQByC0nBo+nRxIvD7ffXrjve1EI1dzFR0FoQcGRobW2tUmYKxKJcIiWgQU6gJ1qRg9U2X6CK+gq4wwKOcQByLUN7e/s/EKGckkCiJQDI6fUOCd9x2O7zgVkZcK3rbIPLGtyHlRSlwRM9tao+loAMOcf1xge8TT4vAPfREaLB5XXwIOTHjh3b35sGVVklge4kAMhhqngTsC0E9dR6fGA2BG70IZwG97/xxhs7aNMVgFdOSSBpEsDNxA5zpXdWuY/MjVW1zTuoowA8BHIGHIMA0Ej00Udga5ubmyuVmQKxKJdMCQByE/ENfd6Tf6C2xR+ofPFITS31UzZRtG7rAWfIvfX19ZsU4MmcWlU3SyAIeQ8UOYCt93g3keelI0R7o34ZcJxrGpx8X2Vl5UYFOESiXCokAMgBI3lxHTDkqxyujVQU2ps1eLDLMuD4MbAG9y1ZsmSTn1wwpwooCSRZAgx5PM3Qg3L+JTX10OAMN3OsVRMOcO2XsGrVqkayw8uVFo9H3CpvbyXAkMNa6e7AQ6DNXl/5W8fqGyk7a/CIgKNvrMFhz3hpufBTBTjEolwqJQDIAXd3DrDWuj34+KvGK/lsgweLyhockQw4fg1essM3KMCDslKBFEqAIY+mxdGdynbPBvIAuGyiIElz8o0eRLB6xy/Bu2DBgk8vvfRSZ1FRkU3Lnaf/VRaVCM/xmpwffSRlBijCuUjxyBsxLUJCpMfbkB39AvD6/jn8fufjh46wBmftHdJCOMCRUbNnPvnkk5bq6uoPRo8efbmRnphOhGvbWiGO/c9fhaeuORHVpaQOt+8rwlmCb7uHyC6k7UgpUctEKBStTEijnSf6iZfzRGhCyxJvO3K96Q7T42mi2nnig21N5dhByPY3Qx7snh5wJEAmmgYn37Nt27a/nnbaaQkDfP+dTwp3dT3aySrn9HtEu9+r2YYQUCw2IgbIeRk0lOO4cOmIk12q88ttI8x9jdR/fX79eXfl9en68pHG7w34xW5nzV8pv4cOeQ08pIpwahltMuDeW2+99SPaPlsfTUuE1NjNSTbCjSEVGS2i0NihD2KFG+U4L3w5jDQ4OY7zyL6cRw7LeeSwnEcOy3nksJwHYb1DXjguw2EtMob/uiuvT+d22Ne3h3iw6Az46he37PyITiNeYKJsNMCh9j20s9Bx+PDhvyUKcDSarQ6Q2wnyaNf4nMa3nTFWjsuEcXNfYu1fsvP3RCbQwLU+598a3W4HBaHBw15gou5wgCMeGpy1uGft2rVv0OskEJ/3DpDbjCYNWoZE9iEghkIOy3nSGZb7JIcj9UnOI4cTlT9SPdHiAeZ2Z93r5MnmCaK7uEiA40eCXwWo9sydO3dXU1PTl0qLd8iPIe84U/+nUgJgsC3g+XJJ8+7d1C4DDlbBbBcX7iITmZCZIUclbloTX1VWVnYnfcMH6XnvADmcKwDZKpcqCUBN13jbVpHnpoPNE+a1SzciaXBkRF1BLb58+fI36GFkVKhcpwQ0Td7lS4xKPMmUgFv4PG+3HXmD2pC1d1jzBP2IBjh+FSgIM8W9dOnS6qqqqjfV/iuShuSwsmJTkEsSSV4Qa9+1Pseb77ZWVVMr0OBgE4yC1bAuVsA1M+XFF19cTFo84q8lbAt5EKkgT80kuwMB/7q2I4upNTZPegU4eh2ixefPn1958ODBd5QW7zqhDHm0q3+V1nMJkPIW9f72d149UVHZCXi3cGOWomlwpENbB80UCrteeeWVZz0eT8Q/CSiUr64DciwhnnQcjnbjArk5PV6fy3KL+va6q6+35fX1d1dfd+n6+vjcL/yBjx3Vz1J5Fx2yeRLVougOcPQHFeBiUzNTfvvb3+4lLf53pcUhmq4ON4IKDB2QY3Lg2JfDPHFyHMLxOq67p/X1try+v93V1126vj6cd9jezr+vaCrfS6dsnoDJqHCjbCyAQ1sHAaewa82aNc+QFkd55cJIQA85w5cKH91hiORwKtpOVhs+4nij89gzNB7W3oAPTHZrScQKOGtx/Hpcc+bM2X706NENSouTNCI4QG4lTZ5qx3AzbGif41Ldl0S0B+1d73dtWNy4ezvVx4Cz9k4I4Ogna3EY9oDcuXr16oVqRQWiiexOavKTiOEyC44vtzisRXbGcxznicfnsrHWp8/P5SL5+v531zd9/fry3aV7aOVkk+P4QsrnpAPsgcGYtDfli8lEQT4ADi3OgLtmz569jd4l/rraowLxRHY2TZPjY6kd/5CTJ1kOR0qPXHP4FK471vr0+blcJF/uc/gehMbq69eXj5buoy2xR31trz/duGMblWPtHdPqCfciFhOF87IW1y420eBdd921gB5MblR7VFhE4X2GPHyqig0ngY49J97GxU27FlA6wx2z7c11xgs42+Jo0Pnee+/Vbtq06U/qgpPFGdkH5FhdUS42CeD5qb2exj997qzFG6tgnoC5mG1vbiUewFFG1uIa5LNmzXqNHmvbri44WaSR/QLaZstLiJyLrXP4sRwox2XkMJeV4+Qwpyfal9uQw5HakfPIYTk/tHej37X9vuMbX6M8DHfc2hv19wRw1uLaxSY6sGLFikecTifegYg6lYsiAUCO1RWeUGRFOFbHeSOV7y491nZizRdve93lB0Nu+qD8O22HH6E+AG6+uIxbe2MM8QKOMgw4flGaFn/ooYd27927dyW9kRbpynUjgSDkeP+HOkJkEKBfwCF/68oXmvdgv7dee4O9uFxPAEcDDDlrcccdd9yxqK6urlqZKrHJH5BbeqRfYqs/G3NhzftEwF39ZNPORdR/PI6m195xD6ungLMtzsuGzs8++6z+pZdeuos+f0JLl8pUiWUmGHL82c73A69hcwm/5/3WQ3eVOxrw2gWGO651b73cewo46mHIg1qclg23EOiL6AaQvh11HkECgNysNDltdPKLfe6GRU837d5CotJr7x5rzN4CDlMlqMUp7Lz88suXHzhw4CN1AygC0WGi8x1y3NCp8To++lXN+uVgqPPgu5ZgLC2AY6rQOA6+4MQvr/3OO++8j+zxGmWPkzRidJq5YuiNvomxoQzLBru72e+pWdS46z7qWjsdYAgmAJhivijYM5cIiaITWD7BLw6dc9ANoGMvv/zy3WSP0zeGevzjo6ryy2H50EKQR7pNnmvx0MvugN/7vuPw3Vucx47RbGv8kA+WeFmwVxAkAnAQjM6wqaJ1kl41sfnTTz99Cva4gjz2OQLk+DBTrjswAbt7r6fhqacbdm6m8cpw9+rCUpZdIgBHfQx5iKkyY8aMJbQh6123Gz9I5WKVwElNnrurK16C+4i39d05NeuXkFz0pgkUZkL+9CcKcMwdQw6acaGATrfPnDnz13TR+ZmCnKQRh4OpYs5Rm9yjXVS2f3ZX3YZfk0g0TsjnZcGEwQ1xJxpw2R7XIKevRJy49tprZ9MDEuVqUxZEHrtjyHNpjRwrJvU+R/nDjZtn13scJ0gaAFyGGwwlRHtD0snY3obOyR00EOTe/fv3r582bdr0pmXv9MVXbpWLTQImklWHQGWRxlY203IB7kaf69CC5p037XDU4osCbXQw4DBvE6q9Mf5kAI564UJAJ1vc1dLSsnFUZctltKOuUEHeIaRY/gfkcBBotq6k+KnzJwKe+mUnym9c13roIA1FD3dCNTfkBZcswGV1Ewxv3bq1rcBk+Xycpd836c+vVUHeMQmx/M+yCgozlkIZkoe2mYrWgKf19ROVt/y55cty6pYebmhuAJ5wlyzA0VGeC/a1zm9z1jaVme17hluKL6HVAgtPXMJHloMVsqxCBJrh4+yA292+tv3Ifz7btAu34WW4E7beHUkMyQRcbhNzEpyXTY5jx+kJly2jrSXTSZPbeeLkAiocXgIsq6Aww2fLiFjY3Cf8nsbX2/bf9mzjLqx1M9xY80463BBCqgEPgr7VWdvQbvCuH28tnUo2eR+eOHRKuegSCMqKTHOY55l44F0mDQHn0eXNX9z8yomKPTSiVjoAOMONmzlJsbup3qBLFeBokJVOEPJyV2PLUW/738+2DbjAZjCV8cVUsHcqEFECgDwoyIi50pOAde46n6NiYePuW9a2HfgH9QJgpxxujD6VgKM9OJ4XzT/gOeHY7W5Yd65t0CS70TRE24nRuWrQkV39H0kCDHmmrK1gZnH7/ZjXsfWRhs23b3HUHKW+682SlGhullmqAQ+Bmzqhndd6He5PHDUfTC48ZXShwXyagpynp3ufzRUIMp0OuwLpWUq6/d72wd21G+fsdzfVUX8YbtzIwQ7BlMINeaQacLQJ1wX0Fr/b9zfnwQ/PKxhUUGKyTjIJo4Enr6OI+j+SBGQ5YcU81Qfgdga8gQpP0/O/qP/4d41eB77yK8ONC8qUww15pQtwtM2Q84VGwEsbyN9srdo60FRYPsRin2wxmOzYS4AHc5WLLoGT5kr0fIlMxY5AvL+k2e9ufK/98Lz7aje9SnPIF5PyHcq0wI2xphNwtM+Qww+CvsFRfaTa2/beuILSM+0G0ynKZIGounephJxNkhpf+7aFjTtvp5WSHdRDaG3W3DBJ5KVAzHHKXaYADrj50KCv8rS0rXFUvXtOwSBTX5P1K8pkiY0NNleSSRNu3sAkKfc0L7+j9sMH97ua8fYpaGyGW795KrbOJyFXugHHkHgu4DPkmjanP3e+Na1V2waa7buHmAsn0/ZRu7YXQ5ksUVE4adIlducKcU0mCW7euBvWOo7c/UDtxr90miQMN9a4+WIyabffow5el5gJgKNLMuQy6Fp4g+PY0cNksoyylgymz4SM7nioS9nmurkMOT0JeUh0j05ga/toiuj78OKQr/W9RY3b57x64stdVBlrbACuv4EDJZV2l2lXb+gPrivxw8OXVgvosNFhp6MQ4TvKzvnni+yn/rLUaB2BJ1/4TzKlKRdGArCVe+PY1m70uw9+7Kz+wx/rt26g+mCCMNS8BMjbXbW/vr1pM5FlMw1wjA19wgHI8SVmKx2AnEG39zfbiu8vu+CasdZ+19HHWAvM2ESqzBYSUXgHDRwv5rxC0ub3uCrI1n6w/tMXGr1OrJBAU+MA2GxrY5WE93LH2xQVTZ7LFBNFP0IWEnwc0Aps0/mcfq/3rbYDO+r9zrUjLMVDaePWCGW26EV48px//Kw5ovl4wxSbI/Ty+Y+fa97zq0WNO9aRzOXlP2hvGW7MT0aYJCdH3RHCWDPZoX9ssrA2Z40Os0XT7HMGnP/1C2yDbulrtI7E64nx7lae1EweXKr7Bq0cybGd7SI7m9a1D3zmqln0WN3nH1J+1tSstbH0xzduWOlErjhSgymKz3TAWQyAnEGHbQ7I2T7XIDebzba7+p1z6STbgGv7GwvGKNBZdNF9GWx6J/eXO5x1z/++aetaejMZg80+r2vLtnZGam15xNkCOPrM2hygQ5sz6ACcYYdf8Kuy8y86zz7g2jKj7SwFOkkkjJPBJlNv9xZH3fOP12/+iLICZBwAm31obIDNdyQBdsZqbepb0GUT4Nxp9BnXDgw6tDlrdA1wOtf828rO+afJtkHXDTLZz7XiNQxUBIXz1XwB1KASa9n0Rilx3Of4fJPz+PKF9Vs/o2gGW/ZlcwRQ84UkBbPDZSPgkKzGKfmy2QKNzkuLMujWG0rPmnRhwZBvDzbbp9sN5kLAni8XpQy1n9AG1I6At51edPnBRlf1G4sbduP2OiCWoUYY2poP2c7OCq1NfQ+6bAWcB4D+A3IGnU0XBp01u6blh5qLi27od8b0Mdb+3xxosp9PoJvwch3Anmvr6Vi/BtRegprA9tX6HJu/dDe+vbhp7wf0RincoGHtDKAZcoaal/2gtbPGHKG+dnHZDjgPSA86TBi20dmEYV/T9FMKTx00q3j0ZSOsfb5ZYrCO0UyYLNbssqbuhBpfS/jyoLvl7f9r3f/O+vajx0kmDDDDLftIg7ZmjZ3VYNM4NJcrgMvjgTbHuAA5NDprdQDO0Mu++Yf9xo2dXDB4Kmn1c/uZCibShWkBPi+CR+gy1ZSRgcbmJzxJQ0t8riafaxdp6883uWo+Xtm0r4LGDG0MeAFzOB/prLFhguQE2DQOzeUa4PK4WKsDdD4Ath54TaNTvJbWz2wr+FHfsRMnWErPG2iyndvfVHAGwW7BBSqA7/jX0QwLL1kXrYAYjg1f+LhMBNC4UCSoPfSmqL21Pufn5Z6GLS83V+xq8jphcjDUAJoPBpzTWFuzxu6ongrkkuM5yqUxyWPB+Bh0va0ua3eGnOPYNw21FRX+oHDcV06zlpxdQvtfCg2mEYVGy1CrMNpZw7Mvwy93AmG9oBlafT6GGPYzQGbfLfyOdr/nSHvAd5B28x2scp/Y+Wr7vu1HnG24qwhIWROzz2DD5zj4DLRsX0fqDmXPbqeXe3aPJnrvGXT2WavLpgyDrfflvNoP5eLiYYMmWctGDjEVj+hrtowsMliG01cayugppEK6k2qnbWCFlNGMxhh81vRsXkAbgywizUuvWWinW+QOT8DX7vL76tsCnkPNXs+Bal/rwR3u+gPrWg/DhmYoZe0rwxsuLOdl84P96BLLgdR8Apynq4O5DqWKMOAFtLIPwBlqhp3P9Xk14DvrCKmbzB3zSGtx0RBjob2fuaCoj8GKHZGiJeBub/K62qr97Y4D7tY2MisAZofyPukzzPBZ68o+wgw0Q4xzOQ+X1ddN2fLD5SPg8syGAEkJMqx6kGXA9WlcDvUhzPWiLTmMc9kxeIjjMGtXBlwPLEPLUPM552ef62NfbjdvwhC+ch0SYFkwkPD14PI5QwzokY99jg9XF1rheEAHx/DpzwEp4gCx7DO87CNNDuMcBxz7HWd5+j8LPE+HH3XYLBv40Q4Zan0+NCDXIzfIAMo+wvqDYdfHy+eoF+fK6STAwtdFq9MIEmB5McjIFi1OTo9QZQiYDCnDizLR4iLVqeI7JcCTowTSOwno5ag/R+36OAZXblkfpz+X86pwDBL4fwN/IZwMBwH5AAAAAElFTkSuQmCC',yellow:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAC4CAYAAAChOH1KAAAlaElEQVR4Ae2dCZhUxbXHTy+zL8ywDDsSVhEVJQoCkoSIIr4kvohLxO2ZfC8an0mQrCQm+uJ7qHkv5hE/xSQaNokBogkxigaUuLDIpsiOMA4MOwyz7zPd7/yLOZfqnu7p7umeXut83+2qW7du3apTv3v63Lr31rWRkUhowOZViPc6Nutpbq/8WPVO8173sYtJ6kgDusI7yme2nYdTdIZQj0NH1vrgwYNtc+bMyb344otzCgsL87KysnLT0tKym5ub6+rr62vKy8urd+7cWfv444/XlJSUAGSB2VfoKw3HM9KBBqQzOsiSspsEXgmhCMTtDGT2TTfdNDwvL28kQzvC6XSOcDgcQ2w2Wzfenme323M5nuN2uwPql/O5OF+dy+Wq4X2rOV7Z2tpa3NLSsp9Pgv3V1dX7XnnllU/4RKnj7S5edNARl4WjRnQNBFS+njnJ4wKxHtp37NgxpG/fvlPY6l7CAI/kZTgv/YMBN1L64hPAzcAf5eUTXvax9d9x/PjxtZdcckkxH0OAF8gljNThE7acVIdbQLZzDyJuX7du3YChQ4dOycnJ+QJb5M+zFR4Qr73L1v4IW/h3amtr/3nw4MG1kyZNOsJ1FdglBOwpKakItwfQTz31VN4dd9wxnd2LL7J1/hzDPCxRSWDYD7BVf5fdmbeXLl26avbs2dXcFsCdkqCnCtwCNEL7gAEDnBs2bPh8QUHBnenp6TdyWi4vySY1TU1NKysqKl6cMGHCO0eOHGnhBuqQJ71FT3a4FczcqQgdu3btGtWvX787MzMzv8YWun+y0eyvPWzRjzY0NPzp2LFjL44ePXoP52vlRbfo/nZN6PRkhdvyoX/7299245GNe9iHvoMvBC9P6N6KQOX5gvRD9tGX8gjMovvuu6+SixRrjjCpJJngRluwAGz78uXLu0+dOvXbDPW3eL2QFyOeGihnyOevWbPm6VtvvfUsbwLcAnpSuCzJALcH1KtXr+4zduzYWbm5uf/OnZWMvrQnouGv1bD8ftu2bf937bXXnmgDPCkgT3S4lZXmDrGvX79+0EUXXTSbRz3u5fXM8Ps85Upo4FGWBbt3735q4sSJh7n1YskRJqQkKtyoN8B2vPzyyz2uueaaX2RnZ9/NN1bSE7IX4qjSfMOoqa6ubvFbb7318xkzZpRx1XDxKZY8jmoauCqJBjfqi8WB4bzNmzf/W/fu3R/j9R6Bm5o6OWyuErK1fMiKwuhfJ8VNZWUVtT/77MTHFrYNI8oIS8L444kEt7ggju3bt182bNiweXwHcXwnuy5pdwPYzrofMdiR8SaaW9wffFLc+N3RE/7xEStNrHhkCu/iXkgEuFFHBfb8+fMLb7vttkf4YvGb7II4u1g3CVm8vXEpORtfiGzdbbaWmpqW3724ou4/v/WDj8u5cMAd965KvMMt1tp56NCh24qKip7gmy99IttzyVWao3ERYekKcbnpxKmy5h/3HbVpGZcvdzzj1ooDnngUnHQOLI8++mhBZWXlC3369FlowI5tV9lt1KdPz7SFdaUTXnj00REF0kccxqWRjMdKWWBv2rTpUn7YfwnfWRwZ225NnKN3peXWtdDion0799TedfkXPvqY0+GLywWnni2m8Xiz3KgPLLbz6NGj3xgzZsw7BuyY8uH34E47jRxzUc47J/eN/wb6ixf0W1zxFC+VEWvtnDt3biE/ybagZ8+ez7CysngxEqcasNkoq6i785m6w1ctmDt3GB5xEMjjwiOIh0qgDjjJMG49hp9ae5Gt9fA47c+4r1a03BJvRbS43J/s3FN3R5ubIhebMR0Tj7XlFoudtm/fvmsuvfTSNQZsb2wSY91ptw0fMzrnrYObr7iGa5zGC9yUmBrPWMItYGOY7xZ+W/wvrIw8XowkqAa4Q/M+MzjjL0d2jL+FmxBzFyVWcOO4OLPT+AH6b/ELuAs5bp4LYSUkujDg6f37Ohee3ncVHjUWCx4TzmJxUAE7/eTJk4/06NHjKb7bGIt6JDpH8Vt/N9l7dnc8dfaTcY9wJWG0YjKSEm2oFNh80ZhRVlb2NL/D+KP47SFTs3A1UFiQ9qOakglPjx7dKyMWgEcTbgX2+PHjs3j6hCX8fMjXw1We2T/+NZCTY//65jeGLRk/vjuGdaNqwaN1NavA5salnz179jl+9evO+O+WxKxhrIYCA2mrtq71xdwLNt7P+Zp4kacLA+0W1vZoWG4BO4197McM2GH1V8LunJPtuLP84FWPcQOidpHZ1XCjfCxppaWl32Ef+6GE7R1T8bA1UJDveOjUnvHfAQ+8CBthl+uvgK6EGy6PAru4uPj23r17z/VXCZOeOhro1cs5t3T7uNu5xQJ4l7nGXQU3KqzGsfmF0+v79+8/P5oTR6YOKgnYUjfZ+vdLm7/vg7HXtwEOTroE8K6AWyy2kx9ZHTdkyJAlbY1IwJ4wVe4KDTAgacMHZy3Z9vbl47h83MkEhxEHvCvgRpnOefPmFfF49lLMU83rRowGPDRgs1POpRdlLZ33xJAi3iCAe+QJdyXSZ4sCmyuVwY+truA5RKaFW0Gzf2gaiNehQH+tqKt3vZkzaAOeRWnkRZ4m9Jc9pPRIWm6cKMrPPnz48HcN2CH1Q8pmzs6yTzux+8rvsgJkiDBiBjdScAvY8LOv4hd58UyBEaOBoDRQ1DPtEfa/r+LMEX2SMFJwoxzHE0880Yv97AXsZ6OSRowGgtIAeLl0VNaCJx4d0ot3wL9/RLiMxF8AKgKY4WcvY3dkOseNxEgDieZz62qqq29dlTNo422cFhH/O9wzBCeHgptfOHjAgK13lYmHqoHsLMf0ozvHPcD7yehJWMY3HLgtsBcvXnwB34F8ONTGmPxGA94a6FuU9vDiZy6+gNPDBjxcuNXoyA033PA4+01mLmzvnjLrIWuA36jP/dcb8h7nHcMePeks3JbV3rp167X8sVF8NMmI0UBENJCXa7/xo3fGXsuFhWW9Ows39nNed911uRdeeOEvI9IiU4jRgKaBi0Zk/vK663rDGxDAta3BRTsDt2W1n3/++Vk8jfDQ4A5lchkNBK+BNKdt6OJfD5rFewjcIV9chgq3BTZ/UGkY36wxz2cH318mZ4ga4Js7Dy1fMHpYZwHvDNzqIpI/1fEkX0Sab8+E2GEme/AasNltmdO/kP8k79Gpi8tQ4Las9rvvvjuBXxe7LvhqmpxGA53TQE6O7bp1r4+RW/PgNWj3JFS4ldXmW+zfY6vdudqavYwGQtAAOONb89/nXUK23sHCbVnt119//TKelmFqCPUzWY0GwtJAbq596j9eueQyLiSki8tQ4IbVdl555ZWzOTRmO6zuMjuHpgGbbfxlOeAOcIPDoPgLBm7LavM3H0fl5+d/KbSKmdxGA+FrID/P/qWXXxw1iksK2noHC7ey2pMnT8bQXzD7hN8aU4LRgIcGbPYpV3UDf0Fb70CgitV2LFq0aAhb7RkexzMrRgNR1ADPezJj0fyLhvAhYWzBbofuSbBwO6dOnfogX7miUCNGAzHRAA+cOP5lSt6DfPCgXJNg4HawO5LDs0XdFJMWmYMaDWgaKChw3DR5ck/MqBDwwrIjuMUlwTQN0/lzHvjuoBGjgZhqwG6ngmfnDsTbXgGtd0dwY5u6kBwwYMCt5qZNTPvUHLxNA+BwYP/0W3lVLiz9MuxvA6w2FsecOXN68fPaX2wr2wRGAzHXQF6O44tzZlsvEwur7erVEdzY5rz77rtv5s9S49anEaOBuNCA3W5L+/rt3W/mynTomnQEt3JJ+JvrmA3IiNFAXGmgX1FawC+m+YJbzLxjyZIlI/mNdtzTN2I0EFcayMqyXfbS7y4cyZWSURNw6yH+4MYOjokTJ95iLiQ99BX3K271Tx331Qy7guBy0vg8WG/FKoft4IbP4i3IpPztwsLCz3tvNOvxrYEW23iqKPs9VxKfnUlc4fncPSrvtUpYb2lygU+/frc33JZLcs899xSwS3KpxxFSdMVWv4dsle+Qzd0c9xqAGevm/AJVVBSTy+ViCDwhQQN8JLVLc1P7/bz39VXOuTye+/rK5zvNcz+U1ZG4XO5Lb5teWbBs1QHMUCXsWoV4w42yYLUd99133yQ2/dBVSoutbhc5997MmkscS4hOK2hxU1mlb7h9daj3f7r3uq99Yp3GJ67jnqktk5atopVcF3Dr0UnecKNNCu5+/fpNNv42m4PyN8jWdDTW/Rjy8TF22yPLTWeriFyWLQu5mLjeAbD26eaezMHfeQG3SLJaiwRdsK7g5icAJ+kbUjVuc+OziYkpPD0Cdc/nDk0EM9xJFedn2ybyrvizEnatknS4oQIsjlmzZvXMzs6+0MplIgmrAQHcwT0tHZxMYXaGe9QDX03vCW55kaap/vIFt33mzJlXt2VUmcxPYmsAgBfmsWkD4Nz9SbbYvnq1G7yC5Q7hRgYH35W82vjbiQ20d+11wL23JfI6OO1TSIBbXBMArsTbciu4eU4Sc1dSNJREoQKcZ9+DBY+U4F8AIv8GEleJUfrJyiTw2g5uGS0R2hXcPL79mSjVyxwmyhpwwkXJdVNFTeRGUQRwNEXiEkajedkZBF4FblUN/nHr5zDi9p/85Cd92NSzh2YkWTUAwAtgwcWkJXhD+UTKm3VLWh9uhmJYmiNwo5lY7Pw8yXDZaMLk1YAArkZRuOdhaRN5mXSxDdyCZ2FZrUgPKrj55s1QczEpKknuEIB347cRYcGFiEQMUf++3V1DubcEbtVx7Sw3v3UzLLm71LRO14AADqudyJKbaQO3ArdqjQ434naeB3CIsdyJ3M2h110Aj+QoSui16Pwe4DUnm4ZwCYphDhXcGC2Rcxahg0dKkMlIimkAgOdnu6mqzvdTg/Gujqx0G7jFiInFM0iHIME+atSo9MzMzAEqxfyknAbOAc4gMBWJdnGZke4eMOozmengmBcFuA63bdq0aYV4jDDletU02NIAAM/LOge3lZgYEceUMa2FXFWAbcEtKzaen4RHP42kugYE8M6Mg8uFqVh+6FLSfOlVtnU2v+yHcFCRG/xaPOt3KG29evUyN2989UAKpgHwXH4evKZee0A6SD0IsMgucQl9FaFvk7iEgfLr27vnucGvwK38E9lu42FAY7lFGyYkZcGz2YkFLgkg+TkOsdyqtjJaomjnZ7gN3AnQidGsosOhWXDrHZdo1iC4YwHgzEzfbglKsBm4g1NkquUSwGsb4neYEG5MTjp5WG6P0RIeBswxN3BSDd3g2gvAc/irox35wsGV1DW5UK/0DDemNlZeCI5ijQkikT91jY1GjAZ8asAX4AI7Qj3us4BOJOpl6nFfRaU77AI3Ntv00RK+gDBw+1KaSTuvgXOAu6mOZwqRuUcEOuSSuITn9+x8TC9L4hLqpTqdynIjCdbbc+6t1tbWFiQaMRroSAMAnF/MpXoA3lHGKG9rddk8+BWfG9WwNTU11fqaoSjKdTSHSwANAPCsjDYTGQf1xb9IYzPVclWU1UaVdLipoaEBG40YDQSlAR1wuYrzDlGQRZuPUmWb937+1r3L0/fnuQM9+NXhdhu4fWjfJHWoAQtwocwrtyQHC6vX7u1WvctDBkmrb7YBbstTErhVQl1dHb82asRoIDQNAHA8j+frIi+0ksLLzRe5wq/iGaMlQrq7oqLCw6yHdyizdypp4JwFd1MDzz4noyjRbD9OrMpaD8vtlqFAAO4uLy8X8qNZL3OsJNEAf6uGLfg5wKPdJMBdXuMCv4plHF/cElWX06dPV5vREqUK89NJDZwDPPouCv4tTpVTtV5tgVvRvnv37hoDt64eE++MBgB4Bs+hDGvqvaA8pIlIXPIhXdIkjx7KNskvIa4q9xyyA24Py40ViHvlypXVPNbNMzobMRoITwMW4F7FeMOJzZKmxwVa71DPg7hIczNVvba+SdwSJFszTgntbh4xKTHWW1RmwnA0AMDTYcG5kK5ccAXLIyUlfBiLY9Rb3BLEscFVW1tbghUjRgOR0IAFuOaKRKJc7zJqG2wlnObiRTwRBbfQjg0uHg781Fhu1oSRiGkAgKfxuJy3ixGpdVS0qtb9KQeKYQ4V02K5BXA3j5gUI7MRo4FIakAAj2SZelmnKuggr1scYxvg1hNaecTkoLHcUI2RSGtAAI+UxZZyUM89h1wwyviamcWzWG5sVyZ94cKFn/L3CxE3YjQQcQ0AcCfPjAMwIyVMq2vhasenXJ5iWMoVuIV2165duxp4xOSYZDCh0UCkNaADLtY3nLC+yXZs14EmfsPTuqAEz9ZoiQU3p7XW1NQY1wTaMdJlGsC7urDg4Qpc6JoGN/xtuCSw3MKyB9xi0l0nTpzYbPzucNVu9g+kAQE8lDFwlOnh0TDKJ8tsmznZ4pfjHpYb+yABGVq3bNmywbjdUImRrtYAAHeE4IML2HJC4OvIW/e3buB66pZbVbudz41MP/3pT3fziwvmNnxX96wpX2kAgHd2ZtnGFqr68QuO3eCWF59uCQ5iWe7q6uqms2fPbjWuCdRiJBoaEMBDORb4LKugLYwrvmGuw62KEcuNFQtujrccO3bsAwO30pH5iZIGBPBgR05QrWNltk0c4K33gHADcGRq2bBhw3rjd7MmjERVAwAccAcj8LfX7Wxdz3kFbvCLRYleDOIYnOEX9tWca93OnDmznmd+7aFypuiP48jjhMVIdDUQjNdQVecuK7iheSLXrJIXPO7KM6ko46wAl9fMOE0Rj0Q1YsJhC8O9mT+Vfb09Ub8EhFaFKc2taVReYRmDMEszu4eigY4Ad7HZPnyKMAQoVtvjYhLH0eHGusCNHVr27du3euDAgRGF21axm2zH1pLN3Yzjxb3YG89Qel02PzIM3bUXf9jjtSdf4i8def3s4veFW39l+StHHaODjaGW5zd/R8fw08pQy2ppddOuva2rGVPFKrdN4EYzlehuCRKwDuB5Pk/KGzlyZM/169e/z5/vi8gXFwC28x9fZrDh1ieOVNW7cBcscSqcAjWtbXRXf/l/K6/ed6rpDDcXr5fh9jtAtzrKl+XGRtDXzJa77siRI6tHjBhxUyRcE9vhV8lWe4SLTizJR3XZLtRiwMlIzDWAx/qOn6HV+04Rf1iQ4AKAV3Brgc1x6/Y74iIw71hwFjTxqMlKniBTtoUV2lyJS0c+f+GLJzc3EgcaYI+EthyilVwVAAVOhVmP2unj3LJBLLfyZe6///5NVVVVRzty7mXHZA8BeC6PJcF3M0tsdADbzF94OPq9P5OMb4NTsdweCPqDWwCHyW8uKSl5zcB9Tm95fDWSg8FSIzHRAC48SyvoNT64YpNDARvMeogvuJEBZh474axo5ikf/trM784bOacBATzYO2kmH1t5/quLxNLCCK/aSX8Fl7yI1Qav7cQf3DgLLL/7ySefLC4rK9turPd5/QHwbOODn1dIFGKw2uW1tP3/3qZiPpzub7ez2qhOMHDj7GjasWPHSy1qSBG7GYEGlAVnwI3/HR0dtLK53XWCXmLVC9hgE0Y4JLg5v9oBO8L8N82cOfM1nvah1FhvqOa85BoLfl4ZXRiD1a6sp9L7lil/G3CDS79goyr+LDe24Wyw/G5+9axh+/btf4jUsCAOkCwigEfCpzRl+PbN8ZDUzhP0h5oadbNG97d9Wm2wFQhuAVxZ729+85t/raysPGmsd/vTEoBn8dRhRiKvAWW1G+jk7OXqQlKstt9REqlBR3AjD8w+CgHcjUePHq3duXPnImO9WRs+xFhw31Y33H8jWO29J2jR0Qr1QSc8+QcewSX49CuB4IblRgHqopLDxm9/+9sr+E2dMmO9fesUY+DGgvvWTWdSYbVrmqjsxytpBfjjRS4mO/S3caxAcCOPWG8FOD9vUrVnz54XjfWGanyLAG5GUcIfRcFzJPtP0ov7jhPe6RWwA1pt9EwwcIv1Vn4379M4Z86cl9h6VxrrDRX6FgW4GQf3rZwgU2G1qxup8ud/V8N/YrXBYUCrjUMEAzfyifVWvvfGjRvLN2/e/LS5awnV+Bfc5MnCOHiE7s6lWjktTN22Unp6awmVs5aD9rWlR4KF29t6N8yYMWMFT96z07xnKar0HQLwTDOK4ls5HaTCHTlVTTvvWqR8bTyrLaMkQVltFB0s3MjrYb358yL1y5Ytm8vzm7iMewL1+BcB3PjgwfnguMPC85G4Xt5Gc3nShnrWbMhWG70RCtztrPfDDz/88f79+/9sLi79gy1bBHBZN6F/DeA2+4Ez9OdfvE4fc65OWW2UHgrcyC/WG38ROJsavv/97/+Gb8ufNdYb6ulY4H/DRUk13zmU9kKDlY109sd/pd9wFGDLhWRQIyTYXyRUuGG9cRAMC+Kg9e+9914ZX2D+mt0UXjUSSAMAPMP75b5AO6XQ9iama0sJ/XrjQSrjZotLAt7AHfgLWkKFGwUL4GrkhNfr+eJyJd+93Gbck+D0LoCHYtFSIS/uRJ6oom23v6BeIROwwVnIYKMnOgs33BPrriXHG+bNm/cIv45WY0ZPoNbAIoAHznk+By5IIXJhKnGV2JYuaXpe2R4o1PfR4/720/PocX/5O0rH6EhlA9U88096hPPp7gg4A28hWW0cqzNwYz/xvS3r/dxzzx1cvXr1L3j0hOfZCLkeKDPlBP43XBSAEcwCBQlEelz21dP0uGwPFOr76HF/++l59Li//P7S20ZHaO0++sXv31cfbvK22uAtZOks3DgQDijWG2da/V133fXm3r17l5ubO1BPcALA01PcB29mp4OnaVj+jcX0JmsNYMsIiVjt4JTplSscuGGeAbhYb1So7pZbbvnV8ePH9xr/20vTHaxaFpxNWyr41nob4WefrKG9dy6kX4EfXsQlAVfgq9NuQAS+SsKHPy829rt5gquWDydMmPCVjIyMdMzaKWI/8S5hMdJeA+r7MNyN6GwR0Zy/v/NIp+O4ckw9LsfR0/S4bA81xAx1VQ1U+8s19K1/7qVjXGYtL7Dc8oCUpg0cMTQJx3LjSDi4t3tSN3/+fPjfjxn/O7TOyGAXJQ2f0ODdsEAkPLfWtb9yLH/HD7Q9lNrhsqyBnY639tJjv39X+dlitQXssKw26hIu3ChDABf3BGdeHfvfq/jR2BVm/BsqCl4E8OD3SMycGM/ec4JW3LuYVnELADa4wb2TsN0RLkNJJOBGQTjLMBbpAfj06dOfLC4uft8ADhUFLwAcF5m6b5pMcVxAlpyl9298jp5krXiDDY7AU9gSKbhREVhwffSkjt+3rLn55pt/WFpa+rEZQQmtrwA3XJRkE4B9pJw+/trz9MPKOjVhvLc7Ao4iIpGGW/xv/L2o0ZMDBw5U8HyDD/HjscUteEDXSNAaEMCTxWrjgaiT1VT84Ev00IFTVMGKELDBiwz7RQzurrYNqqKHDh1q4tvzGyZfmHVNbsUHufz5byNBasDRZn7kvpiuOokj9LXgEJJHj/vKK/kk7Ex+7KOLlIUQdyBP19CJOa/Q/W/soSOchJERwB1RP5vLs6Qr4PZ55vHFZb2r1b1lXNHRa/muXKYB3OqDgBFfgOvg+CtAz6PHuyo/jqEvOA7WYbF5GrSKX71NDyzaQAc4SYb88O+O67SI+dlcliVdATcKB+CyyMFsH+w6WsnTAO+8pD9dx4CnGcBFNYFDAVwfBw+8V+xzAGyeKar+D+vpu798k7ZzjWCtxR3Rh/0iXtmugluvqECO0MbPD5zpmUs7RvamKQx4Rgp/S0rXUVBxAVwpkk1ivPviAPtsPVUv2Uizfvaq+jgToBarDbBhsdGcLpFowo0GqIas3kOnud0fjBlAk/nWc450Wpe0MMkKFV3BB9ddgHiLtzC27GOfmvcWPTj3DfqIuwFQY4ErolvshIab22KdnWiIasyGYqrkZwreu/ICmsivYBVIpyGzkY41IP92cpHZce7ob23icY/jVXToZ3+jB55fR59wDfCNSN1iR3xkxFcro2G55bhyhlqAf3yEanccp7WfG05j+XszRQAcf7VGAmsgHgHHyQawedqzXQ8up/949WNrVEQHO2J3IANpKZpwS10EbhV+eoYaV++lt68dRaPyM2mAAVzUFDgUwJEz1v436oBb6p+W0cZbX6CHNn1KpzkJUMPP1h+GYo/U+ifnaNdJtOHWwZZGus/UUMufPqS114+mfvkZNNxpLHjQPS6Ax9JFwRh2Hdtjnqzy9WnP0k9Ky9QNmpiCDQVGG27pNB1yxF31jeT63Xu07ooLqLx3N7oy3UFO6TjZyYS+NaAPqUbbgquhvkZq5FGwX13/ND3L/YgPnsLH9jXch76OmsQKbjRQQd0WWrAv30r7bXbaOLIPXZHppG7GTQmOBQ/AeZeuHj1B7/HEOXSikg4/vZZmzV5Ba/mwsNbiX8uoiNygQR9HVWIJtzQUjYaLYrkp6w5QOfvhb04aSn3YDx9m3BRRVcehAN7VFCk3hAfz9p6kN29fQD9Y+REd4poJ2GKx9TuPXV0ln4qJNdxotPeiQD9dTc3sprw39gI60zufxsFNkb9cny0xiUoDMtrUFTTBr1e30huo8e399D/TfkPPcj9V8oF1/1qeFRGLHbOeiTXcaLj0g1hvPXSv2EqfuG20bngRXc43fAod/H8rHRgzrcX5gS39tOlKjEI4IcDGmzPHKql43ts0i7/g+y6rQe44yoiIgC19GFNNwTWLF0FdcLJh4cf1Cd/p5Q9SqyW7ex7lLL2X7vjsILq3WyZ/vIBzWZ3ImYy01wDch3AFUOMZbJ5TpH7rYVrAs64uPVvtYan1N2hgrbGIwQr38GHtHw+WW28AlCKLnP0IW+ubyLX0A9pxqIzWXNib+vLk7oPlYtNArqvwfDwcvYgLUsu2eP9pemfOSvrBwyvpHe4HuCAyGgKwvZ/siwuwoYV4styojwieYsaCGT14dj1lxfl7YZYlz3j2drr6Xy6m2T3zqD+PqpAZNmTt+BGAiiVYgcWHC8L3H46+toOeenAZvc/7wuUAzAI01vVnRCLwP8ElRlDiFW40EXUTwAVyAG4tA3tR3oI76J4xA+mOvHTKgKtiIIfq2kswcANquCDVTdTIj0YsvXcpLSo9rcatYZ31RaDmU8Aa5Wp/0BinxDPcUA3qhwXuEwCHLw5LbgGO+MyraNCDk+nuEb1pOrsr6TyyYiBnxXiLP8ABNW6dswvStP8UrXrmPVr8x410mPfXgUYcUGOID1CLbx3CfwLvFUWJd7hFFagnrLhALq4KLjoF9IyvXkb9Zk+lmSOK6Cv8XfZMfl7cQC4a9BECatyIqW6gBob6b0+toT/+5SM1OQ5cDgEbcd0FAdRwQeIWaq6bkkSBG5VFXQVyWHFxVQC4QK7iU4ZTr4e/RLeN7kdfzcugXAU57xnOBRYfIykE1htv8yioG6lm1zH6y3/9nZat/UQ96CQgA2yJ+3JB4h5sdFYiwS1wCeDe/jjAFosOa57+2c9Q4X9/mW4Z3Zdm8J3OQszJJ3c7Uwl0AI0Fkw80sFPBU5iV7zpOL//0VVqx9VP1pTAALEAjrltq8asTwlpz3S1JRLil8gI5XBUs8MdlfFwgV8AX5lHW4zfSxCsG0vUDu9NEfnY8Xax5Ml+Awu0QK13bRE2lZ2n9llJ6g4f11pdXW4+h6hYacfjUcus8YVwQrnM7SWS40RjUXyAXn1wgB+ACucTTxw2mbj+cRlNH9aZp/QroYobcpmZ3QkFcUiJbdLHQ8Bnw0gC7Hu5jFbRzDz8Dwi/nrtlUom6VwzLLIhYa6zrUsNJiqRPCBeH6tpNEh1sa5AtyfXQFcAN6gVydAHdNoAF3j6PpQ3rQlIIcGoxRFoCubg5x5niHXYcZz3wAaIx6VNRSSXEZrV28iVYt2UBHuCkCrkCNdT0O10OsdMJDzW1Rkixw6+0R0MVd0V0WuQgV0MXKO798CRV9bRxdMbQHje3TjS7n0Za+cF0wdq7DjgPFwroDZIgCmkPAjDHpttGO4/zo6YcHy2jbnzbRlld30CnOAmB1qAVoPR1Ay4IjyMLRxJdkg1vvEbQNroq4KwI7ABeovUNsQz7nzHHU78YxdMWQnjSWn0q8LDuNemIObVyQAnbrwpQzQ3Tg9fi5rYF/BV7klLgijX9wIQiYEeKtcn7r5czJKvqo+AxtW7mdtvxxkxq+E+urwytwSyh5BGgu0XI/AlcywXIkM9zSFWLJBXSEFsQcB+BYF+glriBvS7dPGU6FUy+iwcOKaFBRPg3qlkUD89NpIFv4fmzdnQAez1OLK6POLK91bBPLq0IGFxd8AjHSsN5mlVt4/PlYVROV8qQ2paeq6DDPr3d4zW4q4WE7fAsdYAJWAVbiANk7DpiRJjAjVIflMGkFfZAqgrbqC+AF6Ahl8QU20mS7vo86WXIzyfm1K6jfZQNpQEEm5fP0w1lZaZTNw47Z7L9ns4XPZl8+i61+NsOdwQA3svWtY9+4ni1xHfvJdTw8V1fPS2Mz1Vc0UNVHpXTkT1voWE2DB5AAFFCK1RVgBWR9Xc8j+wjMEnJRyS2pBLfekzrkiCtQOdThFaB9wS0nhYRShne5so5jIy4CwCACmncolhWQCpwIJS7wAmyJ+8rrXS5nTx3RFZ46rfZsqQCohwK7Hgr4HaXpZUgcR9PjWBfo9LikIRSQBWZ93V8a0vUyJI5jpKRA6UY8NSAgeoeAGmmBQu/9UDrSvAXwQQRCPRRQA4X6PhI/V6r59al0oxZPDQisSPUVlzQ9lLx6iLi3AEiIHgqk3qHk886rCjA/7TWADjESugZ0vUncO5RSJV3W9VBAlTRZ9w6xXdIkrwkDaKAjxQfY1WwOoIFQdGvADaDMzmz+f6SMYEX4z7hMAAAAAElFTkSuQmCC'};return{FaviconsByHue,};});'use strict';Polymer({is:'tr-ui-b-info-bar-group',ready(){this.messages_=[];},get messageCount(){return this.messages_.length;},clearMessages(){this.messages_=[];this.updateContents_();},addMessage(text,opt_buttons){opt_buttons=opt_buttons||[];for(let i=0;i<opt_buttons.length;i++){if(opt_buttons[i].buttonText===undefined){throw new Error('buttonText must be provided');}
if(opt_buttons[i].onClick===undefined){throw new Error('onClick must be provided');}}
this.messages_.push({text,buttons:opt_buttons||[]});this.updateContents_();},updateContents_(){Polymer.dom(this.$.messages).textContent='';this.messages_.forEach(function(message){const bar=document.createElement('tr-ui-b-info-bar');bar.message=message.text;bar.visible=true;message.buttons.forEach(function(button){bar.addButton(button.buttonText,button.onClick);},this);Polymer.dom(this.$.messages).appendChild(bar);},this);}});'use strict';Polymer({is:'tr-ui-b-toolbar-button'});'use strict';tr.exportTo('tr.ui',function(){const Task=tr.b.Task;function FindController(brushingStateController){this.brushingStateController_=brushingStateController;this.filterHits_=[];this.currentHitIndex_=-1;this.activePromise_=Promise.resolve();this.activeTask_=undefined;}
FindController.prototype={__proto__:Object.prototype,get model(){return this.brushingStateController_.model;},get brushingStateController(){return this.brushingStateController_;},enqueueOperation_(operation){let task;if(operation instanceof tr.b.Task){task=operation;}else{task=new tr.b.Task(operation,this);}
@@ -9789,27 +9584,27 @@ TimelineDisplayTransformPanAnimation.prototype={__proto__:tr.ui.b.Animation.prot
return timestamp>=this.startTimeMs+this.durationMs;},get goalPanX(){return this.startPanX+this.deltaX;},get goalPanY(){return this.startPanY+this.deltaY;}};function TimelineDisplayTransformZoomToAnimation(goalFocalPointXWorld,goalFocalPointXView,goalFocalPointY,zoomInRatioX,opt_durationMs){this.goalFocalPointXWorld=goalFocalPointXWorld;this.goalFocalPointXView=goalFocalPointXView;this.goalFocalPointY=goalFocalPointY;this.zoomInRatioX=zoomInRatioX;if(opt_durationMs===undefined){this.durationMs=kDefaultPanAnimationDurationMs;}else{this.durationMs=opt_durationMs;}
this.startTimeMs=undefined;this.startScaleX=undefined;this.goalScaleX=undefined;this.startPanY=undefined;}
TimelineDisplayTransformZoomToAnimation.prototype={__proto__:tr.ui.b.Animation.prototype,get affectsPanY(){return this.startPanY!==this.goalFocalPointY;},canTakeOverFor(existingAnimation){return false;},takeOverFor(existingAnimation,timestamp,target){this.goalScaleX=target.scaleX*this.zoomInRatioX;},start(timestamp,target){this.startTimeMs=timestamp;this.startScaleX=target.scaleX;this.goalScaleX=this.zoomInRatioX*target.scaleX;this.startPanY=target.panY;},tick(timestamp,target){let percentDone=(timestamp-this.startTimeMs)/this.durationMs;percentDone=tr.b.math.clamp(percentDone,0,1);target.scaleX=lerp(percentDone,this.startScaleX,this.goalScaleX);if(this.affectsPanY){target.panY=lerp(percentDone,this.startPanY,this.goalFocalPointY);}
-target.xPanWorldPosToViewPos(this.goalFocalPointXWorld,this.goalFocalPointXView);return timestamp>=this.startTimeMs+this.durationMs;}};return{TimelineDisplayTransformPanAnimation,TimelineDisplayTransformZoomToAnimation,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const DrawType={GENERAL_EVENT:1,INSTANT_EVENT:2,BACKGROUND:3,GRID:4,FLOW_ARROWS:5,MARKERS:6,HIGHLIGHTS:7,ANNOTATIONS:8};const MAX_OVERSIZE_MULTIPLE=3.0;const REDRAW_SLOP=(MAX_OVERSIZE_MULTIPLE-1)/2;const DrawingContainer=tr.ui.b.define('drawing-container',tr.ui.tracks.Track);DrawingContainer.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('drawing-container');this.canvas_=document.createElement('canvas');this.canvas_.className='drawing-container-canvas';this.canvas_.style.left=tr.ui.b.constants.HEADING_WIDTH+'px';Polymer.dom(this).appendChild(this.canvas_);this.ctx_=this.canvas_.getContext('2d');this.offsetY_=0;this.viewportChange_=this.viewportChange_.bind(this);this.viewport.addEventListener('change',this.viewportChange_);window.addEventListener('resize',this.windowResized_.bind(this));this.addEventListener('scroll',this.scrollChanged_.bind(this));},get canvas(){return this.canvas_;},context(){return this.ctx_;},viewportChange_(){this.invalidate();},windowResized_(){this.invalidate();},scrollChanged_(){if(this.updateOffsetY_()){this.invalidate();}},invalidate(){if(this.rafPending_)return;this.rafPending_=true;tr.b.requestPreAnimationFrame(this.preDraw_,this);},preDraw_(){this.rafPending_=false;this.updateCanvasSizeIfNeeded_();tr.b.requestAnimationFrameInThisFrameIfPossible(this.draw_,this);},draw_(){this.ctx_.clearRect(0,0,this.canvas_.width,this.canvas_.height);const typesToDraw=[DrawType.BACKGROUND,DrawType.HIGHLIGHTS,DrawType.GRID,DrawType.INSTANT_EVENT,DrawType.GENERAL_EVENT,DrawType.MARKERS,DrawType.ANNOTATIONS,DrawType.FLOW_ARROWS];for(const idx in typesToDraw){for(let i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)){continue;}
-this.children[i].drawTrack(typesToDraw[idx]);}}
+target.xPanWorldPosToViewPos(this.goalFocalPointXWorld,this.goalFocalPointXView);return timestamp>=this.startTimeMs+this.durationMs;}};return{TimelineDisplayTransformPanAnimation,TimelineDisplayTransformZoomToAnimation,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const DrawType={GENERAL_EVENT:1,INSTANT_EVENT:2,BACKGROUND:3,GRID:4,FLOW_ARROWS:5,MARKERS:6,HIGHLIGHTS:7,ANNOTATIONS:8};const MAX_OVERSIZE_MULTIPLE=3.0;const REDRAW_SLOP=(MAX_OVERSIZE_MULTIPLE-1)/2;const DrawingContainer=tr.ui.b.define('drawing-container',tr.ui.tracks.Track);DrawingContainer.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('drawing-container');this.canvas_=document.createElement('canvas');this.canvas_.className='drawing-container-canvas';this.canvas_.style.left=tr.ui.b.constants.HEADING_WIDTH+'px';Polymer.dom(this).appendChild(this.canvas_);this.ctx_=this.canvas_.getContext('2d');this.offsetY_=0;this.viewportChange_=this.viewportChange_.bind(this);this.viewport.addEventListener('change',this.viewportChange_);window.addEventListener('resize',this.windowResized_.bind(this));this.addEventListener('scroll',this.scrollChanged_.bind(this));},get canvas(){return this.canvas_;},context(){return this.ctx_;},viewportChange_(){this.invalidate();},windowResized_(){this.invalidate();},scrollChanged_(){if(this.updateOffsetY_()){this.invalidate();}},invalidate(){if(this.rafPending_)return;this.rafPending_=true;tr.b.requestPreAnimationFrame(this.preDraw_,this);},preDraw_(){this.rafPending_=false;this.updateCanvasSizeIfNeeded_();tr.b.requestAnimationFrameInThisFrameIfPossible(this.draw_,this);},draw_(){this.ctx_.clearRect(0,0,this.canvas_.width,this.canvas_.height);const typesToDraw=[DrawType.BACKGROUND,DrawType.HIGHLIGHTS,DrawType.GRID,DrawType.INSTANT_EVENT,DrawType.GENERAL_EVENT,DrawType.MARKERS,DrawType.ANNOTATIONS,DrawType.FLOW_ARROWS];const children=this.children;for(const idx in typesToDraw){for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)){continue;}
+children[i].drawTrack(typesToDraw[idx]);}}
const pixelRatio=window.devicePixelRatio||1;const bounds=this.canvas_.getBoundingClientRect();const dt=this.viewport.currentDisplayTransform;const viewLWorld=dt.xViewToWorld(0);const viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);const viewHeight=bounds.height*pixelRatio;this.viewport.drawGridLines(this.ctx_,viewLWorld,viewRWorld,viewHeight);},updateOffsetY_(){const maxYDelta=window.innerHeight*REDRAW_SLOP;let newOffset=this.scrollTop-maxYDelta;if(Math.abs(newOffset-this.offsetY_)<=maxYDelta)return false;const maxOffset=this.scrollHeight-
this.canvas_.getBoundingClientRect().height;newOffset=Math.max(0,Math.min(newOffset,maxOffset));if(newOffset!==this.offsetY_){this.offsetY_=newOffset;return true;}
return false;},updateCanvasSizeIfNeeded_(){const visibleChildTracks=Array.from(this.children).filter(this.visibleFilter_);if(visibleChildTracks.length===0){return;}
const thisBounds=this.getBoundingClientRect();const firstChildTrackBounds=visibleChildTracks[0].getBoundingClientRect();const lastChildTrackBounds=visibleChildTracks[visibleChildTracks.length-1].getBoundingClientRect();const innerWidth=firstChildTrackBounds.width-
tr.ui.b.constants.HEADING_WIDTH;const innerHeight=Math.min(lastChildTrackBounds.bottom-firstChildTrackBounds.top,Math.floor(window.innerHeight*MAX_OVERSIZE_MULTIPLE));const pixelRatio=window.devicePixelRatio||1;if(this.canvas_.width!==innerWidth*pixelRatio){this.canvas_.width=innerWidth*pixelRatio;this.canvas_.style.width=innerWidth+'px';}
if(this.canvas_.height!==innerHeight*pixelRatio){this.canvas_.height=innerHeight*pixelRatio;this.canvas_.style.height=innerHeight+'px';}
-if(this.canvas_.top!==this.offsetY_){this.canvas_.top=this.offsetY_;this.canvas_.style.top=this.offsetY_+'px';}},visibleFilter_(element){if(!(element instanceof tr.ui.tracks.Track))return false;return window.getComputedStyle(element).display!=='none';},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){for(let i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)){continue;}
-const trackClientRect=this.children[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){this.children[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
-tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addEventsToTrackMap(eventToTrackMap){for(let i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)){continue;}
-this.children[i].addEventsToTrackMap(eventToTrackMap);}}};return{DrawingContainer,DrawType,};});'use strict';tr.exportTo('tr.model',function(){const SelectableItem=tr.model.SelectableItem;const SelectionState=tr.model.SelectionState;function ProxySelectableItem(modelItem){SelectableItem.call(this,modelItem);}
+if(this.canvas_.top!==this.offsetY_){this.canvas_.top=this.offsetY_;this.canvas_.style.top=this.offsetY_+'px';}},visibleFilter_(element){if(!(element instanceof tr.ui.tracks.Track))return false;return window.getComputedStyle(element).display!=='none';},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const children=this.children;for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)){continue;}
+const trackClientRect=children[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){children[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
+tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addEventsToTrackMap(eventToTrackMap){const children=this.children;for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)){continue;}
+children[i].addEventsToTrackMap(eventToTrackMap);}}};return{DrawingContainer,DrawType,};});'use strict';tr.exportTo('tr.model',function(){const SelectableItem=tr.model.SelectableItem;const SelectionState=tr.model.SelectionState;function ProxySelectableItem(modelItem){SelectableItem.call(this,modelItem);}
ProxySelectableItem.prototype={__proto__:SelectableItem.prototype,get selectionState(){const modelItem=this.modelItem_;if(modelItem===undefined){return SelectionState.NONE;}
return modelItem.selectionState;}};return{ProxySelectableItem,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const EventPresenter=tr.ui.b.EventPresenter;const SelectionState=tr.model.SelectionState;const LetterDotTrack=tr.ui.b.define('letter-dot-track',tr.ui.tracks.Track);LetterDotTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('letter-dot-track');this.items_=undefined;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get items(){return this.items_;},set items(items){this.items_=items;this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},get dumpRadiusView(){return 7*(window.devicePixelRatio||1);},draw(type,viewLWorld,viewRWorld,viewHeight){if(this.items_===undefined)return;switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawLetterDots_(viewLWorld,viewRWorld);break;}},drawLetterDots_(viewLWorld,viewRWorld){const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const height=bounds.height*pixelRatio;const halfHeight=height*0.5;const twoPi=Math.PI*2;const dt=this.viewport.currentDisplayTransform;const dumpRadiusView=this.dumpRadiusView;const itemRadiusWorld=dt.xViewVectorToWorld(height);const items=this.items_;const loI=tr.b.findLowIndexInSortedArray(items,function(item){return item.start;},viewLWorld);const oldFont=ctx.font;ctx.font='400 '+Math.floor(9*pixelRatio)+'px Arial';ctx.strokeStyle='rgb(0,0,0)';ctx.textBaseline='middle';ctx.textAlign='center';const drawItems=function(selected){for(let i=loI;i<items.length;++i){const item=items[i];const x=item.start;if(x-itemRadiusWorld>viewRWorld)break;if(item.selected!==selected)continue;const xView=dt.xWorldToView(x);ctx.fillStyle=EventPresenter.getSelectableItemColorAsString(item);ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView+0.5,0,twoPi);ctx.fill();if(item.selected){ctx.lineWidth=3;ctx.strokeStyle='rgb(100,100,0)';ctx.stroke();ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView,0,twoPi);ctx.lineWidth=1.5;ctx.strokeStyle='rgb(255,255,0)';ctx.stroke();}else{ctx.lineWidth=1;ctx.strokeStyle='rgb(0,0,0)';ctx.stroke();}
ctx.fillStyle='rgb(255, 255, 255)';ctx.fillText(item.dotLetter,xView,halfHeight);}};drawItems(false);drawItems(true);ctx.lineWidth=1;ctx.font=oldFont;},addEventsToTrackMap(eventToTrackMap){if(this.items_===undefined)return;this.items_.forEach(function(item){item.addToTrackMap(eventToTrackMap,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){if(this.items_===undefined)return;const itemRadiusWorld=viewPixWidthWorld*this.dumpRadiusView;tr.b.iterateOverIntersectingIntervals(this.items_,function(x){return x.start-itemRadiusWorld;},function(x){return 2*itemRadiusWorld;},loWX,hiWX,function(item){item.addToSelection(selection);}.bind(this));},addEventNearToProvidedEventToSelection(event,offset,selection){if(this.items_===undefined)return;const index=this.items_.findIndex(item=>item.modelItem===event);if(index===-1)return false;const newIndex=index+offset;if(newIndex>=0&&newIndex<this.items_.length){this.items_[newIndex].addToSelection(selection);return true;}
return false;},addAllEventsMatchingFilterToSelection(filter,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){if(this.items_===undefined)return;const item=tr.b.findClosestElementInSortedArray(this.items_,function(x){return x.start;},worldX,worldMaxDist);if(!item)return;item.addToSelection(selection);}};function LetterDot(modelItem,dotLetter,colorId,start){tr.model.ProxySelectableItem.call(this,modelItem);this.dotLetter=dotLetter;this.colorId=colorId;this.start=start;}
LetterDot.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{LetterDotTrack,LetterDot,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const AlertTrack=tr.ui.b.define('alert-track',tr.ui.tracks.LetterDotTrack);AlertTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Alerts';this.alerts_=undefined;},get alerts(){return this.alerts_;},set alerts(alerts){this.alerts_=alerts;if(alerts===undefined){this.items=undefined;return;}
-this.items=this.alerts_.map(function(alert){return new tr.ui.tracks.LetterDot(alert,String.fromCharCode(9888),alert.colorId,alert.start);});}};return{AlertTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const Task=tr.b.Task;const ContainerTrack=tr.ui.b.define('container-track',tr.ui.tracks.Track);ContainerTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);},detach(){Polymer.dom(this).textContent='';},get tracks_(){const tracks=[];for(let i=0;i<this.children.length;i++){if(this.children[i]instanceof tr.ui.tracks.Track){tracks.push(this.children[i]);}}
-return tracks;},drawTrack(type){this.tracks_.forEach(function(track){track.drawTrack(type);});},addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection){for(let i=0;i<this.tracks_.length;i++){const trackClientRect=this.tracks_[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){this.tracks_[i].addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);}}
-tr.ui.tracks.Track.prototype.addIntersectingEventsInRangeToSelection.apply(this,arguments);},addEventsToTrackMap(eventToTrackMap){for(const track of this.tracks_){track.addEventsToTrackMap(eventToTrackMap);}},addAllEventsMatchingFilterToSelection(filter,selection){for(let i=0;i<this.tracks_.length;i++){this.tracks_[i].addAllEventsMatchingFilterToSelection(filter,selection);}},addAllEventsMatchingFilterToSelectionAsTask(filter,selection){const task=new Task();for(let i=0;i<this.tracks_.length;i++){task.subTask(function(i){return function(){this.tracks_[i].addAllEventsMatchingFilterToSelection(filter,selection);};}(i),this);}
-return task;},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){for(let i=0;i<this.tracks_.length;i++){const trackClientRect=this.tracks_[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){this.tracks_[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
+this.items=this.alerts_.map(function(alert){return new tr.ui.tracks.LetterDot(alert,String.fromCharCode(9888),alert.colorId,alert.start);});}};return{AlertTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const Task=tr.b.Task;const ContainerTrack=tr.ui.b.define('container-track',tr.ui.tracks.Track);ContainerTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);},detach(){Polymer.dom(this).textContent='';},get tracks_(){const tracks=[];const children=this.children;for(let i=0;i<children.length;i++){if(children[i]instanceof tr.ui.tracks.Track){tracks.push(children[i]);}}
+return tracks;},drawTrack(type){this.tracks_.forEach(function(track){track.drawTrack(type);});},addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection){const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){const trackClientRect=tracks[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){tracks[i].addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);}}
+tr.ui.tracks.Track.prototype.addIntersectingEventsInRangeToSelection.apply(this,arguments);},addEventsToTrackMap(eventToTrackMap){for(const track of this.tracks_){track.addEventsToTrackMap(eventToTrackMap);}},addAllEventsMatchingFilterToSelection(filter,selection){const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){tracks[i].addAllEventsMatchingFilterToSelection(filter,selection);}},addAllEventsMatchingFilterToSelectionAsTask(filter,selection){const task=new Task();const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){task.subTask(function(i){return function(){tracks[i].addAllEventsMatchingFilterToSelection(filter,selection);};}(i),this);}
+return task;},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){const trackClientRect=tracks[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){tracks[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addContainersToTrackMap(containerToTrackMap){this.tracks_.forEach(function(track){track.addContainersToTrackMap(containerToTrackMap);});},clearTracks_(){this.tracks_.forEach(function(track){Polymer.dom(this).removeChild(track);},this);}};return{ContainerTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartPoint(modelItem,x,y,opt_yBase){tr.model.ProxySelectableItem.call(this,modelItem);this.x=x;this.y=y;this.dotLetter=undefined;this.yBase=opt_yBase;}
ChartPoint.prototype={__proto__:tr.model.ProxySelectableItem.prototype,};return{ChartPoint,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const EventPresenter=tr.ui.b.EventPresenter;const SelectionState=tr.model.SelectionState;const ChartSeriesType={LINE:0,AREA:1};const DEFAULT_RENDERING_CONFIG={chartType:ChartSeriesType.LINE,selectedPointSize:4,unselectedPointSize:3,solidSelectedDots:false,colorId:0,lineWidth:1,skipDistance:1,unselectedPointDensityTransparent:0.10,unselectedPointDensityOpaque:0.05,backgroundOpacity:0.5,stepGraph:true};const LAST_POINT_WIDTH=16;const DOT_LETTER_RADIUS_PX=7;const DOT_LETTER_RADIUS_PADDING_PX=0.5;const DOT_LETTER_SELECTED_OUTLINE_WIDTH_PX=3;const DOT_LETTER_SELECTED_OUTLINE_DETAIL_WIDTH_PX=1.5;const DOT_LETTER_UNSELECTED_OUTLINE_WIDTH_PX=1;const DOT_LETTER_FONT_WEIGHT=400;const DOT_LETTER_FONT_SIZE_PX=9;const DOT_LETTER_FONT='Arial';const ChartSeriesComponent={BACKGROUND:0,LINE:1,DOTS:2};function ChartSeries(points,seriesYAxis,opt_renderingConfig){this.points=points;this.seriesYAxis=seriesYAxis;this.useRenderingConfig_(opt_renderingConfig);}
ChartSeries.prototype={useRenderingConfig_(opt_renderingConfig){const config=opt_renderingConfig||{};for(const[key,defaultValue]of
@@ -9918,9 +9713,9 @@ if(this.ephemeralSettingsByGUID_[object.guid]===undefined){this.ephemeralSetting
return this.ephemeralSettingsByGUID_[object.guid];}};return{ModelSettings,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const CounterTrack=tr.ui.b.define('counter-track',tr.ui.tracks.ChartTrack);CounterTrack.prototype={__proto__:tr.ui.tracks.ChartTrack.prototype,decorate(viewport){tr.ui.tracks.ChartTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('counter-track');},get counter(){return this.chart;},set counter(counter){this.heading=counter.name+': ';this.series=CounterTrack.buildChartSeriesFromCounter(counter);this.autoSetAllAxes({expandMax:true});},getModelEventFromItem(chartValue){return chartValue;}};CounterTrack.buildChartSeriesFromCounter=function(counter){const numSeries=counter.series.length;const totals=counter.totals;const seriesYAxis=new tr.ui.tracks.ChartSeriesYAxis(0,undefined);const chartSeries=counter.series.map(function(series,seriesIndex){const chartPoints=series.samples.map(function(sample,sampleIndex){const total=totals[sampleIndex*numSeries+seriesIndex];return new tr.ui.tracks.ChartPoint(sample,sample.timestamp,total);});const renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:series.color};return new tr.ui.tracks.ChartSeries(chartPoints,seriesYAxis,renderingConfig);});chartSeries.reverse();return chartSeries;};return{CounterTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const startCompare=function(x,y){return x.start-y.start;};const FrameTrack=tr.ui.b.define('frame-track',tr.ui.tracks.LetterDotTrack);FrameTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Frames';this.frames_=undefined;this.items=undefined;},get frames(){return this.frames_;},set frames(frames){this.frames_=frames;if(frames===undefined)return;this.frames_=this.frames_.slice();this.frames_.sort(startCompare);this.items=this.frames_.map(function(frame){return new FrameDot(frame);});}};function FrameDot(frame){tr.ui.tracks.LetterDot.call(this,frame,'F',frame.colorId,frame.start);}
FrameDot.prototype={__proto__:tr.ui.tracks.LetterDot.prototype};return{FrameTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const MultiRowTrack=tr.ui.b.define('multi-row-track',tr.ui.tracks.ContainerTrack);MultiRowTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.tooltip_='';this.heading_='';this.groupingSource_=undefined;this.itemsToGroup_=undefined;this.defaultToCollapsedWhenSubRowCountMoreThan=1;this.currentSubRowsWithHeadings_=undefined;this.expanded_=true;},get itemsToGroup(){return this.itemsToGroup_;},setItemsToGroup(itemsToGroup,opt_groupingSource){this.itemsToGroup_=itemsToGroup;this.groupingSource_=opt_groupingSource;this.currentSubRowsWithHeadings_=undefined;this.updateContents_();this.updateExpandedStateFromGroupingSource_();},setPrebuiltSubRows(groupingSource,subRowsWithHeadings){this.itemsToGroup_=undefined;this.groupingSource_=groupingSource;this.currentSubRowsWithHeadings_=subRowsWithHeadings;this.updateContents_();this.updateExpandedStateFromGroupingSource_();},get heading(){return this.heading_;},set heading(h){this.heading_=h;this.updateHeadingAndTooltip_();},get tooltip(){return this.tooltip_;},set tooltip(t){this.tooltip_=t;this.updateHeadingAndTooltip_();},get subRows(){return this.currentSubRowsWithHeadings_.map(elem=>elem.row);},get hasVisibleContent(){return this.children.length>0;},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_===expanded)return;this.expanded_=expanded;this.expandedStateChanged_();},onHeadingClicked_(e){if(this.subRows.length<=1)return;this.expanded=!this.expanded;if(this.groupingSource_){const modelSettings=new tr.model.ModelSettings(this.groupingSource_.model);modelSettings.setSettingFor(this.groupingSource_,'expanded',this.expanded);}
e.stopPropagation();},updateExpandedStateFromGroupingSource_(){if(this.groupingSource_){const numSubRows=this.subRows.length;const modelSettings=new tr.model.ModelSettings(this.groupingSource_.model);if(numSubRows>1){let defaultExpanded;if(numSubRows>this.defaultToCollapsedWhenSubRowCountMoreThan){defaultExpanded=false;}else{defaultExpanded=true;}
-this.expanded=modelSettings.getSettingFor(this.groupingSource_,'expanded',defaultExpanded);}else{this.expanded=undefined;}}},expandedStateChanged_(){const minH=Math.max(2,Math.ceil(18/this.children.length));const h=(this.expanded_?18:minH)+'px';for(let i=0;i<this.children.length;i++){this.children[i].height=h;if(i===0){this.children[i].arrowVisible=true;}
-this.children[i].expanded=this.expanded;}
-if(this.children.length===1){this.children[0].expanded=true;this.children[0].arrowVisible=false;}},updateContents_(){tr.ui.tracks.ContainerTrack.prototype.updateContents_.call(this);this.detach();if(this.currentSubRowsWithHeadings_===undefined){if(this.itemsToGroup_===undefined){return;}
+this.expanded=modelSettings.getSettingFor(this.groupingSource_,'expanded',defaultExpanded);}else{this.expanded=undefined;}}},expandedStateChanged_(){const children=this.children;const minH=Math.max(2,Math.ceil(18/children.length));const h=(this.expanded_?18:minH)+'px';for(let i=0;i<children.length;i++){children[i].height=h;if(i===0){children[i].arrowVisible=true;}
+children[i].expanded=this.expanded;}
+if(children.length===1){children[0].expanded=true;children[0].arrowVisible=false;}},updateContents_(){tr.ui.tracks.ContainerTrack.prototype.updateContents_.call(this);this.detach();if(this.currentSubRowsWithHeadings_===undefined){if(this.itemsToGroup_===undefined){return;}
const subRows=this.buildSubRows_(this.itemsToGroup_);this.currentSubRowsWithHeadings_=subRows.map(row=>{return{row,heading:undefined};});}
if(this.currentSubRowsWithHeadings_===undefined||this.currentSubRowsWithHeadings_.length===0){return;}
const addSubTrackEx=(items,opt_heading)=>{const track=this.addSubTrack_(items);if(opt_heading!==undefined){track.heading=opt_heading;}
@@ -9962,7 +9757,7 @@ if(this.expanded||!this.collapsible_){for(const thread of this.threads_){const t
ops.sort(function(a,b){return a.time-b.time;});const rects=[];const genericColorId=ColorScheme.getColorIdForReservedName('generic_work');const pushRect=function(start,end,slice){rects.push(new tr.ui.tracks.Rect(slice,slice?slice.title:'',slice?slice.colorId:genericColorId,start,end-start));};let depth=0;let currentSlice=undefined;let lastStart=undefined;ops.forEach(function(op){depth+=op.isStart?1:-1;if(currentSlice){if(!op.isStart&&op.slice===currentSlice){pushRect(lastStart,op.time,currentSlice);lastStart=depth>=1?op.time:undefined;currentSlice=undefined;}}else{if(op.isStart){if(depth===1){lastStart=op.time;currentSlice=op.slice;}else if(op.slice){if(op.time!==lastStart){pushRect(lastStart,op.time,undefined);lastStart=op.time;}
currentSlice=op.slice;}}else{if(depth===0){pushRect(lastStart,op.time,undefined);lastStart=undefined;}}}});return rects;};ProcessSummaryTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get process(){return this.process_;},set process(process){this.process_=process;this.rects=ProcessSummaryTrack.buildRectsFromProcess(process);}};return{ProcessSummaryTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ObjectSnapshotView=tr.ui.analysis.ObjectSnapshotView;const ObjectInstanceView=tr.ui.analysis.ObjectInstanceView;const SpacingTrack=tr.ui.tracks.SpacingTrack;const ProcessTrackBase=tr.ui.b.define('process-track-base',tr.ui.tracks.ContainerTrack);ProcessTrackBase.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.processBase_=undefined;Polymer.dom(this).classList.add('process-track-base');Polymer.dom(this).classList.add('expanded');this.processNameEl_=tr.ui.b.createSpan();Polymer.dom(this.processNameEl_).classList.add('process-track-name');this.closeEl_=tr.ui.b.createSpan();Polymer.dom(this.closeEl_).classList.add('process-track-close');this.closeEl_.textContent='X';this.headerEl_=tr.ui.b.createDiv({className:'process-track-header'});Polymer.dom(this.headerEl_).appendChild(this.processNameEl_);Polymer.dom(this.headerEl_).appendChild(this.closeEl_);this.headerEl_.addEventListener('click',this.onHeaderClick_.bind(this));Polymer.dom(this).appendChild(this.headerEl_);},get processBase(){return this.processBase_;},set processBase(processBase){this.processBase_=processBase;if(this.processBase_){const modelSettings=new tr.model.ModelSettings(this.processBase_.model);const defaultValue=this.processBase_.important;this.expanded=modelSettings.getSettingFor(this.processBase_,'expanded',defaultValue);}
this.updateContents_();},get expanded(){return Polymer.dom(this).classList.contains('expanded');},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;Polymer.dom(this).classList.toggle('expanded');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;const modelSettings=new tr.model.ModelSettings(this.processBase_.model);modelSettings.setSettingFor(this.processBase_,'expanded',expanded);this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},set visible(visible){if(visible===this.visible)return;this.hidden=!visible;tr.b.dispatchSimpleEvent(this,'visibility');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},get visible(){return!this.hidden;},get hasVisibleContent(){if(this.expanded){return this.children.length>1;}
-return true;},onHeaderClick_(e){e.stopPropagation();e.preventDefault();if(e.target===this.closeEl_){this.visible=false;}else{this.expanded=!this.expanded;}},updateContents_(){this.clearTracks_();if(!this.processBase_)return;Polymer.dom(this.processNameEl_).textContent=this.processBase_.userFriendlyName;this.headerEl_.title=this.processBase_.userFriendlyDetails;this.willAppendTracks_();if(this.expanded){this.appendMemoryDumpTrack_();this.appendObjectInstanceTracks_();this.appendCounterTracks_();this.appendFrameTrack_();this.appendThreadTracks_();}else{this.appendSummaryTrack_();}
+return true;},onHeaderClick_(e){e.stopPropagation();e.preventDefault();if(e.target===this.closeEl_){this.visible=false;}else{this.expanded=!this.expanded;}},updateContents_(){this.clearTracks_();if(!this.processBase_)return;if(!this.visible)return;Polymer.dom(this.processNameEl_).textContent=this.processBase_.userFriendlyName;this.headerEl_.title=this.processBase_.userFriendlyDetails;this.willAppendTracks_();if(this.expanded){this.appendMemoryDumpTrack_();this.appendObjectInstanceTracks_();this.appendCounterTracks_();this.appendFrameTrack_();this.appendThreadTracks_();}else{this.appendSummaryTrack_();}
this.didAppendTracks_();},willAppendTracks_(){},didAppendTracks_(){},appendMemoryDumpTrack_(){},appendSummaryTrack_(){const track=new tr.ui.tracks.ProcessSummaryTrack(this.viewport);track.process=this.process;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendFrameTrack_(){const frames=this.process?this.process.frames:undefined;if(!frames||!frames.length)return;const track=new tr.ui.tracks.FrameTrack(this.viewport);track.frames=frames;Polymer.dom(this).appendChild(track);},appendObjectInstanceTracks_(){const instancesByTypeName=this.processBase_.objects.getAllInstancesByTypeName();const instanceTypeNames=Object.keys(instancesByTypeName);instanceTypeNames.sort();let didAppendAtLeastOneTrack=false;instanceTypeNames.forEach(function(typeName){const allInstances=instancesByTypeName[typeName];let instanceViewInfo=ObjectInstanceView.getTypeInfo(undefined,typeName);let snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(instanceViewInfo&&!instanceViewInfo.metadata.showInTrackView){instanceViewInfo=undefined;}
if(snapshotViewInfo&&!snapshotViewInfo.metadata.showInTrackView){snapshotViewInfo=undefined;}
const hasViewInfo=instanceViewInfo||snapshotViewInfo;const visibleInstances=[];for(let i=0;i<allInstances.length;i++){const instance=allInstances[i];if(instance.snapshots.length===0)continue;if(instance.hasImplicitSnapshots&&!hasViewInfo)continue;visibleInstances.push(instance);}
@@ -9973,8 +9768,8 @@ this.subRows_.push(...tr.ui.tracks.groupAsyncSlicesIntoSubRows(slices,true));ret
if(this.hasVisibleContent){this.items=this.buildMemoryLetterDots_(this.lowMemoryEvents_);}},get hasVisibleContent(){return!!this.lowMemoryEvents_&&this.lowMemoryEvents_.length!==0;},buildMemoryLetterDots_(memoryEvents){return memoryEvents.map(memoryEvent=>new tr.ui.tracks.LetterDot(memoryEvent,'K',ColorScheme.getColorIdForReservedName('background_memory_dump'),memoryEvent.start));},};return{MemoryTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ALLOCATED_MEMORY_TRACK_HEIGHT=50;const ProcessMemoryDumpTrack=tr.ui.b.define('process-memory-dump-track',tr.ui.tracks.ContainerTrack);ProcessMemoryDumpTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.memoryDumps_=undefined;},get memoryDumps(){return this.memoryDumps_;},set memoryDumps(memoryDumps){this.memoryDumps_=memoryDumps;this.updateContents_();},updateContents_(){this.clearTracks_();if(!this.memoryDumps_||!this.memoryDumps_.length)return;this.appendAllocatedMemoryTrack_();},appendAllocatedMemoryTrack_(){const series=tr.ui.tracks.buildProcessAllocatedMemoryChartSeries(this.memoryDumps_);if(!series)return;const track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per component';track.height=ALLOCATED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}};return{ProcessMemoryDumpTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ProcessTrackBase=tr.ui.tracks.ProcessTrackBase;const ProcessTrack=tr.ui.b.define('process-track',ProcessTrackBase);ProcessTrack.prototype={__proto__:ProcessTrackBase.prototype,decorate(viewport){tr.ui.tracks.ProcessTrackBase.prototype.decorate.call(this,viewport);},drawTrack(type){switch(type){case tr.ui.tracks.DrawType.INSTANT_EVENT:{if(!this.processBase.instantEvents||this.processBase.instantEvents.length===0){break;}
const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));const dt=this.viewport.currentDisplayTransform;const viewLWorld=dt.xViewToWorld(0);const viewRWorld=dt.xViewToWorld(canvasBounds.width*pixelRatio);tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.processBase.instantEvents,2);ctx.restore();break;}
case tr.ui.tracks.DrawType.BACKGROUND:this.drawBackground_();return;}
-tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawBackground_(){const ctx=this.context();const canvasBounds=ctx.canvas.getBoundingClientRect();const pixelRatio=window.devicePixelRatio||1;let draw=false;ctx.fillStyle='#eee';for(let i=0;i<this.children.length;++i){if(!(this.children[i]instanceof tr.ui.tracks.Track)||(this.children[i]instanceof tr.ui.tracks.SpacingTrack)){continue;}
-draw=!draw;if(!draw)continue;const bounds=this.children[i].getBoundingClientRect();ctx.fillRect(0,pixelRatio*(bounds.top-canvasBounds.top),ctx.canvas.width,pixelRatio*bounds.height);}},set process(process){this.processBase=process;},get process(){return this.processBase;},get eventContainer(){return this.process;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.process,this);},appendMemoryDumpTrack_(){const processMemoryDumps=this.process.memoryDumps;if(processMemoryDumps.length){const pmdt=new tr.ui.tracks.ProcessMemoryDumpTrack(this.viewport_);pmdt.memoryDumps=processMemoryDumps;Polymer.dom(this).appendChild(pmdt);}},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
+tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawBackground_(){const ctx=this.context();const canvasBounds=ctx.canvas.getBoundingClientRect();const pixelRatio=window.devicePixelRatio||1;const children=this.children;let draw=false;ctx.fillStyle='#eee';for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)||(children[i]instanceof tr.ui.tracks.SpacingTrack)){continue;}
+draw=!draw;if(!draw)continue;const bounds=children[i].getBoundingClientRect();ctx.fillRect(0,pixelRatio*(bounds.top-canvasBounds.top),ctx.canvas.width,pixelRatio*bounds.height);}},set process(process){this.processBase=process;},get process(){return this.processBase;},get eventContainer(){return this.process;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.process,this);},appendMemoryDumpTrack_(){const processMemoryDumps=this.process.memoryDumps;if(processMemoryDumps.length){const pmdt=new tr.ui.tracks.ProcessMemoryDumpTrack(this.viewport_);pmdt.memoryDumps=processMemoryDumps;Polymer.dom(this).appendChild(pmdt);}},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
const instantEventWidth=2*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.processBase.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.processBase.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ProcessTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SelectionState=tr.model.SelectionState;const ColorScheme=tr.b.ColorScheme;const EventPresenter=tr.ui.b.EventPresenter;const ModelTrack=tr.ui.b.define('model-track',tr.ui.tracks.ContainerTrack);ModelTrack.VSYNC_HIGHLIGHT_ALPHA=0.1;ModelTrack.VSYNC_DENSITY_TRANSPARENT=0.20;ModelTrack.VSYNC_DENSITY_OPAQUE=0.10;ModelTrack.VSYNC_DENSITY_RANGE=ModelTrack.VSYNC_DENSITY_TRANSPARENT-ModelTrack.VSYNC_DENSITY_OPAQUE;ModelTrack.generateStripes_=function(times,minTime,maxTime){if(times.length===0)return[];const lowIndex=tr.b.findLowIndexInSortedArray(times,(x=>x),minTime);let highIndex=lowIndex-1;while(times[highIndex+1]<=maxTime){highIndex++;}
const stripes=[];for(let i=lowIndex-(lowIndex%2);i<=highIndex;i+=2){const left=i<lowIndex?minTime:times[i];const right=i+1>highIndex?maxTime:times[i+1];stripes.push(tr.b.math.Range.fromExplicitRange(left,right));}
return stripes;};ModelTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('model-track');this.upperMode_=false;this.annotationViews_=[];this.vSyncTimes_=[];},get processViews(){return Polymer.dom(this).querySelectorAll('.process-track-base');},get upperMode(){return this.upperMode_;},set upperMode(upperMode){this.upperMode_=upperMode;this.updateContents_();},detach(){tr.ui.tracks.ContainerTrack.prototype.detach.call(this);},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();this.model_.addEventListener('annotationChange',this.updateAnnotations_.bind(this));},get hasVisibleContent(){return this.children.length>0;},updateContents_(){Polymer.dom(this).textContent='';if(!this.model_)return;if(this.upperMode_){this.updateContentsForUpperMode_();}else{this.updateContentsForLowerMode_();}},updateContentsForUpperMode_(){},updateContentsForLowerMode_(){if(this.model_.userModel.expectations.length>1){const mrt=new tr.ui.tracks.InteractionTrack(this.viewport_);mrt.model=this.model_;Polymer.dom(this).appendChild(mrt);}
@@ -9988,7 +9783,7 @@ this.drawFlowArrows_(viewLWorld,viewRWorld);ctx.restore();return;case tr.ui.trac
tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.model_.instantEvents,4);break;case tr.ui.tracks.DrawType.MARKERS:if(!this.viewport.interestRange.isEmpty){this.viewport.interestRange.draw(ctx,viewLWorld,viewRWorld,viewHeight);this.viewport.interestRange.drawIndicators(ctx,viewLWorld,viewRWorld);}
ctx.restore();return;case tr.ui.tracks.DrawType.HIGHLIGHTS:this.drawVSyncHighlight(ctx,dt,viewLWorld,viewRWorld,viewHeight);ctx.restore();return;case tr.ui.tracks.DrawType.ANNOTATIONS:for(let i=0;i<this.annotationViews_.length;i++){this.annotationViews_[i].draw(ctx);}
ctx.restore();return;}
-ctx.restore();tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawFlowArrows_(viewLWorld,viewRWorld){const ctx=this.context();ctx.strokeStyle='rgba(0, 0, 0, 0.4)';ctx.fillStyle='rgba(0, 0, 0, 0.4)';ctx.lineWidth=1;const events=this.model_.flowIntervalTree.findIntersection(viewLWorld,viewRWorld);const onlyHighlighted=!this.viewport.showFlowEvents;const canvasBounds=ctx.canvas.getBoundingClientRect();for(let i=0;i<events.length;++i){if(onlyHighlighted&&events[i].selectionState!==SelectionState.SELECTED&&events[i].selectionState!==SelectionState.HIGHLIGHTED){continue;}
+ctx.restore();tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawFlowArrows_(viewLWorld,viewRWorld){const ctx=this.context();ctx.strokeStyle='rgba(0, 0, 0, 0.4)';ctx.fillStyle='rgba(0, 0, 0, 0.4)';ctx.lineWidth=1;const events=this.model_.flowIntervalTree.findIntersection(viewLWorld,viewRWorld);const canvasBounds=ctx.canvas.getBoundingClientRect();for(let i=0;i<events.length;++i){const onlyHighlighted=!tr.b.getCategoryParts(events[i].category).some((x)=>this.viewport.selectedFlowEvents.has(x));if(onlyHighlighted&&events[i].selectionState!==SelectionState.SELECTED&&events[i].selectionState!==SelectionState.HIGHLIGHTED){continue;}
this.drawFlowArrow_(ctx,events[i],canvasBounds);}},drawFlowArrow_(ctx,flowEvent,canvasBounds){const dt=this.viewport.currentDisplayTransform;const pixelRatio=window.devicePixelRatio||1;const startTrack=this.viewport.trackForEvent(flowEvent.startSlice);const endTrack=this.viewport.trackForEvent(flowEvent.endSlice);if(startTrack===undefined||endTrack===undefined)return;const startBounds=startTrack.getBoundingClientRect();const endBounds=endTrack.getBoundingClientRect();if(flowEvent.selectionState===SelectionState.SELECTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[tr.b.ColorScheme.getVariantColorId(flowEvent.colorId,tr.b.ColorScheme.properties.brightenedOffsets[0])];}else if(flowEvent.selectionState===SelectionState.HIGHLIGHTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[tr.b.ColorScheme.getVariantColorId(flowEvent.colorId,tr.b.ColorScheme.properties.brightenedOffsets[0])];}else if(flowEvent.selectionState===SelectionState.DIMMED){ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[flowEvent.colorId];}else{let hasBoost=false;const startSlice=flowEvent.startSlice;hasBoost|=startSlice.selectionState===SelectionState.SELECTED;hasBoost|=startSlice.selectionState===SelectionState.HIGHLIGHTED;const endSlice=flowEvent.endSlice;hasBoost|=endSlice.selectionState===SelectionState.SELECTED;hasBoost|=endSlice.selectionState===SelectionState.HIGHLIGHTED;if(hasBoost){ctx.shadowBlur=1;ctx.shadowColor='rgba(255, 0, 0, 0.4)';ctx.shadowOffsety=2;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[tr.b.ColorScheme.getVariantColorId(flowEvent.colorId,tr.b.ColorScheme.properties.brightenedOffsets[0])];}else{ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[flowEvent.colorId];}}
const startSize=startBounds.left+startBounds.top+
startBounds.bottom+startBounds.right;const endSize=endBounds.left+endBounds.top+
@@ -10078,15 +9873,19 @@ this.removeAttribute('expanded');unsupportedLabelEls.push(labelEl);}}
for(const labelEl of unsupportedLabelEls){Polymer.dom(this.tabStrip_).appendChild(labelEl);}
if(previouslyActivePanelType&&supportedPanelTypes.includes(previouslyActivePanelType)){this.activePanelType=previouslyActivePanelType;Polymer.dom(this).setAttribute('expanded',true);}else{if(this.activePanel){Polymer.dom(this.activePanelContainer_).removeChild(this.activePanel);}
Polymer.dom(this).removeAttribute('expanded');}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){if(range===undefined){throw new Error('Must not be undefined');}
-this.rangeOfInterest_=range;if(this.activePanel){this.activePanel.rangeOfInterest=range;}}});'use strict';Polymer({is:'tr-ui-timeline-view-help-overlay',ready(){const mod=tr.isMac?'cmd ':'ctrl';const spans=Polymer.dom(this.root).querySelectorAll('span.mod');for(let i=0;i<spans.length;i++){Polymer.dom(spans[i]).textContent=mod;}}});'use strict';Polymer({is:'tr-ui-timeline-view-metadata-overlay',created(){this.metadata_=undefined;},ready(){this.$.table.tableColumns=[{title:'name',value:d=>d.name,},{title:'value',value:d=>{const gov=document.createElement('tr-ui-a-generic-object-view');gov.object=d.value;return gov;},}];},get metadata(){return this.metadata_;},set metadata(metadata){this.metadata_=metadata;this.$.table.tableRows=this.metadata_;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-preferred-display-unit',ready(){this.preferredTimeDisplayMode_=undefined;},attached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},detached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},get preferredTimeDisplayMode(){return this.preferredTimeDisplayMode_;},set preferredTimeDisplayMode(v){if(this.preferredTimeDisplayMode_===v)return;this.preferredTimeDisplayMode_=v;tr.b.Unit.didPreferredTimeDisplayUnitChange();}});'use strict';Polymer({is:'tr-ui-timeline-view',created(){this.trackViewContainer_=undefined;this.queuedModel_=undefined;this.builtPromise_=undefined;this.doneBuilding_=undefined;},attached(){this.async(function(){this.trackViewContainer_=Polymer.dom(this).querySelector('#track_view_container');if(!this.trackViewContainer_){throw new Error('missing trackviewContainer');}
-if(this.queuedModel_)this.updateContents_();});},ready(){this.tabIndex=0;this.titleEl_=this.$.title;this.leftControlsEl_=this.$.left_controls;this.rightControlsEl_=this.$.right_controls;this.collapsingControlsEl_=this.$.collapsing_controls;this.sidePanelContainer_=this.$.side_panel_container;this.brushingStateController_=new tr.c.BrushingStateController(this);this.findCtl_=this.$.view_find_control;this.findCtl_.controller=new tr.ui.FindController(this.brushingStateController_);this.scriptingCtl_=document.createElement('tr-ui-scripting-control');this.scriptingCtl_.controller=new tr.c.ScriptingController(this.brushingStateController_);this.sidePanelContainer_.brushingStateController=this.brushingStateController_;if(window.tr.metrics&&window.tr.metrics.sh&&window.tr.metrics.sh.SystemHealthMetric){this.railScoreSpan_=document.createElement('tr-metrics-ui-sh-system-health-span');Polymer.dom(this.rightControls).appendChild(this.railScoreSpan_);}else{this.railScoreSpan_=undefined;}
-this.processFilter_=this.$.process_filter_dropdown;this.optionsDropdown_=this.$.view_options_dropdown;Polymer.dom(this.optionsDropdown_.iconElement).textContent='View Options';this.showFlowEvents_=false;Polymer.dom(this.optionsDropdown_).appendChild(tr.ui.b.createCheckBox(this,'showFlowEvents','tr.ui.TimelineView.showFlowEvents',false,'Flow events'));this.highlightVSync_=false;this.highlightVSyncCheckbox_=tr.ui.b.createCheckBox(this,'highlightVSync','tr.ui.TimelineView.highlightVSync',false,'Highlight VSync');Polymer.dom(this.optionsDropdown_).appendChild(this.highlightVSyncCheckbox_);this.initMetadataButton_();this.initConsoleButton_();this.initHelpButton_();Polymer.dom(this.collapsingControls).appendChild(this.scriptingCtl_);this.dragEl_=this.$.drag_handle;this.analysisEl_=this.$.analysis;this.analysisEl_.brushingStateController=this.brushingStateController_;this.addEventListener('requestSelectionChange',function(e){const sc=this.brushingStateController_;sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);}.bind(this));this.onViewportChanged_=this.onViewportChanged_.bind(this);this.bindKeyListeners_();this.dragEl_.target=this.analysisEl_;},get globalMode(){return this.hotkeyController.globalMode;},set globalMode(globalMode){globalMode=!!globalMode;this.brushingStateController_.historyEnabled=globalMode;this.hotkeyController.globalMode=globalMode;},get hotkeyController(){return this.$.hkc;},updateDocumentFavicon(){let hue;if(!this.model){hue='blue';}else{hue=this.model.faviconHue;}
+this.rangeOfInterest_=range;if(this.activePanel){this.activePanel.rangeOfInterest=range;}}});'use strict';Polymer({is:'tr-ui-timeline-view-help-overlay',ready(){const mod=tr.isMac?'cmd ':'ctrl';const spans=Polymer.dom(this.root).querySelectorAll('span.mod');for(let i=0;i<spans.length;i++){Polymer.dom(spans[i]).textContent=mod;}}});'use strict';Polymer({is:'tr-ui-timeline-view-metadata-overlay',created(){this.metadata_=undefined;},ready(){this.$.table.tableColumns=[{title:'name',value:d=>d.name,},{title:'value',value:d=>{const gov=document.createElement('tr-ui-a-generic-object-view');gov.object=d.value;return gov;},}];},get metadata(){return this.metadata_;},set metadata(metadata){this.metadata_=metadata;this.$.table.tableRows=this.metadata_;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-preferred-display-unit',ready(){this.preferredTimeDisplayMode_=undefined;},attached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},detached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},get preferredTimeDisplayMode(){return this.preferredTimeDisplayMode_;},set preferredTimeDisplayMode(v){if(this.preferredTimeDisplayMode_===v)return;this.preferredTimeDisplayMode_=v;tr.b.Unit.didPreferredTimeDisplayUnitChange();}});'use strict';const POLYFILL_WARNING_MESSAGE='Trace Viewer is running with WebComponentsV0 polyfill, and some '+'features may be broken. As a workaround, you may try running chrome '+'with "--enable-blink-features=ShadowDOMV0,CustomElementsV0,HTMLImports" '+'flag. See crbug.com/1036492.';Polymer({is:'tr-ui-timeline-view',created(){this.trackViewContainer_=undefined;this.queuedModel_=undefined;this.builtPromise_=undefined;this.doneBuilding_=undefined;},attached(){this.async(function(){this.trackViewContainer_=Polymer.dom(this).querySelector('#track_view_container');if(!this.trackViewContainer_){throw new Error('missing trackviewContainer');}
+if(this.queuedModel_)this.updateContents_();});},ready(){this.tabIndex=0;this.polyfillWarnedOnce_=false;this.titleEl_=this.$.title;this.leftControlsEl_=this.$.left_controls;this.rightControlsEl_=this.$.right_controls;this.collapsingControlsEl_=this.$.collapsing_controls;this.sidePanelContainer_=this.$.side_panel_container;this.brushingStateController_=new tr.c.BrushingStateController(this);this.findCtl_=this.$.view_find_control;this.findCtl_.controller=new tr.ui.FindController(this.brushingStateController_);this.scriptingCtl_=document.createElement('tr-ui-scripting-control');this.scriptingCtl_.controller=new tr.c.ScriptingController(this.brushingStateController_);this.sidePanelContainer_.brushingStateController=this.brushingStateController_;if(window.tr.metrics&&window.tr.metrics.sh&&window.tr.metrics.sh.SystemHealthMetric){this.railScoreSpan_=document.createElement('tr-metrics-ui-sh-system-health-span');Polymer.dom(this.rightControls).appendChild(this.railScoreSpan_);}else{this.railScoreSpan_=undefined;}
+this.flowEventFilter_=this.$.flow_event_filter_dropdown;this.processFilter_=this.$.process_filter_dropdown;this.optionsDropdown_=this.$.view_options_dropdown;this.selectedFlowEvents_=new Set();this.highlightVSync_=false;this.highlightVSyncCheckbox_=tr.ui.b.createCheckBox(this,'highlightVSync','tr.ui.TimelineView.highlightVSync',false,'Highlight VSync');Polymer.dom(this.optionsDropdown_).appendChild(this.highlightVSyncCheckbox_);this.initMetadataButton_();this.initConsoleButton_();this.initHelpButton_();Polymer.dom(this.collapsingControls).appendChild(this.scriptingCtl_);this.dragEl_=this.$.drag_handle;this.analysisEl_=this.$.analysis;this.analysisEl_.brushingStateController=this.brushingStateController_;this.addEventListener('requestSelectionChange',function(e){const sc=this.brushingStateController_;sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);}.bind(this));this.onViewportChanged_=this.onViewportChanged_.bind(this);this.bindKeyListeners_();this.dragEl_.target=this.analysisEl_;},get globalMode(){return this.hotkeyController.globalMode;},set globalMode(globalMode){globalMode=!!globalMode;this.brushingStateController_.historyEnabled=globalMode;this.hotkeyController.globalMode=globalMode;},get hotkeyController(){return this.$.hkc;},warnPolyfill(){if(this.polyfillWarnedOnce_)return;console.warn(POLYFILL_WARNING_MESSAGE);this.polyfillWarnedOnce_=true;if(!window.__hideTraceViewerPolyfillWarning){const polyfillWarningsEl=Polymer.dom(this.root).querySelector('#polyfill-warning');polyfillWarningsEl.addMessage(POLYFILL_WARNING_MESSAGE,[{buttonText:'Hide',onClick:()=>polyfillWarningsEl.clearMessages()}]);}},updateDocumentFavicon(){let hue;if(!this.model){hue='blue';}else{hue=this.model.faviconHue;}
let faviconData=tr.ui.b.FaviconsByHue[hue];if(faviconData===undefined){faviconData=tr.ui.b.FaviconsByHue.blue;}
let link=Polymer.dom(document.head).querySelector('link[rel="shortcut icon"]');if(!link){link=document.createElement('link');link.rel='shortcut icon';Polymer.dom(document.head).appendChild(link);}
-link.href=faviconData;},get showFlowEvents(){return this.showFlowEvents_;},set showFlowEvents(showFlowEvents){this.showFlowEvents_=showFlowEvents;if(!this.trackView_)return;this.trackView_.viewport.showFlowEvents=showFlowEvents;},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;if(!this.trackView_)return;this.trackView_.viewport.highlightVSync=highlightVSync;},initHelpButton_(){const helpButtonEl=this.$.view_help_button;const dlg=new tr.ui.b.Overlay();dlg.title='Chrome Tracing Help';dlg.visible=false;dlg.appendChild(document.createElement('tr-ui-timeline-view-help-overlay'));function onClick(e){dlg.visible=!dlg.visible;e.stopPropagation();}
+link.href=faviconData;},get selectedFlowEvents(){return this.selectedFlowEvents_;},set selectedFlowEvents(selectedFlowEvents){this.selectedFlowEvents_=selectedFlowEvents;},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;if(!this.trackView_)return;this.trackView_.viewport.highlightVSync=highlightVSync;},initHelpButton_(){const helpButtonEl=this.$.view_help_button;const dlg=new tr.ui.b.Overlay();dlg.title='Chrome Tracing Help';dlg.visible=false;dlg.appendChild(document.createElement('tr-ui-timeline-view-help-overlay'));function onClick(e){dlg.visible=!dlg.visible;e.stopPropagation();}
helpButtonEl.addEventListener('click',onClick.bind(this));},initConsoleButton_(){const toggleEl=this.$.view_console_button;function onClick(e){this.scriptingCtl_.toggleVisibility();e.stopPropagation();return false;}
toggleEl.addEventListener('click',onClick.bind(this));},initMetadataButton_(){const showEl=this.$.view_metadata_button;function onClick(e){const dlg=new tr.ui.b.Overlay();dlg.title='Metadata for trace';const metadataOverlay=document.createElement('tr-ui-timeline-view-metadata-overlay');metadataOverlay.metadata=this.model.metadata;Polymer.dom(dlg).appendChild(metadataOverlay);dlg.visible=true;e.stopPropagation();return false;}
-showEl.addEventListener('click',onClick.bind(this));this.updateMetadataButtonVisibility_();},updateMetadataButtonVisibility_(){const showEl=this.$.view_metadata_button;showEl.style.display=(this.model&&this.model.metadata.length)?'':'none';},updateProcessList_(){const dropdown=Polymer.dom(this.processFilter_);while(dropdown.firstChild){dropdown.removeChild(dropdown.firstChild);}
+showEl.addEventListener('click',onClick.bind(this));this.updateMetadataButtonVisibility_();},updateMetadataButtonVisibility_(){const showEl=this.$.view_metadata_button;showEl.style.display=(this.model&&this.model.metadata.length)?'':'none';},updateFlowEventList_(){const dropdown=Polymer.dom(this.flowEventFilter_);while(dropdown.firstChild){dropdown.removeChild(dropdown.firstChild);}
+if(!this.model)return;const cboxes=[];const updateAll=(checked)=>{for(const cbox of cboxes){cbox.checked=checked;}};dropdown.appendChild(tr.ui.b.createButton('All',()=>updateAll(true)));dropdown.appendChild(tr.ui.b.createButton('None',()=>updateAll(false)));const categories=new Set();for(const event of this.model.flowEvents){for(const category of tr.b.getCategoryParts(event.category)){categories.add(category);}}
+const sortedCategories=[...categories].sort((a,b)=>a.localeCompare(b,'en',{sensitivity:'base'}));for(const category of sortedCategories){const cbox=tr.ui.b.createCheckBox(undefined,undefined,'tr.ui.TimelineView.selectedFlowEvents.'+category,false,category,()=>{if(cbox.checked){this.selectedFlowEvents.add(category);}else{this.selectedFlowEvents.delete(category);}
+if(this.trackView_){this.trackView_.viewport.dispatchChangeEvent();}});if(cbox.checked){this.selectedFlowEvents.add(category);}
+cboxes.push(cbox);dropdown.appendChild(cbox);}},updateProcessList_(){const dropdown=Polymer.dom(this.processFilter_);while(dropdown.firstChild){dropdown.removeChild(dropdown.firstChild);}
if(!this.model)return;const trackView=this.trackViewContainer_.querySelector('tr-ui-timeline-track-view');const processViews=trackView.processViews;const cboxes=[];const updateAll=(checked)=>{for(const cbox of cboxes){cbox.checked=checked;}};dropdown.appendChild(tr.ui.b.createButton('All',()=>updateAll(true)));dropdown.appendChild(tr.ui.b.createButton('None',()=>updateAll(false)));for(const view of processViews){const cbox=tr.ui.b.createCheckBox(undefined,undefined,undefined,true,view.processBase.userFriendlyName,()=>view.visible=cbox.checked);cbox.checked=view.visible;cboxes.push(cbox);view.addEventListener('visibility',()=>cbox.checked=view.visible);dropdown.appendChild(cbox);}},get leftControls(){return this.leftControlsEl_;},get rightControls(){return this.rightControlsEl_;},get collapsingControls(){return this.collapsingControlsEl_;},get viewTitle(){return Polymer.dom(this.titleEl_).textContent.substring(Polymer.dom(this.titleEl_).textContent.length-2);},set viewTitle(text){if(text===undefined){Polymer.dom(this.titleEl_).textContent='';this.titleEl_.hidden=true;return;}
this.titleEl_.hidden=false;Polymer.dom(this.titleEl_).textContent=text;},get model(){if(this.trackView_){return this.trackView_.model;}
return undefined;},set model(model){this.build(model);},async build(model){this.queuedModel_=model;this.builtPromise_=new Promise((resolve,reject)=>{this.doneBuilding_=resolve;});if(this.trackViewContainer_)await this.updateContents_();},get builtPromise(){return this.builtPromise_;},async updateContents_(){if(this.trackViewContainer_===undefined){throw new Error('timeline-view.updateContents_ requires trackViewContainer_');}
@@ -10094,10 +9893,11 @@ const model=this.queuedModel_;this.queuedModel_=undefined;const modelInstanceCha
Polymer.dom(this.trackViewContainer_).textContent='';if(this.trackView_){this.trackView_.viewport.removeEventListener('change',this.onViewportChanged_);this.trackView_.brushingStateController=undefined;this.trackView_.detach();this.trackView_=undefined;}
this.brushingStateController_.modelWillChange();}
if(modelValid&&!this.trackView_){this.trackView_=document.createElement('tr-ui-timeline-track-view');this.trackView_.timelineView=this;this.trackView.brushingStateController=this.brushingStateController_;Polymer.dom(this.trackViewContainer_).appendChild(this.trackView_);this.trackView_.viewport.addEventListener('change',this.onViewportChanged_);}
-if(modelValid){this.trackView_.model=model;this.trackView_.viewport.showFlowEvents=this.showFlowEvents;this.trackView_.viewport.highlightVSync=this.highlightVSync;if(this.railScoreSpan_){this.railScoreSpan_.model=model;}
+if(modelValid){this.trackView_.model=model;this.trackView_.viewport.selectedFlowEvents=this.selectedFlowEvents;this.trackView_.viewport.highlightVSync=this.highlightVSync;if(this.railScoreSpan_){this.railScoreSpan_.model=model;}
this.$.display_unit.preferredTimeDisplayMode=model.intrinsicTimeUnit;}
+if(window.CustomElements&&!window.CustomElements.hasNative){this.warnPolyfill();}
if(model){for(const warning of model.importWarningsThatShouldBeShownToUser){importWarningsEl.addMessage(`Import Warning: ${warning.type}: ${warning.message}`,[{buttonText:'Dismiss',onClick(event,infobar){infobar.visible=false;}}]);}}
-if(modelInstanceChanged){this.updateProcessList_();this.updateMetadataButtonVisibility_();this.brushingStateController_.modelDidChange();this.onViewportChanged_();}
+if(modelInstanceChanged){this.updateFlowEventList_();this.updateProcessList_();this.updateMetadataButtonVisibility_();this.brushingStateController_.modelDidChange();this.onViewportChanged_();}
this.doneBuilding_();},get brushingStateController(){return this.brushingStateController_;},get trackView(){return this.trackView_;},get settings(){if(!this.settings_){this.settings_=new tr.b.Settings();}
return this.settings_;},set focusElement(value){throw new Error('This is deprecated. Please set globalMode to true.');},bindKeyListeners_(){const hkc=this.hotkeyController;hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'`'.charCodeAt(0),useCapture:true,thisArg:this,callback(e){this.scriptingCtl_.toggleVisibility();if(!this.scriptingCtl_.hasFocus){this.focus();}
e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'/'.charCodeAt(0),useCapture:true,thisArg:this,callback(e){if(this.scriptingCtl_.hasFocus)return;if(this.findCtl_.hasFocus){this.focus();}else{this.findCtl_.focus();}
@@ -10110,8 +9910,8 @@ return this.rowStats_;},rebuildSubRowsIfNeeded_(){if(this.subRowsBuilt_)return;t
const dataByKey={};let hasValues=false;this.data_.forEach(function(datum){const key=groupingKeyFunc(datum);hasValues=hasValues||(key!==undefined);if(dataByKey[key]===undefined){dataByKey[key]=[];}
dataByKey[key].push(datum);});if(!hasValues){this.subRows_=undefined;return;}
this.subRows_=[];for(const key in dataByKey){const row=new Row(key,dataByKey[key],this.groupingKeyFuncs_.slice(1),this.rowStatsConstructor_);this.subRows_.push(row);}},get isExpanded(){return(this.subRows&&(this.subRows.length>0)&&(this.subRows.length<5));},get subRows(){this.rebuildSubRowsIfNeeded_();return this.subRows_;}};Polymer({is:'tr-ui-b-grouping-table',created(){this.dataToGroup_=undefined;this.groupBy_=undefined;this.rowStatsConstructor_=undefined;},get tableColumns(){return this.$.table.tableColumns;},set tableColumns(tableColumns){this.$.table.tableColumns=tableColumns;},get tableRows(){return this.$.table.tableRows;},get sortColumnIndex(){return this.$.table.sortColumnIndex;},set sortColumnIndex(sortColumnIndex){this.$.table.sortColumnIndex=sortColumnIndex;},get sortDescending(){return this.$.table.sortDescending;},set sortDescending(sortDescending){this.$.table.sortDescending=sortDescending;},get selectionMode(){return this.$.table.selectionMode;},set selectionMode(selectionMode){this.$.table.selectionMode=selectionMode;},get rowHighlightStyle(){return this.$.table.rowHighlightStyle;},set rowHighlightStyle(rowHighlightStyle){this.$.table.rowHighlightStyle=rowHighlightStyle;},get cellHighlightStyle(){return this.$.table.cellHighlightStyle;},set cellHighlightStyle(cellHighlightStyle){this.$.table.cellHighlightStyle=cellHighlightStyle;},get selectedColumnIndex(){return this.$.table.selectedColumnIndex;},set selectedColumnIndex(selectedColumnIndex){this.$.table.selectedColumnIndex=selectedColumnIndex;},get selectedTableRow(){return this.$.table.selectedTableRow;},set selectedTableRow(selectedTableRow){this.$.table.selectedTableRow=selectedTableRow;},get groupBy(){return this.groupBy_;},set groupBy(groupBy){this.groupBy_=groupBy;this.updateContents_();},get dataToGroup(){return this.dataToGroup_;},set dataToGroup(dataToGroup){this.dataToGroup_=dataToGroup;this.updateContents_();},get rowStatsConstructor(){return this.rowStatsConstructor_;},set rowStatsConstructor(rowStatsConstructor){this.rowStatsConstructor_=rowStatsConstructor;this.updateContents_();},rebuild(){this.$.table.rebuild();},updateContents_(){const groupBy=this.groupBy_||[];const dataToGroup=this.dataToGroup_||[];const rowStatsConstructor=this.rowStatsConstructor_||function(){};const superRow=new Row('',dataToGroup,groupBy,rowStatsConstructor);this.$.table.tableRows=superRow.subRows||[];}});return{};});'use strict';tr.exportTo('tr.ui.b',function(){const THIS_DOC=document.currentScript.ownerDocument;Polymer({is:'tr-ui-b-grouping-table-groupby-picker-group',created(){this.picker_=undefined;this.group_=undefined;},get picker(){return this.picker_;},set picker(picker){this.picker_=picker;},get group(){return this.group_;},set group(g){this.group_=g;this.$.label.textContent=g.label;},get enabled(){return this.$.enabled.checked;},set enabled(enabled){this.$.enabled.checked=enabled;if(!this.enabled){this.$.left.style.display='none';this.$.right.style.display='none';}},set isFirst(isFirst){this.$.left.style.display=(!this.enabled||isFirst)?'none':'inline';},set isLast(isLast){this.$.right.style.display=(!this.enabled||isLast)?'none':'inline';},moveLeft_(){this.picker.moveLeft_(this);},moveRight_(){this.picker.moveRight_(this);},onEnableChanged_(){if(!this.enabled){this.$.left.style.display='none';this.$.right.style.display='none';}
-this.picker.onEnableChanged_(this);}});Polymer({is:'tr-ui-b-grouping-table-groupby-picker',created(){this.settingsKey_=undefined;},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;if(this.$.container.children.length){this.restoreSetting_();}},restoreSetting_(){if(this.settingsKey_===undefined)return;this.currentGroupKeys=tr.b.Settings.get(this.settingsKey_,this.currentGroupKeys);},get possibleGroups(){return[...this.$.container.children].map(groupEl=>groupEl.group);},set possibleGroups(possibleGroups){Polymer.dom(this.$.container).textContent='';for(let i=0;i<possibleGroups.length;++i){const groupEl=document.createElement('tr-ui-b-grouping-table-groupby-picker-group');groupEl.picker=this;groupEl.group=possibleGroups[i];Polymer.dom(this.$.container).appendChild(groupEl);}
-this.restoreSetting_();this.updateFirstLast_();},updateFirstLast_(){const groupEls=this.$.container.children;const enabledGroupEls=[...groupEls].filter(el=>el.enabled);for(let i=0;i<enabledGroupEls.length;++i){enabledGroupEls[i].isFirst=i===0;enabledGroupEls[i].isLast=i===enabledGroupEls.length-1;}},get currentGroupKeys(){return this.currentGroups.map(group=>group.key);},get currentGroups(){const groups=[];for(const groupEl of this.$.container.children){if(groupEl.enabled){groups.push(groupEl.group);}}
+this.picker.onEnableChanged_(this);}});Polymer({is:'tr-ui-b-grouping-table-groupby-picker',created(){this.settingsKey_=undefined;},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;if(this.$.container.children.length){this.restoreSetting_();}},restoreSetting_(){if(this.settingsKey_===undefined)return;this.currentGroupKeys=tr.b.Settings.get(this.settingsKey_,this.currentGroupKeys);},get possibleGroups(){return Array.from(this.$.container.children).map(groupEl=>groupEl.group);},set possibleGroups(possibleGroups){Polymer.dom(this.$.container).textContent='';for(let i=0;i<possibleGroups.length;++i){const groupEl=document.createElement('tr-ui-b-grouping-table-groupby-picker-group');groupEl.picker=this;groupEl.group=possibleGroups[i];Polymer.dom(this.$.container).appendChild(groupEl);}
+this.restoreSetting_();this.updateFirstLast_();},updateFirstLast_(){const groupEls=Array.from(this.$.container.children);const enabledGroupEls=groupEls.filter(el=>el.enabled);for(let i=0;i<enabledGroupEls.length;++i){enabledGroupEls[i].isFirst=i===0;enabledGroupEls[i].isLast=i===enabledGroupEls.length-1;}},get currentGroupKeys(){return this.currentGroups.map(group=>group.key);},get currentGroups(){const groups=[];for(const groupEl of Array.from(this.$.container.children)){if(groupEl.enabled){groups.push(groupEl.group);}}
return groups;},set currentGroupKeys(newKeys){if(!tr.b.compareArrays(this.currentGroupKeys,newKeys,(x,y)=>x.localeCompare(y))){return;}
const possibleGroups=new Map();for(const group of this.possibleGroups){possibleGroups.set(group.key,group);}
const groupEls=this.$.container.children;let i=0;for(i=0;i<newKeys.length;++i){const group=possibleGroups.get(newKeys[i]);if(group===undefined){newKeys.splice(i,1);--i;continue;}
@@ -10253,7 +10053,7 @@ for(const name of names){const option=document.createElement('option');option.te
if(names.includes(this.viewState.displayStatisticName)){this.displayStatisticName=this.viewState.displayStatisticName;this.$.statistic.value=this.displayStatisticName;}else{this.viewState.displayStatisticName=names[0]||'';}},get anyOverviewCharts_(){for(const row of tr.v.ui.HistogramSetTableRowState.walkAll(this.viewState.tableRowStates.values())){if(row.isOverviewed)return true;}
return false;},async toggleOverviewLineCharts_(){const showOverviews=!this.anyOverviewCharts_;const mark=tr.b.Timing.mark('histogram-set-controls',(showOverviews?'show':'hide')+'OverviewCharts');for(const row of tr.v.ui.HistogramSetTableRowState.walkAll(this.viewState.tableRowStates.values())){await row.update({isOverviewed:showOverviews});}
this.$.hide_overview.style.display=showOverviews?'inline':'none';this.$.show_overview.style.display=showOverviews?'none':'inline';await tr.b.animationFrame();mark.end();},set helpHref(href){this.$.help.href=href;this.$.help.style.display='inline';},set feedbackHref(href){this.$.feedback.href=href;this.$.feedback.style.display='inline';},clearSearch_(){this.set('searchQuery','');this.$.search.focus();},getAlphaString_(alphaIndex){return(''+ALPHA_OPTIONS[alphaIndex]).substr(0,5);},openAlphaSlider_(){const alphaButtonRect=this.$.alpha.getBoundingClientRect();this.$.alpha_slider_container.style.display='flex';this.$.alpha_slider_container.style.top=alphaButtonRect.bottom+'px';this.$.alpha_slider_container.style.left=alphaButtonRect.left+'px';this.$.alpha_slider.focus();},closeAlphaSlider_(){this.$.alpha_slider_container.style.display='';},updateAlpha_(){this.alphaIndex=this.$.alpha_slider.value;},getAlphaIndexFromViewState_(){for(let i=0;i<ALPHA_OPTIONS.length;++i){if(ALPHA_OPTIONS[i]>=this.viewState.alpha)return i;}
-return ALPHA_OPTIONS.length-1;},set enableVisualization(enable){this.$.show_visualization.style.display=enable?'inline':'none';},loadVisualization_(){tr.b.dispatchSimpleEvent(this,'loadVisualization',true,true,{});},});return{};});'use strict';tr.exportTo('tr.v',function(){class HistogramSetHierarchy{constructor(name){this.name=name;this.description='';this.depth=0;this.subRows=[];this.columns=new Map();}*walk(){yield this;for(const row of this.subRows)yield*row.walk();}
+return ALPHA_OPTIONS.length-1;},});return{};});'use strict';tr.exportTo('tr.v',function(){class HistogramSetHierarchy{constructor(name){this.name=name;this.description='';this.depth=0;this.subRows=[];this.columns=new Map();}*walk(){yield this;for(const row of this.subRows)yield*row.walk();}
static*walkAll(rootRows){for(const rootRow of rootRows)yield*rootRow.walk();}
static build(histogramArrayMap){const rootRows=[];HistogramSetHierarchy.buildInternal_(histogramArrayMap,[],rootRows);const histograms=new tr.v.HistogramSet();for(const row of HistogramSetHierarchy.walkAll(rootRows)){for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;histograms.addHistogram(hist);}}
histograms.deduplicateDiagnostics();for(const row of HistogramSetHierarchy.walkAll(rootRows)){row.maybeRebin_();}
@@ -10337,7 +10137,7 @@ if(!this.hierarchies_){await this.progress_('Merging Histograms...');this.hierar
const tableRowsDirty=this.tableRows_===undefined;if(tableRowsDirty){this.tableRows_=this.hierarchies_.map(hierarchy=>new tr.v.ui.HistogramSetTableRow(hierarchy,this.$.table,this.viewState));tr.b.Timing.instant('histogram-set-table','rootRowCount',this.tableRows_.length);const namesToRowStates=new Map();for(const row of this.tableRows_){namesToRowStates.set(row.name,row.viewState);}
await this.viewState.update({tableRowStates:namesToRowStates});}
await this.progress_('Configuring table...');this.nameColumnTitle_.children[1].style.filter=this.viewState.constrainNameColumn?'invert(100%)':'';const referenceDisplayLabelIndex=this.displayLabels_.indexOf(this.viewState.referenceDisplayLabel);this.$.table.selectedTableColumnIndex=(referenceDisplayLabelIndex<0)?undefined:(1+referenceDisplayLabelIndex);this.removeEventListener('sort-column-changed',this.sortColumnChangedListener_);this.$.table.sortColumnIndex=this.viewState.sortColumnIndex;this.$.table.sortDescending=this.viewState.sortDescending;this.addEventListener('sort-column-changed',this.sortColumnChangedListener_);if(tableRowsDirty){await this.progress_('Building DOM...');this.$.table.tableRows=this.tableRows_;for(const row of this.tableRows_){const previousState=previousRowStates.get(row.name);if(!previousState)continue;await row.restoreState(previousState);}}
-this.$.table.rebuild();},async onRowExpandedChanged_(event){event.row.viewState.isExpanded=this.$.table.getExpandedForTableRow(event.row);tr.b.Timing.instant('histogram-set-table','row'+(event.row.viewState.isExpanded?'Expanded':'Collapsed'));if(this.nameColumnTitle_.children[1].style.display==='block')return;await tr.b.animationFrame();this.checkNameColumnOverflow_(event.row.subRows);},checkNameColumnOverflow_(rows){for(const row of rows){if(!row.nameCell.isOverflowing)continue;const[nameSpan,dots]=this.nameColumnTitle_.children;dots.style.display='block';const labelWidthPx=tr.v.ui.NAME_COLUMN_WIDTH_PX-
+this.$.table.rebuild();},async onRowExpandedChanged_(event){event.row.viewState.isExpanded=this.$.table.getExpandedForTableRow(event.row);tr.b.Timing.instant('histogram-set-table','row'+(event.row.viewState.isExpanded?'Expanded':'Collapsed'));if(this.nameColumnTitle_.children[1].style.display==='block')return;await tr.b.animationFrame();this.checkNameColumnOverflow_(event.row.subRows);},checkNameColumnOverflow_(rows){for(const row of rows){if(!row.nameCell.isOverflowing)continue;const[nameSpan,dots]=Array.from(this.nameColumnTitle_.children);dots.style.display='block';const labelWidthPx=tr.v.ui.NAME_COLUMN_WIDTH_PX-
dots.getBoundingClientRect().width;nameSpan.style.width=labelWidthPx+'px';return;}},groupHistograms_(){const groupings=this.viewState.groupings.slice();groupings.push(tr.v.HistogramGrouping.DISPLAY_LABEL);function canSkipGrouping(grouping,groupedHistograms){if(groupedHistograms.size>1)return false;if(grouping.key===groupings[0].key)return false;if(grouping.key===tr.v.HistogramGrouping.DISPLAY_LABEL.key){return false;}
return true;}
this.groupedHistograms_=this.filteredHistograms_.groupHistogramsRecursively(groupings,canSkipGrouping);this.hierarchies_=undefined;},async onViewStateUpdate_(event){if(this.histograms_===undefined)return;if(event.delta.searchQuery!==undefined||event.delta.showAll!==undefined){this.filteredHistograms_=undefined;}
@@ -10349,48 +10149,11 @@ for(const row of this.tableRows_){if(this.viewState.tableRowStates.get(row.name)
return;}
await this.updateContents_();},onSortColumnChanged_(event){tr.b.Timing.instant('histogram-set-table','sortColumn');this.viewState.update({sortColumnIndex:event.sortColumnIndex,sortDescending:event.sortDescending,});},onRequestSelectionChange_(event){if(event.selection instanceof tr.model.EventSet)return;event.stopPropagation();tr.b.Timing.instant('histogram-set-table','selectHistogramNames');let histogramNames=event.selection;histogramNames.sort();histogramNames=histogramNames.map(escapeRegExp).join('|');this.viewState.update({showAll:true,searchQuery:`^(${histogramNames})$`,});},get leafHistograms(){const histograms=new tr.v.HistogramSet();for(const row of
tr.v.ui.HistogramSetTableRow.walkAll(this.$.table.tableRows)){if(row.subRows.length)continue;for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;histograms.addHistogram(hist);}}
-return histograms;}});return{MIDLINE_HORIZONTAL_ELLIPSIS,};});'use strict';tr.exportTo('tr.v.ui',function(){const PAGE_BREAKDOWN_KEY='pageBreakdown';Polymer({is:'tr-v-ui-metrics-visualization',created(){this.charts_=new Map();},ready(){this.$.start.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.filterByPercentile_();});this.$.end.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.filterByPercentile_();});this.$.search_page.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.searchByPage_();});},build(chartData){this.title_=chartData.title;this.aggregateData_=chartData.aggregate;this.data_=chartData.page;this.submetricsData_=chartData.submetrics;this.benchmarkCount_=chartData.aggregate.length;const aggregateChart=this.initializeColumnChart(this.title_);Polymer.dom(this.$.aggregateContainer).appendChild(aggregateChart);this.charts_.set(tr.v.ui.AGGREGATE_KEY,aggregateChart);this.setChartColors_(tr.v.ui.AGGREGATE_KEY);aggregateChart.data=chartData.aggregate;this.setChartSize_(tr.v.ui.AGGREGATE_KEY);const newChart=this.initializeColumnChart(this.title_+' Breakdown');newChart.enableToolTip=true;newChart.toolTipCallBack=(rect)=>this.openChildChart_(rect);Polymer.dom(this.$.pageByPageContainer).appendChild(newChart);this.charts_.set(PAGE_BREAKDOWN_KEY,newChart);this.setChartColors_(PAGE_BREAKDOWN_KEY);newChart.data=this.data_;this.setChartSize_(PAGE_BREAKDOWN_KEY);},setChartSize_(page){const chart=this.charts_.get(page);const pageCount=chart.data.length;chart.graphHeight=tr.b.math.clamp(pageCount*20,400,600);chart.graphWidth=tr.b.math.clamp(pageCount*30,200,1000);},setChartColors_(page){const chart=this.charts_.get(page);const metrics=tr.v.ui.METRICS.get(this.title_);for(let i=0;i<this.benchmarkCount_;++i){for(let j=0;j<metrics.length;++j){const mainColorIndex=j%tr.v.ui.COLORS.length;const subColorIndex=i%tr.v.ui.COLORS[mainColorIndex].length;const color=tr.v.ui.COLORS[mainColorIndex][subColorIndex];const series=metrics[j]+'-'+this.aggregateData_[i].x;chart.getDataSeries(series).color=color;if(i===0){chart.getDataSeries(series).title=metrics[j];}else{chart.getDataSeries(series).title='';}}}},initializeColumnChart(title){const newChart=new tr.ui.b.NameColumnChart();newChart.hideLegend=false;newChart.isStacked=true;newChart.yAxisLabel='ms';newChart.hideXAxis=true;newChart.displayXInHover=true;newChart.isGrouped=true;newChart.showTitleInLegend=true;newChart.chartTitle=title;newChart.titleHeight='14pt';return newChart;},initializeChildChart_(title,height,width){const div=document.createElement('div');div.classList.add('container');Polymer.dom(this.$.submetricsContainer).insertBefore(div,this.$.submetricsContainer.firstChild);const childChart=new tr.ui.b.NameBarChart();childChart.xAxisLabel='ms';childChart.chartTitle=title;childChart.graphHeight=height;childChart.graphWidth=width;childChart.titleHeight='14pt';childChart.isStacked=true;childChart.hideLegend=true;childChart.isGrouped=true;childChart.isWaterfall=true;div.appendChild(childChart);const button=this.initializeCloseButton_(div,this.$.submetricsContainer);div.appendChild(button);return childChart;},initializeCloseButton_(div,parent){const button=this.$.close.cloneNode(true);button.style.display='inline-block';button.addEventListener('click',()=>{Polymer.dom(parent).removeChild(div);});return button;},openChildChart_(rect){const metrics=tr.v.ui.METRICS.get(this.title_);let metric;let metricIndex;for(let i=0;i<metrics.length;++i){if(rect.key.startsWith(metrics[i])){metric=metrics[i];metricIndex=i;break;}}
-const page=rect.datum.group;const title=this.title_+' '+metric+': '+page;const submetrics=this.submetricsData_.get(page).get(metric);const width=tr.b.math.clamp(submetrics.size*150,300,700);const height=tr.b.math.clamp(submetrics.size*this.benchmarkCount_*50,300,700);const childChart=this.initializeChildChart_(title,height,width);childChart.data=this.processSubmetrics_(childChart,submetrics,0,metricIndex).data;},processSubmetrics_(chart,submetrics,hideValue,metricIndex){const finalData=[];let submetricIndex=0;for(const submetric of submetrics.values()){let benchmarkIndex=0;for(const benchmark of submetric.values()){benchmark.hide=!hideValue?0:hideValue;const series=benchmark.x+'-'+benchmark.group;const mainColorIndex=metricIndex%tr.v.ui.COLORS.length;const subColorIndex=benchmarkIndex%tr.v.ui.COLORS[mainColorIndex].length;chart.getDataSeries(series).color=tr.v.ui.COLORS[mainColorIndex][subColorIndex];if(benchmarkIndex===(this.benchmarkCount_-1)){hideValue+=benchmark[series];}
-finalData.push(benchmark);benchmarkIndex++;}
-submetricIndex++;}
-return{data:finalData,hide:hideValue};},filterByPercentile_(){const startPercentile=this.$.start.value;const endPercentile=this.$.end.value;if(startPercentile===''||endPercentile==='')return;const length=this.data_.length/(this.benchmarkCount_+1);const startIndex=this.getPercentileIndex_(startPercentile,length);const endIndex=this.getPercentileIndex_(endPercentile,length);this.charts_.get(PAGE_BREAKDOWN_KEY).data=this.data_.slice(startIndex,endIndex);},getPercentileIndex_(percentile,arrayLength){const index=Math.ceil(arrayLength*(percentile/100.0));if(index===-1)return 0;if(index>=arrayLength)return arrayLength;return index*this.benchmarkCount_;},searchByPage_(){const criteria=this.$.search_page.value;if(criteria==='')return;const query=new RegExp(criteria);const filteredData=[...this.data_].filter(group=>{if(group.group)return group.group.match(query);return false;});if(filteredData.length<1){this.$.search_error.style.display='block';return;}
-const page=filteredData[0].group;const title=this.title_+' Breakdown: '+page;const metricToSubmetricMap=this.submetricsData_.get(page);let totalSubmetrics=0;for(const submetrics of metricToSubmetricMap.values()){for(const benchmark of submetrics.values()){totalSubmetrics+=benchmark.length;}}
-const width=tr.b.math.clamp(totalSubmetrics*150,300,700);const height=tr.b.math.clamp(totalSubmetrics*this.benchmarkCount_*30,300,700);const childChart=this.initializeChildChart_(title,height,width);const childData=[];let hide=0;let metricIndex=0;for(const submetrics of metricToSubmetricMap.values()){const submetricsData=this.processSubmetrics_(childChart,submetrics,hide,metricIndex);childData.push(...submetricsData.data);hide=submetricsData.hide;metricIndex++;}
-childChart.data=childData;},});});'use strict';Polymer({is:'tr-v-ui-raster-visualization',ready(){this.$.pageSelector.addEventListener('click',()=>{this.selectPage_();});this.$.search_page.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.searchByPage_();});this.$.search_button.addEventListener('click',()=>{this.searchByPage_();});},build(chartData){this.data_=chartData;const aggregateChart=this.createChart_('Aggregate Data by Run');Polymer.dom(this.$.aggregateContainer).appendChild(aggregateChart);aggregateChart.enableToolTip=true;aggregateChart.toolTipCallBack=(rect)=>this.openBenchmarkChart_(rect);this.setChartColors_(aggregateChart,this.data_.get(tr.v.ui.AGGREGATE_KEY));aggregateChart.data=this.data_.get(tr.v.ui.AGGREGATE_KEY);this.setChartSize_(aggregateChart,this.data_.get(tr.v.ui.AGGREGATE_KEY).length);for(const page of this.data_.keys()){if(page===tr.v.ui.AGGREGATE_KEY)continue;const option=document.createElement('option');option.textContent=page;option.value=page;this.$.pageSelector.appendChild(option);}},setChartSize_(chart,pageCount,dataLength){chart.graphHeight=tr.b.math.clamp(pageCount*25,175,1000);chart.graphWidth=tr.b.math.clamp(pageCount*25,500,1000);},setChartColors_(chart,data){const metrics=new Map();let count=0;for(const thread of tr.v.ui.FRAME.values()){for(const metric of thread.keys()){metrics.set(metric,count);count++;}}
-for(let i=0;i<Math.floor(data.length/tr.v.ui.FRAME.length);++i){let j=0;for(const[threadName,thread]of tr.v.ui.FRAME.entries()){for(const metric of thread.keys()){let color='transparent';if(thread.get(metric)){const mainColorIndex=metrics.get(metric)%tr.v.ui.COLORS.length;const subColorIndex=i%tr.v.ui.COLORS[mainColorIndex].length;color=tr.v.ui.COLORS[mainColorIndex][subColorIndex];}
-const series=metric+'-'+data[i*2+j].x+'-'+threadName;chart.getDataSeries(series).color=color;chart.getDataSeries(series).title=!i?metric:'';}
-j++;}}},createChart_(title){const newChart=new tr.ui.b.NameBarChart();newChart.chartTitle=title;newChart.xAxisLabel='ms';newChart.hideLegend=false;newChart.showTitleInLegend=true;newChart.hideYAxis=true;newChart.isStacked=true;newChart.displayXInHover=true;newChart.isGrouped=true;return newChart;},openBenchmarkChart_(rect){const benchmarkIndex=Math.floor(rect.index/tr.v.ui.FRAME.length);const title=rect.datum.x;const div=document.createElement('div');Polymer.dom(this.$.pageContainer).insertBefore(div,this.$.pageContainer.firstChild);const chart=this.createChart_(title);div.appendChild(chart);const button=this.initializeCloseButton_(div,this.$.pageContainer);div.appendChild(button);const newDataSet=[];for(const page of this.data_.keys()){if(page===tr.v.ui.AGGREGATE_KEY)continue;for(let i=0;i<tr.v.ui.FRAME.length;i++){newDataSet.push(this.data_.get(page)[benchmarkIndex*tr.v.ui.FRAME.length+i]);}}
-this.setChartColors_(chart,newDataSet);chart.data=newDataSet;this.setChartSize_(chart,newDataSet.length);},selectPage_(){const div=document.createElement('div');const page=this.$.pageSelector.value;if(page==='')return;Polymer.dom(this.$.pageContainer).insertBefore(div,this.$.pageContainer.firstChild);const pageChart=this.createChart_(page);div.appendChild(pageChart);const button=this.initializeCloseButton_(div,this.$.pageContainer);div.appendChild(button);const pageData=this.data_.get(page);this.setChartColors_(pageChart,pageData);pageChart.data=pageData;this.setChartSize_(pageChart,pageData.length);},searchByPage_(){const criteria=this.$.search_page.value;if(criteria==='')return;const query=new RegExp(criteria);const filteredData=[...this.data_.keys()].filter(page=>page.match(query));if(filteredData.length<1){this.$.search_error.style.display='block';return;}
-const page=filteredData[0];const div=document.createElement('div');Polymer.dom(this.$.pageContainer).insertBefore(div,this.$.pageContainer.firstChild);const pageChart=this.createChart_(page);div.appendChild(pageChart);const button=this.initializeCloseButton_(div,this.$.pageContainer);div.appendChild(button);const pageData=this.data_.get(page);this.setChartColors_(pageChart,pageData);pageChart.data=pageData;this.setChartSize_(pageChart,pageData.length);},initializeCloseButton_(div,parent){const button=this.$.close.cloneNode(true);button.style.display='inline-block';button.addEventListener('click',()=>{Polymer.dom(parent).removeChild(div);});return button;},});'use strict';tr.exportTo('tr.v.ui',function(){const STATISTICS_KEY='statistics';const SUBMETRICS_KEY='submetrics';const AGGREGATE_KEY='aggregate';const RASTER_START_METRIC_KEY='pipeline:begin_frame_to_raster_start';const COLORS=[['#FFD740','#FFC400','#FFAB00','#E29800'],['#FF6E40','#FF3D00','#DD2C00','#A32000'],['#40C4FF','#00B0FF','#0091EA','#006DAF'],['#89C641','#54B503','#4AA510','#377A0D'],['#B388FF','#7C4DFF','#651FFF','#6200EA'],['#FF80AB','#FF4081','#F50057','#C51162'],['#FFAB40','#FF9100','#FF6D00','#D65C02'],['#8C9EFF','#536DFE','#3D5AFE','#304FFE']];const FRAME=[new Map([['pipeline:begin_frame_to_raster_start',false],['pipeline:begin_frame_to_raster_end',true]]),new Map([['pipeline:begin_frame_transport',true],['pipeline:begin_frame_to_frame_submission',true],['pipeline:frame_submission_to_display',true],['pipeline:draw',true]])];const METRICS=new Map([['Pipeline',['pipeline:begin_frame_transport','pipeline:begin_frame_to_frame_submission','pipeline:frame_submission_to_display','pipeline:draw']],['Thread',['thread_browser_cpu_time_per_frame','thread_display_compositor_cpu_time_per_frame','thread_GPU_cpu_time_per_frame','thread_IO_cpu_time_per_frame','thread_other_cpu_time_per_frame','thread_raster_cpu_time_per_frame','thread_renderer_compositor_cpu_time_per_frame','thread_renderer_main_cpu_time_per_frame']]]);function getValueFromMap(key,map){let retrievedValue=map.get(key);if(!retrievedValue){retrievedValue=new Map();map.set(key,retrievedValue);}
-return retrievedValue;}
-Polymer({is:'tr-v-ui-visualizations-data-container',created(){this.orderedBenchmarks_=[];this.groupedData_=new Map();},build(leafHistograms,histograms){if(!leafHistograms||leafHistograms.length<1||!histograms||histograms.length<1){this.$.data_error.style.display='block';return;}
-this.processHistograms_(this.groupHistograms_(histograms),this.groupHistograms_(leafHistograms));this.buildCharts_();},processHistograms_(histograms,leafHistograms){const benchmarkStartGrouping=tr.v.HistogramGrouping.BY_KEY.get(tr.v.d.RESERVED_NAMES.BENCHMARK_START);const benchmarkToStartTime=new Map();for(const[metric,benchmarks]of histograms.entries()){for(const[benchmark,pages]of leafHistograms.get(metric).entries()){for(const[page,histograms]of pages.entries()){for(const histogram of histograms){const aggregateToBenchmarkMap=getValueFromMap(AGGREGATE_KEY,this.groupedData_);const benchmarkToMetricMap=getValueFromMap(benchmark,aggregateToBenchmarkMap);benchmarkToMetricMap.set(metric,new Map([[STATISTICS_KEY,histogram.running]]));}}}
-for(const[benchmark,pages]of benchmarks.entries()){for(const[page,histograms]of pages.entries()){for(const histogram of histograms){if(!benchmarkToStartTime.get(benchmark)){benchmarkToStartTime.set(benchmark,benchmarkStartGrouping.callback(histogram));}
-const pageToBenchmarkMap=getValueFromMap(page,this.groupedData_);const benchmarkToMetricMap=getValueFromMap(benchmark,pageToBenchmarkMap);const mergedSubmetrics=new tr.v.d.DiagnosticMap();for(const bin of histogram.allBins){for(const map of bin.diagnosticMaps){mergedSubmetrics.addDiagnostics(map);}}
-if(benchmarkToMetricMap.get(metric))continue;benchmarkToMetricMap.set(metric,new Map([[STATISTICS_KEY,histogram.running],[SUBMETRICS_KEY,mergedSubmetrics.get('breakdown')]]));}}}}
-this.orderedBenchmarks_=this.sortBenchmarks_(benchmarkToStartTime);},groupHistograms_(histograms){const groupings=[tr.v.HistogramGrouping.HISTOGRAM_NAME,tr.v.HistogramGrouping.DISPLAY_LABEL,tr.v.HistogramGrouping.BY_KEY.get(tr.v.d.RESERVED_NAMES.STORIES)];return histograms.groupHistogramsRecursively(groupings);},sortBenchmarks_(benchmarks){return Array.from(benchmarks.keys()).sort((a,b)=>{Date.parse(benchmarks.get(a))-Date.parse(benchmarks.get(b));});},getSeriesKey_(metric,benchmark){return metric+'-'+benchmark;},buildCharts_(){const rasterDataToBePassed=this.buildRasterChart_();this.$.rasterVisualization.build(rasterDataToBePassed);for(const chartName of METRICS.keys()){const metricsDataToBePassed=this.buildMetricsData_(chartName);const newChart=this.$.metricsVisualization.cloneNode(true);newChart.style.display='block';Polymer.dom(this.$.metrics_container).appendChild(newChart);newChart.build(metricsDataToBePassed);}},buildRasterChart_(){const orderedPages=[...this.groupedData_.keys()].filter((page)=>this.filterPagesWithoutRasterMetric_(page)).sort((a,b)=>this.sortByRasterStart_(a,b));const allChartData=new Map();for(const page of orderedPages){const pageMap=this.groupedData_.get(page);let chartData=[];for(const benchmark of this.orderedBenchmarks_){if(!pageMap.has(benchmark))continue;const benchmarkMap=pageMap.get(benchmark);const benchmarkData=[];if(benchmarkMap.get(RASTER_START_METRIC_KEY)===undefined){continue;}
-for(const[threadName,thread]of FRAME.entries()){const data={x:benchmark,hide:0};if(page!==AGGREGATE_KEY)data.group=page;let rasterBegin=0;for(const metric of thread.keys()){const metricMap=benchmarkMap.get(metric);const key=this.getSeriesKey_(metric,data.x+'-'+threadName);const stats=metricMap.get(STATISTICS_KEY);const mean=stats?stats.mean:0;let roundedMean=Math.round(mean*100)/100;if(metric===RASTER_START_METRIC_KEY){rasterBegin=roundedMean;}else if(metric==='pipeline:begin_frame_to_raster_end'){roundedMean-=rasterBegin;}
-data[key]=roundedMean;}
-benchmarkData.push(data);}
-chartData=chartData.concat(benchmarkData);}
-allChartData.set(page,chartData);}
-return allChartData;},buildMetricsData_(chartName){const orderedPages=[...this.groupedData_.keys()].sort((a,b)=>this.sortByTotal_(a,b,chartName));const chartData=[];const aggregateChart=[];for(const page of orderedPages){const pageMap=this.groupedData_.get(page);for(const benchmark of this.orderedBenchmarks_){if(!pageMap.has(benchmark))continue;const data={x:benchmark,group:page};const benchmarkMap=pageMap.get(benchmark);for(const metric of METRICS.get(chartName)){const metricMap=benchmarkMap.get(metric);const key=this.getSeriesKey_(metric,benchmark);const stats=metricMap.get(STATISTICS_KEY);const mean=stats?stats.mean:0;data[key]=Math.round(mean*100)/100;}
-if(page===AGGREGATE_KEY){aggregateChart.push(data);}else{chartData.push(data);}}
-chartData.push({});}
-chartData.shift();return{title:chartName,aggregate:aggregateChart,page:chartData,submetrics:this.processSubmetricsData_(chartName)};},submetricsHelper_(submetric,value,benchmark,metricToSubmetricMap){let submetricToBenchmarkMap=metricToSubmetricMap.get(submetric);if(!submetricToBenchmarkMap){submetricToBenchmarkMap=[];metricToSubmetricMap.set(submetric,submetricToBenchmarkMap);}
-const data={x:submetric,hide:0,group:benchmark};const mean=value;const roundedMean=Math.round(mean*100)/100;if(!roundedMean)return;data[this.getSeriesKey_(submetric,benchmark)]=roundedMean;submetricToBenchmarkMap.push(data);},processSubmetricsData_(chartName){const submetrics=new Map();for(const[page,pageMap]of this.groupedData_.entries()){if(page===AGGREGATE_KEY)continue;const pageToMetricMap=getValueFromMap(page,submetrics);for(const benchmark of this.orderedBenchmarks_){const benchmarkMap=pageMap.get(benchmark);if(!benchmarkMap)continue;for(const metric of METRICS.get(chartName)){const metricMap=benchmarkMap.get(metric);const metricToSubmetricMap=getValueFromMap(metric,pageToMetricMap);const submetrics=metricMap.get(SUBMETRICS_KEY);if(!submetrics){this.submetricsHelper_(metric,metricMap.get(STATISTICS_KEY),benchmark,metricToSubmetricMap);continue;}
-for(const[submetric,value]of[...submetrics]){this.submetricsHelper_(submetric,value,benchmark,metricToSubmetricMap);}}}}
-return submetrics;},sortByTotal_(a,b,chartName){if(a===AGGREGATE_KEY)return-1;if(b===AGGREGATE_KEY)return 1;let aValue=0;const aMap=this.groupedData_.get(a);if(aMap.get(this.orderedBenchmarks_[0])!==undefined){for(const metric of METRICS.get(chartName)){const aMetricMap=aMap.get(this.orderedBenchmarks_[0]).get(metric);const aStats=aMetricMap.get(STATISTICS_KEY);aValue+=aStats?aStats.mean:0;}}
-let bValue=0;const bMap=this.groupedData_.get(b);if(bMap.get(this.orderedBenchmarks_[0])!==undefined){for(const metric of METRICS.get(chartName)){const bMetricMap=bMap.get(this.orderedBenchmarks_[0]).get(metric);const bStats=bMetricMap.get(STATISTICS_KEY);bValue+=bStats?bStats.mean:0;}}
-return aValue-bValue;},filterPagesWithoutRasterMetric_(page){const pageMap=this.groupedData_.get(page);for(const benchmark of this.orderedBenchmarks_){const pageMetricMap=pageMap.get(benchmark);if(!pageMetricMap)continue;const wantedMetric=pageMetricMap.get(RASTER_START_METRIC_KEY);if(wantedMetric!==undefined)return true;}
-return false;},sortByRasterStart_(a,b){if(a===AGGREGATE_KEY)return 1;if(b===AGGREGATE_KEY)return-1;let aValue=0;const aMap=this.groupedData_.get(a);if(aMap.get(this.orderedBenchmarks_[0])!==undefined){const aMetricMap=aMap.get(this.orderedBenchmarks_[0]).get(RASTER_START_METRIC_KEY);const aStats=aMetricMap.get(STATISTICS_KEY);aValue=aStats?aStats.mean:0;}
-let bValue=0;const bMap=this.groupedData_.get(b);if(bMap.get(this.orderedBenchmarks_[0])!==undefined){const bMetricMap=bMap.get(this.orderedBenchmarks_[0]).get(RASTER_START_METRIC_KEY);const bStats=bMetricMap.get(STATISTICS_KEY);bValue=bStats?bStats.mean:0;}
-return bValue-aValue;},});return{STATISTICS_KEY,SUBMETRICS_KEY,AGGREGATE_KEY,COLORS,FRAME,METRICS,getValueFromMap,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-view',listeners:{export:'onExport_',loadVisualization:'onLoadVisualization_'},created(){this.brushingStateController_=new tr.ui.NullBrushingStateController();this.viewState_=new tr.v.ui.HistogramSetViewState();this.visualizationLoaded_=false;},ready(){this.$.table.viewState=this.viewState;this.$.controls.viewState=this.viewState;},attached(){this.brushingStateController.parentController=tr.c.BrushingStateController.getControllerForElement(this.parentNode);},get brushingStateController(){return this.brushingStateController_;},get viewState(){return this.viewState_;},get histograms(){return this.$.table.histograms;},async build(histograms,opt_options){const options=opt_options||{};const progress=options.progress||(()=>Promise.resolve());if(options.helpHref)this.$.controls.helpHref=options.helpHref;if(options.feedbackHref){this.$.controls.feedbackHref=options.feedbackHref;}
+return histograms;}});return{MIDLINE_HORIZONTAL_ELLIPSIS,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-view',listeners:{export:'onExport_',},created(){this.brushingStateController_=new tr.ui.NullBrushingStateController();this.viewState_=new tr.v.ui.HistogramSetViewState();},ready(){this.$.table.viewState=this.viewState;this.$.controls.viewState=this.viewState;},attached(){this.brushingStateController.parentController=tr.c.BrushingStateController.getControllerForElement(this.parentNode);},get brushingStateController(){return this.brushingStateController_;},get viewState(){return this.viewState_;},get histograms(){return this.$.table.histograms;},async build(histograms,opt_options){const options=opt_options||{};const progress=options.progress||(()=>Promise.resolve());if(options.helpHref)this.$.controls.helpHref=options.helpHref;if(options.feedbackHref){this.$.controls.feedbackHref=options.feedbackHref;}
if(histograms===undefined||histograms.length===0){this.$.container.style.display='none';this.$.zero.style.display='block';this.style.display='block';return;}
-this.$.zero.style.display='none';this.$.container.style.display='block';this.$.container.style.maxHeight=(window.innerHeight-16)+'px';const buildMark=tr.b.Timing.mark('histogram-set-view','build');await progress('Finding important Histograms...');const sourceHistogramsMark=tr.b.Timing.mark('histogram-set-view','sourceHistograms');const sourceHistograms=histograms.sourceHistograms;sourceHistogramsMark.end();this.$.controls.showAllEnabled=(sourceHistograms.length!==histograms.length);await progress('Collecting parameters...');const collectParametersMark=tr.b.Timing.mark('histogram-set-view','collectParameters');const parameterCollector=new tr.v.HistogramParameterCollector();parameterCollector.process(histograms);this.$.controls.baseStatisticNames=parameterCollector.statisticNames;this.$.controls.possibleGroupings=parameterCollector.possibleGroupings;const displayLabels=parameterCollector.labels;this.$.controls.displayLabels=displayLabels;collectParametersMark.end();const hist=[...histograms][0];const benchmarks=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.BENCHMARKS);let enable=false;if(benchmarks!==undefined&&benchmarks.length>0){for(const benchmark of benchmarks){if(benchmark.includes('rendering')){enable=true;break;}}}
-this.$.controls.enableVisualization=enable;await this.$.table.build(histograms,sourceHistograms,displayLabels,progress);buildMark.end();},onExport_(event){const mark=tr.b.Timing.mark('histogram-set-view','export'+
+this.$.zero.style.display='none';this.$.container.style.display='block';this.$.container.style.maxHeight=(window.innerHeight-16)+'px';const buildMark=tr.b.Timing.mark('histogram-set-view','build');await progress('Finding important Histograms...');const sourceHistogramsMark=tr.b.Timing.mark('histogram-set-view','sourceHistograms');const sourceHistograms=histograms.sourceHistograms;sourceHistogramsMark.end();this.$.controls.showAllEnabled=(sourceHistograms.length!==histograms.length);await progress('Collecting parameters...');const collectParametersMark=tr.b.Timing.mark('histogram-set-view','collectParameters');const parameterCollector=new tr.v.HistogramParameterCollector();parameterCollector.process(histograms);this.$.controls.baseStatisticNames=parameterCollector.statisticNames;this.$.controls.possibleGroupings=parameterCollector.possibleGroupings;const displayLabels=parameterCollector.labels;this.$.controls.displayLabels=displayLabels;collectParametersMark.end();await this.$.table.build(histograms,sourceHistograms,displayLabels,progress);buildMark.end();},onExport_(event){const mark=tr.b.Timing.mark('histogram-set-view','export'+
(event.merged?'Merged':'Raw')+event.format.toUpperCase());const histograms=event.merged?this.$.table.leafHistograms:this.histograms;let blob;if(event.format==='csv'){const csv=new tr.v.CSVBuilder(histograms);csv.build();blob=new window.Blob([csv.toString()],{type:'text/csv'});}else if(event.format==='json'){blob=new window.Blob([JSON.stringify(histograms.asDicts())],{type:'text/json'});}else{throw new Error(`Unable to export format "${event.format}"`);}
-const path=window.location.pathname.split('/');const basename=path[path.length-1].split('.')[0]||'histograms';const anchor=document.createElement('a');anchor.download=`${basename}.${event.format}`;anchor.href=window.URL.createObjectURL(blob);anchor.click();mark.end();},onLoadVisualization_(event){if(!this.visualizationLoaded_){this.$.visualizations.style.display='block';this.$.visualizations.build(this.$.table.leafHistograms,this.histograms);this.visualizationLoaded_=true;}else if(this.$.visualizations.style.display==='none'){this.$.visualizations.style.display='block';}else{this.$.visualizations.style.display='none';}},});return{};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-ui-sp-metrics-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.model_=undefined;this.rangeOfInterest_=undefined;this.metricLatenciesMs_=[];this.metrics_=[];tr.metrics.MetricRegistry.getAllRegisteredTypeInfos().forEach(function(m){if(m.constructor.name==='sampleMetric')return;this.metrics_.push({label:m.constructor.name,value:m.constructor.name});},this);this.metrics_.sort((x,y)=>x.label.localeCompare(y.label));this.settingsKey_='metrics-side-panel-metric-name';this.currentMetricName_='responsivenessMetric';const metricSelector=tr.ui.b.createSelector(this,'currentMetricName_',this.settingsKey_,this.currentMetricName_,this.metrics_);Polymer.dom(this.$.top_left_controls).appendChild(metricSelector);metricSelector.addEventListener('change',this.onMetricChange_.bind(this));this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.recomputeButton_=tr.ui.b.createButton('Recompute',this.onRecompute_,this);Polymer.dom(this.$.top_left_controls).appendChild(this.recomputeButton_);this.$.results.addEventListener('display-ready',()=>{this.$.results.style.display='';});},async build(model){this.model_=model;await this.updateContents_();},get metricLatencyMs(){return tr.b.math.Statistics.mean(this.metricLatenciesMs_);},onMetricChange_(){this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.metricLatenciesMs_=[];this.updateContents_();},onRecompute_(){this.updateContents_();},get textLabel(){return'Metrics';},supportsModel(m){if(!m){return{supported:false,reason:'No model available'};}
+const path=window.location.pathname.split('/');const basename=path[path.length-1].split('.')[0]||'histograms';const anchor=document.createElement('a');anchor.download=`${basename}.${event.format}`;anchor.href=window.URL.createObjectURL(blob);anchor.click();mark.end();},});return{};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-ui-sp-metrics-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.model_=undefined;this.rangeOfInterest_=undefined;this.metricLatenciesMs_=[];this.metrics_=[];tr.metrics.MetricRegistry.getAllRegisteredTypeInfos().forEach(function(m){if(m.constructor.name==='sampleMetric')return;this.metrics_.push({label:m.constructor.name,value:m.constructor.name});},this);this.metrics_.sort((x,y)=>x.label.localeCompare(y.label));this.settingsKey_='metrics-side-panel-metric-name';this.currentMetricName_='responsivenessMetric';const metricSelector=tr.ui.b.createSelector(this,'currentMetricName_',this.settingsKey_,this.currentMetricName_,this.metrics_);Polymer.dom(this.$.top_left_controls).appendChild(metricSelector);metricSelector.addEventListener('change',this.onMetricChange_.bind(this));this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.recomputeButton_=tr.ui.b.createButton('Recompute',this.onRecompute_,this);Polymer.dom(this.$.top_left_controls).appendChild(this.recomputeButton_);this.$.results.addEventListener('display-ready',()=>{this.$.results.style.display='';});},async build(model){this.model_=model;await this.updateContents_();},get metricLatencyMs(){return tr.b.math.Statistics.mean(this.metricLatenciesMs_);},onMetricChange_(){this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.metricLatenciesMs_=[];this.updateContents_();},onRecompute_(){this.updateContents_();},get textLabel(){return'Metrics';},supportsModel(m){if(!m){return{supported:false,reason:'No model available'};}
return{supported:true};},get model(){return this.model_;},set model(model){this.build(model);},get selection(){},set selection(_){},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){this.rangeOfInterest_=range;if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest){if((this.metricLatencyMs===undefined)||(this.metricLatencyMs<100)){this.updateContents_();}else{this.recomputeButton_.style.background='red';}}},async updateContents_(){Polymer.dom(this.$.error).textContent='';this.$.results.style.display='none';if(!this.model_){Polymer.dom(this.$.error).textContent='Missing model';return;}
const options={metrics:[this.currentMetricName_]};if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest&&this.rangeOfInterest&&!this.rangeOfInterest.isEmpty){options.rangeOfInterest=this.rangeOfInterest;}
const startDate=new Date();const addFailureCb=failure=>{Polymer.dom(this.$.error).textContent=failure.description;};const histograms=tr.metrics.runMetrics(this.model_,options,addFailureCb);this.metricLatenciesMs_.push(new Date()-startDate);while(this.metricLatenciesMs_.length>20){this.metricLatenciesMs_.shift();}
diff --git a/catapult/systrace/systrace/test_data/cgroup_dump b/catapult/systrace/systrace/test_data/cgroup_dump
new file mode 100644
index 00000000..95b4e61a
--- /dev/null
+++ b/catapult/systrace/systrace/test_data/cgroup_dump
@@ -0,0 +1,16 @@
+CGROUP DUMP
+#subsys_name hierarchy num_cgroups enabled
+cpuset 4 7 1
+cpu 2 1 1
+cpuacct 3 565 1
+schedtune 7 7 1
+blkio 1 2 1
+memory 6 3 1
+freezer 5 2 1
+# cgroup task attachment
+/background (root=4) : 466 467 484 485 486 514 518 520 923 925
+/camera-daemon (root=4) : 971 1155 1209 1298 1299 1312 1313 1314
+/foreground (root=4) : 653 669 670 671 959 960 968 972 975 976 977 991 993
+/restricted (root=4) :
+/system-background (root=4) : 632 633 634 635 636 639
+/top-app (root=4) : 466 467 484 485 486 514 518 520 925
diff --git a/catapult/systrace/systrace/tracing_agents/android_cgroup_agent.py b/catapult/systrace/systrace/tracing_agents/android_cgroup_agent.py
new file mode 100644
index 00000000..75e0872d
--- /dev/null
+++ b/catapult/systrace/systrace/tracing_agents/android_cgroup_agent.py
@@ -0,0 +1,96 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Tracing agent that captures cgroup information from /dev/cpuset on
+# an Android device.
+
+import stat
+import py_utils
+
+from devil.android import device_utils
+from systrace import tracing_agents
+from systrace import trace_result
+
+# identify this as trace of cgroup state
+TRACE_HEADER = 'CGROUP DUMP\n'
+
+def add_options(parser): # pylint: disable=unused-argument
+ return None
+
+def try_create_agent(config):
+ if config.target != 'android':
+ return None
+ if not config.atrace_categories:
+ return None
+ # 'sched' contains cgroup events
+ if 'sched' not in config.atrace_categories:
+ return None
+ if config.from_file is not None:
+ return None
+ return AndroidCgroupAgent()
+
+def get_config(options):
+ return options
+
+def parse_proc_cgroups(cgroups, subsys):
+ for line in cgroups.split('\n'):
+ if line.startswith(subsys):
+ return line.split()[1]
+ return '-1'
+
+class AndroidCgroupAgent(tracing_agents.TracingAgent):
+ def __init__(self):
+ super(AndroidCgroupAgent, self).__init__()
+ self._config = None
+ self._device_utils = None
+ self._trace_data = ""
+
+ def __repr__(self):
+ return 'cgroup_data'
+
+ @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
+ def StartAgentTracing(self, config, timeout=None):
+ self._config = config
+ self._device_utils = device_utils.DeviceUtils(
+ self._config.device_serial_number)
+
+ if not self._device_utils.HasRoot():
+ return False
+
+ self._trace_data += self._get_cgroup_info()
+ return True
+
+ @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
+ def StopAgentTracing(self, timeout=None):
+ return True
+
+ @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
+ def GetResults(self, timeout=None):
+ result = TRACE_HEADER + self._trace_data
+ return trace_result.TraceResult('cgroupDump', result)
+
+ def SupportsExplicitClockSync(self):
+ return False
+
+ def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
+ pass
+
+ def _get_cgroup_info(self):
+ data = []
+ CGROUP_SUBSYS = 'cpuset'
+ CGROUP_ROOT = '/dev/cpuset/'
+
+ cgroups = self._device_utils.ReadFile('/proc/cgroups')
+ header = '# cgroup task attachment\n'
+
+ root_id = parse_proc_cgroups(cgroups, CGROUP_SUBSYS)
+
+ for cgrp in self._device_utils.StatDirectory(CGROUP_ROOT):
+ if not stat.S_ISDIR(cgrp['st_mode']):
+ continue
+ tasks_file = CGROUP_ROOT + cgrp['filename'] + '/tasks'
+ tasks = self._device_utils.ReadFile(tasks_file).split('\n')
+ cgrp_info = '/%s (root=%s) : ' % (cgrp['filename'], root_id)
+ data.append(cgrp_info + ' '.join(tasks))
+ return cgroups + header + '\n'.join(data) + '\n'
diff --git a/catapult/systrace/systrace/tracing_agents/atrace_agent.py b/catapult/systrace/systrace/tracing_agents/atrace_agent.py
index caf78f44..95046850 100644
--- a/catapult/systrace/systrace/tracing_agents/atrace_agent.py
+++ b/catapult/systrace/systrace/tracing_agents/atrace_agent.py
@@ -28,8 +28,9 @@ ADB_LARGE_OUTPUT_TIMEOUT = 600
ATRACE_BASE_ARGS = ['atrace']
# If a custom list of categories is not specified, traces will include
# these categories (if available on the device).
-DEFAULT_CATEGORIES = 'sched,freq,gfx,view,dalvik,webview,'\
- 'input,disk,am,wm,rs,binder_driver'
+DEFAULT_CATEGORIES = 'am,binder_driver,camera,dalvik,freq,'\
+ 'gfx,hal,idle,input,memory,memreclaim,'\
+ 'res,sched,sync,view,webview,wm,workq'
# The command to list trace categories.
LIST_CATEGORIES_ARGS = ATRACE_BASE_ARGS + ['--list_categories']
# Minimum number of seconds between displaying status updates.
@@ -103,7 +104,7 @@ def try_create_agent(config):
'Your device SDK version is %d.' % device_sdk_version)
return None
- return AtraceAgent(device_sdk_version)
+ return AtraceAgent(device_sdk_version, util.get_tracing_path())
def _construct_extra_atrace_args(config, categories):
"""Construct extra arguments (-a, -k, categories) for atrace command.
@@ -157,9 +158,10 @@ def _construct_atrace_args(config, categories):
class AtraceAgent(tracing_agents.TracingAgent):
- def __init__(self, device_sdk_version):
+ def __init__(self, device_sdk_version, tracing_path):
super(AtraceAgent, self).__init__()
self._device_sdk_version = device_sdk_version
+ self._tracing_path = tracing_path
self._adb = None
self._trace_data = None
self._tracer_args = None
@@ -231,7 +233,7 @@ class AtraceAgent(tracing_agents.TracingAgent):
sync_id: ID string for clock sync marker.
"""
cmd = 'echo trace_event_clock_sync: name=%s >' \
- ' /sys/kernel/debug/tracing/trace_marker' % sync_id
+ ' %s/trace_marker' % (sync_id, self._tracing_path)
with self._device_utils.adb.PersistentShell(
self._device_serial_number) as shell:
t1 = trace_time_module.Now()
@@ -245,7 +247,7 @@ class AtraceAgent(tracing_agents.TracingAgent):
doesn't stop tracing and clears trace buffer before dumping it rendering
results unusable."""
if self._device_sdk_version < version_codes.MARSHMALLOW:
- is_trace_enabled_file = '/sys/kernel/debug/tracing/tracing_on'
+ is_trace_enabled_file = '%s/tracing_on' % self._tracing_path
# Stop tracing first so new data won't arrive while dump is performed (it
# may take a non-trivial time and tracing buffer may overflow).
self._device_utils.WriteFile(is_trace_enabled_file, '0')
diff --git a/catapult/systrace/systrace/util.py b/catapult/systrace/systrace/util.py
index c9571b6f..02981458 100644
--- a/catapult/systrace/systrace/util.py
+++ b/catapult/systrace/systrace/util.py
@@ -52,6 +52,27 @@ def run_adb_shell(shell_args, device_serial):
return (adb_output, adb_return_code)
+def get_tracing_path():
+ """Uses adb to attempt to determine tracing path. The newest kernel doesn't
+ support mounting debugfs, so the Android master uses tracefs to replace it.
+
+ Returns:
+ /sys/kernel/debug/tracing for device with debugfs mount support;
+ /sys/kernel/tracing for device with tracefs support;
+ /sys/kernel/debug/tracing if support can't be determined.
+ """
+ mount_info_args = ['mount']
+ parser = OptionParserIgnoreErrors()
+ parser.add_option('-e', '--serial', dest='device_serial', type='string')
+ options, _ = parser.parse_args()
+
+ adb_output, adb_return_code = run_adb_shell(mount_info_args,
+ options.device_serial)
+ if adb_return_code == 0 and 'debugfs' not in adb_output:
+ return '/sys/kernel/tracing'
+ return '/sys/kernel/debug/tracing'
+
+
def get_device_sdk_version():
"""Uses adb to attempt to determine the SDK version of a running device."""
diff --git a/catapult/third_party/polymer/.bowerrc b/catapult/third_party/polymer/.bowerrc
new file mode 100644
index 00000000..1d83b677
--- /dev/null
+++ b/catapult/third_party/polymer/.bowerrc
@@ -0,0 +1,3 @@
+{
+ "directory:" : "components"
+}
diff --git a/catapult/third_party/polymer/LICENSE.polymer b/catapult/third_party/polymer/LICENSE.polymer
new file mode 100644
index 00000000..92d60b01
--- /dev/null
+++ b/catapult/third_party/polymer/LICENSE.polymer
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/catapult/third_party/polymer/README.chromium b/catapult/third_party/polymer/README.chromium
new file mode 100644
index 00000000..af8e9ad5
--- /dev/null
+++ b/catapult/third_party/polymer/README.chromium
@@ -0,0 +1,26 @@
+Name: Polymer
+Short Name: polymer
+URL: http://www.polymer-project.org
+Version: 0.5.2-4
+Revision: (See components/<component>/.bower.json)
+License: BSD
+License File: LICENSE.polymer
+Security Critical: no
+
+Description:
+This directory contains a subset of the components provided by the
+Polymer project. See bower.json for a full list of components.
+
+The version can be found in header of polymer/polymer.js. The license can
+be found in polymer/LICENSE.
+
+The source git repositories can be found at:
+https://github.com/Polymer/<component>
+
+Note on Bower:
+New components can be added by editing bower.json and running `bower update`,
+which requires first installing bower (see http://bower.io/).
+
+Local Modifications:
+Removed unused file with no license header:
+components/web-animations-js/.travis-setup.sh
diff --git a/catapult/third_party/polymer/bower.json b/catapult/third_party/polymer/bower.json
new file mode 100644
index 00000000..9fead1ca
--- /dev/null
+++ b/catapult/third_party/polymer/bower.json
@@ -0,0 +1,34 @@
+{
+ "name": "catapult",
+ "private": true,
+ "dependencies": {
+ "app-route": "PolymerElements/app-route#^1.0.0",
+ "google-signin": "GoogleWebComponents/google-signin#^2.0.0",
+ "iron-ajax": "PolymerElements/iron-ajax#^2.0.0",
+ "iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
+ "iron-form": "PolymerElements/iron-form#^2.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^1.0.0",
+ "iron-pages": "PolymerElements/iron-pages#^1.0.8",
+ "iron-selector": "PolymerElements/iron-selector#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
+ "paper-dialog": "PolymerElements/paper-dialog#^1.0.0",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
+ "paper-fab": "PolymerElements/paper-fab#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "paper-input": "PolymerElements/paper-input#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-listbox": "polymerelements/paper-listbox#^1.1.2",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
+ "paper-progress": "PolymerElements/paper-progress#^1.0.0",
+ "paper-radio-button": "PolymerElements/paper-radio-button#^1.0.0",
+ "paper-radio-group": "PolymerElements/paper-radio-group#^1.0.0",
+ "paper-spinner": "PolymerElements/paper-spinner#^1.0.0",
+ "paper-tabs": "PolymerElements/paper-tabs#^1.0.0",
+ "paper-toast": "PolymerElements/paper-toast#^1.0.0",
+ "paper-tooltip": "PolymerElements/paper-tooltip#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/app-route/.bower.json b/catapult/third_party/polymer/components/app-route/.bower.json
new file mode 100644
index 00000000..78bb960c
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/.bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "app-route",
+ "version": "1.0.1",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "description": "App routing expressed as Polymer Custom Elements.",
+ "main": [
+ "app-route.html",
+ "app-location.html",
+ "app-route-converter.html"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/app-route",
+ "private": true,
+ "ignore": [],
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.13",
+ "paper-input": "polymerelements/paper-input#^1.1.2",
+ "web-component-tester": "^4.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "iron-pages": "PolymerElements/iron-pages#^1.0.7",
+ "paper-card": "PolymerElements/paper-card#^1.1.1",
+ "paper-icon-button": "polymerelements/paper-icon-button#^v1.0.0",
+ "paper-toggle-button": "polymerelements/paper-toggle-button#^v1.0.0",
+ "google-youtube": "GoogleWebComponents/google-youtube#^1.2.1",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.0",
+ "paper-spinner": "PolymerElements/paper-spinner#^1.1.1"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.3.1",
+ "iron-location": "PolymerElements/iron-location#^0.8.1"
+ },
+ "_release": "1.0.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.1",
+ "commit": "4993cfbc114de494ee9df6890da0509221c38587"
+ },
+ "_source": "https://github.com/PolymerElements/app-route.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/app-route"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/app-route/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/app-route/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..0c018d64
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/app-route/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/hirore/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+* Open the page in a web browser.
+* Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/app-route/.gitignore b/catapult/third_party/polymer/components/app-route/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/app-route/.travis.yml b/catapult/third_party/polymer/components/app-route/.travis.yml
new file mode 100644
index 00000000..4cdcd1ec
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+env:
+ global:
+ - secure: >-
+ WdFRmsbFi5zjdD/yaGR0HITPFsjSWeVYj8JcLE95nUN0fkkJuNh/A0/cDHDcDpxzY4xCNdc/IwPTtE4+awCGavot71OXlpEYD8aZUdVw9VcYVTc1IG2B/hgFcdYhXHYOLbs7JlOA/H6/RSiOensRv7QESNY2cMGzRsccav9jdAbLJqHNPI5orMRBT6/H5roZvXXEn5XmIemjCMwtGlqUeRd3nHM2n0PsQKETb+8Y04fTFuqggSEVt/SgU/UdNNm2T2mVFUDWChvjAM36l4FvgUKZm8gh2CYrXc0rHOVt7BCJN5OzZSwCmxxauhUe+7QOgZO3UE5l+M+MX9CjnSdy/HJRauMJy2eCSr9RDoChPKZOC14QrZff06L9WhDnVEHkz8PN2mwP73/uDVYyJC9Szs+4MSKK+WEIIiywEnxCp2Nq9eBNoWNDySOaud+9WxLosh01bTvytVZrd3Lqkf3MUm2sJ+v17e4JagW13zwBXjxnLP1V+H+IP/imrRRV9x5gdvK+9iL3ZYsIQmcEkqNlWrqHe3eZTgpkc/HhewfZCu49ClM6pnoJiC/7/YM3CarwkfMhGJKDU5iXfJiQABIfZ3XhTKG7nltz6VRpm8eFwxInpxNAIBghkz6PGeESHgtmsoQKtGqAg7+cRfjTS9uLMVomYHWPz0brCIQloAjSKc8=
+ - secure: >-
+ k6zYpUF0NdxNqVyfCbEztJ25Z229mpcvSdHfkOF8YqFse+1CkSR1M83Dts1K34eK43Yov375YJ/Op/MXEA0wrbnyEDg/AvwqBv5XZTslIvOjA1WvZJDGBiaZ6lvqs6tEhLfB79eZvTHP8DSDlLabklDAPaio1VC+Q6zyLIlsTIr6fH1VK1gX/uHkvSGKl8C9bfeGHgFlXaWmShN+FvLwW4PiRw2MwzXeq/qQqfS0Nn3gtuGN2kNNBpKVJwcDkYDOcJ2+49ojF8RAdbt8hsZkjGq21hpY6eKhbte66guN7lRTKY5+g2aLkg1CrBp9ZHZ4kAlfbhibhiLt/W1UbXUL/nnTFkrotuJ6vdU6gLN0EhjZN3bMAtKqHC+Bl2223ClNMs17iXReSqqOSc9D3gL9b3FYqQElS4bQIa+0L76TbkMM/H6ilQjSIvVnUXLszMIeyliIVpnI0ClTUbqe5Et51urk+KjJrOE95wyxkiL2bzKxcXCLzdvSy0nxwwcu7DnBQPG0Fg6/JVRMHM17PDhjgQLSvKr8wXtO042xzAosoZuDVOHHgpA5v9iKI/xPUseu3utfBT5ZGcyjKKuxI9Dsjoi4Go0B6uQ4rY/XUp+k4aa1Ucg3Ngx8zYgzJlFGiZq7OnG+quSTpZ32hpQqDmtYKNMxf5p/YRhxWSeuBWheLeQ=
+dist: trusty
+sudo: required
diff --git a/catapult/third_party/polymer/components/app-route/CONTRIBUTING.md b/catapult/third_party/polymer/components/app-route/CONTRIBUTING.md
new file mode 100644
index 00000000..34d61544
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/hirore/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/hirore/edit?html,output](https://jsbin.com/hirore/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/app-route/README.md b/catapult/third_party/polymer/components/app-route/README.md
new file mode 100644
index 00000000..ee6bdd1d
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/README.md
@@ -0,0 +1,217 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+app-location.html app-route-converter-behavior.html app-route-converter.html app-route.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/app-route.svg?branch=master)](https://travis-ci.org/PolymerElements/app-route)
+
+
+## &lt;app-route&gt;
+
+`app-route` is an element that enables declarative, self-describing routing
+for a web app.
+
+> *n.b. app-route is still in beta. We expect it will need some changes. We're counting on your feedback!*
+
+In its typical usage, a `app-route` element consumes an object that describes
+some state about the current route, via the `route` property. It then parses
+that state using the `pattern` property, and produces two artifacts: some `data`
+related to the `route`, and a `tail` that contains the rest of the `route` that
+did not match.
+
+Here is a basic example, when used with `app-location`:
+
+```html
+<app-location route="{{route}}"></app-location>
+<app-route
+ route="{{route}}"
+ pattern="/:page"
+ data="{{data}}"
+ tail="{{tail}}">
+</app-route>
+```
+
+In the above example, the `app-location` produces a `route` value. Then, the
+`route.path` property is matched by comparing it to the `pattern` property. If
+the `pattern` property matches `route.path`, the `app-route` will set or update
+its `data` property with an object whose properties correspond to the parameters
+in `pattern`. So, in the above example, if `route.path` was `'/about'`, the value
+of `data` would be `{"page": "about"}`.
+
+The `tail` property represents the remaining part of the route state after the
+`pattern` has been applied to a matching `route`.
+
+Here is another example, where `tail` is used:
+
+```html
+<app-location route="{{route}}"></app-location>
+<app-route
+ route="{{route}}"
+ pattern="/:page"
+ data="{{routeData}}"
+ tail="{{subroute}}">
+</app-route>
+<app-route
+ route="{{subroute}}"
+ pattern="/:id"
+ data="{{subrouteData}}">
+</app-route>
+```
+
+In the above example, there are two `app-route` elements. The first
+`app-route` consumes a `route`. When the `route` is matched, the first
+`app-route` also produces `routeData` from its `data`, and `subroute` from
+its `tail`. The second `app-route` consumes the `subroute`, and when it
+matches, it produces an object called `subrouteData` from its `data`.
+
+So, when `route.path` is `'/about'`, the `routeData` object will look like
+this: `{ page: 'about' }`
+
+And `subrouteData` will be null. However, if `route.path` changes to
+`'/article/123'`, the `routeData` object will look like this:
+`{ page: 'article' }`
+
+And the `subrouteData` will look like this: `{ id: '123' }`
+
+`app-route` is responsive to bi-directional changes to the `data` objects
+they produce. So, if `routeData.page` changed from `'article'` to `'about'`,
+the `app-route` will update `route.path`. This in-turn will update the
+`app-location`, and cause the global location bar to change its value.
+
+
+
+## &lt;app-location&gt;
+
+`app-location` is an element that provides synchronization between the
+browser location bar and the state of an app. When created, `app-location`
+elements will automatically watch the global location for changes. As changes
+occur, `app-location` produces and updates an object called `route`. This
+`route` object is suitable for passing into a `app-route`, and other similar
+elements.
+
+An example of the public API of a route object that describes the URL
+`https://elements.polymer-project.org/elements/app-location`:
+
+```css
+{
+ prefix: '',
+ path: '/elements/app-location'
+}
+```
+
+Example Usage:
+
+```html
+<app-location route="{{route}}"></app-location>
+<app-route route="{{route}}" pattern="/:page" data="{{data}}"></app-route>
+```
+
+As you can see above, the `app-location` element produces a `route` and that
+property is then bound into the `app-route` element. The bindings are two-
+directional, so when changes to the `route` object occur within `app-route`,
+they automatically reflect back to the global location.
+
+### Hashes vs Paths
+
+By default `app-location` routes using the pathname portion of the URL. This has
+broad browser support but it does require cooperation of the backend server. An
+`app-location` can be configured to use the hash part of a URL instead using
+the `use-hash-as-path` attribute, like so:
+
+```html
+<app-location route="{{route}}" use-hash-as-path></app-location>
+```
+
+### Integrating with other routing code
+
+There is no standard event that is fired when window.location is modified.
+`app-location` fires a `location-changed` event on `window` when it updates the
+location. It also listens for that same event, and re-reads the URL when it's
+fired. This makes it very easy to interop with other routing code.
+
+So for example if you want to navigate to `/new_path` imperatively you could
+call `window.location.pushState` or `window.location.replaceState` followed by
+firing a `location-changed` event on `window`. i.e.
+
+```javascript
+window.history.pushState({}, null, '/new_path');
+window.dispatchEvent(new CustomEvent('location-changed'));
+```
+
+
+
+## &lt;app-route-converter&gt;
+
+`app-route-converter` provides a means to convert a path and query
+parameters into a route object and vice versa. This produced route object
+is to be fed into route-consuming elements such as `app-route`.
+
+> n.b. This element is intended to be a primitive of the routing system and for
+creating bespoke routing solutions from scratch. To simply include routing in
+an app, please refer to [app-location](https://github.com/PolymerElements/app-route/blob/master/app-location.html)
+and [app-route](https://github.com/PolymerElements/app-route/blob/master/app-route.html).
+
+An example of a route object that describes
+`https://elements.polymer-project.org/elements/app-route-converter?foo=bar&baz=qux`
+and should be passed to other `app-route` elements:
+
+```css
+{
+ prefix: '',
+ path: '/elements/app-route-converter',
+ __queryParams: {
+ foo: 'bar',
+ baz: 'qux'
+ }
+}
+```
+
+`__queryParams` is private to discourage directly data-binding to it. This is so
+that routing elements like `app-route` can intermediate changes to the query
+params and choose whether to propagate them upstream or not. `app-route` for
+example will not propagate changes to its `queryParams` property if it is not
+currently active. A public queryParams object will also be produced in which you
+should perform data-binding operations.
+
+Example Usage:
+
+```html
+<iron-location path="{{path}}" query="{{query}}"></iron-location>
+<iron-query-params
+ params-string="{{query}}"
+ params-object="{{queryParams}}">
+</iron-query-params>
+<app-route-converter
+ path="{{path}}"
+ query-params="{{queryParams}}"
+ route="{{route}}">
+</app-route-converter>
+<app-route route='{{route}}' pattern='/:page' data='{{data}}'>
+</app-route>
+```
+
+This is a simplified implementation of the `app-location` element. Here the
+`iron-location` produces a path and a query, the `iron-query-params` consumes
+the query and produces a queryParams object, and the `app-route-converter`
+consumes the path and the query params and converts it into a route which is in
+turn is consumed by the `app-route`.
+
+
+
+## Polymer.AppRouteConverterBehavior
+
+Provides bidirectional mapping between `path` and `queryParams` and a
+app-route compatible `route` object.
+
+For more information, see the docs for `app-route-converter`.
+
+
diff --git a/catapult/third_party/polymer/components/app-route/app-location.html b/catapult/third_party/polymer/components/app-route/app-location.html
new file mode 100644
index 00000000..3e001ae1
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/app-location.html
@@ -0,0 +1,194 @@
+<!--
+@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
+-->
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-location/iron-location.html">
+<link rel="import" href="../iron-location/iron-query-params.html">
+<link rel="import" href="app-route-converter-behavior.html">
+
+<!--
+`app-location` is an element that provides synchronization between the
+browser location bar and the state of an app. When created, `app-location`
+elements will automatically watch the global location for changes. As changes
+occur, `app-location` produces and updates an object called `route`. This
+`route` object is suitable for passing into a `app-route`, and other similar
+elements.
+
+An example of the public API of a route object that describes the URL
+`https://elements.polymer-project.org/elements/app-location`:
+
+ {
+ prefix: '',
+ path: '/elements/app-location'
+ }
+
+Example Usage:
+
+ <app-location route="{{route}}"></app-location>
+ <app-route route="{{route}}" pattern="/:page" data="{{data}}"></app-route>
+
+As you can see above, the `app-location` element produces a `route` and that
+property is then bound into the `app-route` element. The bindings are two-
+directional, so when changes to the `route` object occur within `app-route`,
+they automatically reflect back to the global location.
+
+### Hashes vs Paths
+
+By default `app-location` routes using the pathname portion of the URL. This has
+broad browser support but it does require cooperation of the backend server. An
+`app-location` can be configured to use the hash part of a URL instead using
+the `use-hash-as-path` attribute, like so:
+
+ <app-location route="{{route}}" use-hash-as-path></app-location>
+
+### Integrating with other routing code
+
+There is no standard event that is fired when window.location is modified.
+`app-location` fires a `location-changed` event on `window` when it updates the
+location. It also listens for that same event, and re-reads the URL when it's
+fired. This makes it very easy to interop with other routing code.
+
+So for example if you want to navigate to `/new_path` imperatively you could
+call `window.location.pushState` or `window.location.replaceState` followed by
+firing a `location-changed` event on `window`. i.e.
+
+ window.history.pushState({}, null, '/new_path');
+ window.dispatchEvent(new CustomEvent('location-changed'));
+
+@element app-location
+@demo demo/index.html
+-->
+<dom-module id="app-location">
+ <template>
+ <iron-location
+ path="{{__path}}"
+ query="{{__query}}"
+ hash="{{__hash}}"
+ url-space-regex={{urlSpaceRegex}}>
+ </iron-location>
+ <iron-query-params
+ params-string="{{__query}}"
+ params-object="{{queryParams}}">
+ </iron-query-params>
+ </template>
+ <script>
+ (function() {
+ 'use strict';
+
+ Polymer({
+ is: 'app-location',
+
+ properties: {
+ /**
+ * A model representing the deserialized path through the route tree, as
+ * well as the current queryParams.
+ */
+ route: {
+ type: Object,
+ notify: true
+ },
+
+ /**
+ * In many scenarios, it is convenient to treat the `hash` as a stand-in
+ * alternative to the `path`. For example, if deploying an app to a static
+ * web server (e.g., Github Pages) - where one does not have control over
+ * server-side routing - it is usually a better experience to use the hash
+ * to represent paths through one's app.
+ *
+ * When this property is set to true, the `hash` will be used in place of
+
+ * the `path` for generating a `route`.
+ */
+ useHashAsPath: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * A regexp that defines the set of URLs that should be considered part
+ * of this web app.
+ *
+ * Clicking on a link that matches this regex won't result in a full page
+ * navigation, but will instead just update the URL state in place.
+ *
+ * This regexp is given everything after the origin in an absolute
+ * URL. So to match just URLs that start with /search/ do:
+ * url-space-regex="^/search/"
+ *
+ * @type {string|RegExp}
+ */
+ urlSpaceRegex: {
+ type: String,
+ notify: true
+ },
+
+ /**
+ * A set of key/value pairs that are universally accessible to branches
+ * of the route tree.
+ */
+ __queryParams: {
+ type: Object
+ },
+
+ /**
+ * The pathname component of the current URL.
+ */
+ __path: {
+ type: String
+ },
+
+ /**
+ * The query string portion of the current URL.
+ */
+ __query: {
+ type: String
+ },
+
+ /**
+ * The hash portion of the current URL.
+ */
+ __hash: {
+ type: String
+ },
+
+ /**
+ * The route path, which will be either the hash or the path, depending
+ * on useHashAsPath.
+ */
+ path: {
+ type: String,
+ observer: '__onPathChanged'
+ }
+ },
+
+ behaviors: [Polymer.AppRouteConverterBehavior],
+
+ observers: [
+ '__computeRoutePath(useHashAsPath, __hash, __path)'
+ ],
+
+ __computeRoutePath: function() {
+ this.path = this.useHashAsPath ? this.__hash : this.__path;
+ },
+
+ __onPathChanged: function() {
+ if (!this._readied) {
+ return;
+ }
+
+ if (this.useHashAsPath) {
+ this.__hash = this.path;
+ } else {
+ this.__path = this.path;
+ }
+ }
+ });
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/app-route-converter-behavior.html b/catapult/third_party/polymer/components/app-route/app-route-converter-behavior.html
new file mode 100644
index 00000000..23cc9dc7
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/app-route-converter-behavior.html
@@ -0,0 +1,112 @@
+<!--
+@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
+-->
+<script>
+ (function() {
+ 'use strict';
+
+ /**
+ * Provides bidirectional mapping between `path` and `queryParams` and a
+ * app-route compatible `route` object.
+ *
+ * For more information, see the docs for `app-route-converter`.
+ *
+ * @polymerBehavior
+ */
+ Polymer.AppRouteConverterBehavior = {
+ properties: {
+ /**
+ * A model representing the deserialized path through the route tree, as
+ * well as the current queryParams.
+ *
+ * A route object is the kernel of the routing system. It is intended to
+ * be fed into consuming elements such as `app-route`.
+ *
+ * @type {?Object}
+ */
+ route: {
+ type: Object,
+ notify: true
+ },
+
+ /**
+ * A set of key/value pairs that are universally accessible to branches of
+ * the route tree.
+ *
+ * @type {?Object}
+ */
+ queryParams: {
+ type: Object,
+ notify: true
+ },
+
+ /**
+ * The serialized path through the route tree. This corresponds to the
+ * `window.location.pathname` value, and will update to reflect changes
+ * to that value.
+ */
+ path: {
+ type: String,
+ notify: true,
+ }
+ },
+
+ observers: [
+ '_locationChanged(path, queryParams)',
+ '_routeChanged(route.prefix, route.path)',
+ '_routeQueryParamsChanged(route.__queryParams)'
+ ],
+
+ created: function() {
+ this.linkPaths('route.__queryParams', 'queryParams');
+ this.linkPaths('queryParams', 'route.__queryParams');
+ },
+
+ /**
+ * Handler called when the path or queryParams change.
+ */
+ _locationChanged: function() {
+ if (this.route &&
+ this.route.path === this.path &&
+ this.queryParams === this.route.__queryParams) {
+ return;
+ }
+ this.route = {
+ prefix: '',
+ path: this.path,
+ __queryParams: this.queryParams
+ };
+ },
+
+ /**
+ * Handler called when the route prefix and route path change.
+ */
+ _routeChanged: function() {
+ if (!this.route) {
+ return;
+ }
+
+ this.path = this.route.prefix + this.route.path;
+ },
+
+ /**
+ * Handler called when the route queryParams change.
+ *
+ * @param {Object} queryParams A set of key/value pairs that are
+ * universally accessible to branches of the route tree.
+ */
+ _routeQueryParamsChanged: function(queryParams) {
+ if (!this.route) {
+ return;
+ }
+ this.queryParams = queryParams;
+ }
+ };
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/app-route/app-route-converter.html b/catapult/third_party/polymer/components/app-route/app-route-converter.html
new file mode 100644
index 00000000..e16b653f
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/app-route-converter.html
@@ -0,0 +1,79 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="./app-route-converter-behavior.html">
+
+<!--
+`app-route-converter` provides a means to convert a path and query
+parameters into a route object and vice versa. This produced route object
+is to be fed into route-consuming elements such as `app-route`.
+
+> n.b. This element is intended to be a primitive of the routing system and for
+creating bespoke routing solutions from scratch. To simply include routing in
+an app, please refer to [app-location](https://github.com/PolymerElements/app-route/blob/master/app-location.html)
+and [app-route](https://github.com/PolymerElements/app-route/blob/master/app-route.html).
+
+An example of a route object that describes
+`https://elements.polymer-project.org/elements/app-route-converter?foo=bar&baz=qux`
+and should be passed to other `app-route` elements:
+
+ {
+ prefix: '',
+ path: '/elements/app-route-converter',
+ __queryParams: {
+ foo: 'bar',
+ baz: 'qux'
+ }
+ }
+
+`__queryParams` is private to discourage directly data-binding to it. This is so
+that routing elements like `app-route` can intermediate changes to the query
+params and choose whether to propagate them upstream or not. `app-route` for
+example will not propagate changes to its `queryParams` property if it is not
+currently active. A public queryParams object will also be produced in which you
+should perform data-binding operations.
+
+Example Usage:
+
+ <iron-location path="{{path}}" query="{{query}}"></iron-location>
+ <iron-query-params
+ params-string="{{query}}"
+ params-object="{{queryParams}}">
+ </iron-query-params>
+ <app-route-converter
+ path="{{path}}"
+ query-params="{{queryParams}}"
+ route="{{route}}">
+ </app-route-converter>
+ <app-route route='{{route}}' pattern='/:page' data='{{data}}'>
+ </app-route>
+
+This is a simplified implementation of the `app-location` element. Here the
+`iron-location` produces a path and a query, the `iron-query-params` consumes
+the query and produces a queryParams object, and the `app-route-converter`
+consumes the path and the query params and converts it into a route which is in
+turn is consumed by the `app-route`.
+
+@element app-route-converter
+@demo demo/index.html
+-->
+
+<script>
+ (function() {
+ 'use strict';
+
+ Polymer({
+ is: 'app-route-converter',
+
+ behaviors: [Polymer.AppRouteConverterBehavior]
+ });
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/app-route/app-route.html b/catapult/third_party/polymer/components/app-route/app-route.html
new file mode 100644
index 00000000..7ed66b46
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/app-route.html
@@ -0,0 +1,421 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!--
+`app-route` is an element that enables declarative, self-describing routing
+for a web app.
+
+> *n.b. app-route is still in beta. We expect it will need some changes. We're counting on your feedback!*
+
+In its typical usage, a `app-route` element consumes an object that describes
+some state about the current route, via the `route` property. It then parses
+that state using the `pattern` property, and produces two artifacts: some `data`
+related to the `route`, and a `tail` that contains the rest of the `route` that
+did not match.
+
+Here is a basic example, when used with `app-location`:
+
+ <app-location route="{{route}}"></app-location>
+ <app-route
+ route="{{route}}"
+ pattern="/:page"
+ data="{{data}}"
+ tail="{{tail}}">
+ </app-route>
+
+In the above example, the `app-location` produces a `route` value. Then, the
+`route.path` property is matched by comparing it to the `pattern` property. If
+the `pattern` property matches `route.path`, the `app-route` will set or update
+its `data` property with an object whose properties correspond to the parameters
+in `pattern`. So, in the above example, if `route.path` was `'/about'`, the value
+of `data` would be `{"page": "about"}`.
+
+The `tail` property represents the remaining part of the route state after the
+`pattern` has been applied to a matching `route`.
+
+Here is another example, where `tail` is used:
+
+ <app-location route="{{route}}"></app-location>
+ <app-route
+ route="{{route}}"
+ pattern="/:page"
+ data="{{routeData}}"
+ tail="{{subroute}}">
+ </app-route>
+ <app-route
+ route="{{subroute}}"
+ pattern="/:id"
+ data="{{subrouteData}}">
+ </app-route>
+
+In the above example, there are two `app-route` elements. The first
+`app-route` consumes a `route`. When the `route` is matched, the first
+`app-route` also produces `routeData` from its `data`, and `subroute` from
+its `tail`. The second `app-route` consumes the `subroute`, and when it
+matches, it produces an object called `subrouteData` from its `data`.
+
+So, when `route.path` is `'/about'`, the `routeData` object will look like
+this: `{ page: 'about' }`
+
+And `subrouteData` will be null. However, if `route.path` changes to
+`'/article/123'`, the `routeData` object will look like this:
+`{ page: 'article' }`
+
+And the `subrouteData` will look like this: `{ id: '123' }`
+
+`app-route` is responsive to bi-directional changes to the `data` objects
+they produce. So, if `routeData.page` changed from `'article'` to `'about'`,
+the `app-route` will update `route.path`. This in-turn will update the
+`app-location`, and cause the global location bar to change its value.
+
+@element app-route
+@demo demo/index.html
+@demo demo/data-loading-demo.html
+@demo demo/simple-demo.html
+-->
+
+<script>
+ (function() {
+ 'use strict';
+
+ Polymer({
+ is: 'app-route',
+
+ properties: {
+ /**
+ * The URL component managed by this element.
+ */
+ route: {
+ type: Object,
+ notify: true
+ },
+
+ /**
+ * The pattern of slash-separated segments to match `route.path` against.
+ *
+ * For example the pattern "/foo" will match "/foo" or "/foo/bar"
+ * but not "/foobar".
+ *
+ * Path segments like `/:named` are mapped to properties on the `data` object.
+ */
+ pattern: {
+ type: String
+ },
+
+ /**
+ * The parameterized values that are extracted from the route as
+ * described by `pattern`.
+ */
+ data: {
+ type: Object,
+ value: function() {return {};},
+ notify: true
+ },
+
+ /**
+ * @type {?Object}
+ */
+ queryParams: {
+ type: Object,
+ value: function() {
+ return {};
+ },
+ notify: true
+ },
+
+ /**
+ * The part of `route.path` NOT consumed by `pattern`.
+ */
+ tail: {
+ type: Object,
+ value: function() {return {path: null, prefix: null, __queryParams: null};},
+ notify: true
+ },
+
+ /**
+ * Whether the current route is active. True if `route.path` matches the
+ * `pattern`, false otherwise.
+ */
+ active: {
+ type: Boolean,
+ notify: true,
+ readOnly: true
+ },
+
+ _queryParamsUpdating: {
+ type: Boolean,
+ value: false
+ },
+ /**
+ * @type {?string}
+ */
+ _matched: {
+ type: String,
+ value: ''
+ }
+ },
+
+ observers: [
+ '__tryToMatch(route.path, pattern)',
+ '__updatePathOnDataChange(data.*)',
+ '__tailPathChanged(tail.path)',
+ '__routeQueryParamsChanged(route.__queryParams)',
+ '__tailQueryParamsChanged(tail.__queryParams)',
+ '__queryParamsChanged(queryParams.*)'
+ ],
+
+ created: function() {
+ this.linkPaths('route.__queryParams', 'tail.__queryParams');
+ this.linkPaths('tail.__queryParams', 'route.__queryParams');
+ },
+
+ /**
+ * Deal with the query params object being assigned to wholesale.
+ * @export
+ */
+ __routeQueryParamsChanged: function(queryParams) {
+ if (queryParams && this.tail) {
+ this.set('tail.__queryParams', queryParams);
+
+ if (!this.active || this._queryParamsUpdating) {
+ return;
+ }
+
+ // Copy queryParams and track whether there are any differences compared
+ // to the existing query params.
+ var copyOfQueryParams = {};
+ var anythingChanged = false;
+ for (var key in queryParams) {
+ copyOfQueryParams[key] = queryParams[key];
+ if (anythingChanged ||
+ !this.queryParams ||
+ queryParams[key] !== this.queryParams[key]) {
+ anythingChanged = true;
+ }
+ }
+ // Need to check whether any keys were deleted
+ for (var key in this.queryParams) {
+ if (anythingChanged || !(key in queryParams)) {
+ anythingChanged = true;
+ break;
+ }
+ }
+
+ if (!anythingChanged) {
+ return;
+ }
+ this._queryParamsUpdating = true;
+ this.set('queryParams', copyOfQueryParams);
+ this._queryParamsUpdating = false;
+ }
+ },
+
+ /**
+ * @export
+ */
+ __tailQueryParamsChanged: function(queryParams) {
+ if (queryParams && this.route) {
+ this.set('route.__queryParams', queryParams);
+ }
+ },
+
+ /**
+ * @export
+ */
+ __queryParamsChanged: function(changes) {
+ if (!this.active || this._queryParamsUpdating) {
+ return;
+ }
+
+ this.set('route.__' + changes.path, changes.value);
+ },
+
+ __resetProperties: function() {
+ this._setActive(false);
+ this._matched = null;
+ //this.tail = { path: null, prefix: null, queryParams: null };
+ //this.data = {};
+ },
+
+ /**
+ * @export
+ */
+ __tryToMatch: function() {
+ if (!this.route) {
+ return;
+ }
+ var path = this.route.path;
+ var pattern = this.pattern;
+ if (!pattern) {
+ return;
+ }
+
+ if (!path) {
+ this.__resetProperties();
+ return;
+ }
+
+ var remainingPieces = path.split('/');
+ var patternPieces = pattern.split('/');
+
+ var matched = [];
+ var namedMatches = {};
+
+ for (var i=0; i < patternPieces.length; i++) {
+ var patternPiece = patternPieces[i];
+ if (!patternPiece && patternPiece !== '') {
+ break;
+ }
+ var pathPiece = remainingPieces.shift();
+
+ // We don't match this path.
+ if (!pathPiece && pathPiece !== '') {
+ this.__resetProperties();
+ return;
+ }
+ matched.push(pathPiece);
+
+ if (patternPiece.charAt(0) == ':') {
+ namedMatches[patternPiece.slice(1)] = pathPiece;
+ } else if (patternPiece !== pathPiece) {
+ this.__resetProperties();
+ return;
+ }
+ }
+
+ this._matched = matched.join('/');
+
+ // Properties that must be updated atomically.
+ var propertyUpdates = {};
+
+ //this.active
+ if (!this.active) {
+ propertyUpdates.active = true;
+ }
+
+ // this.tail
+ var tailPrefix = this.route.prefix + this._matched;
+ var tailPath = remainingPieces.join('/');
+ if (remainingPieces.length > 0) {
+ tailPath = '/' + tailPath;
+ }
+ if (!this.tail ||
+ this.tail.prefix !== tailPrefix ||
+ this.tail.path !== tailPath) {
+ propertyUpdates.tail = {
+ prefix: tailPrefix,
+ path: tailPath,
+ __queryParams: this.route.__queryParams
+ };
+ }
+
+ // this.data
+ propertyUpdates.data = namedMatches;
+ this._dataInUrl = {};
+ for (var key in namedMatches) {
+ this._dataInUrl[key] = namedMatches[key];
+ }
+
+ this.__setMulti(propertyUpdates);
+ },
+
+ /**
+ * @export
+ */
+ __tailPathChanged: function(path) {
+ if (!this.active) {
+ return;
+ }
+ var tailPath = path;
+ var newPath = this._matched;
+ if (tailPath) {
+ if (tailPath.charAt(0) !== '/') {
+ tailPath = '/' + tailPath;
+ }
+ newPath += tailPath;
+ }
+ this.set('route.path', newPath);
+ },
+
+ /**
+ * @export
+ */
+ __updatePathOnDataChange: function() {
+ if (!this.route || !this.active) {
+ return;
+ }
+ var newPath = this.__getLink({});
+ var oldPath = this.__getLink(this._dataInUrl);
+ if (newPath === oldPath) {
+ return;
+ }
+ this.set('route.path', newPath);
+ },
+
+ __getLink: function(overrideValues) {
+ var values = {tail: null};
+ for (var key in this.data) {
+ values[key] = this.data[key];
+ }
+ for (var key in overrideValues) {
+ values[key] = overrideValues[key];
+ }
+ var patternPieces = this.pattern.split('/');
+ var interp = patternPieces.map(function(value) {
+ if (value[0] == ':') {
+ value = values[value.slice(1)];
+ }
+ return value;
+ }, this);
+ if (values.tail && values.tail.path) {
+ if (interp.length > 0 && values.tail.path.charAt(0) === '/') {
+ interp.push(values.tail.path.slice(1));
+ } else {
+ interp.push(values.tail.path);
+ }
+ }
+ return interp.join('/');
+ },
+
+ __setMulti: function(setObj) {
+ // HACK(rictic): skirting around 1.0's lack of a setMulti by poking at
+ // internal data structures. I would not advise that you copy this
+ // example.
+ //
+ // In the future this will be a feature of Polymer itself.
+ // See: https://github.com/Polymer/polymer/issues/3640
+ //
+ // Hacking around with private methods like this is juggling footguns,
+ // and is likely to have unexpected and unsupported rough edges.
+ //
+ // Be ye so warned.
+ for (var property in setObj) {
+ this._propertySetter(property, setObj[property]);
+ }
+ //notify in a specific order
+ if (setObj.data !== undefined) {
+ this._pathEffector('data', this.data);
+ this._notifyChange('data');
+ }
+ if (setObj.active !== undefined) {
+ this._pathEffector('active', this.active);
+ this._notifyChange('active');
+ }
+ if (setObj.tail !== undefined) {
+ this._pathEffector('tail', this.tail);
+ this._notifyChange('tail');
+ }
+
+ }
+ });
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/app-route/bower.json b/catapult/third_party/polymer/components/app-route/bower.json
new file mode 100644
index 00000000..29023ae6
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "app-route",
+ "version": "1.0.1",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "description": "App routing expressed as Polymer Custom Elements.",
+ "main": [
+ "app-route.html",
+ "app-location.html",
+ "app-route-converter.html"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/app-route",
+ "private": true,
+ "ignore": [],
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.13",
+ "paper-input": "polymerelements/paper-input#^1.1.2",
+ "web-component-tester": "^4.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "iron-pages": "PolymerElements/iron-pages#^1.0.7",
+ "paper-card": "PolymerElements/paper-card#^1.1.1",
+ "paper-icon-button": "polymerelements/paper-icon-button#^v1.0.0",
+ "paper-toggle-button": "polymerelements/paper-toggle-button#^v1.0.0",
+ "google-youtube": "GoogleWebComponents/google-youtube#^1.2.1",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.0",
+ "paper-spinner": "PolymerElements/paper-spinner#^1.1.1"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.3.1",
+ "iron-location": "PolymerElements/iron-location#^0.8.1"
+ }
+}
diff --git a/catapult/third_party/polymer/components/app-route/demo/data-loading-demo.html b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo.html
new file mode 100644
index 00000000..20797f7f
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!--
+@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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>carbon-route data loading example</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/url-bar.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="./data-loading-demo/flickr-search-demo.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ </style>
+</head>
+<body>
+
+<url-bar></url-bar>
+
+<flickr-search-demo></flickr-search-demo>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-image-page.html b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-image-page.html
new file mode 100644
index 00000000..eeb3a1e2
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-image-page.html
@@ -0,0 +1,107 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-ajax/iron-ajax.html">
+<link rel="import" href="../../../paper-spinner/paper-spinner.html">
+<link rel="import" href="../../app-route.html">
+
+<dom-module id="flickr-image-page">
+ <template>
+ <style>
+ paper-spinner {
+ display: block;
+ }
+ .tags span {
+ display: inline-block;
+ padding-right: 10px;
+ font-size: 110%;
+ }
+ .tags span::after {
+ content: ', ';
+ }
+ .tags span:last-of-type::after {
+ content: '';
+ }
+ </style>
+ <app-route route="{{route}}" pattern="/:farm/:server/:id/:secret" data="{{data}}">
+ </app-route>
+ <img src="{{_computeSrc(data)}}">
+ <iron-ajax auto url="https://www.flickr.com/services/rest/"
+ handle-as="json"
+ params="{{params}}"
+ last-response="{{metadata}}"
+ last-error="{{error}}"
+ loading="{{loading}}">
+ </iron-ajax>
+ <paper-spinner active="{{loading}}"></paper-spinner>
+ <div>
+ <h1>{{metadata.photo.title._content}}</h1>
+ <div class="tags">
+ <template is="dom-repeat" items="{{metadata.photo.tags.tag}}">
+ <span>{{item.raw}}</span>
+ </template>
+ </div>
+ <div>
+ <ul>
+ <template is="dom-repeat" items="{{metadata.photo.urls.url}}">
+ <li>
+ <a target="_blank" href="{{item._content}}">
+ {{item._content}}
+ </a>
+ </li>
+ </template>
+ </ul>
+ </div>
+ </div>
+ </template>
+ <script>
+ Polymer({
+ is: 'flickr-image-page',
+ properties: {
+ apiKey: {
+ type: String,
+ },
+
+ params: {
+ type: Object,
+ computed: '_computeParams(apiKey, data.id, data.secret)'
+ }
+
+ },
+ observers: [
+ '_clearOldMetadata(route.path)'
+ ],
+
+ _clearOldMetadata: function() {
+ this.metadata = null;
+ },
+
+ _computeParams: function(apiKey, id, secret) {
+ return {
+ method: 'flickr.photos.getInfo',
+ api_key: apiKey,
+ photo_id: id,
+ secret: secret,
+ format: 'json',
+ nojsoncallback: 1
+ };
+ },
+
+ _computeSrc: function(photo) {
+ if (!photo || !photo.farm) {
+ return '';
+ }
+ return 'https://farm' + photo.farm + '.staticflickr.com/' +
+ photo.server + '/' + photo.id + '_' + photo.secret + '.jpg';
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-demo.html b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-demo.html
new file mode 100644
index 00000000..b13fc7bf
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-demo.html
@@ -0,0 +1,66 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-pages/iron-pages.html">
+<link rel="import" href="../../app-location.html">
+<link rel="import" href="../../app-route.html">
+<link rel="import" href="./flickr-search-page.html">
+<link rel="import" href="./flickr-image-page.html">
+
+
+<dom-module id="flickr-search-demo">
+ <template>
+ <style>
+ a {
+ text-decoration: none;
+ color: inherit;
+ }
+ a:hover {
+ text-decoration: underline;
+ }
+ </style>
+ <app-location route="{{route}}" use-hash-as-path></app-location>
+ <app-route route="{{route}}" pattern="/:page" data="{{data}}">
+ </app-route>
+ <app-route route="{{route}}" pattern="/search" tail="{{searchRoute}}">
+ </app-route>
+ <app-route route="{{route}}" pattern="/image" tail="{{imageRoute}}">
+ </app-route>
+
+ <h1><a href="#/search/">Public Domain Image Search</a></h1>
+
+ <iron-pages attr-for-selected="id" selected="{{data.page}}"
+ selected-attribute="selected">
+ <flickr-search-page id="search" api-key="{{apiKey}}"
+ route="{{searchRoute}}">
+ </flickr-search-page>
+ <flickr-image-page id="image" api-key="{{apiKey}}" route="{{imageRoute}}">
+ </flickr-image-page>
+ </iron-pages>
+ </template>
+ <script>
+ Polymer({
+ is: 'flickr-search-demo',
+ properties: {
+ apiKey: {
+ type: String,
+ value: '5358d9830b6865a13d251e5e1acb4c30'
+ }
+ },
+
+ attached: function() {
+ if (this.route.path === '') {
+ this.set('route.path', '/search/');
+ }
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-page.html b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-page.html
new file mode 100644
index 00000000..dedb3eba
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/data-loading-demo/flickr-search-page.html
@@ -0,0 +1,107 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-spinner/paper-spinner.html">
+<link rel="import" href="../../../paper-input/paper-input.html">
+<link rel="import" href="../../app-route.html">
+
+<dom-module id="flickr-search-page">
+ <template>
+ <style>
+ paper-spinner {
+ display: block;
+ }
+ img {
+ max-width: 200px;
+ max-height: 200px;
+ }
+ </style>
+ <app-route pattern="/" route="{{route}}" query-params="{{queryParams}}"
+ active="{{active}}">
+ </app-route>
+ <paper-input autofocus label="Search the public domain on Flickr"
+ value="{{queryParams.search}}">
+ </paper-input>
+
+ <iron-ajax auto url="https://www.flickr.com/services/rest/"
+ handle-as="json"
+ debounce-duration="300"
+ params="{{params}}"
+ last-response="{{response}}"
+ last-error="{{error}}"
+ loading="{{loading}}">
+ </iron-ajax>
+ <paper-spinner active="{{loading}}"></paper-spinner>
+ <template is="dom-repeat" items="{{response.photos.photo}}" as="photo">
+ <a href="{{_computeLink(photo)}}">
+ <img src="{{_computeSrc(photo)}}">
+ </a>
+ </template>
+ <template is="dom-if" if="{{error}}">
+ <span>{{error.statusCode}}</span> Error:
+ <pre>{{error.response}}</pre>
+ </template>
+ </template>
+ <script>
+ Polymer({
+ is: 'flickr-search-page',
+ properties: {
+ apiKey: {
+ type: String,
+ },
+
+ params: {
+ type: Object,
+ computed: '_computeParams(apiKey, queryParams.search)'
+ },
+ },
+
+ observers: [
+ '_clearOldSearchResults(queryParams.search)',
+ '_setDefaultSearch(active)'
+ ],
+
+ _clearOldSearchResults: function() {
+ this.response = null;
+ },
+
+ _computeParams: function(apiKey, search) {
+ return {
+ method: 'flickr.photos.search',
+ api_key: apiKey,
+ text: search,
+ license: '7,8',
+ format: 'json',
+ nojsoncallback: 1
+ };
+ },
+
+ _computeSrc: function(photo) {
+ if (!photo || !photo.farm) {
+ return '';
+ }
+ return 'https://farm' + photo.farm + '.staticflickr.com/' +
+ photo.server + '/' + photo.id + '_' + photo.secret + '.jpg';
+ },
+
+ _computeLink: function(photo) {
+ return window.location.pathname + '#/image/' + photo.farm + '/' +
+ photo.server + '/' + photo.id + '/' + photo.secret;
+ },
+
+ _setDefaultSearch: function(active) {
+ if (active && !this.queryParams.search) {
+ this.set('queryParams.search', 'spaceship')
+ }
+ }
+ })
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/index.html b/catapult/third_party/polymer/components/app-route/demo/index.html
new file mode 100644
index 00000000..4a80ad08
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/index.html
@@ -0,0 +1,214 @@
+<!DOCTYPE html>
+<!--
+@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
+-->
+<html>
+<head>
+<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+<title>app-route Demo</title>
+<link rel="import" href="../../polymer/polymer.html">
+
+<link rel="import" href="../../iron-demo-helpers/url-bar.html">
+<link rel="import" href="../../iron-pages/iron-pages.html">
+
+<link rel="import" href="../app-location.html">
+<link rel="import" href="../app-route.html">
+
+<link rel="import" href="youtube-demo/youtube-search.html">
+<link rel="import" href="youtube-demo/youtube-toolbar.html">
+<link rel="import" href="youtube-demo/video-viewer.html">
+<link rel="import" href="youtube-demo/search-results.html">
+
+<style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ url-bar {
+ background-color: white;
+ }
+</style>
+</head>
+
+<body>
+
+<url-bar></url-bar>
+
+<app-router-demo></app-router-demo>
+
+<dom-module id="app-router-demo">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ height: 100vh;
+ @apply(--paper-font-common-base);
+ }
+
+ :host([video-page-active]) {
+ overflow: hidden;
+ }
+
+ :host([video-page-active]) iron-pages {
+ transform: translateY(-170px);
+ }
+
+ iron-pages {
+ transition: transform 0.3s;
+ }
+ </style>
+
+
+ <!-- app-location binds with the URL and produces a route for
+ app-route elements to consume. Since this demo needs to run without
+ server cooperation (e.g. with polyserve, in the elements catalog, etc) we'll
+ use the hash portion of the URL for our route paths. -->
+ <app-location route="{{route}}" use-hash-as-path></app-location>
+
+ <!-- app-routes parse route paths based on the their `pattern`.
+ Parameters are extracted into the `data` object. The rest of the path that
+ comes after the `pattern` is put into the `tail` object, which can be
+ passed to the `route` property of downstream app-routes. -->
+ <app-route route="{{route}}" pattern="/:page" data="{{data}}"></app-route>
+ <app-route route="{{route}}" pattern="/search" tail="{{searchTail}}"></app-route>
+ <app-route route="{{route}}" pattern="/video" tail="{{videoTail}}" active="{{videoPageActive}}"></app-route>
+
+ <youtube-toolbar collapsed$="{{videoPageActive}}">
+ <!-- The youtube-search has a app-route that consumes the tail of
+ another route (`searchTail`) -->
+ <youtube-search
+ route="{{searchTail}}"
+ video-data="{{videoData}}">
+ </youtube-search>
+ </youtube-toolbar>
+
+ <iron-pages attr-for-selected="id" selected="{{data.page}}">
+ <search-results id="search" items="{{videos}}"></search-results>
+
+ <!-- The video-viewer has a app-route that consumes the tail of
+ another route -->
+ <video-viewer id="video" route="{{videoTail}}"></video-viewer>
+ </iron-pages>
+
+ </template>
+
+ <script>
+ window.addEventListener('WebComponentsReady', function() {
+ Polymer({
+ is: 'app-router-demo',
+
+ properties: {
+ route: {
+ type: Object
+ },
+
+ videoData: {
+ type: Object,
+ observer: '_videoDataChanged'
+ },
+
+ videoPageActive: {
+ type: Boolean,
+ reflectToAttribute: true,
+ observer: '_videoPageActiveChanged'
+ },
+
+ searchTail: {
+ type: Object,
+ notify: true
+ },
+
+ videoTail: {
+ type: Object,
+ notify: true
+ },
+
+ newCategory: {
+ type: String
+ },
+
+ videos: {
+ type: Array,
+ value: function() {
+ return [];
+ }
+ },
+
+ data: {
+ type: Object,
+ value: function() {
+ return {
+ page: '/search/'
+ };
+ }
+ }
+ },
+
+ observers: [
+ '_onRoutePathChanged(route.path)'
+ ],
+
+ _onRoutePathChanged: function(path) {
+ // If we do not have an initial URL, we redirect to /search
+ if (!path) {
+ this.set('route.path', '/search/');
+ }
+ },
+
+ _videoDataChanged: function(data) {
+ var allVideos = [];
+
+ var that = this;
+
+ data.items.forEach(function (videoItem) {
+ var youtubeVideo = {
+ id: videoItem.id.videoId,
+ title: videoItem.snippet.title,
+ thumbnail: videoItem.snippet.thumbnails.high.url
+ };
+
+ allVideos.push(youtubeVideo);
+ });
+
+ this.set('videos', allVideos);
+ },
+
+ _videoPageActiveChanged: function(videoPageActive, previousValue) {
+ // change color of page on page change
+ var newColor;
+
+ if (videoPageActive) {
+ // black
+ newColor = 0;
+ } else {
+ // white
+ newColor = 255;
+ }
+
+ document.body.style.backgroundColor = 'rgb(' + newColor + ',' + newColor
+ + ',' + newColor + ')';
+
+ // on first load, set the color then allow color transition animations
+ if (previousValue === undefined) {
+ document.body.style.transition = 'background-color .2s linear';
+ return;
+ }
+ }
+ });
+ });
+ </script>
+</dom-module>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/app-route/demo/simple-demo.html b/catapult/third_party/polymer/components/app-route/demo/simple-demo.html
new file mode 100644
index 00000000..83f50141
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/simple-demo.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<!--
+@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
+-->
+<html>
+<head>
+<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+<title>app-route Demo</title>
+<link rel="import" href="../../polymer/polymer.html">
+
+<link rel="import" href="../../iron-demo-helpers/url-bar.html">
+<link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+<link rel="import" href="../../iron-pages/iron-pages.html">
+<link rel="import" href="../../paper-input/paper-input.html">
+<link rel="import" href="../../paper-button/paper-button.html">
+
+<link rel="import" href="../app-location.html">
+<link rel="import" href="../app-route.html">
+</head>
+
+<body>
+<dom-module id="route-display">
+ <template>
+ <div>
+ <div>route<template is="dom-if" if="{{tail}}"> / tail</template>: {</div>
+ <div>&nbsp;&nbsp;prefix: {{route.prefix}}</div>
+ <div>&nbsp;&nbsp;path: {{route.path}}</div>
+ <div>}</div>
+ </div>
+ </template>
+ <script>
+ Polymer({
+ is: 'route-display',
+
+ properties: {
+ route: Object,
+ tail: Boolean
+ }
+ });
+ </script>
+</dom-module>
+
+<template is="dom-bind">
+ <url-bar></url-bar>
+
+ <app-location route="{{route}}" use-hash-as-path></app-location>
+
+ <app-route
+ route="{{route}}"
+ pattern="/:demoType"
+ data="{{demoSelectionData}}"
+ tail="{{demoSelectionTail}}">
+ </app-route>
+
+ <app-route
+ route="{{route}}"
+ pattern="/pathDemo/:firstPath/:secondPath"
+ data="{{pathData}}"
+ tail="{{pathDataTail}}">
+ </app-route>
+
+ <app-route
+ route="{{route}}"
+ pattern="/queryParamsDemo"
+ query-params="{{queryParams}}"
+ tail="{{qpDemoTail}}">
+ </app-route>
+
+ <div>App location route object
+ <route-display route="{{route}}"></route-display>
+ </div>
+
+ <paper-button raised>
+ <a href="#/pathDemo/firstPath/secondPath/thirdPath">Changes in Path</a>
+ </paper-button>
+
+ <paper-button raised>
+ <a href="?hello=world&foo=bar#/queryParamsDemo">Changes in Query Params</a>
+ </paper-button>
+
+ <iron-pages selected={{demoSelectionData.demoType}} attr-for-selected="demo">
+ <div demo="pathDemo">
+ Change location of first part of the path:
+ <paper-input value="{{pathData.firstPath}}"></paper-input>
+ Change location of second part of the path:
+ <paper-input value="{{pathData.secondPath}}"></paper-input>
+
+ <app-route
+ route="{{pathDataTail}}"
+ pattern="/:thirdPath"
+ data="{{tailExampleData}}">
+ </app-route>
+ You can pass the tail of an app-route to be the route another app-route. Here is
+ the tail object of the first app-route which is the route object of this new app-route:
+ <route-display route="{{pathDataTail}}" tail></route-display>
+ You can also bind to this new route. Change the location of the third part of
+ the path:
+ <paper-input value="{{tailExampleData.thirdPath}}"></paper-input>
+ </div>
+
+ <div demo="queryParamsDemo">
+ Change the value of the hello query param
+ <paper-input value="{{queryParams.hello}}"></paper-input>
+ Change the value of the foo param
+ <paper-input value="{{queryParams.foo}}"></paper-input>
+ </div>
+ </iron-pages>
+</template>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/app-route/demo/youtube-demo/route-info.html b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/route-info.html
new file mode 100644
index 00000000..26e79094
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/route-info.html
@@ -0,0 +1,48 @@
+<!--
+@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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+
+<dom-module id="route-info">
+ <template>
+ <style>
+ :host {
+ font-style: italic;
+ font-size: 0.85em;
+ font-weight: 200;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ color: #fff;
+ }
+ </style>
+ <span>Route prefix: {{route.prefix}} &middot; Route path: {{route.path}} &middot; Query params: {{_stringifyQueryParams(route.queryParams.*)}}</span>
+ </template>
+ <script>
+ Polymer({
+ is: 'route-info',
+
+ properties: {
+ route: {
+ type: Object
+ }
+ },
+
+ _stringifyQueryParams: function() {
+ var params = [];
+ if (this.route && this.route.queryParams) {
+ for (var key in this.route.queryParams) {
+ params.push(key + ' = ' + this.route.queryParams[key]);
+ }
+ }
+ return params.join(', ');
+ }
+ })
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/youtube-demo/search-results.html b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/search-results.html
new file mode 100644
index 00000000..752fdb16
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/search-results.html
@@ -0,0 +1,63 @@
+<!--
+@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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-card/paper-card.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+
+<dom-module id="search-results">
+ <template>
+ <style>
+ :host {
+ @apply(--layout-horizontal);
+ @apply(--layout-center-center);
+ @apply(--layout-wrap);
+ }
+
+ a {
+ color: black;
+ text-decoration: none;
+ }
+
+ paper-card {
+ width: 300px;
+ margin: 1em 0.5em 0em;
+ font-size: 14px;
+ }
+
+ .card-content {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ </style>
+ <template is="dom-repeat" items="{{items}}" as="video">
+ <!-- The '#' is included because the use-hash-as-path property is
+ set to true in the app-location -->
+ <a href="./#/video/{{video.id}}">
+ <paper-card image="{{video.thumbnail}}">
+ <div class="card-content">
+ {{video.title}}
+ </div>
+ </paper-card>
+ </a>
+ </template>
+ </template>
+ <script>
+ Polymer({
+ is: 'search-results',
+
+ properties: {
+ items: {
+ type: Array
+ }
+ }
+ })
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/youtube-demo/video-viewer.html b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/video-viewer.html
new file mode 100644
index 00000000..4786e4af
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/video-viewer.html
@@ -0,0 +1,133 @@
+<!--
+@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
+-->
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-styles/color.html">
+<link rel="import" href="../../../paper-input/paper-input.html">
+<link rel="import" href="../../../paper-toggle-button/paper-toggle-button.html">
+
+<link rel="import" href="../../app-route.html">
+
+<link rel="import" href="youtube-lite.html">
+<link rel="import" href="route-info.html">
+
+<dom-module id="video-viewer">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ height: calc(100vh - 60px);
+ --primary-color: var(--paper-red-500);
+ --primary-text-color: #fff;
+ --paper-toggle-button-unchecked-bar-color: #888;
+ }
+
+ paper-input {
+ width: 100px;
+ }
+
+ #controls {
+ color: #fff;
+ @apply(--layout-vertical);
+ @apply(--layout-center-center);
+ height: 30%;
+ }
+
+ #controls > div {
+ @apply(--layout-horizontal);
+ padding-bottom: 1em;
+ }
+
+ #state {
+ margin-left: 16px;
+ }
+
+ #player {
+ height: 70%;
+ }
+ </style>
+
+ <!-- This app-route consumes the route which was provided by the tail of
+ a app-route in the host of this element. This means that the parent that
+ provides this route decides where this element lives in the URL space. In
+ this case, the parent, which uses hashes, matches #/video and hence this
+ element lives in <App serving point>?querParams#/video/:vid -->
+ <app-route route="{{route}}" pattern="/:vid" data="{{data}}" query-params="{{queryParams}}">
+ </app-route>
+
+ <!-- You can bind any element's state into the URL by binding their
+ properties into the queryParams object. youtube-lite doesn't have any code
+ that's even aware of routing or the URL. -->
+ <youtube-lite
+ id="player"
+ video-id="{{data.vid}}"
+ state="{{queryParams.state}}"
+ current-time="{{queryParams.time}}"
+ start-time="{{queryParams.time}}">
+ </youtube-lite>
+
+ <div id="controls">
+ <div>
+ <paper-input
+ id="time"
+ type="number"
+ on-focus="pause"
+ label="Time"
+ value="{{queryParams.time}}">
+ </paper-input>
+ <paper-toggle-button id="state" active="{{playing}}">[[queryParams.state]]</paper-toggle-button>
+ </div>
+ <route-info route="[[route]]"></route-info>
+ </div>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'video-viewer',
+
+ properties: {
+ route: {
+ type: Object,
+ notify: true
+ },
+
+ data: {
+ type: Object
+ },
+
+ playing: {
+ type: Boolean
+ },
+
+ queryParams: {
+ type: Object
+ }
+ },
+
+ observers: [
+ '_playingChanged(playing)',
+ '_stateChanged(queryParams.state)'
+ ],
+
+ pause: function() {
+ this.set('queryParams.state', 'paused');
+ },
+
+ _playingChanged: function(playing) {
+ this.set('queryParams.state', playing ? 'playing' : 'paused');
+ },
+
+ _stateChanged: function(state) {
+ this.playing = state === 'playing';
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-lite.html b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-lite.html
new file mode 100644
index 00000000..24f1a7b7
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-lite.html
@@ -0,0 +1,204 @@
+<!--
+@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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../google-youtube/google-youtube.html">
+<!--
+youtube-lite provides a simple subset of the google-youtube element's API. By
+simplifying the API we're also able to make it more amenable to two-way data
+binding.
+
+Note that this element is totally agnostic to routing!
+-->
+<dom-module id="youtube-lite">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ width: 100%;
+ }
+
+ google-youtube {
+ height: 100%;
+ }
+ </style>
+
+ <google-youtube
+ id="player"
+ video-id="{{videoId}}"
+ state="{{__state}}"
+ currenttime="{{__currentTime}}"
+ width="100%"
+ height="100%">
+ </google-youtube>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'youtube-lite',
+
+ properties: {
+ videoId: {
+ type: String,
+ notify: true
+ },
+
+ state: {
+ type: String,
+ notify: true,
+ observer: '_stateChanged'
+ },
+
+ currentTime: {
+ type: Number,
+ notify: true,
+ observer: '_currentTimeChanged'
+ },
+
+ startTime: {
+ type: Number
+ },
+
+ __state: {
+ type: String,
+ observer: '__ytApiStateChange'
+ },
+
+ __currentTime: {
+ type: String,
+ observer: '_ytCurrentTimeChanged'
+ },
+
+ __pauseOnFirstSeek: {
+ type: Boolean
+ }
+ },
+
+ listeners: {
+ 'google-youtube-ready': '_onYoutubeReady'
+ },
+
+ _seekTo: function(newTime) {
+ var player = this.$.player;
+
+ if (player.duration == 1 || newTime < player.duration) {
+ player.seekTo(newTime);
+ }
+ },
+
+ _onYoutubeReady: function() {
+ this.__pauseOnFirstSeek = this.state == 'paused';
+
+ if (!this.__pauseOnFirstSeek || this.startTime) {
+ this._seekTo(this.startTime);
+ }
+ },
+
+ _currentTimeChanged: function(newTime, oldTime) {
+ var apiState = this.__readableStateToApiState(this.state);
+
+ if (apiState != 2 || this.__state != 2) {
+ return;
+ }
+
+ this._seekTo(newTime);
+ },
+
+ _ytCurrentTimeChanged: function(ytCurrentTime) {
+ if (this.__state === this.__apiStates.PAUSED) {
+ return;
+ }
+
+ this.currentTime = ytCurrentTime;
+ },
+
+ _stateChanged: function(newState, oldState) {
+ var newApiState = this.__readableStateToApiState(newState);
+
+ if (newApiState == this.__state ||
+ this.__state == this.__apiStates.UNSTARTED) {
+ return;
+ }
+
+ this.currentTime = this.__currentTime;
+ var player = this.$.player;
+
+ switch (newApiState) {
+ case this.__apiStates.PLAYING:
+ player.play();
+ break;
+ case this.__apiStates.PAUSED:
+ player.pause();
+ break;
+ default:
+ return;
+ }
+ },
+
+ __ytApiStateChange: function(newState, oldState) {
+ var readableState;
+
+ switch (newState) {
+ case this.__apiStates.ENDED:
+ readableState = this.__states.PAUSED;
+ break;
+ case this.__apiStates.PLAYING:
+ readableState = this.__states.PLAYING;
+ break;
+ case this.__apiStates.PAUSED:
+ readableState = this.__states.PAUSED;
+ break;
+ default:
+ return;
+ }
+
+ if (this.state == readableState) {
+ return;
+ }
+
+ if (this.__pauseOnFirstSeek && readableState == this.__states.PLAYING) {
+ this.__pauseOnFirstSeek = false;
+ this.$.player.pause();
+ return;
+ }
+
+ this.state = readableState;
+ this.currentTime = this.__currentTime;
+ },
+
+ __readableStateToApiState: function(readableState) {
+ var newApiState = -2;
+
+ if (readableState == this.__states.PLAYING) {
+ newApiState = this.__apiStates.PLAYING;
+
+ } else if (readableState = this.__states.PAUSED) {
+ newApiState = this.__apiStates.PAUSED;
+ }
+
+ return newApiState;
+ },
+
+ __states: {
+ PLAYING: 'playing',
+ PAUSED: 'paused'
+ },
+
+ __apiStates: {
+ UNSTARTED: -1,
+ ENDED: 0,
+ PLAYING: 1,
+ PAUSED: 2,
+ BUFFERING: 3,
+ QUEUED: 5
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-search.html b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-search.html
new file mode 100644
index 00000000..845c8655
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-search.html
@@ -0,0 +1,103 @@
+<!--
+@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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-input/paper-input.html">
+<link rel="import" href="../../../iron-ajax/iron-ajax.html">
+
+<link rel="import" href="../../app-route.html">
+
+<link rel="import" href="route-info.html">
+
+<dom-module id="youtube-search">
+ <template>
+ <style>
+ :host {
+ --primary-color: #fff;
+ --paper-input-container-color: #fff;
+ display: block;
+ position: relative;
+ padding: 1em;
+ }
+
+ route-info {
+ color: #fff;
+ }
+ </style>
+
+ <!-- This app-route consumes the route which was provided by the tail of
+ a app-route in the host of this element -->
+ <app-route route="{{route}}" pattern="/:searchQuery" data="{{data}}">
+ </app-route>
+
+ <paper-input label="Search Youtube" value="{{data.searchQuery}}"></paper-input>
+
+ <route-info route="[[route]]"></route-info>
+
+ <iron-ajax auto
+ id="youtubeSearch"
+ url="https://www.googleapis.com/youtube/v3/search"
+ params="{{params}}"
+ last-response="{{videoData}}">
+ </iron-ajax>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'youtube-search',
+
+ properties: {
+ route: {
+ type: Object,
+ notify: true
+ },
+
+ data: {
+ type: Object
+ },
+
+ category: {
+ type: String,
+ notify: true,
+ observer: '_categoryChanged'
+ },
+
+ params: {
+ type: String,
+ computed: '_setParams(data.searchQuery)'
+ },
+
+ videoData: {
+ type: Object,
+ notify: true
+ }
+ },
+
+ observers: ['_pathChanged(route.path)'],
+
+ _pathChanged: function() {
+ this.async(function() {
+ if (!this.route.path) {
+ this.set('route.path', '/');
+ }
+ });
+ },
+
+ _setParams: function(category) {
+ return {
+ part: 'snippet',
+ q: this.data.searchQuery,
+ key: 'AIzaSyAuecFZ9xJXbGDkQYWBmYrtzOGJD-iDIgI',
+ type: 'video'
+ }
+ },
+
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-toolbar.html b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-toolbar.html
new file mode 100644
index 00000000..7adc0b76
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/demo/youtube-demo/youtube-toolbar.html
@@ -0,0 +1,181 @@
+<!--
+@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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-styles/shadow.html">
+<link rel="import" href="../../../paper-icon-button/paper-icon-button.html">
+<link rel="import" href="../../../iron-icons/iron-icons.html">
+
+<dom-module id="youtube-toolbar">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ --paper-icon-button-ink-color: #fff;
+ --iron-icon-fill-color: #fff;
+ }
+
+ :host([collapsed]) #background {
+ transform: scaleY(calc(60/230));
+ }
+
+ :host([collapsed]) #youtube-logo {
+ transform: scale(calc(60/230)) translateY(-195px);
+ }
+
+ :host([collapsed]) #back {
+ transform: translateX(0);
+ }
+
+ :host([collapsed]) #content {
+ opacity: 0;
+ transition-delay: 0s;
+ transform: translateY(-10px);
+ }
+
+ #background {
+ height: 230px;
+ background-image: linear-gradient(#E7291A, #C21616);
+ @apply(--shadow-elevation-2dp);
+ transform-origin: 0 0;
+ transition: transform 0.3s;
+ transform: scaleY(1);
+ }
+
+ #youtube-logo {
+ display: block;
+ position: absolute;
+ margin: auto;
+ top: 30px;
+
+ left: calc(50% - 75px);
+
+ width: 150px;
+ height: calc(150px / 1.45);
+ background-image: radial-gradient(transparent 50%, #fff 50%);
+ border-radius: 9% / 13%;
+ transition: transform 0.3s;
+ }
+
+ #youtube-logo:before,
+ #youtube-logo:after {
+ content: '';
+ display: block;
+ position: absolute;
+ background-color: #fff;
+ width: 90%;
+ height: 14%;
+ left: 5%;
+ border-radius: 100% / 90%;
+ }
+
+ #youtube-logo:before {
+ top: -3.7%;
+ }
+
+ #youtube-logo:after {
+ bottom: -3.7%;
+ }
+
+ #youtube-logo > .lr-edge {
+ display: block;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ }
+
+ #youtube-logo > .lr-edge:before,
+ #youtube-logo > .lr-edge:after {
+ content: '';
+ display: block;
+ position: absolute;
+ background-color: #fff;
+ width: 10%;
+ height: 90%;
+ top: 5%;
+
+ border-radius: 100% / 90%;
+ }
+
+ #youtube-logo > .lr-edge:before {
+ left: -2.5%;
+ }
+
+ #youtube-logo > .lr-edge:after {
+ right: -2.5%;
+ }
+
+ #youtube-logo > .play-icon {
+ display: block;
+ position: absolute;
+ width: 80%;
+ height: 80%;
+ top: 10%;
+ left: 10%;
+ overflow: hidden;
+ background-image:
+ linear-gradient(90deg, #fff 38%, transparent 38%),
+ linear-gradient(35deg, transparent 57%, rgba(0, 0, 0, 0.3) 57%);
+ }
+
+ #youtube-logo > .play-icon:before,
+ #youtube-logo > .play-icon:after {
+ content: '';
+ display: block;
+ position: absolute;
+ width: 200%;
+ height: 65%;
+ background-color: #fff;
+ }
+
+ #youtube-logo > .play-icon:before {
+ transform-origin: top left;
+ top: -80%;
+ transform: rotate(29deg);
+ }
+
+ #youtube-logo > .play-icon:after {
+ transform-origin: bottom left;
+ bottom: -80%;
+ transform: rotate(-29deg);
+ }
+
+ #content {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ transition: transform 0.15s, opacity 0.15s;
+ transition-delay: 0.2s;
+ }
+
+ #back {
+ position: absolute;
+ top: 10px;
+ transform: translateX(-64px);
+ transition: transform 0.3s;
+ }
+ </style>
+ <div id="background"></div>
+ <div id="youtube-logo">
+ <div class="lr-edge"></div>
+ <div class="play-icon"></div>
+ </div>
+ <div id="content">
+ <content></content>
+ </div>
+ <a id="back" href="../#/search/"><paper-icon-button icon="icons:arrow-back"></paper-icon-button></a>
+ </template>
+ <script>
+ Polymer({
+ is: 'youtube-toolbar'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/index.html b/catapult/third_party/polymer/components/app-route/index.html
new file mode 100644
index 00000000..5b354f51
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+
+ <title>app-router</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/app-route/test/app-example-1.html b/catapult/third_party/polymer/components/app-route/test/app-example-1.html
new file mode 100644
index 00000000..e86a7038
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/app-example-1.html
@@ -0,0 +1,45 @@
+<!--
+@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
+-->
+
+<link rel='import' href='../app-route.html'>
+<link rel='import' href='../app-location.html'>
+
+<dom-module id='app-example-1'>
+ <template>
+ <app-location route='{{route}}'>
+ </app-location>
+ <app-route id="page" route='{{route}}' pattern='/:page' data='{{data}}'>
+ </app-route>
+ <app-route id="user" route='{{route}}' pattern='/user' tail='{{userRoute}}'>
+ </app-route>
+ <app-route id="tail" route='{{userRoute}}' pattern='/:page' data='{{userData}}' query-params="{{userQueryParams}}">
+ </app-route>
+ </template>
+ <script>
+ Polymer({
+ is: 'app-example-1',
+ observers: [
+ 'pageChanged(data.page)',
+ 'userPathChanged(userRoute.path)',
+ ],
+ pageChanged: function(page) {
+ if (page === 'redirectToUser') {
+ this.set('data.page', 'user');
+ }
+ },
+ userPathChanged: function(path) {
+ // Redirect from /user/ and /user to /user/view
+ if (path === '/' || path === '') {
+ this.set('userRoute.path', '/view');
+ }
+ }
+ })
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/test/app-location.html b/catapult/third_party/polymer/components/app-route/test/app-location.html
new file mode 100644
index 00000000..cdb71237
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/app-location.html
@@ -0,0 +1,168 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+ <title>app-location</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../iron-test-helpers/mock-interactions.html">
+ <link rel="import" href="../app-location.html">
+</head>
+<body>
+ <test-fixture id="BasicLocation">
+ <template>
+ <app-location></app-location>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="LocationViaHash">
+ <template>
+ <app-location use-hash-as-path></app-location>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ClickableLink">
+ <template>
+ <a></a>
+ </template>
+ </test-fixture>
+
+ <script>
+ 'use strict';
+
+ function setLocation(url) {
+ window.history.pushState({}, '', url);
+ Polymer.Base.fire('location-changed', {}, { node: window });
+ }
+
+ function assign(a, b) {
+ if (Object.assign) {
+ return Object.assign.apply(Object, arguments);
+ }
+
+ for (var property in b) {
+ a[property] = b[property];
+ }
+
+ return a;
+ }
+
+ suite('<app-location>', function () {
+ var initialUrl;
+
+ setup(function() {
+ initialUrl = window.location.href;
+ });
+
+ teardown(function() {
+ window.history.replaceState({}, '', initialUrl);
+ });
+
+ suite('in the default configuration', function() {
+ var appLocation;
+
+ setup(function() {
+ appLocation = fixture('BasicLocation');
+ });
+
+ test('it automatically exposes the current route', function() {
+ expect(appLocation.route).to.be.ok;
+ expect(appLocation.route.path).to.be.equal(window.location.pathname);
+ });
+
+ suite('manipulating the route', function() {
+ var originalPath;
+ var originalQueryParams;
+
+ setup(function() {
+ originalPath = appLocation.route.path;
+ originalQueryParams = assign({}, appLocation.route.__queryParams);
+ });
+
+ teardown(function() {
+ appLocation.set('route.prefix', '');
+ appLocation.set('route.path', originalPath);
+ appLocation.set('route.__queryParams', originalQueryParams);
+ });
+
+ test('it reflects path to location.pathname', function() {
+ appLocation.set('route.path', '/foo/bar');
+ expect(window.location.pathname).to.be.equal('/foo/bar');
+ });
+
+ test('it reflects queryParams values to location.search', function() {
+ appLocation.set('route.__queryParams.foo', 1);
+ expect(window.location.search).to.match(/foo=1/);
+ });
+
+ test('it reflects completely replaced queryParams', function() {
+ appLocation.set('route.__queryParams', { bar: 1 });
+ expect(window.location.search).to.be.equal('?bar=1');
+ });
+
+ test('it reflects the prefix to location.pathname', function() {
+ appLocation.set('route.prefix', '/fiz');
+ expect(window.location.pathname).to.be.equal('/fiz' + originalPath);
+ });
+ });
+
+ /**
+ * NOTE: For a more thorough spec describing this behavior, please refer
+ * to the `iron-location` component.
+ */
+ suite('manipulating the history state', function() {
+ var originalLocation;
+
+ setup(function() {
+ originalLocation = window.location.toString();
+ });
+
+ teardown(function() {
+ setLocation(originalLocation);
+ });
+
+ test('it reflects location.pathname to route.path', function() {
+ setLocation('/fiz/buz');
+ expect(appLocation.route.path).to.be.equal('/fiz/buz');
+ });
+
+ test('it reflects location.search to route.__queryParams', function() {
+ setLocation('?fiz=buz');
+ expect(appLocation.route.__queryParams).to.be.eql({
+ fiz: 'buz'
+ });
+ });
+ });
+ });
+
+ suite('using the hash as the route path', function() {
+ var appLocation;
+
+ setup(function() {
+ appLocation = fixture('LocationViaHash');
+ });
+
+ test('it reflects location.hash to route.path', function() {
+ setLocation('#/fiz/buz');
+ expect(appLocation.route.path).to.be.equal('/fiz/buz');
+ });
+
+ test('it reflects route.path to location.hash', function() {
+ appLocation.set('route.path', '/foo/bar');
+ expect(window.location.hash).to.be.equal('#/foo/bar');
+ });
+ });
+ });
+ </script>
+</body>
diff --git a/catapult/third_party/polymer/components/app-route/test/app-route-converter.html b/catapult/third_party/polymer/components/app-route/test/app-route-converter.html
new file mode 100644
index 00000000..b5b8c559
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/app-route-converter.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+ <title>app-route-converter</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../app-route-converter.html">
+</head>
+<body>
+ <test-fixture id="BasicRouteConversion">
+ <template>
+ <app-route-converter>
+ </app-route-converter>
+ </template>
+ </test-fixture>
+
+ <script>
+ 'use strict';
+
+ suite('<app-route-converter>', function() {
+ test('it bidirectionally maps path and queryParams to route', function() {
+ var converter = fixture('BasicRouteConversion');
+
+ var queryParams = {x: '10'};
+ converter.path = '/a/b/c';
+ converter.queryParams = queryParams;
+
+ expect(converter.route).to.be.deep.equal({
+ prefix: '',
+ path: '/a/b/c',
+ __queryParams: queryParams
+ });
+
+ converter.set('route.path', '/d/e/f');
+ expect(converter.path).to.be.equal('/d/e/f');
+
+ queryParams = {y: '11'};
+ converter.set('route.__queryParams', queryParams);
+ expect(converter.queryParams).to.be.deep.equal(queryParams);
+
+ queryParams['z'] = '12';
+ expect(converter.queryParams).to.be.deep.equal(queryParams);
+ });
+ });
+ </script>
+</body>
diff --git a/catapult/third_party/polymer/components/app-route/test/app-route.html b/catapult/third_party/polymer/components/app-route/test/app-route.html
new file mode 100644
index 00000000..da4af2ca
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/app-route.html
@@ -0,0 +1,488 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+ <title>app-route</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../app-route.html">
+ <link rel="import" href="./redirection.html">
+</head>
+<body>
+ <test-fixture id="BasicRoute">
+ <template>
+ <app-route pattern='/user/:username'>
+ </app-route>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ChainedRoutes">
+ <template is="dom-template">
+ <app-route
+ pattern="/foo/:foo"
+ route="{{numberOneTopRoute}}"
+ data="{{fooData}}"
+ tail="{{fooRoute}}">
+ </app-route>
+
+ <app-route
+ pattern="/bar/:bar"
+ route="{{fooRoute}}"
+ data="{{barData}}">
+ </app-route>
+
+ <app-route
+ pattern="/baz/:baz"
+ route="{{fooRoute}}"
+ data="{{bazData}}">
+ </app-route>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="Redirection">
+ <template>
+ <redirect-app-route></redirect-app-route>
+ </template>
+ </test-fixture>
+
+<script>
+ 'use strict';
+
+ function fixtureChainedRoutes(route) {
+ var routes = fixture('ChainedRoutes', {
+ numberOneTopRoute: {
+ path: route.path || '',
+ prefix: route.prefix || '',
+ __queryParams: route.__queryParams || {}
+ }
+ });
+
+ return {
+ foo: routes[0],
+ bar: routes[1],
+ baz: routes[2]
+ };
+ }
+
+ suite('<app-route>', function () {
+ var route;
+
+ setup(function() {
+ route = fixture('BasicRoute');
+
+ // This works around a bug in `dom-template` that is somehow
+ // exaserbated by the `app-route` implementation. A reduced test case
+ // is hard to come by. Track polymerelements/test-fixture#31 and remove
+ // this when that has been resolved:
+ var tmpl = document.querySelector('#ChainedRoutes').fixtureTemplates[0];
+ tmpl._parentProps = {};
+ });
+
+ test('it parses a path', function() {
+ route.route = {
+ prefix: '',
+ path: '/user/papyrus/details',
+ __queryParams: {}
+ }
+ expect(route.tail.prefix).to.be.equal('/user/papyrus');
+ expect(route.tail.path).to.be.equal('/details');
+ expect(route.data.username).to.be.equal('papyrus');
+ });
+
+ test('it bidirectionally maps changes between tail and route', function() {
+ route.route = {
+ prefix: '',
+ path: '/user/papyrus/details',
+ __queryParams: {}
+ };
+
+ route.set('tail.path', '/messages');
+ expect(route.route.path).to.be.deep.equal('/user/papyrus/messages');
+ route.set('route.path', '/user/toriel');
+ expect(route.tail).to.be.deep.equal({
+ prefix: '/user/toriel',
+ path: '',
+ __queryParams: {}
+ });
+ });
+
+ test('it creates data as described by pattern', function() {
+ route.route = {
+ prefix: '',
+ path: '/user/sans'
+ };
+
+ expect(route.data).to.be.deep.equal({username: 'sans'});
+ expect(route.active).to.be.equal(true);
+
+ route.pattern = '/user/:username/likes/:count';
+
+ // At the moment, we don't reset data when we no longer match.
+ expect(route.data).to.be.deep.equal({username: 'sans'});
+ expect(route.active).to.be.equal(false);
+
+ route.set('route.path', "/does/not/match");
+
+ expect(route.data).to.be.deep.equal({username: 'sans'});
+ expect(route.active).to.be.equal(false);
+
+ route.set('route.path', '/user/undyne/likes/20');
+ expect(route.data).to.be.deep.equal({username: 'undyne', count: '20'});
+ expect(route.active).to.be.equal(true);
+ });
+
+ test('changing data changes the path', function() {
+ route.route = {
+ prefix: '',
+ path: '/user/asgore'
+ };
+
+ expect(route.data).to.be.deep.equal({username: 'asgore'});
+ route.data = {username: 'toriel'};
+ expect(route.route.path).to.be.equal('/user/toriel');
+ });
+
+ suite('propagating data', function() {
+ test('data is empty if no routes in the tree have matched', function() {
+ var routes = fixtureChainedRoutes({ path: '' });
+
+ expect(routes.foo.data).to.be.eql({});
+ expect(routes.bar.data).to.be.eql({});
+ expect(routes.baz.data).to.be.eql({});
+ });
+
+ test('limits propagation to last matched route', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123' });
+
+ expect(routes.foo.data).to.be.eql({ foo: '123' });
+ expect(routes.bar.data).to.be.eql({});
+ expect(routes.baz.data).to.be.eql({});
+ });
+
+ test('propagates data to matching chained routes', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
+
+ expect(routes.foo.data).to.be.eql({ foo: '123' });
+ expect(routes.bar.data).to.be.eql({ bar: 'abc' });
+ expect(routes.baz.data).to.be.eql({});
+ });
+
+ test('chained route state is untouched when deactivated', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
+
+ routes.foo.set('route.path', '/foo/321/baz/zyx');
+
+ expect(routes.foo.data).to.be.eql({ foo: '321' });
+ expect(routes.bar.data).to.be.eql({ bar: 'abc' });
+ expect(routes.baz.data).to.be.eql({ baz: 'zyx' });
+ });
+
+ suite('updating the global path', function() {
+ test('happens when data changes if the route is active', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
+
+ expect(routes.bar.active).to.be.eql(true);
+ routes.bar.set('data.bar', 'cba');
+ expect(routes.foo.route.path).to.be.eql('/foo/123/bar/cba');
+ });
+
+ test('ignores changes when the route is inactive', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
+
+ expect(routes.baz.active).to.be.eql(false);
+ routes.baz.set('data.baz', 'cba');
+ expect(routes.foo.route.path).to.be.eql('/foo/123/bar/abc');
+ });
+
+ test('ignores changes after a route deactives', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc' });
+
+ routes.foo.set('route.path', '/foo/123/baz/zyx');
+
+ expect(routes.bar.active).to.be.eql(false);
+ expect(routes.baz.active).to.be.eql(true);
+ routes.bar.set('data.bar', 'cba');
+ expect(routes.foo.route.path).to.be.eql('/foo/123/baz/zyx');
+ });
+ });
+ });
+
+ suite('propagating query params', function() {
+ test('query params are empty if no routes match', function() {
+ var routes = fixtureChainedRoutes({ path: '', __queryParams: {
+ qux: 'zot'
+ }});
+ expect(routes.foo.queryParams).to.be.eql({});
+ expect(routes.bar.queryParams).to.be.eql({});
+ expect(routes.baz.queryParams).to.be.eql({});
+ });
+
+ test('updates query params for all matched routes', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
+ qux: 'zot'
+ }});
+ expect(routes.foo.queryParams).to.be.eql({ qux: 'zot' });
+ expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' });
+ expect(routes.baz.queryParams).to.be.eql({});
+ });
+
+ test('retains query params after routes deactivate', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
+ qux: 'zot'
+ }});
+ routes.foo.set('route.path', '/foo/123/baz/xyz')
+ routes.foo.set('queryParams', {
+ qux: 'quux'
+ });
+ expect(routes.foo.queryParams).to.be.eql({ qux: 'quux' });
+ expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' });
+ expect(routes.baz.queryParams).to.be.eql({ qux: 'quux' });
+ });
+
+ suite('updating global query params', function() {
+ test('happens when query params change on active routes', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
+ qux: 'zot'
+ }});
+
+ routes.bar.set('queryParams', { qux: 'quux' });
+
+ expect(routes.foo.queryParams).to.be.eql({ qux: 'quux' });
+ expect(routes.bar.queryParams).to.be.eql({ qux: 'quux' });
+ expect(routes.baz.queryParams).to.be.eql({});
+ });
+
+ test('updates are ignored for routes that are inactive', function() {
+ var routes = fixtureChainedRoutes({ path: '/foo/123/bar/abc', __queryParams: {
+ qux: 'zot'
+ }});
+
+ routes.baz.set('queryParams', { qux: 'quux' });
+
+ expect(routes.foo.queryParams).to.be.eql({ qux: 'zot' });
+ expect(routes.bar.queryParams).to.be.eql({ qux: 'zot' });
+ expect(routes.baz.queryParams).to.be.eql({ qux: 'quux' });
+ });
+
+ test('doesn\'t generate excess query-params-changed events', function() {
+ var routes = fixtureChainedRoutes({});
+ var appRoutes = [routes.foo, routes.bar, routes.baz];
+ var numChanges = 0;
+ for (var i = 0; i < appRoutes.length; i++) {
+ appRoutes[i].addEventListener('query-params-changed', function() {
+ numChanges++;
+ });
+ }
+
+ // Messing with paths but not query params shouldn't generate any
+ // change events.
+ expect(numChanges).to.be.equal(0);
+ routes.foo.set('route.path', '/foo/123/bar/456');
+ expect(numChanges).to.be.equal(0);
+ routes.foo.set('route.path', '/foo/456/baz/789');
+ expect(numChanges).to.be.equal(0);
+
+ // Changing queryParams here should update foo and baz
+ routes.foo.set('route.__queryParams', {key: 'value'});
+ expect(numChanges).to.be.equal(2);
+ // Then this should update bar
+ routes.foo.set('route.path', '/foo/123/bar/456');
+ expect(numChanges).to.be.equal(3);
+
+ // Changing back to baz shouldn't generate a change event.
+ routes.foo.set('route.path', '/foo/456/baz/789');
+ expect(numChanges).to.be.equal(3);
+
+ routes.foo.set('route.__queryParams', {});
+ expect(numChanges).to.be.equal(5);
+ routes.foo.set('route.path', '/foo/123/bar/456');
+ expect(numChanges).to.be.equal(6);
+
+ });
+ });
+ });
+
+ suite('handles reentrent changes to its properties', function() {
+ var initialUrl;
+ setup(function() {
+ initialUrl = window.location.href;
+ });
+
+ teardown(function() {
+ window.history.replaceState({}, '', initialUrl);
+ });
+
+ test('changing path in response to path changing', function() {
+ var r = fixture('Redirection');
+ r.addEventListener('route-changed', function() {
+ r.set('route.path', '/bar/baz');
+ });
+ r.set('route.path', '/foo');
+ expect(window.location.pathname).to.be.equal('/bar/baz');
+ expect(r.data).to.be.deep.equal({page: 'bar'});
+ expect(r.route.path).to.be.equal('/bar/baz');
+ expect(r.tail.path).to.be.equal('/baz');
+ });
+
+ test('changing data wholesale in response to path changing', function() {
+ var r = fixture('Redirection');
+ r.set('data.page', 'bar');
+ r.addEventListener('route-changed', function(e) {
+ if (e.detail.path === 'route.path' && r.route.path === '/foo/baz') {
+ r.data = {page: 'bar'};
+ }
+ });
+ r.set('route.path', '/foo/baz');
+ expect(window.location.pathname).to.be.equal('/bar');
+ expect(r.data).to.be.deep.equal({page: 'bar'});
+ expect(r.route.path).to.be.equal('/bar');
+ expect(r.tail.path).to.be.equal('');
+ });
+
+ test('changing a data piece in response to path changing', function() {
+ var r = fixture('Redirection');
+ r.set('data.page', 'bar');
+ r.addEventListener('route-changed', function(e) {
+ r.set('data.page', 'bar');
+ });
+ r.set('route.path', '/foo/baz');
+ expect(window.location.pathname).to.be.equal('/bar');
+ expect(r.data).to.be.deep.equal({page: 'bar'});
+ expect(r.route.path).to.be.equal('/bar');
+ expect(r.tail.path).to.be.equal('');
+ });
+
+ test('changing the tail in response to path changing', function() {
+ var r = fixture('Redirection');
+ r.addEventListener('route-changed', function() {
+ r.set('tail.path', '/bar');
+ });
+ r.set('route.path', '/foo');
+ expect(window.location.pathname).to.be.equal('/foo/bar');
+ expect(r.data).to.be.deep.equal({page: 'foo'});
+ expect(r.route.path).to.be.equal('/foo/bar');
+ expect(r.tail.path).to.be.equal('/bar');
+
+ r.set('route.path', '/foo/baz');
+ expect(window.location.pathname).to.be.equal('/foo/bar');
+ expect(r.data).to.be.deep.equal({page: 'foo'});
+ expect(r.route.path).to.be.equal('/foo/bar');
+ expect(r.tail.path).to.be.equal('/bar');
+ });
+
+ test('changing the path in response to data changing', function() {
+ var r = fixture('Redirection');
+ r.addEventListener('data-changed', function() {
+ r.set('route.path', '/bar');
+ });
+ r.set('data', {page: 'foo'});
+ expect(window.location.pathname).to.be.equal('/bar');
+ expect(r.data).to.be.deep.equal({page: 'bar'});
+ expect(r.route.path).to.be.equal('/bar');
+ expect(r.tail.path).to.be.equal('');
+ });
+
+ test('changing data in response to data changing', function() {
+ var r = fixture('Redirection');
+ r.addEventListener('data-changed', function() {
+ r.set('data.page', 'bar');
+ });
+ r.set('data', {page: 'foo'});
+ expect(window.location.pathname).to.be.equal('/bar');
+ expect(r.data).to.be.deep.equal({page: 'bar'});
+ expect(r.route.path).to.be.equal('/bar');
+ expect(r.tail.path).to.be.equal('');
+ });
+
+ test('changing the data object wholesale in response to data changing', function() {
+ var r = fixture('Redirection');
+ r.addEventListener('data-changed', function() {
+ if (r.data.page == 'foo') {
+ r.set('data', {page: 'bar'});
+ }
+ });
+ r.set('data', {page: 'foo'});
+ expect(window.location.pathname).to.be.equal('/bar');
+ expect(r.data).to.be.deep.equal({page: 'bar'});
+ expect(r.route.path).to.be.equal('/bar');
+ expect(r.tail.path).to.be.equal('');
+ });
+
+ test('changing the tail in response to data changing', function() {
+ var r = fixture('Redirection');
+ r.addEventListener('data-changed', function() {
+ r.set('tail.path', '/bar');
+ });
+ r.set('data', {page: 'foo'});
+ expect(window.location.pathname).to.be.equal('/foo/bar');
+ expect(r.data).to.be.deep.equal({page: 'foo'});
+ expect(r.route.path).to.be.equal('/foo/bar');
+ expect(r.tail.path).to.be.equal('/bar');
+ });
+
+ test('changing the path in response to tail changing', function() {
+ var r = fixture('Redirection');
+ r.set('route.path', '/foo/');
+ r.addEventListener('tail-changed', function() {
+ r.set('route.path', '/baz' + r.tail.path);
+ });
+ r.set('tail.path', '/bar');
+ expect(window.location.pathname).to.be.equal('/baz/bar');
+ expect(r.data).to.be.deep.equal({page: 'baz'});
+ expect(r.route.path).to.be.equal('/baz/bar');
+ expect(r.tail.path).to.be.equal('/bar');
+ });
+
+ test('changing the data in response to tail changing', function() {
+ var r = fixture('Redirection');
+ r.set('route.path', '/foo/');
+ r.addEventListener('tail-changed', function() {
+ r.set('data.page', 'baz');
+ });
+ r.set('tail.path', '/bar');
+ expect(window.location.pathname).to.be.equal('/baz');
+ expect(r.data).to.be.deep.equal({page: 'baz'});
+ expect(r.route.path).to.be.equal('/baz');
+ expect(r.tail.path).to.be.equal('');
+ });
+
+ test('changing the data object wholesale in response to tail changing', function() {
+ var r = fixture('Redirection');
+ r.set('route.path', '/foo/');
+ r.addEventListener('tail-changed', function() {
+ r.set('data', {page: 'baz'});
+ });
+ r.set('tail.path', '/bar');
+ expect(window.location.pathname).to.be.equal('/baz');
+ expect(r.data).to.be.deep.equal({page: 'baz'});
+ expect(r.route.path).to.be.equal('/baz');
+ expect(r.tail.path).to.be.equal('');
+ });
+
+ test('changing the tail in response to tail changing', function() {
+ var r = fixture('Redirection');
+ r.set('route.path', '/foo/');
+ r.addEventListener('tail-changed', function() {
+ r.set('tail.path', '/baz');
+ });
+ r.set('tail.path', '/bar');
+ expect(window.location.pathname).to.be.equal('/foo/baz');
+ expect(r.data).to.be.deep.equal({page: 'foo'});
+ expect(r.route.path).to.be.equal('/foo/baz');
+ expect(r.tail.path).to.be.equal('/baz');
+ });
+ });
+ });
+</script>
+</body>
diff --git a/catapult/third_party/polymer/components/app-route/test/index.html b/catapult/third_party/polymer/components/app-route/test/index.html
new file mode 100644
index 00000000..f82b1f30
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/index.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'app-route-converter.html',
+ 'app-route.html',
+ 'app-location.html',
+ 'test-observer-app.html',
+ 'test-app-example-1.html',
+
+ ]);
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/app-route/test/observer-tester.html b/catapult/third_party/polymer/components/app-route/test/observer-tester.html
new file mode 100644
index 00000000..95c4b22a
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/observer-tester.html
@@ -0,0 +1,47 @@
+<!--
+@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
+-->
+ <link rel='import' href='../app-route.html'>
+ <link rel='import' href='../app-location.html'>
+
+
+
+ <dom-module id="observer-tester">
+ <template>
+ <app-location route="{{route}}"></app-location>
+ <app-route
+ route="{{route}}"
+ pattern="/report/:id"
+ data="{{data}}"
+ active="{{active}}"></app-route>
+ </template>
+ <script>
+ Polymer({
+ is: 'observer-tester',
+ properties: {
+ route: {
+ type: Object,
+ notify:true
+ },
+ data: {
+ type: Object,
+ notify: true
+ },
+ active: {
+ type: Boolean,
+ value: false,
+ observer: 'checkActive'
+ }
+ },
+ checkActive: function(active) {
+ var x = 1;
+ }
+ });
+ </script>
+ </dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/test/redirection.html b/catapult/third_party/polymer/components/app-route/test/redirection.html
new file mode 100644
index 00000000..020c9a82
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/redirection.html
@@ -0,0 +1,44 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../app-location.html">
+<link rel="import" href="../app-route.html">
+
+<!--
+ There are three relevant factors to route.path, and when any one of them
+ changes we want to support synchronously updating any of the others.
+-->
+
+<dom-module id='redirect-app-route'>
+ <template>
+ <app-location route='{{route}}'>
+ </app-location>
+ <app-route route='{{route}}' pattern="/:page" data="{{data}}" tail="{{tail}}">
+ </app-route>
+ </template>
+ <script>
+ Polymer({
+ is: 'redirect-app-route',
+ properties: {
+ route: {
+ notify: true
+ },
+ data: {
+ type: Object,
+ notify: true
+ },
+ tail: {
+ notify: true
+ },
+ },
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/app-route/test/test-app-example-1.html b/catapult/third_party/polymer/components/app-route/test/test-app-example-1.html
new file mode 100644
index 00000000..29b8104a
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/test-app-example-1.html
@@ -0,0 +1,137 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+ <title>app-route</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="./app-example-1.html">
+</head>
+<body>
+ <test-fixture id="ExampleApp">
+ <template>
+ <app-example-1></app-example-1>
+ </template>
+ </test-fixture>
+<script>
+ 'use strict';
+
+ function setLocation(url) {
+ window.history.pushState({}, '', url);
+ Polymer.Base.fire('location-changed', {}, { node: window });
+ }
+
+ suite('<test-app-example-1>', function () {
+ var originalLocation;
+ var exampleApp;
+
+ setup(function() {
+ originalLocation = window.location.href;
+ exampleApp = fixture('ExampleApp');
+ });
+
+ teardown(function() {
+ window.history.replaceState({}, '', originalLocation);
+ });
+
+ test('runs through basic usage', function() {
+ // Navigate to /lol
+ setLocation('/lol');
+
+ expect(exampleApp.data).to.be.deep.eq({
+ page: 'lol'
+ });
+ expect(exampleApp.userData).to.be.deep.eq({
+ });
+ expect(exampleApp.route).to.be.deep.eq({
+ prefix: '',
+ path: '/lol',
+ __queryParams: {}
+ });
+ expect(exampleApp.userRoute).to.be.deep.eq({
+ prefix: null,
+ path: null,
+ __queryParams: {}
+ });
+ expect(window.location.pathname).to.be.equal('/lol');
+
+ // Navigate to /user
+ setLocation('/user');
+ expect(exampleApp.data).to.be.deep.eq({
+ page: 'user'
+ });
+
+ // We should have redirected to /user/view because of a redirect in
+ // the example app code.
+ expect(exampleApp.route).to.be.deep.eq({
+ prefix: '',
+ path: '/user/view',
+ __queryParams: {}
+ });
+ expect(exampleApp.userRoute).to.be.deep.eq({
+ prefix: '/user',
+ path: '/view',
+ __queryParams: {}
+ });
+ expect(window.location.pathname).to.be.equal('/user/view');
+
+ // Navigate to /user/details
+ setLocation('/user/details');
+ expect(exampleApp.data).to.be.deep.eq({
+ page: 'user'
+ });
+ expect(exampleApp.userData).to.be.deep.eq({
+ page: 'details'
+ });
+ expect(exampleApp.route).to.be.deep.eq({
+ prefix: '',
+ path: '/user/details',
+ __queryParams: {}
+ });
+ expect(exampleApp.userRoute).to.be.deep.eq({
+ prefix: '/user',
+ path: '/details',
+ __queryParams: {}
+ });
+ expect(window.location.pathname).to.be.equal('/user/details');
+
+ exampleApp.set('data.page', 'redirectToUser');
+ expect(window.location.pathname).to.be.equal('/user/view');
+
+ // This triggers two redirects in a row!
+ setLocation('/redirectToUser');
+ expect(window.location.pathname).to.be.equal('/user/view');
+
+ // Data binding changes to a different user subpage.
+ exampleApp.set('userData.page', 'profile');
+ expect(window.location.pathname).to.be.eq('/user/profile');
+
+ // Data binding changes to the aunt of the current page.
+ exampleApp.set('data.page', 'feed');
+ expect(window.location.pathname).to.be.eq('/feed');
+
+ setLocation('/user/etc');
+ exampleApp.set('userData.page', 'details');
+ expect(window.location.pathname).to.be.eq('/user/details')
+
+ expect(window.location.search).to.be.eq('');
+ exampleApp.set('userQueryParams.foo', 'bar');
+ expect(window.location.search).to.be.eq('?foo=bar');
+
+ exampleApp.userQueryParams = {bar: 'baz'};
+ expect(window.location.search).to.be.eq('?bar=baz');
+ });
+ });
+</script>
+</body>
diff --git a/catapult/third_party/polymer/components/app-route/test/test-observer-app.html b/catapult/third_party/polymer/components/app-route/test/test-observer-app.html
new file mode 100644
index 00000000..d2bcf070
--- /dev/null
+++ b/catapult/third_party/polymer/components/app-route/test/test-observer-app.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<!--
+@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
+-->
+
+<html>
+<head>
+ <title>app-route 0bserver Test</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="./observer-tester.html">
+
+</head>
+<body>
+
+ <test-fixture id="observer_app">
+ <template>
+ <observer-tester id="testel"></observer-tester>
+ </template>
+ </test-fixture>
+ <script>
+ 'use strict';
+ function setLocation(url) {
+ window.history.pushState({}, '', url);
+ Polymer.Base.fire('location-changed', {}, { node: window });
+ }
+
+
+ suite('observe app-route active changes', function(){
+ var originalLocation;
+ var sandbox, el;
+ setup(function(){
+ originalLocation = window.location.href;
+ sandbox = sinon.sandbox.create();
+ el = fixture('observer_app');
+ });
+ teardown(function(){
+ sandbox.restore();
+ window.history.replaceState({}, '', originalLocation);
+ });
+
+ test('observer should fire when route selected', function(){
+ sandbox.spy(el,'checkActive');
+ setLocation('/report/1000');
+ expect(el.checkActive).to.have.been.called.once;
+ expect(el.checkActive).to.have.been.calledWith(true);
+ });
+ test('observer should fire when route deselected',function(){
+ setLocation('/report/1000');
+ sandbox.spy(el,'checkActive');
+ setLocation('/menu');
+ expect(el.checkActive).to.have.been.called.once;
+ expect(el.checkActive).to.have.been.calledWith(false);
+ });
+ });
+ </script>
+</body>
+
diff --git a/catapult/third_party/polymer/components/core-tooltip/README.md b/catapult/third_party/polymer/components/core-tooltip/README.md
new file mode 100644
index 00000000..22d55c10
--- /dev/null
+++ b/catapult/third_party/polymer/components/core-tooltip/README.md
@@ -0,0 +1,4 @@
+core-tooltip
+============
+
+See the [component page](http://polymer-project.org/docs/elements/core-elements.html#core-tooltip) for more information.
diff --git a/catapult/third_party/polymer/components/core-tooltip/bower.json b/catapult/third_party/polymer/components/core-tooltip/bower.json
new file mode 100644
index 00000000..9f116444
--- /dev/null
+++ b/catapult/third_party/polymer/components/core-tooltip/bower.json
@@ -0,0 +1,13 @@
+{
+ "name": "core-tooltip",
+ "private": true,
+ "description": "Tooltip popup for content",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^0.5",
+ "core-focusable": "Polymer/core-focusable#^0.5",
+ "core-icon-button": "Polymer/core-icon-button#^0.5",
+ "paper-fab": "Polymer/paper-fab#^0.5",
+ "core-resizable": "Polymer/core-resizable#^0.5"
+ },
+ "version": "0.5.5"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/core-tooltip/core-tooltip.css b/catapult/third_party/polymer/components/core-tooltip/core-tooltip.css
new file mode 100644
index 00000000..aa24c39f
--- /dev/null
+++ b/catapult/third_party/polymer/components/core-tooltip/core-tooltip.css
@@ -0,0 +1,104 @@
+/* Copyright (c) 2014 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 */
+
+:host {
+ box-sizing: border-box;
+ position: relative;
+ display: inline-block;
+ outline: none;
+}
+
+:host(:hover:not([disabled])) .core-tooltip {
+ visibility: visible !important;
+}
+
+:host([focused]) .core-tooltip {
+ visibility: visible !important;
+}
+
+.core-tooltip:not(.show) {
+ visibility: hidden;
+}
+
+.core-tooltip {
+ position: absolute;
+ font-size: 10px;
+ font-weight: 500;
+ padding: 8px;
+ color: white;
+ background-color: rgba(0, 0, 0, 0.9);
+ box-sizing: border-box;
+ border-radius: 3px; /* TODO: not in spec. */
+ white-space: nowrap;
+ line-height: 6px;
+ z-index: 1002; /* TODO: this is brittle. */
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+:host([large]) .core-tooltip {
+ line-height: 14px;
+ font-size: 14px;
+ padding: 16px;
+}
+
+.core-tooltip.noarrow::after {
+ display: none;
+}
+
+.core-tooltip::after {
+ position: absolute;
+ border: solid transparent;
+ content: '';
+ height: 0;
+ width: 0;
+ border-width: 4px;
+}
+
+.top {
+ margin-bottom: 10px; /* TODO: not specified in spec */
+ bottom: 100%;
+}
+
+.right {
+ margin-left: 10px; /* TODO: not specified in spec */
+ left: 100%;
+}
+
+.bottom {
+ top: 100%;
+ margin-top: 10px; /* TODO: not specified in spec */
+}
+
+.left {
+ margin-right: 10px; /* TODO: not specified in spec */
+ right: 100%;
+}
+
+.core-tooltip.bottom::after {
+ bottom: 100%;
+ left: calc(50% - 4px);
+ border-bottom-color: rgba(0,0,0,0.8);
+}
+
+.core-tooltip.left::after {
+ left: 100%;
+ top: calc(50% - 4px);
+ border-left-color: rgba(0,0,0,0.8);
+}
+
+.core-tooltip.top::after {
+ top: 100%;
+ left: calc(50% - 4px);
+ border-top-color: rgba(0,0,0,0.8);
+}
+
+.core-tooltip.right::after {
+ right: 100%;
+ top: calc(50% - 4px);
+ border-right-color: rgba(0,0,0,0.8);
+}
diff --git a/catapult/third_party/polymer/components/core-tooltip/core-tooltip.html b/catapult/third_party/polymer/components/core-tooltip/core-tooltip.html
new file mode 100644
index 00000000..634be469
--- /dev/null
+++ b/catapult/third_party/polymer/components/core-tooltip/core-tooltip.html
@@ -0,0 +1,217 @@
+<!--
+Copyright (c) 2015 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
+-->
+
+<!--
+The `core-tooltip` element creates a hover tooltip centered for the content
+it contains. It can be positioned on the top|bottom|left|right of content using
+the `position` attribute.
+
+To include HTML in the tooltip, include the `tip` attribute on the relevant
+content.
+
+<b>Example</b>:
+
+ <core-tooltip label="I'm a tooltip">
+ <span>Hover over me.</span>
+ </core-tooltip>
+
+<b>Example</b> - positioning the tooltip to the right:
+
+ <core-tooltip label="I'm a tooltip to the right" position="right">
+ <core-icon-button icon="drawer"></core-icon-button>
+ </core-tooltip>
+
+<b>Example</b> - no arrow and showing by default:
+
+ <core-tooltip label="Tooltip with no arrow and always on" noarrow show>
+ <img src="image.jpg">
+ </core-tooltip>
+
+<b>Example</b> - disable the tooltip.
+
+ <core-tooltip label="Disabled label never shows" disabled>
+ ...
+ </core-tooltip>
+
+<b>Example</b> - rich tooltip using the `tip` attribute:
+
+ <core-tooltip>
+ <div>Example of a rich information tooltip</div>
+ <div tip>
+ <img src="profile.jpg">Foo <b>Bar</b> - <a href="#">@baz</a>
+ </div>
+ </core-tooltip>
+
+By default, the `tip` attribute specifies the HTML content for a rich tooltip.
+You can customize this attribute with the `tipAttribute` attribute:
+
+ <core-tooltip tipAttribute="htmltooltip">
+ <div>Example of a rich information tooltip</div>
+ <div htmltooltip>
+ ...
+ </div>
+ </core-tooltip>
+
+@group Polymer Core Elements
+@element core-tooltip
+@mixins Polymer.CoreFocusable https://github.com/polymer/core-focusable
+@mixins Polymer.CoreResizable https://github.com/polymer/core-resizable
+@homepage http://www.polymer-project.org/components/core-tooltip/index.html
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../core-focusable/core-focusable.html">
+<link rel="import" href="../core-resizable/core-resizable.html">
+
+<!-- TODO: would be nice to inherit from label to get .htmlFor, and .control,
+ but the latter is readonly. -->
+<!-- TODO: support off center arrows. -->
+<!-- TODO: detect mobile and apply the .large class, instead of manual
+ control. -->
+<!-- TODO: possibly reuse core-overlay. -->
+<polymer-element name="core-tooltip" attributes="noarrow position label show tipAttribute" role="tooltip" tabindex="0">
+<template>
+ <link rel="stylesheet" href="core-tooltip.css">
+
+ <div id="tooltip" hidden?="{{!hasTooltipContent}}"
+ class="core-tooltip {{position}} {{ {noarrow: noarrow, show: show && !disabled} | tokenList}}">
+ <content id="c" select="[{{tipAttribute}}]">{{label}}</content>
+ </div>
+
+ <content></content>
+
+</template>
+<script>
+(function() {
+
+ var proto = {
+
+ /**
+ * A simple string label for the tooltip to display. To display a rich
+ * HTML tooltip instead, omit `label` and include the `tip` attribute
+ * on a child node of `core-tooltip`.
+ *
+ * @attribute label
+ * @type string
+ * @default null
+ */
+ label: null,
+
+ eventDelegates: {
+ 'core-resize': 'positionChanged'
+ },
+
+ computed: {
+ // Indicates whether the tooltip has a set label propety or
+ // an element with the `tip` attribute.
+ hasTooltipContent: 'label || !!tipElement'
+ },
+
+ publish: {
+ /**
+ * Forces the tooltip to display. If `disabled` is set, this property is ignored.
+ *
+ * @attribute show
+ * @type boolean
+ * @default false
+ */
+ show: {value: false, reflect: true},
+
+ /**
+ * Positions the tooltip to the top, right, bottom, left of its content.
+ *
+ * @attribute position
+ * @type string
+ * @default 'bottom'
+ */
+ position: {value: 'bottom', reflect: true},
+
+ /**
+ * If true, the tooltip an arrow pointing towards the content.
+ *
+ * @attribute noarrow
+ * @type boolean
+ * @default false
+ */
+ noarrow: {value: false, reflect: true}
+ },
+
+ /**
+ * Customizes the attribute used to specify which content
+ * is the rich HTML tooltip.
+ *
+ * @attribute tipAttribute
+ * @type string
+ * @default 'tip'
+ */
+ tipAttribute: 'tip',
+
+ attached: function() {
+ this.updatedChildren();
+ this.resizableAttachedHandler();
+ },
+
+ detached: function() {
+ this.resizableDetachedHandler();
+ },
+
+ updatedChildren: function () {
+ this.tipElement = null;
+
+ for (var i = 0, el; el = this.$.c.getDistributedNodes()[i]; ++i) {
+ if (el.hasAttribute && el.hasAttribute(this.tipAttribute)) {
+ this.tipElement = el;
+ break;
+ }
+ }
+
+ // Job ensures we're not double calling setPosition() on DOM attach.
+ this.job('positionJob', this.setPosition);
+
+ // Monitor children to re-position tooltip when light dom changes.
+ this.onMutation(this, this.updatedChildren);
+ },
+
+ labelChanged: function(oldVal, newVal) {
+ this.job('positionJob', this.setPosition);
+ },
+
+ positionChanged: function(oldVal, newVal) {
+ this.job('positionJob', this.setPosition);
+ },
+
+ setPosition: function() {
+ var controlWidth = this.clientWidth;
+ var controlHeight = this.clientHeight;
+ var toolTipWidth = this.$.tooltip.clientWidth;
+ var toolTipHeight = this.$.tooltip.clientHeight;
+
+ switch (this.position) {
+ case 'top':
+ case 'bottom':
+ this.$.tooltip.style.left = (controlWidth - toolTipWidth) / 2 + 'px';
+ this.$.tooltip.style.top = null;
+ break;
+ case 'left':
+ case 'right':
+ this.$.tooltip.style.left = null;
+ this.$.tooltip.style.top = (controlHeight - toolTipHeight) / 2 + 'px';
+ break;
+ }
+ }
+
+ };
+
+ Polymer.mixin2(proto, Polymer.CoreFocusable);
+ Polymer.mixin(proto, Polymer.CoreResizable);
+ Polymer(proto);
+ })();
+
+</script>
+</polymer-element>
diff --git a/catapult/third_party/polymer/components/core-tooltip/demo.html b/catapult/third_party/polymer/components/core-tooltip/demo.html
new file mode 100644
index 00000000..7ea6aea9
--- /dev/null
+++ b/catapult/third_party/polymer/components/core-tooltip/demo.html
@@ -0,0 +1,211 @@
+<!--
+ @license
+ Copyright (c) 2015 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
+-->
+<!doctype html>
+<html lang="en">
+<head>
+
+ <meta charset="utf-8">
+ <title>Core Tooltip</title>
+
+ <script src="../webcomponentsjs/webcomponents.js"></script>
+
+ <link rel="import" href="../core-icon-button/core-icon-button.html">
+ <link rel="import" href="../paper-fab/paper-fab.html">
+ <link rel="import" href="core-tooltip.html">
+
+ <style>
+ body {
+ font-family: "Open Sans", sans-serif;
+ font-weight: 300;
+ padding: 24px;
+ }
+
+ .example {
+ margin: 35px 15px;
+ }
+
+ .example > * {
+ margin: 0 15px;
+ }
+
+ .fakebutton {
+ box-shadow: 0 0 3px #aaa inset;
+ border-radius: 3px;
+ padding: 7px 5px;
+ }
+ .fakebutton:hover {
+ background-color: white;
+ }
+
+ img {
+ width: 400px;
+ height: 150px;
+ object-fit: cover;
+ }
+
+ img.large {
+ border: 15px solid white;
+ box-sizing: border-box;
+ }
+
+ .profile {
+ width: 60px;
+ height: auto;
+ border-radius: 50%;
+ vertical-align: middle;
+ }
+
+ a {
+ color: currentcolor;
+ text-decoration: none;
+ }
+
+ .rich {
+ background: hotpink;
+ color: white;
+ padding:20px;
+ border-radius: 5px;
+ }
+
+ </style>
+
+ <style shim-shadowdom>
+ core-tooltip.fancy::shadow .core-tooltip {
+ opacity: 0;
+ -webkit-transition: all 300ms cubic-bezier(0,1.92,.99,1.07);
+ transition: all 300ms cubic-bezier(0,1.92,.99,1.07);
+ -webkit-transform: translate3d(0, -10px, 0);
+ transform: translate3d(0, -10px, 0);
+ }
+
+ core-tooltip.fancy:hover::shadow .core-tooltip,
+ core-tooltip.fancy:focus::shadow .core-tooltip {
+ opacity: 1;
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+ </style>
+
+</head>
+<body unresolved>
+
+ <article>
+
+ <button>Toggle all</button>
+
+ <section class="small" layout horizontal center-center>
+
+ <div class="example">
+
+ <core-tooltip label='position="left"' position="left">
+ <core-icon-button icon="drawer"></core-icon-button>
+ </core-tooltip>
+
+ <core-tooltip label='position="top"' position="top">
+ <core-icon-button icon="drawer"></core-icon-button>
+ </core-tooltip>
+
+ <core-tooltip label='position="bottom"' position="bottom">
+ <core-icon-button icon="drawer"></core-icon-button>
+ </core-tooltip>
+
+ <core-tooltip label='position="right"' position="right">
+ <core-icon-button icon="drawer"></core-icon-button>
+ </core-tooltip>
+
+ </div>
+
+ <div class="example">
+
+ <core-tooltip label="Fancy effect" class="fancy">
+ <paper-fab icon="add"></paper-fab>
+ </core-tooltip>
+
+ </div>
+
+ </section>
+
+ <section layout horizontal center-center>
+
+ <div class="example">
+
+ <core-tooltip>
+ <div class="rich">Rich tooltip with HTML</div>
+ <div tip>
+ <img src="https://pbs.twimg.com/profile_images/378800000548263523/c110b0a4c3e3e4d3dcce1d2020f3eded.jpeg
+ " class="profile" style="width: 40px;margin-right: 8px;">Eric <b>Bidelman</b> - <a href="#">@ebidel</a></div>
+ </core-tooltip>
+
+ </div>
+
+ <div class="example">
+
+ <core-tooltip label="<core-tooltip large>" large>
+ Larger tooltips for mobile
+ </core-tooltip>
+
+ </div>
+
+ <div class="example">
+
+ <core-tooltip label="disabled" disabled>
+ Disabled Tooltip
+ </core-tooltip>
+
+ </div>
+
+ </section>
+
+ <section layout horizontal center-center>
+
+ <div class="example">
+
+ <core-tooltip label="Tooltip with no arrow and always on: <core-tooltip noarrow show>" position="bottom" noarrow show>
+ <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/building.jpg" class="large">
+ </core-tooltip>
+
+ </div>
+
+ </section>
+
+ <section layout horizontal center-center>
+
+ <div class="example">
+
+ <core-tooltip id="dynamic" show>
+ Tooltips are only shown (on hover) when a label is set<br> or a html rich snippet is given. &rarr;
+ </core-tooltip>
+ <button id="fillbutton">Fill tooltip</button>
+
+ </div>
+
+ </section>
+
+ </article>
+
+<script>
+ document.querySelector('button').addEventListener('click', function(e) {
+ var tooltips = document.querySelectorAll('core-tooltip');
+ Array.prototype.forEach.call(tooltips, function(tooltip) {
+ tooltip.show = !tooltip.show;
+ });
+ });
+
+ document.querySelector('#fillbutton').addEventListener('click', function(e) {
+ e.stopPropagation();
+
+ var el = document.querySelector('#dynamic');
+ el.insertAdjacentHTML('beforeend', '<div tip><b>See</b>. Told ya so!</div>');
+
+ });
+</script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/core-tooltip/index.html b/catapult/third_party/polymer/components/core-tooltip/index.html
new file mode 100644
index 00000000..cdf58085
--- /dev/null
+++ b/catapult/third_party/polymer/components/core-tooltip/index.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<!--
+Copyright (c) 2015 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
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <script src="../webcomponentsjs/webcomponents.js"></script>
+ <link rel="import" href="../core-component-page/core-component-page.html">
+
+</head>
+<body unresolved>
+
+ <core-component-page></core-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/core-tooltip/metadata.html b/catapult/third_party/polymer/components/core-tooltip/metadata.html
new file mode 100644
index 00000000..ffcf73ac
--- /dev/null
+++ b/catapult/third_party/polymer/components/core-tooltip/metadata.html
@@ -0,0 +1,20 @@
+<!--
+ @license
+ Copyright (c) 2015 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
+-->
+<x-meta id="core-tooltip" label="Tooltip" group="Core">
+ <template>
+ <core-tooltip label="I'm a tooltip">
+ <span>Hover over me.</span>
+ </core-tooltip>
+ </template>
+ <template id="imports">
+ <link rel="import" href="core-tooltip.html">
+ </template>
+</x-meta>
+
diff --git a/catapult/third_party/polymer/components/font-roboto/.bower.json b/catapult/third_party/polymer/components/font-roboto/.bower.json
new file mode 100644
index 00000000..ac86d013
--- /dev/null
+++ b/catapult/third_party/polymer/components/font-roboto/.bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "font-roboto",
+ "version": "1.0.3",
+ "description": "An HTML import for Roboto",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "font",
+ "roboto"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/font-roboto.git"
+ },
+ "main": "roboto.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/font-roboto/",
+ "ignore": [
+ "/.*"
+ ],
+ "_release": "1.0.3",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.3",
+ "commit": "6b16584ff654fea05d6bf3e812fb8b225202663f"
+ },
+ "_source": "https://github.com/PolymerElements/font-roboto.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/font-roboto"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/font-roboto/CONTRIBUTING.md b/catapult/third_party/polymer/components/font-roboto/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/font-roboto/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/font-roboto/README.md b/catapult/third_party/polymer/components/font-roboto/README.md
new file mode 100644
index 00000000..61c6394c
--- /dev/null
+++ b/catapult/third_party/polymer/components/font-roboto/README.md
@@ -0,0 +1 @@
+# font-roboto
diff --git a/catapult/third_party/polymer/components/font-roboto/bower.json b/catapult/third_party/polymer/components/font-roboto/bower.json
new file mode 100644
index 00000000..7099d7ff
--- /dev/null
+++ b/catapult/third_party/polymer/components/font-roboto/bower.json
@@ -0,0 +1,22 @@
+{
+ "name": "font-roboto",
+ "version": "1.0.3",
+ "description": "An HTML import for Roboto",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "font",
+ "roboto"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/font-roboto.git"
+ },
+ "main": "roboto.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/font-roboto/",
+ "ignore": [
+ "/.*"
+ ]
+}
diff --git a/catapult/third_party/polymer/components/font-roboto/package.json b/catapult/third_party/polymer/components/font-roboto/package.json
new file mode 100644
index 00000000..f02f2fb7
--- /dev/null
+++ b/catapult/third_party/polymer/components/font-roboto/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@polymerelements/font-roboto",
+ "version": "1.0.1",
+ "description": "An HTML import for Roboto",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "font",
+ "roboto"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/font-roboto.git"
+ },
+ "main": "roboto.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/font-roboto/"
+}
diff --git a/catapult/third_party/polymer/components/font-roboto/roboto.html b/catapult/third_party/polymer/components/font-roboto/roboto.html
new file mode 100644
index 00000000..417ee18c
--- /dev/null
+++ b/catapult/third_party/polymer/components/font-roboto/roboto.html
@@ -0,0 +1,10 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto+Mono:400,700|Roboto:400,300,300italic,400italic,500,500italic,700,700italic" crossorigin="anonymous">
diff --git a/catapult/third_party/polymer/components/google-apis/.bower.json b/catapult/third_party/polymer/components/google-apis/.bower.json
new file mode 100644
index 00000000..9ce28351
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/.bower.json
@@ -0,0 +1,56 @@
+{
+ "name": "google-apis",
+ "version": "2.0.0",
+ "description": "Web components to load Google API libraries",
+ "homepage": "https://elements.polymer-project.org/elements/google-apis?active=google-js-api",
+ "main": "google-apis.html",
+ "authors": [
+ "Scott Miles <sjmiles@google.com>",
+ "Eric Bidelman <ebidel@gmail.com>"
+ ],
+ "license": "Apache-2.0",
+ "ignore": [
+ "/.*",
+ "/test/"
+ ],
+ "keywords": [
+ "web-component",
+ "web-components",
+ "polymer",
+ "google",
+ "apis"
+ ],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2",
+ "iron-jsonp-library": "PolymerElements/iron-jsonp-library#1 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0",
+ "iron-jsonp-library": "PolymerElements/iron-jsonp-library#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0"
+ },
+ "resolutions": {
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ },
+ "_release": "2.0.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v2.0.0",
+ "commit": "519662dcc5357e088979b7cad502cd468ac67d9f"
+ },
+ "_source": "https://github.com/GoogleWebComponents/google-apis.git",
+ "_target": "1 - 2",
+ "_originalSource": "GoogleWebComponents/google-apis"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/google-apis/LICENSE b/catapult/third_party/polymer/components/google-apis/LICENSE
new file mode 100644
index 00000000..3dde5009
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2015 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
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT 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/catapult/third_party/polymer/components/google-apis/README.md b/catapult/third_party/polymer/components/google-apis/README.md
new file mode 100644
index 00000000..dd9268cd
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/README.md
@@ -0,0 +1,4 @@
+google-apis
+===========
+
+See https://elements.polymer-project.org/elements/google-apis
diff --git a/catapult/third_party/polymer/components/google-apis/bower.json b/catapult/third_party/polymer/components/google-apis/bower.json
new file mode 100644
index 00000000..295bb36a
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "google-apis",
+ "version": "2.0.0",
+ "description": "Web components to load Google API libraries",
+ "homepage": "https://elements.polymer-project.org/elements/google-apis?active=google-js-api",
+ "main": "google-apis.html",
+ "authors": [
+ "Scott Miles <sjmiles@google.com>",
+ "Eric Bidelman <ebidel@gmail.com>"
+ ],
+ "license": "Apache-2.0",
+ "ignore": [
+ "/.*",
+ "/test/"
+ ],
+ "keywords": [
+ "web-component",
+ "web-components",
+ "polymer",
+ "google",
+ "apis"
+ ],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2",
+ "iron-jsonp-library": "PolymerElements/iron-jsonp-library#1 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0",
+ "iron-jsonp-library": "PolymerElements/iron-jsonp-library#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0"
+ },
+ "resolutions": {
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/google-apis/demo/index.html b/catapult/third_party/polymer/components/google-apis/demo/index.html
new file mode 100644
index 00000000..0455cbb6
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/demo/index.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
+<html>
+<head>
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>google-apis Demo</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../google-apis.html">
+</head>
+<body>
+ <div id="messages"></div>
+
+ <dom-bind id="bind">
+ <template id="t" is="dom-bind">
+
+ <google-client-loader id="shortener"
+ name="urlshortener"
+ version="v1"
+ on-google-api-load="loadedShortener"></google-client-loader>
+ <google-js-api on-js-api-load="loaded"></google-js-api>
+ <google-plusone-api on-api-load="loaded"></google-plusone-api>
+ <google-realtime-api on-api-load="loaded"></google-realtime-api>
+ <google-maps-api on-api-load="loaded"></google-maps-api>
+ <google-youtube-api on-api-load="loaded"></google-youtube-api>
+ <google-legacy-loader on-api-load="loaded"></google-legacy-loader>
+
+ </template>
+ </dom-bind>
+ <script>
+ // polymer 1.x compatibility
+ t.loadedShortener = function(event) {
+ var request = event.target.api.url.get({
+ shortUrl: 'http://goo.gl/fbsS'
+ })
+ request.execute(function(resp) {
+ console.log(resp);
+ });
+ }
+
+ t.loaded = function(e) {
+ document.querySelector('#messages').innerHTML +=
+ e.target.localName + ' loaded' + '<br>';
+ console.log(e.target.localName + ' loaded', event.target.api);
+ }
+
+ // Polymer 2.0 compatibility
+ bind.loadedShortener = t.loadedShortener;
+ bind.loaded = t.loaded;
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/google-apis/google-apis.html b/catapult/third_party/polymer/components/google-apis/google-apis.html
new file mode 100644
index 00000000..29f9883b
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-apis.html
@@ -0,0 +1,16 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<!-- Load all Google APIs, for backwards compatibility -->
+<link rel="import" href="google-client-loader.html">
+<link rel="import" href="google-legacy-loader.html">
+<link rel="import" href="google-maps-api.html">
+<link rel="import" href="google-plusone-api.html">
+<link rel="import" href="google-realtime-api.html">
+<link rel="import" href="google-youtube-api.html">
diff --git a/catapult/third_party/polymer/components/google-apis/google-client-loader.html b/catapult/third_party/polymer/components/google-apis/google-client-loader.html
new file mode 100644
index 00000000..0257a86b
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-client-loader.html
@@ -0,0 +1,232 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="google-js-api.html">
+
+<!--
+Element for loading a specific client Google API with the JavaScript client library.
+
+For loading `gapi.client` libraries
+
+##### Example
+
+ <google-client-loader id="shortener"
+ name="urlshortener"
+ version="v1"></google-client-loader>
+
+ <script>
+ var shortener = document.getElementById('shortener');
+ shortener.addEventListener('google-api-load', function(event) {
+ var request = shortener.api.url.get({
+ shortUrl: 'http://goo.gl/fbsS'
+ });
+ request.execute(function(resp) {
+ console.log(resp);
+ });
+ });
+ </script>
+
+@demo
+-->
+
+<script>
+ (function() {
+ 'use strict';
+
+ // Stores whether the API client is done loading.
+ var _clientLoaded = false;
+
+ // Loaders and loading statuses for all APIs, indexed by API name.
+ // This helps prevent multiple loading requests being fired at the same time
+ // by multiple google-api-loader elements.
+ var _statuses = {};
+ var _loaders = {};
+
+ Polymer({
+
+ is: 'google-client-loader',
+
+ /**
+ * Fired when the requested API is loaded. Override this name
+ * by setting `successEventName`.
+ * @event google-api-load
+ */
+
+ /**
+ * Fired if an error occurs while loading the requested API. Override this name
+ * by setting `errorEventName`.
+ * @event google-api-load-error
+ */
+
+ properties: {
+ /**
+ * Name of the API to load, e.g. 'urlshortener'.
+ *
+ * You can find the full list of APIs on the
+ * <a href="https://developers.google.com/apis-explorer"> Google APIs
+ * Explorer</a>.
+ */
+ name: String,
+
+ /**
+ * Version of the API to load, e.g. 'v1'.
+ */
+ version: String,
+
+ /**
+ * App Engine application ID for loading a Google Cloud Endpoints API.
+ */
+ appId: String,
+
+ /**
+ * Root URL where to load the API from, e.g. 'http://host/apis'.
+ * For App Engine dev server this would be something like:
+ * 'http://localhost:8080/_ah/api'.
+ * Overrides 'appId' if both are specified.
+ */
+ apiRoot: String,
+
+ /**
+ * Name of the event fired when API library is loaded.
+ */
+ successEventName: {
+ type: String,
+ value: 'google-api-load'
+ },
+
+ /**
+ * Name of the event fired when there is an error loading the library.
+ */
+ errorEventName: {
+ type: String,
+ value: 'google-api-load-error'
+ }
+ },
+
+ hostAttributes: {
+ hidden: true // remove from rendering tree.
+ },
+
+ // Used to fix events potentially being fired multiple times by
+ // iron-jsonp-library.
+ _waiting: false,
+
+ /**
+ * Returns the loaded API.
+ */
+ get api() {
+ if (window.gapi && window.gapi.client &&
+ window.gapi.client[this.name]) {
+ return window.gapi.client[this.name];
+ } else {
+ return undefined;
+ }
+ },
+
+ /**
+ * Wrapper for `gapi.auth`.
+ */
+ get auth() {
+ return gapi.auth;
+ },
+
+ ready: function() {
+ this._loader = document.createElement('google-js-api');
+ this.listen(this._loader, 'js-api-load', '_loadClient');
+ },
+
+ detached: function() {
+ this.unlisten(this._loader, 'js-api-load', '_loadClient');
+ },
+
+ _loadClient: function() {
+ gapi.load('client', this._doneLoadingClient.bind(this));
+ },
+
+ _handleLoadResponse: function(response) {
+ if (response && response.error) {
+ _statuses[this.name] = 'error';
+ this._fireError(response);
+ } else {
+ _statuses[this.name] = 'loaded';
+ this._fireSuccess();
+ }
+ },
+
+ _fireSuccess: function() {
+ this.fire(this.successEventName,
+ { 'name': this.name, 'version': this.version });
+ },
+
+ _fireError: function(response) {
+ if (response && response.error) {
+ this.fire(this.errorEventName, {
+ 'name': this.name,
+ 'version': this.version,
+ 'error': response.error });
+ } else {
+ this.fire(this.errorEventName, {
+ 'name': this.name,
+ 'version': this.version });
+ }
+ },
+
+ _doneLoadingClient: function() {
+ _clientLoaded = true;
+ // Fix for API client load event being fired multiple times by
+ // iron-jsonp-library.
+ if (!this._waiting) {
+ this._loadApi();
+ }
+ },
+
+ _createSelfRemovingListener: function(eventName) {
+ var handler = function () {
+ _loaders[this.name].removeEventListener(eventName, handler);
+ this._loadApi();
+ }.bind(this);
+
+ return handler;
+ },
+
+ _loadApi: function() {
+ if (_clientLoaded && this.name && this.version) {
+ this._waiting = false;
+ // Is this API already loaded?
+ if (_statuses[this.name] == 'loaded') {
+ this._fireSuccess();
+ // Is a different google-api-loader already loading this API?
+ } else if (_statuses[this.name] == 'loading') {
+ this._waiting = true;
+ _loaders[this.name].addEventListener(this.successEventName,
+ this._createSelfRemovingListener(this.successEventName));
+ _loaders[this.name].addEventListener(this.errorEventName,
+ this._createSelfRemovingListener(this.errorEventName));
+ // Did we get an error when we tried to load this API before?
+ } else if (_statuses[this.name] == 'error') {
+ this._fireError(null);
+ // Otherwise, looks like we're loading a new API.
+ } else {
+ var root;
+ if (this.apiRoot) {
+ root = this.apiRoot;
+ } else if (this.appId) {
+ root = 'https://' + this.appId + '.appspot.com/_ah/api';
+ }
+ _statuses[this.name] = 'loading';
+ _loaders[this.name] = this;
+ gapi.client.load(this.name, this.version,
+ this._handleLoadResponse.bind(this), root);
+ }
+ }
+ }
+ });
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/google-apis/google-js-api.html b/catapult/third_party/polymer/components/google-apis/google-js-api.html
new file mode 100644
index 00000000..3b05a109
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-js-api.html
@@ -0,0 +1,63 @@
+<!--
+Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-jsonp-library/iron-jsonp-library.html">
+
+<!--
+Dynamically loads Google JavaScript API `gapi`, firing the `js-api-load` event when ready.
+
+Any number of components can use `<google-js-api>` elements, and the library will only be loaded once.
+
+##### Example
+
+ <google-js-api></google-js-api>
+ <script>
+ var api = document.querySelector('google-js-api');
+ api.addEventListener('js-api-load', function(e) {
+ console.log('API loaded', gapi);
+ });
+ </script>
+-->
+<script>
+ Polymer({
+
+ is: 'google-js-api',
+
+ behaviors: [
+ Polymer.IronJsonpLibraryBehavior
+ ],
+
+ properties: {
+
+ /** @private */
+ libraryUrl: {
+ type: String,
+ value: 'https://apis.google.com/js/api.js?onload=%%callback%%'
+ },
+
+ /**
+ * Fired when the API library is loaded and available.
+ * @event js-api-load
+ */
+ /**
+ * Name of event fired when library is loaded and available.
+ */
+ notifyEvent: {
+ type: String,
+ value: 'js-api-load'
+ },
+ },
+
+ get api() {
+ return gapi;
+ }
+
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/google-apis/google-legacy-loader.html b/catapult/third_party/polymer/components/google-apis/google-legacy-loader.html
new file mode 100644
index 00000000..02e7a5c4
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-legacy-loader.html
@@ -0,0 +1,55 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-jsonp-library/iron-jsonp-library.html">
+
+<!--
+Dynamically loads the legacy Google JavaScript API Loader (https://developers.google.com/loader/).
+
+Fires `api-load` event when ready.
+-->
+<script>
+ Polymer({
+
+ is: 'google-legacy-loader',
+
+ behaviors: [
+ Polymer.IronJsonpLibraryBehavior
+ ],
+
+ properties: {
+
+ /** @private */
+ libraryUrl: {
+ type: String,
+ value: 'https://www.google.com/jsapi?callback=%%callback%%'
+ },
+
+ /**
+ * Fired when the API library is loaded and available.
+ * @event js-api-load
+ */
+ /**
+ * Name of event fired when library is loaded and available.
+ */
+ notifyEvent: {
+ type: String,
+ value: 'api-load'
+ }
+ },
+
+ /**
+ * Wrapper for `google` API namespace.
+ */
+ get api() {
+ return google;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/google-apis/google-maps-api.html b/catapult/third_party/polymer/components/google-apis/google-maps-api.html
new file mode 100644
index 00000000..fb9bbaca
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-maps-api.html
@@ -0,0 +1,150 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-jsonp-library/iron-jsonp-library.html">
+
+<!--
+Dynamically loads the Google Maps JavaScript API, firing the `api-load` event when ready.
+
+#### Example
+
+ <google-maps-api api-key="abc123" version="3.exp"></google-maps-api>
+ <script>
+ var mapsAPI = document.querySelector('google-maps-api');
+ mapsAPI.addEventListener('api-load', function(e) {
+ // this.api === google.maps
+ });
+ </script>
+
+Any number of components can use `<google-maps-api>` elements, and the library will only be loaded once.
+
+@summary Element wrapper around Google Maps API.
+-->
+<script>
+ Polymer({
+
+ is: 'google-maps-api',
+
+ behaviors: [
+ Polymer.IronJsonpLibraryBehavior
+ ],
+
+ properties: {
+
+ /** @private */
+ mapsUrl: {
+ type: String,
+ value: 'https://maps.googleapis.com/maps/api/js?callback=%%callback%%'
+ },
+
+ /**
+ * A Maps API key. To obtain an API key, see developers.google.com/maps/documentation/javascript/tutorial#api_key.
+ */
+ apiKey: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * A Maps API for Business Client ID. To obtain a Maps API for Business Client ID, see developers.google.com/maps/documentation/business/.
+ * If set, a Client ID will take precedence over an API Key.
+ */
+ clientId: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * Version of the Maps API to use.
+ */
+ version: {
+ type: String,
+ value: '3.exp'
+ },
+
+ /**
+ * The localized language to load the Maps API with. For more information
+ * see https://developers.google.com/maps/documentation/javascript/basics#Language
+ *
+ * Note: the Maps API defaults to the preffered language setting of the browser.
+ * Use this parameter to override that behavior.
+ */
+ language: {
+ type: String,
+ value: ''
+ },
+ /**
+ * If true, sign-in is enabled.
+ * See https://developers.google.com/maps/documentation/javascript/signedin#enable_sign_in
+ */
+ signedIn: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Fired when the Maps API library is loaded and ready.
+ * @event api-load
+ */
+ /**
+ * Name of event fired when library is loaded and available.
+ */
+ notifyEvent: {
+ type: String,
+ value: 'api-load'
+ },
+
+ /** @private */
+ libraryUrl: {
+ type: String,
+ computed: '_computeUrl(mapsUrl, version, apiKey, clientId, language, signedIn)'
+ }
+ },
+
+ _computeUrl: function(mapsUrl, version, apiKey, clientId, language, signedIn) {
+ var url = mapsUrl + '&v=' + version;
+
+ // Always load all Maps API libraries.
+ url += '&libraries=drawing,geometry,places,visualization';
+
+ if (apiKey && !clientId) {
+ url += '&key=' + apiKey;
+ }
+
+ if (clientId) {
+ url += '&client=' + clientId;
+ }
+
+ // Log a warning if the user is not using an API Key or Client ID.
+ if (!apiKey && !clientId) {
+ var warning = 'No Google Maps API Key or Client ID specified. ' +
+ 'See https://developers.google.com/maps/documentation/javascript/get-api-key ' +
+ 'for instructions to get started with a key or client id.';
+ console.warn(warning);
+ }
+
+ if (language) {
+ url += '&language=' + language;
+ }
+
+ if (signedIn) {
+ url += '&signed_in=' + signedIn;
+ }
+ return url;
+ },
+
+ /**
+ * Provides the google.maps JS API namespace.
+ */
+ get api() {
+ return google.maps;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/google-apis/google-plusone-api.html b/catapult/third_party/polymer/components/google-apis/google-plusone-api.html
new file mode 100644
index 00000000..205658e0
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-plusone-api.html
@@ -0,0 +1,54 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-jsonp-library/iron-jsonp-library.html">
+
+<!--
+Dynamically loads the Google+ JavaScript API, firing the `api-load` event when ready.
+
+Any number of components can use `<google-plusone-api>` elements, and the library will only be loaded once.
+-->
+<script>
+ Polymer({
+
+ is: 'google-plusone-api',
+
+ behaviors: [
+ Polymer.IronJsonpLibraryBehavior
+ ],
+
+ properties: {
+
+ /** @private */
+ libraryUrl: {
+ type: String,
+ value: 'https://apis.google.com/js/plusone.js?onload=%%callback%%'
+ },
+
+ /**
+ * Fired when the API library is loaded and available.
+ * @event js-api-load
+ */
+ /**
+ * Name of event fired when library is loaded and available.
+ */
+ notifyEvent: {
+ type: String,
+ value: 'api-load'
+ }
+
+ },
+
+ get api() {
+ return gapi;
+ }
+
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/google-apis/google-realtime-api.html b/catapult/third_party/polymer/components/google-apis/google-realtime-api.html
new file mode 100644
index 00000000..a41fb517
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-realtime-api.html
@@ -0,0 +1,57 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-jsonp-library/iron-jsonp-library.html">
+
+<!--
+Dynamically loads the Google Drive Realtime API, firing the `api-load` event when ready.
+
+Any number of components can use `<google-realtime-api>` elements, and the library will only be loaded once.
+-->
+<script>
+ Polymer({
+
+ is: 'google-realtime-api',
+
+ behaviors: [
+ Polymer.IronJsonpLibraryBehavior
+ ],
+
+ properties: {
+
+ /** @private */
+ libraryUrl: {
+ type: String,
+ value: 'https://apis.google.com/js/drive-realtime.js?onload=%%callback%%'
+ },
+
+ /**
+ * Fired when the API library is loaded and available.
+ * @event api-load
+ */
+ /**
+ * Name of event fired when library is loaded and available.
+ */
+ notifyEvent: {
+ type: String,
+ value: 'api-load'
+ }
+
+ },
+
+ /**
+ * Returns `gapi.drive.realtime`
+ */
+ get api() {
+ return gapi.drive.realtime;
+ }
+
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/google-apis/google-youtube-api.html b/catapult/third_party/polymer/components/google-apis/google-youtube-api.html
new file mode 100644
index 00000000..7f8960d7
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/google-youtube-api.html
@@ -0,0 +1,61 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at https://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 https://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-jsonp-library/iron-jsonp-library.html">
+
+<!--
+Dynamically loads the Google Youtube Iframe API, firing the `api-load` event when ready.
+
+Any number of components can use `<google-youtube-api>` elements, and the library will only be loaded once.
+
+https://developers.google.com/youtube/iframe_api_reference
+-->
+<script>
+ Polymer({
+
+ is: 'google-youtube-api',
+
+ behaviors: [
+ Polymer.IronJsonpLibraryBehavior
+ ],
+
+ properties: {
+
+ /** @private */
+ libraryUrl: {
+ type: String,
+ value: 'https://www.youtube.com/iframe_api'
+ },
+
+ /**
+ * Fired when the API library is loaded and available.
+ * @event api-load
+ */
+ /**
+ * Name of event fired when library loads.
+ */
+ notifyEvent: {
+ type: String,
+ value: 'api-load'
+ },
+
+ callbackName: {
+ type: String,
+ value: 'onYouTubeIframeAPIReady'
+ }
+
+ },
+
+ get api() {
+ return YT;
+ }
+
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/google-apis/index.html b/catapult/third_party/polymer/components/google-apis/index.html
new file mode 100644
index 00000000..203f4fa4
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-apis/index.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
+<html>
+<head>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/google-signin/.bower.json b/catapult/third_party/polymer/components/google-signin/.bower.json
new file mode 100644
index 00000000..813638ae
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/.bower.json
@@ -0,0 +1,63 @@
+{
+ "name": "google-signin",
+ "version": "2.0.0",
+ "description": "Web components to authenticate with Google services",
+ "homepage": "https://googlewebcomponents.github.io/google-signin",
+ "main": "google-signin.html",
+ "authors": [
+ "Addy Osmani",
+ "Randy Merrill"
+ ],
+ "license": "Apache-2.0",
+ "ignore": [
+ "/.*",
+ "/test/"
+ ],
+ "keywords": [
+ "web-component",
+ "web-components",
+ "polymer",
+ "sign-in",
+ "google",
+ "authentication"
+ ],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2",
+ "font-roboto": "PolymerElements/font-roboto#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#1 - 2",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#1 - 2",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#1 - 2",
+ "paper-ripple": "PolymerElements/paper-ripple#1 - 2",
+ "paper-material": "PolymerElements/paper-material#1 - 2",
+ "google-apis": "GoogleWebComponents/google-apis#1 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0",
+ "font-roboto": "PolymerElements/font-roboto#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.3.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.0",
+ "google-apis": "GoogleWebComponents/google-apis#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0"
+ }
+ }
+ },
+ "_release": "2.0.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v2.0.0",
+ "commit": "6a3182651e531f221abc0a8341c8fd0246acd08f"
+ },
+ "_source": "https://github.com/GoogleWebComponents/google-signin.git",
+ "_target": "^2.0.0",
+ "_originalSource": "GoogleWebComponents/google-signin"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/google-signin/LICENSE b/catapult/third_party/polymer/components/google-signin/LICENSE
new file mode 100644
index 00000000..52aea391
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2014 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
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT 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/catapult/third_party/polymer/components/google-signin/README.md b/catapult/third_party/polymer/components/google-signin/README.md
new file mode 100644
index 00000000..e2fc16e4
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/README.md
@@ -0,0 +1,4 @@
+google-signin
+================
+
+See https://elements.polymer-project.org/elements/google-signin
diff --git a/catapult/third_party/polymer/components/google-signin/bower.json b/catapult/third_party/polymer/components/google-signin/bower.json
new file mode 100644
index 00000000..06febde7
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/bower.json
@@ -0,0 +1,54 @@
+{
+ "name": "google-signin",
+ "version": "2.0.0",
+ "description": "Web components to authenticate with Google services",
+ "homepage": "https://googlewebcomponents.github.io/google-signin",
+ "main": "google-signin.html",
+ "authors": [
+ "Addy Osmani",
+ "Randy Merrill"
+ ],
+ "license": "Apache-2.0",
+ "ignore": [
+ "/.*",
+ "/test/"
+ ],
+ "keywords": [
+ "web-component",
+ "web-components",
+ "polymer",
+ "sign-in",
+ "google",
+ "authentication"
+ ],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2",
+ "font-roboto": "PolymerElements/font-roboto#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#1 - 2",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#1 - 2",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#1 - 2",
+ "paper-ripple": "PolymerElements/paper-ripple#1 - 2",
+ "paper-material": "PolymerElements/paper-material#1 - 2",
+ "google-apis": "GoogleWebComponents/google-apis#1 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0",
+ "font-roboto": "PolymerElements/font-roboto#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.3.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.0",
+ "google-apis": "GoogleWebComponents/google-apis#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0"
+ }
+ }
+ }
+}
diff --git a/catapult/third_party/polymer/components/google-signin/demo/index.html b/catapult/third_party/polymer/components/google-signin/demo/index.html
new file mode 100644
index 00000000..0db7e5a9
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/demo/index.html
@@ -0,0 +1,208 @@
+<!doctype html>
+<!-- Copyright (c) 2014 Google Inc. All rights reserved. -->
+<html>
+<head>
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>google-signin Demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../google-signin.html">
+ <link rel="import" href="../google-signin-aware.html">
+
+ <!-- Demo only styles -->
+ <style>
+ body {
+ font-family: 'RobotoDraft', 'Roboto', sans-serif;
+ line-height:1.2;
+ vertical-align:middle;
+ background: rgba(204, 204, 204, 0.31);
+ }
+
+
+ .map {
+ background: whitesmoke;
+ margin: .5rem -1.5rem 0 -1.5rem;
+ padding: 0.5rem;
+ }
+
+ h1 {
+ font-size: 2rem;
+ font-weight:200;
+ clear: both;
+ }
+
+ h1 strong {
+ font-weight:300;
+ color:#539D00;
+ }
+
+ h2 {
+ font-size:.9rem;
+ line-height:2.5;
+ color:gray;
+ font-weight:400;
+ clear: both;
+ }
+
+ .showcase {
+ display: inline-block;
+ margin-right: 2rem;
+ float: left;
+ }
+ </style>
+
+</head>
+
+<body>
+ <p>A <code>&lt;google-signin&gt;</code> element looks like this button:</p>
+
+ <p><google-signin brand="google" client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com"></google-signin>
+or like this if plus scopes are present
+ <google-signin brand="google-plus"></google-signin>
+ </p>
+ <p>Signin button can vary its appearance:</p>
+ <p>Width:
+ <google-signin brand="google" width="wide"></google-signin>
+ <google-signin brand="google" width="iconOnly"></google-signin>
+ Height:
+ <google-signin brand="google" height="tall"></google-signin>
+ <google-signin brand="google" height="standard"></google-signin>
+ <google-signin brand="google" height="short"></google-signin>
+ </p>
+ <p>
+ Theme:
+ <google-signin brand="google" theme="dark"></google-signin>
+ <google-signin brand="google" theme="light"></google-signin>
+ <google-signin brand="google-plus" theme="dark"></google-signin>
+ <google-signin brand="google-plus" theme="light"></google-signin>
+ <google-signin brand="google-plus" theme="light" raised></google-signin>
+ </p>
+ <!-- Demo the ability to use the google-signin-aware element. -->
+ <p><code>&lt;google-signin-aware&gt;</code> is a companion element.</p>
+ <p>You can use it inside your components to request additional scopes.</p>
+ <p>Every signin button will request all the scopes present in the document,
+ and change its appearance to match</p>
+ <p>For example, here is a signin-aware scope. You can change its scopes via popup</p>
+ <dom-bind id="awareness">
+ <template is="dom-bind">
+ <div><code>&lt;google-signin-aware
+ <div>scope=
+ <select value="{{scope::change}}">
+ <option value="">None</option>
+ <option value="https://www.googleapis.com/auth/analytics">Google Analytics</option>
+ <option value="https://www.googleapis.com/auth/plus.login">Google Plus view circles</option>
+ <option value="https://www.googleapis.com/auth/youtube">YouTube</option>
+ <option value="https://www.googleapis.com/auth/calendar">Calendar</option>
+ <option value="profile">Profile info</option>
+ </select>
+ </div>
+ <div>openid-prompt=
+ <input type="checkbox" checked="{{openidPrompt.none::change}}">none
+ <input type="checkbox" checked="{{openidPrompt.login::change}}">login
+ <input type="checkbox" checked="{{openidPrompt.consent::change}}">consent
+ <input type="checkbox"
+ checked="{{openidPrompt.select_account::change}}">select_account
+ </div>
+ <div>offline=<input type="checkbox" checked="{{offline::change}}"></div>
+ <div>initialized="<span>{{initialized}}</span>"</div>
+ <div>signedIn="<span>{{signedIn}}</span>"</div>
+ <div>isAuthorized="<span>{{isAuthorized}}</span>"</div>
+ <div>needAdditionalAuth:"<span>{{needAdditionalAuth}}</span>"&gt;</div>
+ </code></div>
+ <p>Every new scope you select will be added to requested scopes.</p>
+ <p>When you select a Google Plus scope, button will turn red.</p>
+ <google-signin></google-signin>
+ </p>
+ <google-signin-aware
+ scopes="{{scope}}"
+ openid-prompt="{{openidPromptValue}}"
+ initialized="{{initialized}}"
+ signed-in="{{signedIn}}"
+ offline="{{offline}}"
+ is-authorized="{{isAuthorized}}"
+ need-additional-auth="{{needAdditionalAuth}}"
+ on-google-signin-aware-error="handleSignInError"
+ on-google-signin-aware-success="handleSignIn"
+ on-google-signin-offline-success="handleOffline"
+ on-google-signin-aware-signed-out="handleSignOut"
+ on-signed-in-changed="handleStateChange"
+ on-initialized-changed="handleStateChange"></google-signin-aware>
+ <p>User name:<span>{{userName}}</span></p>
+ <p>Testing <code>google-signin-aware</code> events: <span>{{status}}</span></p>
+ <p>Testing <code>google-signin-offline</code> events: <span>{{offlineCode}}</span></p>
+ <p>Only display "not signed in" element after auth state is initialized (avoid flickering): <b hidden id="not-signed-in">Not signed in!</b></p>
+ <p><button on-click="disconnect">Disconnect to start over</button></p>
+ </template>
+ </dom-bind>
+ <script>
+ var aware = document.querySelector('#awareness');
+ if (!Polymer.Element) {
+ aware = aware.querySelector('template');
+ }
+
+ aware.status = 'Not granted';
+ aware.offlineCode = 'No offline login.';
+ aware.userName = 'N/A';
+ aware.openidPrompt = {};
+
+ aware.handleSignInError = function(event) {
+ this.status = JSON.stringify(event.detail);
+ };
+ aware.handleSignIn = function(response) {
+ this.status = 'Signin granted';
+ // console.log('[Aware] Signin Response', response);
+ this.userName = gapi.auth2.getAuthInstance().currentUser.get().getBasicProfile().getName();
+ };
+ aware.handleOffline = function(response) {
+ this.offlineCode = response.detail.code;
+ };
+ aware.handleSignOut = function(response) {
+ this.status = 'Signed out';
+ // console.log('[Aware] Signout Response', response);
+ this.userName = 'N/A';
+ };
+ aware.disconnect = function() {
+ var currentUser = gapi.auth2.getAuthInstance().currentUser.get();
+ if (currentUser) {
+ currentUser.disconnect();
+ }
+ gapi.auth2.getAuthInstance().signOut();
+ };
+
+ aware.handleStateChange = function(e) {
+ var signedIn = e.target.signedIn;
+ var initialized = e.target.initialized;
+ if(initialized && !signedIn) {
+ document.querySelector("#not-signed-in").removeAttribute("hidden");
+ } else {
+ document.querySelector("#not-signed-in").setAttribute("hidden", true);
+ }
+ };
+
+ aware.addEventListener('openid-prompt-changed', function(e) {
+ if (e.detail.value) {
+ if (e.detail.path === 'openidPrompt.none') {
+ aware.set('openidPrompt', {
+ none: true,
+ login: false,
+ consent: false,
+ select_account: false
+ });
+ } else {
+ aware.set('openidPrompt.none', false);
+ }
+ }
+
+ var values = [];
+ Object.keys(aware.openidPrompt).forEach(function(k) {
+ if (aware.openidPrompt[k]) {
+ values.push(k);
+ }
+ });
+ this.set('openidPromptValue', values.join(' '));
+ });
+
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/google-signin/google-icons.html b/catapult/third_party/polymer/components/google-signin/google-icons.html
new file mode 100644
index 00000000..0787b94f
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/google-icons.html
@@ -0,0 +1,36 @@
+<!--
+Copyright 2014 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
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="google" size="24">
+<svg>
+ <defs>
+ <g id="google">
+ <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
+ <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
+ <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
+ <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
+ <path fill="none" d="M1 1h22v22H1z"/>
+ </g>
+ <g id="google-plus">
+ <path fill="none" d="M0 0h24v24H0V0z"/>
+ <path d="M23 11h-2V9h-2v2h-2v2h2v2h2v-2h2zM8 11v2.4h3.97c-.16 1.03-1.2 3.02-3.97 3.02-2.39 0-4.34-1.98-4.34-4.42S5.61 7.58 8 7.58c1.36 0 2.27.58 2.79 1.08l1.9-1.83C11.47 5.69 9.89 5 8 5c-3.87 0-7 3.13-7 7s3.13 7 7 7c4.04 0 6.72-2.84 6.72-6.84 0-.46-.05-.81-.11-1.16H8z"/>
+ <path fill="none" d="M1 5h14v14H1z"/>
+ </g>
+ </defs>
+</svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/google-signin/google-signin-aware.html b/catapult/third_party/polymer/components/google-signin/google-signin-aware.html
new file mode 100644
index 00000000..e6e731bf
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/google-signin-aware.html
@@ -0,0 +1,824 @@
+<!--
+Copyright 2014 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
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../google-apis/google-js-api.html">
+
+<script>
+ (function() {
+
+ /**
+ * Enum of attributes to be passed through to the login API call.
+ * @readonly
+ * @enum {string}
+ */
+ var ProxyLoginAttributes = {
+ 'appPackageName': 'apppackagename',
+ 'clientId': 'clientid',
+ 'cookiePolicy': 'cookiepolicy',
+ 'hostedDomain': 'hostedDomain',
+ 'openidPrompt': 'prompt',
+ 'requestVisibleActions': 'requestvisibleactions'
+ };
+
+ /**
+ * AuthEngine does all interactions with gapi.auth2
+ *
+ * It is tightly coupled with <google-signin-aware> element
+ * The elements configure AuthEngine.
+ * AuthEngine propagates all authentication events to all google-signin-aware elements
+ *
+ * API used: https://developers.google.com/identity/sign-in/web/reference
+ *
+ */
+ var AuthEngine = {
+
+ /**
+ * oauth2 argument, set by google-signin-aware
+ */
+ _clientId: null,
+
+ get clientId() {
+ return this._clientId;
+ },
+
+ set clientId(val) {
+ if (this._clientId && val && val != this._clientId) {
+ throw new Error('clientId cannot change. Values do not match. New: ' + val + ' Old:' + this._clientId);
+ }
+ if (val && val != this._clientId) {
+ this._clientId = val;
+ this.initAuth2();
+ }
+ },
+
+ /**
+ * oauth2 argument, set by google-signin-aware
+ */
+ _cookiePolicy: 'single_host_origin',
+
+ get cookiePolicy() {
+ return this._cookiePolicy;
+ },
+
+ set cookiePolicy(val) {
+ if (val) {
+ this._cookiePolicy = val;
+ }
+ },
+
+ /**
+ * oauth2 argument, set by google-signin-aware
+ */
+ _appPackageName: '',
+
+ get appPackageName() {
+ return this._appPackageName;
+ },
+
+ set appPackageName(val) {
+ if (this._appPackageName && val && val != this._appPackageName) {
+ throw new Error('appPackageName cannot change. Values do not match. New: ' + val + ' Old: ' + this._appPackageName);
+ }
+ if (val) {
+ this._appPackageName = val;
+ }
+ },
+
+ /**
+ * oauth2 argument, set by google-signin-aware
+ */
+ _requestVisibleActions: '',
+
+ get requestVisibleactions() {
+ return this._requestVisibleActions;
+ },
+
+ set requestVisibleactions(val) {
+ if (this._requestVisibleActions && val && val != this._requestVisibleActions) {
+ throw new Error('requestVisibleactions cannot change. Values do not match. New: ' + val + ' Old: ' + this._requestVisibleActions);
+ }
+ if (val)
+ this._requestVisibleActions = val;
+ },
+
+ /**
+ * oauth2 argument, set by google-signin-aware
+ */
+ _hostedDomain: '',
+
+ get hostedDomain() {
+ return this._hostedDomain;
+ },
+
+ set hostedDomain(val) {
+ if (this._hostedDomain && val && val != this._hostedDomain) {
+ throw new Error('hostedDomain cannot change. Values do not match. New: ' + val + ' Old: ' + this._hostedDomain);
+ }
+ if (val)
+ this._hostedDomain = val;
+ },
+
+ /**
+ * oauth2 argument, set by google-signin-aware
+ */
+ _openidPrompt: '',
+
+ get openidPrompt() {
+ return this._openidPrompt;
+ },
+
+ set openidPrompt(val) {
+ if (typeof val !== 'string') {
+ throw new Error(
+ 'openidPrompt must be a string. Received ' + typeof val);
+ }
+ if (val) {
+ var values = val.split(' ');
+ values = values.map(function(v) {
+ return v.trim();
+ });
+ values = values.filter(function(v) {
+ return v;
+ });
+ var validValues = {none: 0, login: 0, consent: 0, select_account: 0};
+ values.forEach(function(v) {
+ if (v == 'none' && values.length > 1) {
+ throw new Error(
+ 'none cannot be combined with other openidPrompt values');
+ }
+ if (!(v in validValues)) {
+ throw new Error(
+ 'invalid openidPrompt value ' + v +
+ '. Valid values: ' + Object.keys(validValues).join(', '));
+ }
+ });
+ }
+ this._openidPrompt = val;
+ },
+
+ /** Is offline access currently enabled in the google-signin-aware element? */
+ _offline: false,
+
+ get offline() {
+ return this._offline;
+ },
+
+ set offline(val) {
+ this._offline = val;
+ this.updateAdditionalAuth();
+ },
+
+ /** Should we force a re-prompt for offline access? */
+ _offlineAlwaysPrompt: false,
+
+ get offlineAlwaysPrompt() {
+ return this._offlineAlwaysPrompt;
+ },
+
+ set offlineAlwaysPrompt(val) {
+ this._offlineAlwaysPrompt = val;
+ this.updateAdditionalAuth();
+ },
+
+ /** Have we already gotten offline access from Google during this session? */
+ offlineGranted: false,
+
+ /** <google-js-api> */
+ _apiLoader: null,
+
+ /** an array of wanted scopes. oauth2 argument */
+ _requestedScopeArray: [],
+
+ /** _requestedScopeArray as string */
+ get requestedScopes() {
+ return this._requestedScopeArray.join(' ');
+ },
+
+ /** Is auth library initalized? */
+ _initialized: false,
+
+ /** Is user signed in? */
+ _signedIn: false,
+
+ /** Currently granted scopes */
+ _grantedScopeArray: [],
+
+ /** True if additional authorization is required */
+ _needAdditionalAuth: true,
+
+ /** True if have google+ scopes */
+ _hasPlusScopes: false,
+
+ /**
+ * array of <google-signin-aware>
+ * state changes are broadcast to them
+ */
+ signinAwares: [],
+
+ init: function() {
+ this._apiLoader = document.createElement('google-js-api');
+ this._apiLoader.addEventListener('js-api-load', this.loadAuth2.bind(this));
+ if (Polymer.Element) {
+ document.body.appendChild(this._apiLoader);
+ }
+ },
+
+ loadAuth2: function() {
+ gapi.load('auth2', this.initAuth2.bind(this));
+ },
+
+ initAuth2: function() {
+ if (!('gapi' in window) || !('auth2' in window.gapi) || !this.clientId) {
+ return;
+ }
+ var auth = gapi.auth2.init({
+ 'client_id': this.clientId,
+ 'cookie_policy': this.cookiePolicy,
+ 'scope': this.requestedScopes,
+ 'hosted_domain': this.hostedDomain
+ });
+
+ auth['currentUser'].listen(this.handleUserUpdate.bind(this));
+
+ auth.then(
+ function onFulfilled() {
+ // Let the current user listener trigger the changes.
+ },
+ function onRejected(error) {
+ console.error(error);
+ }
+ );
+ },
+
+ handleUserUpdate: function(newPrimaryUser) {
+ // update and broadcast currentUser
+ var isSignedIn = newPrimaryUser.isSignedIn();
+ if (isSignedIn != this._signedIn) {
+ this._signedIn = isSignedIn;
+ for (var i=0; i<this.signinAwares.length; i++) {
+ this.signinAwares[i]._setSignedIn(isSignedIn);
+ }
+ }
+ // update and broadcast initialized property the first time the isSignedIn property is set.
+ if(!this._initialized) {
+ for (var i=0; i<this.signinAwares.length; i++) {
+ this.signinAwares[i]._setInitialized(true);
+ }
+ this._initialized = true;
+ }
+
+
+ // update granted scopes
+ this._grantedScopeArray = this.strToScopeArray(
+ newPrimaryUser.getGrantedScopes());
+ // console.log(this._grantedScopeArray);
+ this.updateAdditionalAuth();
+
+ var response = newPrimaryUser.getAuthResponse();
+ for (var i=0; i<this.signinAwares.length; i++) {
+ this.signinAwares[i]._updateScopeStatus(response);
+ }
+ },
+
+ setOfflineCode: function(code) {
+ for (var i=0; i<this.signinAwares.length; i++) {
+ this.signinAwares[i]._updateOfflineCode(code);
+ }
+ },
+
+ /** convert scope string to scope array */
+ strToScopeArray: function(str) {
+ if (!str) {
+ return [];
+ }
+ // remove extra spaces, then split
+ var scopes = str.replace(/\ +/g, ' ').trim().split(' ');
+ for (var i=0; i<scopes.length; i++) {
+ scopes[i] = scopes[i].toLowerCase();
+ // Handle scopes that will be deprecated but are still returned with their old value
+ if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.profile') {
+ scopes[i] = 'profile';
+ }
+ if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.email') {
+ scopes[i] = 'email';
+ }
+ }
+ // return with duplicates filtered out
+ return scopes.filter( function(value, index, self) {
+ return self.indexOf(value) === index;
+ });
+ },
+
+ /** true if scopes have google+ scopes */
+ isPlusScope: function(scope) {
+ return (scope.indexOf('/auth/games') > -1)
+ || (scope.indexOf('auth/plus.') > -1 && scope.indexOf('auth/plus.me') < 0);
+ },
+
+ /** true if scopes have been granted */
+ hasGrantedScopes: function(scopeStr) {
+ var scopes = this.strToScopeArray(scopeStr);
+ for (var i=0; i< scopes.length; i++) {
+ if (this._grantedScopeArray.indexOf(scopes[i]) === -1)
+ return false;
+ }
+ return true;
+ },
+
+ /** request additional scopes */
+ requestScopes: function(newScopeStr) {
+ var newScopes = this.strToScopeArray(newScopeStr);
+ var scopesUpdated = false;
+ for (var i=0; i<newScopes.length; i++) {
+ if (this._requestedScopeArray.indexOf(newScopes[i]) === -1) {
+ this._requestedScopeArray.push(newScopes[i]);
+ scopesUpdated = true;
+ }
+ }
+ if (scopesUpdated) {
+ this.updateAdditionalAuth();
+ this.updatePlusScopes();
+ }
+ },
+
+ /** update status of _needAdditionalAuth */
+ updateAdditionalAuth: function() {
+ var needMoreAuth = false;
+ if ((this.offlineAlwaysPrompt || this.offline ) && !this.offlineGranted) {
+ needMoreAuth = true;
+ } else {
+ for (var i=0; i<this._requestedScopeArray.length; i++) {
+ if (this._grantedScopeArray.indexOf(this._requestedScopeArray[i]) === -1) {
+ needMoreAuth = true;
+ break;
+ }
+ }
+ }
+ if (this._needAdditionalAuth != needMoreAuth) {
+ this._needAdditionalAuth = needMoreAuth;
+ // broadcast new value
+ for (var i=0; i<this.signinAwares.length; i++) {
+ this.signinAwares[i]._setNeedAdditionalAuth(needMoreAuth);
+ }
+ }
+ },
+
+ updatePlusScopes: function() {
+ var hasPlusScopes = false;
+ for (var i = 0; i < this._requestedScopeArray.length; i++) {
+ if (this.isPlusScope(this._requestedScopeArray[i])) {
+ hasPlusScopes = true;
+ break;
+ }
+ }
+ if (this._hasPlusScopes != hasPlusScopes) {
+ this._hasPlusScopes = hasPlusScopes;
+ for (var i=0; i<this.signinAwares.length; i++) {
+ this.signinAwares[i]._setHasPlusScopes(hasPlusScopes);
+ }
+ }
+ },
+ /**
+ * attached <google-signin-aware>
+ * @param {!GoogleSigninAwareElement} aware element to add
+ */
+ attachSigninAware: function(aware) {
+ if (this.signinAwares.indexOf(aware) == -1) {
+ this.signinAwares.push(aware);
+ // Initialize aware properties
+ aware._setNeedAdditionalAuth(this._needAdditionalAuth);
+ aware._setInitialized(this._initialized);
+ aware._setSignedIn(this._signedIn);
+ aware._setHasPlusScopes(this._hasPlusScopes);
+ } else {
+ console.warn('signinAware attached more than once', aware);
+ }
+ },
+
+ detachSigninAware: function(aware) {
+ var index = this.signinAwares.indexOf(aware);
+ if (index != -1) {
+ this.signinAwares.splice(index, 1);
+ } else {
+ console.warn('Trying to detach unattached signin-aware');
+ }
+ },
+
+ /** returns scopes not granted */
+ getMissingScopes: function() {
+ return this._requestedScopeArray.filter( function(scope) {
+ return this._grantedScopeArray.indexOf(scope) === -1;
+ }.bind(this)).join(' ');
+ },
+
+ assertAuthInitialized: function() {
+ if (!this.clientId) {
+ throw new Error("AuthEngine not initialized. clientId has not been configured.");
+ }
+ if (!('gapi' in window)) {
+ throw new Error("AuthEngine not initialized. gapi has not loaded.");
+ }
+ if (!('auth2' in window.gapi)) {
+ throw new Error("AuthEngine not initialized. auth2 not loaded.");
+ }
+ },
+
+ /** pops up sign-in dialog */
+ signIn: function() {
+ this.assertAuthInitialized();
+ var params = {
+ 'scope': this.getMissingScopes()
+ };
+
+ // Proxy specific attributes through to the signIn options.
+ Object.keys(ProxyLoginAttributes).forEach(function(key) {
+ if (this[key] && this[key] !== '') {
+ params[ProxyLoginAttributes[key]] = this[key];
+ }
+ }, this);
+
+ var promise;
+ var user = gapi.auth2.getAuthInstance()['currentUser'].get();
+ if (!(this.offline || this.offlineAlwaysPrompt)) {
+ if (user.getGrantedScopes()) {
+ // additional auth, skip multiple account dialog
+ promise = user.grant(params);
+ } else {
+ // initial signin
+ promise = gapi.auth2.getAuthInstance().signIn(params);
+ }
+ } else {
+ params.redirect_uri = 'postmessage';
+ if (this.offlineAlwaysPrompt) {
+ params.approval_prompt = 'force';
+ }
+
+ // Despite being documented at https://goo.gl/tiO0Bk
+ // It doesn't seem like user.grantOfflineAccess() actually exists in
+ // the current version of the Google Sign-In JS client we're using
+ // through GoogleWebComponents. So in the offline case, we will not
+ // distinguish between a first auth and an additional one.
+ promise = gapi.auth2.getAuthInstance().grantOfflineAccess(params);
+ }
+ promise.then(
+ function onFulfilled(response) {
+ // If login was offline, response contains one string "code"
+ // Otherwise it contains the user object already
+ var newUser;
+ if (response.code) {
+ AuthEngine.offlineGranted = true;
+ newUser = gapi.auth2.getAuthInstance()['currentUser'].get();
+ AuthEngine.setOfflineCode(response.code);
+ } else {
+ newUser = response;
+ }
+
+ var authResponse = newUser.getAuthResponse();
+ // Let the current user listener trigger the changes.
+ },
+ function onRejected(error) {
+ // Access denied is not an error, user hit cancel
+ if ("Access denied." !== error.reason) {
+ this.signinAwares.forEach(function(awareInstance) {
+ awareInstance.errorNotify(error);
+ });
+ }
+ }.bind(this)
+ );
+ },
+
+ /** signs user out */
+ signOut: function() {
+ this.assertAuthInitialized();
+ gapi.auth2.getAuthInstance().signOut().then(
+ function onFulfilled() {
+ // Let the current user listener trigger the changes.
+ },
+ function onRejected(error) {
+ console.error(error);
+ }
+ );
+ }
+ };
+
+ AuthEngine.init();
+
+/**
+`google-signin-aware` is used to enable authentication in custom elements by
+interacting with a google-signin element that needs to be present somewhere
+on the page.
+
+The `scopes` attribute allows you to specify which scope permissions are required
+(e.g do you want to allow interaction with the Google Drive API).
+
+The `google-signin-aware-success` event is triggered when a user successfully
+authenticates. If either `offline` or `offlineAlwaysPrompt` is set to true, successful
+authentication will also trigger the `google-signin-offline-success`event.
+The `google-signin-aware-signed-out` event is triggered when a user explicitly
+signs out via the google-signin element.
+
+You can bind to `isAuthorized` property to monitor authorization state.
+##### Example
+
+ <google-signin-aware scopes="https://www.googleapis.com/auth/drive"></google-signin-aware>
+
+
+##### Example with offline
+ <template id="awareness" is="dom-bind">
+ <google-signin-aware
+ scopes="https://www.googleapis.com/auth/drive"
+ offline
+ on-google-signin-aware-success="handleSignin"
+ on-google-signin-offline-success="handleOffline"></google-signin-aware>
+ <\/template>
+ <script>
+ var aware = document.querySelector('#awareness');
+ aware.handleSignin = function(response) {
+ var user = gapi.auth2.getAuthInstance()['currentUser'].get();
+ console.log('User name: ' + user.getBasicProfile().getName());
+ };
+ aware.handleOffline = function(response) {
+ console.log('Offline code received: ' + response.detail.code);
+ // Here you would POST response.detail.code to your webserver, which can
+ // exchange the authorization code for an access token. More info at:
+ // https://developers.google.com/identity/protocols/OAuth2WebServer
+ };
+ <\/script>
+*/
+ Polymer({
+
+ is: 'google-signin-aware',
+
+ /**
+ * Fired when this scope has been authorized
+ * @param {Object} result Authorization result.
+ * @event google-signin-aware-success
+ */
+
+ /**
+ * Fired when an offline authorization is successful.
+ * @param {{code: string}} detail -
+ * code: The one-time authorization code from Google.
+ * Your application can exchange this for an `access_token` and `refresh_token`
+ * @event google-signin-offline-success
+ */
+
+ /**
+ * Fired when this scope is not authorized
+ * @event google-signin-aware-signed-out
+ */
+
+ /**
+ * Fired when there is an error during the signin flow.
+ * @param {Object} detail The error object returned from the OAuth 2 flow.
+ * @event google-signin-aware-error
+ */
+
+ /**
+ * This block is needed so the previous @param is not assigned to the next property.
+ */
+
+ properties: {
+ /**
+ * App package name for android over-the-air installs.
+ * See the relevant [docs](https://developers.google.com/+/web/signin/android-app-installs)
+ */
+ appPackageName: {
+ type: String,
+ observer: '_appPackageNameChanged'
+ },
+
+ /**
+ * a Google Developers clientId reference
+ */
+ clientId: {
+ type: String,
+ observer: '_clientIdChanged'
+ },
+
+ /**
+ * The cookie policy defines what URIs have access to the session cookie
+ * remembering the user's sign-in state.
+ * See the relevant [docs](https://developers.google.com/+/web/signin/reference#determining_a_value_for_cookie_policy) for more information.
+ * @default 'single_host_origin'
+ */
+ cookiePolicy: {
+ type: String,
+ observer: '_cookiePolicyChanged'
+ },
+
+ /**
+ * The app activity types you want to write on behalf of the user
+ * (e.g http://schemas.google.com/AddActivity)
+ *
+ */
+ requestVisibleActions: {
+ type: String,
+ observer: '_requestVisibleActionsChanged'
+ },
+
+ /**
+ * The Google Apps domain to which users must belong to sign in.
+ * See the relevant [docs](https://developers.google.com/identity/sign-in/web/reference) for more information.
+ */
+ hostedDomain: {
+ type: String,
+ observer: '_hostedDomainChanged'
+ },
+
+ /**
+ * Allows for offline `access_token` retrieval during the signin process.
+ * See also `offlineAlwaysPrompt`. You only need to set one of the two; if both
+ * are set, the behavior of `offlineAlwaysPrompt` will override `offline`.
+ */
+ offline: {
+ type: Boolean,
+ value: false,
+ observer: '_offlineChanged'
+ },
+
+ /**
+ * Works the same as `offline` with the addition that it will always
+ * force a re-prompt to the user, guaranteeing that you will get a
+ * refresh_token even if the user has already granted offline access to
+ * this application. You only need to set one of `offline` or
+ * `offlineAlwaysPrompt`, not both.
+ */
+ offlineAlwaysPrompt: {
+ type: Boolean,
+ value: false,
+ observer: '_offlineAlwaysPromptChanged'
+ },
+
+ /**
+ * The scopes to provide access to (e.g https://www.googleapis.com/auth/drive)
+ * and should be space-delimited.
+ */
+ scopes: {
+ type: String,
+ value: 'profile',
+ observer: '_scopesChanged'
+ },
+
+ /**
+ * Space-delimited, case-sensitive list of strings that
+ * specifies whether the the user is prompted for reauthentication
+ * and/or consent. The defined values are:
+ * none: do not display authentication or consent pages.
+ * This value is mutually exclusive with the rest.
+ * login: always prompt the user for reauthentication.
+ * consent: always show consent screen.
+ * select_account: always show account selection page.
+ * This enables a user who has multiple accounts to select amongst
+ * the multiple accounts that they might have current sessions for.
+ * For more information, see "prompt" parameter description in
+ * https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters
+ */
+ openidPrompt: {
+ type: String,
+ value: '',
+ observer: '_openidPromptChanged'
+ },
+
+ /**
+ * True when the auth library has been initialized, and signedIn property value is set from the first api response.
+ */
+ initialized: {
+ type: Boolean,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * True if user is signed in
+ */
+ signedIn: {
+ type: Boolean,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * True if authorizations for *this* element have been granted
+ */
+ isAuthorized: {
+ type: Boolean,
+ notify: true,
+ readOnly: true,
+ value: false
+ },
+
+ /**
+ * True if additional authorizations for *any* element are required
+ */
+ needAdditionalAuth: {
+ type: Boolean,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * True if *any* element has google+ scopes
+ */
+ hasPlusScopes: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ readOnly: true
+ }
+ },
+
+ attached: function() {
+ AuthEngine.attachSigninAware(this);
+ },
+
+ detached: function() {
+ AuthEngine.detachSigninAware(this);
+ },
+
+ /** pops up the authorization dialog */
+ signIn: function() {
+ AuthEngine.signIn();
+ },
+
+ /** signs user out */
+ signOut: function() {
+ AuthEngine.signOut();
+ },
+
+ errorNotify: function(error) {
+ this.fire('google-signin-aware-error', error);
+ },
+
+ _appPackageNameChanged: function(newName, oldName) {
+ AuthEngine.appPackageName = newName;
+ },
+
+ _clientIdChanged: function(newId, oldId) {
+ AuthEngine.clientId = newId;
+ },
+
+ _cookiePolicyChanged: function(newPolicy, oldPolicy) {
+ AuthEngine.cookiePolicy = newPolicy;
+ },
+
+ _requestVisibleActionsChanged: function(newVal, oldVal) {
+ AuthEngine.requestVisibleActions = newVal;
+ },
+
+ _hostedDomainChanged: function(newVal, oldVal) {
+ AuthEngine.hostedDomain = newVal;
+ },
+
+ _offlineChanged: function(newVal, oldVal) {
+ AuthEngine.offline = newVal;
+ },
+
+ _offlineAlwaysPromptChanged: function(newVal, oldVal) {
+ AuthEngine.offlineAlwaysPrompt = newVal;
+ },
+
+ _scopesChanged: function(newVal, oldVal) {
+ AuthEngine.requestScopes(newVal);
+ this._updateScopeStatus(undefined);
+ },
+
+ _openidPromptChanged: function(newVal, oldVal) {
+ AuthEngine.openidPrompt = newVal;
+ },
+
+ _updateScopeStatus: function(user) {
+ var newAuthorized = this.signedIn && AuthEngine.hasGrantedScopes(this.scopes);
+ if (newAuthorized !== this.isAuthorized) {
+ this._setIsAuthorized(newAuthorized);
+ if (newAuthorized) {
+ this.fire('google-signin-aware-success', user);
+ }
+ else {
+ this.fire('google-signin-aware-signed-out', user);
+ }
+ }
+ },
+
+ _updateOfflineCode: function(code) {
+ if (code) {
+ this.fire('google-signin-offline-success', {code: code});
+ }
+ }
+ });
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/google-signin/google-signin-styles.html b/catapult/third_party/polymer/components/google-signin/google-signin-styles.html
new file mode 100644
index 00000000..5a111199
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/google-signin-styles.html
@@ -0,0 +1,267 @@
+<!--
+Copyright 2014 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
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<dom-module id="google-signin-styles">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ position: relative;
+ box-sizing: border-box;
+ margin: 0 0.29em;
+ background: transparent;
+ text-align: center;
+ font: inherit;
+ outline: none;
+ border-radius: 3px;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ z-index: 0;
+ }
+
+ :host([disabled]) {
+ cursor: auto;
+ pointer-events: none;
+ }
+
+ :host([disabled]) #button {
+ background: #eaeaea;
+ color: #a8a8a8;
+ }
+
+ #button {
+ position: relative;
+ outline: none;
+ font-size: 14px;
+ font-weight: 400;
+ font-family: 'RobotoDraft','Roboto',arial,sans-serif;
+ white-space: nowrap;
+ border-radius: inherit;
+ }
+
+ iron-icon {
+ width: 22px;
+ height: 22px;
+ margin: 6px;
+ }
+
+ .icon {
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ #shadow {
+ border-radius: inherit;
+ }
+
+ #ripple {
+ pointer-events: none;
+ }
+
+ .button-content {
+ outline: none;
+ }
+
+ .buttonText {
+ display: inline-block;
+ vertical-align: middle;
+ padding-right: .8em;
+ }
+
+ /*
+ * Dark Theme
+ */
+ .theme-dark {
+ background: #da4336;
+ color: #ffffff;
+ border: 1px solid transparent;
+ }
+
+ .theme-dark.signedIn-true.additionalAuth-false {
+ background: #999;
+ border: 1px solid #888;
+ }
+
+ .theme-dark.signedIn-true.additionalAuth-false:hover,
+ .theme-dark.signedIn-true.additionalAuth-false:focus {
+ background: #aaa;
+ }
+
+ :host([noink]) .theme-dark:hover,
+ :host([noink]) .theme-dark:focus {
+ background: #e74b37;
+ }
+
+ :host([noink]) .theme-dark.signedIn-true.additionalAuth-false:hover,
+ :host([noink]) .theme-dark.signedIn-true.additionalAuth-false:focus {
+ background: #aaa;
+ }
+
+ /*
+ * Light Theme
+ */
+ .theme-light {
+ background: #fff;
+ color: #737373;
+ border: 1px solid #d9d9d9;
+ }
+
+ .theme-light.signedIn-true.additionalAuth-false {
+ background: #c0c0c0;
+ color: #fff;
+ border: #888 1px solid;
+ }
+
+ .theme-light.signedIn-true.additionalAuth-false:hover,
+ .theme-light.signedIn-true.additionalAuth-false:focus {
+ background: #aaa;
+ }
+
+ :host([noink]) .theme-light .button-content:hover,
+ :host([noink]) .theme-light:focus {
+ border: 1px solid #c0c0c0;
+ }
+
+ :host([noink]) .theme-light.signedIn-true.additionalAuth-false:hover,
+ :host([noink]) .theme-light.signedIn-true.additionalAuth-false:focus {
+ background: #aaa;
+ }
+
+ /*
+ * Icon Only Width
+ */
+ .width-iconOnly .buttonText {
+ display: none;
+ }
+
+ /*
+ * Tall Height
+ */
+ .height-tall .buttonText {
+ font-size: 15px;
+ font-weight: 700;
+ }
+
+ .height-tall iron-icon {
+ width: 30px;
+ height: 30px;
+ margin: 8px;
+ }
+
+ /*
+ * Short Height
+ */
+ .height-short .buttonText {
+ font-size: 11px;
+ }
+
+ .height-short iron-icon {
+ width: 16px;
+ height: 16px;
+ margin: 3px;
+ }
+
+
+ /*
+ * Branding
+ */
+
+ /* Google Scopes */
+
+ /* Dark Theme */
+ .brand-google.theme-dark {
+ background: #4184F3;
+ color: #fff;
+ border: 1px solid #3266d5;
+ }
+
+ .brand-google.theme-dark .icon {
+ background: #fff;
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+ }
+
+ .brand-google.theme-dark.width-iconOnly .icon {
+ border-radius: 2px;
+ }
+
+ .brand-google.theme-dark .buttonText {
+ padding-left: .8em;
+ }
+
+ .brand-google.theme-dark #ripple {
+ color: #1b39a8;
+ }
+
+ :host([noink]) .brand-google.theme-dark:hover,
+ :host([noink]) .brand-google.theme-dark:focus {
+ background: #e74b37;
+ }
+
+ .brand-google.theme-light .icon {
+ color: #4184F3;
+ }
+
+ .brand-google.theme-light.signedIn-true.additionalAuth-false .icon {
+ color: #fff;
+ }
+
+ .brand-google.theme-light #ripple {
+ color: #444;
+ }
+
+ :host([noink]) .brand-google.theme-light:hover,
+ :host([noink]) .brand-google.theme-light:focus {
+ border: 1px solid #c0c0c0;
+ }
+
+ .brand-google-plus.theme-dark {
+ background: #da4336;
+ color: #fff;
+ border: 1px solid transparent;
+ }
+
+ .brand-google-plus.theme-dark #ripple {
+ color: #c43828;
+ }
+
+ /* Light Theme */
+ .brand-google-plus.theme-light {
+ background: #fff;
+ color: #737373;
+ border: 1px solid #d9d9d9;
+ }
+
+ .brand-google-plus.theme-light .icon {
+ color: #e74b37;
+ }
+
+ .brand-google-plus.theme-light.signedIn-true.additionalAuth-false .icon {
+ color: #fff;
+ }
+
+ .brand-google-plus.theme-light #ripple {
+ color: #400;
+ }
+
+ :host([noink]) .brand-google-plus.theme-light:hover,
+ :host([noink]) .brand-google-plus.theme-light:focus {
+ border: 1px solid #c0c0c0;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/google-signin/google-signin.html b/catapult/third_party/polymer/components/google-signin/google-signin.html
new file mode 100644
index 00000000..dfafc8ce
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/google-signin.html
@@ -0,0 +1,596 @@
+<!--
+Copyright 2014 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
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="google-signin-aware.html">
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../font-roboto/roboto.html">
+<link rel="import" href="../google-apis/google-js-api.html">
+<link rel="import" href="../paper-ripple/paper-ripple.html">
+<link rel="import" href="../paper-material/paper-material.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="google-icons.html">
+<link rel="import" href="google-signin-styles.html">
+
+<dom-module id="google-signin">
+ <template>
+ <style include="google-signin-styles iron-positioning"></style>
+
+ <google-signin-aware id="aware"
+ app-package-name="{{appPackageName}}"
+ client-id="{{clientId}}"
+ cookie-policy="{{cookiePolicy}}"
+ request-visible-actions="{{requestVisibleActions}}"
+ hosted-domain="{{hostedDomain}}"
+ offline="{{offline}}"
+ offline-always-prompt="{{offlineAlwaysPrompt}}"
+ scopes="{{scopes}}"
+ openid-prompt="{{openidPrompt}}"
+ initialized="{{initialized}}"
+ signed-in="{{signedIn}}"
+ is-authorized="{{isAuthorized}}"
+ need-additional-auth="{{needAdditionalAuth}}"
+ has-plus-scopes="{{hasPlusScopes}}"></google-signin-aware>
+ <template is="dom-if" if="{{raised}}">
+ <paper-material id="shadow" class="fit" elevation="2" animated></paper-material>
+ </template>
+ <div id="button"
+ class$="[[_computeButtonClass(height, width, theme, signedIn, _brand, needAdditionalAuth)]]">
+
+ <paper-ripple id="ripple" class="fit"></paper-ripple>
+ <!-- this div is needed to position the ripple behind text content -->
+ <div>
+ <template is="dom-if" if="{{_computeButtonIsSignIn(signedIn, needAdditionalAuth)}}">
+ <div class="button-content signIn" tabindex="0"
+ on-click="signIn" on-keydown="_signInKeyPress">
+ <span class="icon"><iron-icon icon="[[_brandIcon]]"></iron-icon></span>
+ <span class="buttonText">{{_labelSignin}}</span>
+ </div>
+ </template>
+ <template is="dom-if" if="{{_computeButtonIsSignOut(signedIn, needAdditionalAuth) }}">
+ <div class="button-content signOut" tabindex="0"
+ on-click="signOut" on-keydown="_signOutKeyPress">
+ <span class="icon"><iron-icon icon="[[_brandIcon]]"></iron-icon></span>
+ <span class="buttonText">{{labelSignout}}</span>
+ </div>
+ </template>
+ <template is="dom-if" if="{{_computeButtonIsSignOutAddl(signedIn, needAdditionalAuth) }}">
+ <div class="button-content signIn" tabindex="0"
+ on-click="signIn" on-keydown="_signInKeyPress">
+ <span class="icon"><iron-icon icon="[[_brandIcon]]"></iron-icon></span>
+ <span class="buttonText">{{labelAdditional}}</span>
+ </div>
+ </template>
+ </div>
+
+ </div>
+ </template>
+</dom-module>
+<script>
+ (function() {
+
+ /**
+ * Enum brand values.
+ * @readonly
+ * @enum {string}
+ */
+ var BrandValue = {
+ GOOGLE: 'google',
+ PLUS: 'google-plus'
+ };
+
+ /**
+ * Enum height values.
+ * @readonly
+ * @enum {string}
+ */
+ var HeightValue = {
+ SHORT: 'short',
+ STANDARD: 'standard',
+ TALL: 'tall'
+ };
+
+ /**
+ * Enum button label default values.
+ * @readonly
+ * @enum {string}
+ */
+ var LabelValue = {
+ STANDARD: 'Sign in',
+ WIDE: 'Sign in with Google',
+ WIDE_PLUS: 'Sign in with Google+'
+ };
+
+ /**
+ * Enum theme values.
+ * @readonly
+ * @enum {string}
+ */
+ var ThemeValue = {
+ LIGHT: 'light',
+ DARK: 'dark'
+ };
+
+ /**
+ * Enum width values.
+ * @readonly
+ * @enum {string}
+ */
+ var WidthValue = {
+ ICON_ONLY: 'iconOnly',
+ STANDARD: 'standard',
+ WIDE: 'wide'
+ };
+
+/**
+&lt;google-signin&gt; is used to authenticate with Google, allowing you to interact
+with other Google APIs such as Drive and Google+.
+
+<img style="max-width:100%;" src="https://cloud.githubusercontent.com/assets/107076/6791176/5c868822-d16a-11e4-918c-ec9b84a2db45.png"/>
+
+If you do not need to show the button, use companion `<google-signin-aware>` element to declare scopes, check authentication state.
+
+#### Examples
+
+ <google-signin client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin>
+
+ <google-signin label-signin="Sign-in" client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin>
+
+ <google-signin theme="dark" width="iconOnly" client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin>
+
+
+#### Notes
+
+The attribute `clientId` is provided in your Google Developers Console
+(https://console.developers.google.com).
+
+The `scopes` attribute allows you to specify which scope permissions are required
+(e.g do you want to allow interaction with the Google Drive API). Many APIs also
+need to be enabled in the Google Developers Console before you can use them.
+
+The `requestVisibleActions` attribute is necessary if you want to write app
+activities (https://developers.google.com/+/web/app-activities/) on behalf of
+the user. Please note that this attribute is only valid in combination with the
+plus.login scope (https://www.googleapis.com/auth/plus.login).
+
+The `offline` attribute allows you to get an auth code which your server can
+redeem for an offline access token
+(https://developers.google.com/identity/sign-in/web/server-side-flow).
+You can also set `offline-always-prompt` instead of `offline` to ensure that your app
+will re-prompt the user for offline access and generate a working `refresh_token`
+even if they have already granted offline access to your app in the past.
+
+Use label properties to customize prompts.
+
+The button can be styled in using the `height`, `width`, and `theme` attributes.
+These attributes help you follow the Google+ Sign-In button branding guidelines
+(https://developers.google.com/+/branding-guidelines).
+
+The `google-signin-success` event is triggered when a user successfully authenticates
+and `google-signed-out` is triggered when user signs out.
+You can also use `isAuthorized` attribute to observe user's authentication state.
+
+Additional events, such as `google-signout-attempted` are
+triggered when the user attempts to sign-out and successfully signs out.
+
+When requesting offline access, the `google-signin-offline-success` event is
+triggered when the user successfully consents with offline support.
+
+The `google-signin-necessary` event is fired when scopes requested via
+google-signin-aware elements require additional user permissions.
+
+#### Testing
+
+By default, the demo accompanying this element is setup to work on localhost with
+port 8080. That said, you *should* update the `clientId` to your own one for
+any apps you're building. See the Google Developers Console
+(https://console.developers.google.com) for more info.
+
+@demo
+*/
+
+ Polymer({
+
+ is: 'google-signin',
+
+ /**
+ * Fired when user is signed in.
+ * You can use auth2 api to retrieve current user: `gapi.auth2.getAuthInstance()['currentUser'].get();`
+ * @event google-signin-success
+ */
+
+ /**
+ * Fired when the user is signed-out.
+ * @event google-signed-out
+ */
+
+ /**
+ * Fired if user requires additional authorization
+ * @event google-signin-necessary
+ */
+
+ /**
+ * Fired when signed in, and scope has been authorized
+ * @param {Object} result Authorization result.
+ * @event google-signin-aware-success
+ */
+
+ /**
+ * Fired when there is an error during the signin flow.
+ * @param {Object} detail The error object returned from the OAuth 2 flow.
+ * @event google-signin-aware-error
+ */
+
+ /**
+ * Fired when an offline authorization is successful.
+ * @param {{code: string}} detail -
+ * code: The one-time authorization code from Google.
+ * Your application can exchange this for an `access_token` and `refresh_token`
+ * @event google-signin-offline-success
+ */
+
+ /**
+ * This block is needed so the previous @param is not assigned to the next property.
+ */
+
+ properties: {
+ /**
+ * App package name for android over-the-air installs.
+ * See the relevant [docs](https://developers.google.com/+/web/signin/android-app-installs)
+ */
+ appPackageName: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * The brand being used for logo and styling.
+ *
+ * @default 'google'
+ */
+ brand: {
+ type: String,
+ value: ''
+ },
+
+ /** @private */
+ _brand: {
+ type: String,
+ computed: '_computeBrand(brand, hasPlusScopes)'
+ },
+
+ /**
+ * a Google Developers clientId reference
+ */
+ clientId: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * The cookie policy defines what URIs have access to the session cookie
+ * remembering the user's sign-in state.
+ * See the relevant [docs](https://developers.google.com/+/web/signin/reference#determining_a_value_for_cookie_policy) for more information.
+ *
+ * @default 'single_host_origin'
+ */
+ cookiePolicy: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * The height to use for the button.
+ *
+ * Available options: short, standard, tall.
+ *
+ * @type {string}
+ */
+ height: {
+ type: String,
+ value: 'standard'
+ },
+
+ /**
+ * By default the ripple expands to fill the button. Set this to true to
+ * constrain the ripple to a circle within the button.
+ */
+ fill: {
+ type: Boolean,
+ value: true
+ },
+
+ /**
+ * An optional label for the button for additional permissions.
+ */
+ labelAdditional: {
+ type: String,
+ value: 'Additional permissions required'
+ },
+
+ /**
+ * An optional label for the sign-in button.
+ */
+ labelSignin: {
+ type: String,
+ value: ''
+ },
+
+ _labelSignin: {
+ type: String,
+ computed: '_computeSigninLabel(labelSignin, width, _brand)'
+ },
+
+ /**
+ * An optional label for the sign-out button.
+ */
+ labelSignout: {
+ type: String,
+ value: 'Sign out'
+ },
+
+ /**
+ * If true, the button will be styled with a shadow.
+ */
+ raised: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The app activity types you want to write on behalf of the user
+ * (e.g http://schemas.google.com/AddActivity)
+ */
+ requestVisibleActions: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * The Google Apps domain to which users must belong to sign in.
+ * See the relevant [docs](https://developers.google.com/identity/sign-in/web/reference) for more information.
+ */
+ hostedDomain: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * Allows for offline `access_token` retrieval during the signin process.
+ */
+ offline: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Forces a re-prompt, even if the user has already granted offline
+ * access to your application in the past. You only need one of
+ * `offline` and `offlineAlwaysPrompt`.
+ */
+ offlineAlwaysPrompt: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The scopes to provide access to (e.g https://www.googleapis.com/auth/drive)
+ * and should be space-delimited.
+ */
+ scopes: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * Space-delimited, case-sensitive list of strings that
+ * specifies whether the the user is prompted for reauthentication
+ * and/or consent. The defined values are:
+ * none: do not display authentication or consent pages.
+ * This value is mutually exclusive with the rest.
+ * login: always prompt the user for reauthentication.
+ * consent: always show consent screen.
+ * select_account: always show account selection page.
+ * This enables a user who has multiple accounts to select amongst
+ * the multiple accounts that they might have current sessions for.
+ * For more information, see "prompt" parameter description in
+ * https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters
+ */
+ openidPrompt: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * The theme to use for the button.
+ *
+ * Available options: light, dark.
+ *
+ * @attribute theme
+ * @type {string}
+ * @default 'dark'
+ */
+ theme: {
+ type: String,
+ value: 'light'
+ },
+
+ /**
+ * The width to use for the button.
+ *
+ * Available options: iconOnly, standard, wide.
+ *
+ * @type {string}
+ */
+ width: {
+ type: String,
+ value: 'standard'
+ },
+
+ _brandIcon: {
+ type: String,
+ computed: '_computeIcon(_brand)'
+ },
+
+ /**
+ * True if *any* element has google+ scopes
+ */
+ hasPlusScopes: {
+ type: Boolean,
+ notify: true,
+ value: false
+ },
+
+ /**
+ * True if additional authorization required globally
+ */
+ needAdditionalAuth: {
+ type: Boolean,
+ notify: true,
+ value: false
+ },
+
+ /**
+ * True when the auth library has been initialized, and signedIn property value is set from the first api response.
+ */
+ initialized: {
+ type: Boolean,
+ notify: true,
+ value: false
+ },
+
+ /**
+ * Is user signed in?
+ */
+ signedIn: {
+ type: Boolean,
+ notify: true,
+ value: false,
+ observer: '_observeSignedIn'
+ },
+
+ /**
+ * True if authorizations for *this* element have been granted
+ */
+ isAuthorized: {
+ type: Boolean,
+ notify: true,
+ value: false
+ }
+
+ },
+
+ _computeButtonClass: function(height, width, theme, signedIn, brand, needAdditionalAuth) {
+ return "height-" + height + " width-" + width + " theme-" + theme + " signedIn-" + signedIn + " brand-" + brand + " additionalAuth-" + needAdditionalAuth;
+ },
+
+ _computeIcon: function(brand) {
+ return "google:" + brand;
+ },
+
+ /* Button state computed */
+ _computeButtonIsSignIn: function(signedIn, additionalAuth) {
+ return !signedIn;
+ },
+
+ _computeButtonIsSignOut: function(signedIn, additionalAuth) {
+ return signedIn && !additionalAuth;
+ },
+
+ _computeButtonIsSignOutAddl: function(signedIn, additionalAuth) {
+ return signedIn && additionalAuth;
+ },
+
+ _computeBrand: function(attrBrand, hasPlusScopes) {
+ var newBrand;
+ if (attrBrand) {
+ newBrand = attrBrand;
+ } else if (hasPlusScopes) {
+ newBrand = BrandValue.PLUS;
+ } else {
+ newBrand = BrandValue.GOOGLE;
+ };
+ return newBrand;
+ },
+
+ _observeSignedIn: function(newVal, oldVal) {
+ if (newVal) {
+ if (this.needAdditionalAuth) {
+ this.fire('google-signin-necessary');
+ }
+ this.fire('google-signin-success');
+ }
+ // Use `oldVal` avoids to fire the event at the initialization of
+ // `signedIn`.
+ else if (oldVal) {
+ this.fire('google-signed-out');
+ }
+ },
+
+ /**
+ * Determines the proper label based on the attributes.
+ */
+ _computeSigninLabel: function(labelSignin, width, _brand) {
+ if (labelSignin) {
+ return labelSignin;
+ } else {
+ switch(width) {
+
+ case WidthValue.WIDE:
+ return (_brand == BrandValue.PLUS) ?
+ LabelValue.WIDE_PLUS : LabelValue.WIDE;
+
+ case WidthValue.STANDARD:
+ return LabelValue.STANDARD;
+
+ case WidthValue.ICON_ONLY:
+ return '';
+
+ default:
+ console.warn("bad width value: ", width);
+ return LabelValue.STANDARD;
+ }
+ }
+ },
+
+ /** Sign in user. Opens the authorization dialog for signing in.
+ * The dialog will be blocked by a popup blocker unless called inside click handler.
+ */
+ signIn: function () {
+ this.$.aware.signIn();
+ },
+
+ _signInKeyPress: function (e) {
+ if (e.which == 13 || e.keyCode == 13 || e.which == 32 || e.keyCode == 32) {
+ e.preventDefault();
+ this.signIn();
+ }
+ },
+
+ /** Sign out the user */
+ signOut: function () {
+ this.fire('google-signout-attempted');
+ this.$.aware.signOut();
+ },
+
+ _signOutKeyPress: function (e) {
+ if (e.which == 13 || e.keyCode == 13 || e.which == 32 || e.keyCode == 32) {
+ e.preventDefault();
+ this.signOut();
+ }
+ }
+ });
+ }());
+</script>
diff --git a/catapult/third_party/polymer/components/google-signin/index.html b/catapult/third_party/polymer/components/google-signin/index.html
new file mode 100644
index 00000000..e8f85ae2
--- /dev/null
+++ b/catapult/third_party/polymer/components/google-signin/index.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<!-- Copyright (c) 2014 Google Inc. All rights reserved. -->
+<html>
+<head>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/.bower.json b/catapult/third_party/polymer/components/iron-a11y-announcer/.bower.json
new file mode 100644
index 00000000..988d27e9
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/.bower.json
@@ -0,0 +1,43 @@
+{
+ "name": "iron-a11y-announcer",
+ "version": "1.0.6",
+ "description": "A singleton element that simplifies announcing text to screen readers.",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "a11y",
+ "live"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-a11y-announcer.git"
+ },
+ "main": "iron-a11y-announcer.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "dependencies": {
+ "polymer": "polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-button": "polymerelements/paper-button#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-a11y-announcer",
+ "_release": "1.0.6",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.6",
+ "commit": "f2420aadc83b78923cba06f67fad8e523fd5d792"
+ },
+ "_source": "https://github.com/PolymerElements/iron-a11y-announcer.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-a11y-announcer"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-a11y-announcer/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..594376ff
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-a11y-announcer/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/.gitignore b/catapult/third_party/polymer/components/iron-a11y-announcer/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/.travis.yml b/catapult/third_party/polymer/components/iron-a11y-announcer/.travis.yml
new file mode 100644
index 00000000..70c1510a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ QlyQxWnziNgzWqptOtM0Oq1s/q6YHT/GPuslA31yC0nex7Wi6X9DFoF5rNlDEY0Y6WxCh8xzsZpwXrI9vZypgKoZlAwO1e3RLDsGV0APPrWg66MyImAwlHAJolvJg+ASsLJ8pm9nxMP/xapRamciqUMTUTeZh0V/5SR82BXQCf3zXe+9cMmAFNXBrIg6LDCm77AwW+1vkE/IZAL1oShSBwXEybn9kpYsdCiOGdnvMlChhaeqRlOUXkr3LHPUDtV8hbd2EswmPeHSV+RGdB/UFyRGa4g7gPgqtz7U2FS0/BqT0G2iJlAXGCttJnS6fXwyCriREplYjXzqrX0MeRWHD5vNxAucO/Va1n2tHEmJM3OhIYgO8VDM8S3nRRUDE/ifSjsu1UgBN7b1vml0zWo7rdvpD8fedx6+g4ph390kg5XRFNJZJ03YijfcNVUfnOTEWt9LntGRq5aKPB6RSbEvkuZFjLGHHD/xQu2LfyIjmueIWJNk4JeCNrm3zFPMhBAZfu7WTJPTk3dZ2L+0mty1QbaJ/lyTWIBYbTVBwwHzXKrPbgg5u/9e6DjhT8Zg/eJvmN/+sXAlUqnmTAE9coQ0LIivg6COWPoRDmdGl2uMygiCaLkFAy4sqhjP99Aq5/ekZWXUrTTa4NGw2qnvM4JY9YcEzUC0ZqzM11gj8x8ATi0=
+ - secure: >-
+ d0wz93AwXyNVmCr33od+TFC51nZgUzcdwHiJWxX0E+msZ8VgYCjj21D6OOZy84O7vYiPFy8vO03dvyqkj1uclEvfu2YlfiEaRxifKaxN6mQx142WjBtdHFjEUfBJR5eqm5qSeGj7aSZzPgerUl6yAkYH5tFldBatevF5Ax98Yr1dCsgpegsLCmBmusPH7tERnBilalcvXKVBfRXrnrkFkVoWroBb04W79aZSTlLGTlpBJCzR9Xe7RiXqnanSQQb1LjyCl55P0NvVVRjwpoVnikRqkIV/jehcNfIiJSC/vetepqqUehD6RdP2T8Nio7YvlLtXnW9vptlKYL2uZjhg23DyhgGW/4ZPaIABWVBqVUBbyaX6GCXo3EMyQcZhi17qCWEKnFGCrorC/4ZM6A0kJ+olOfQxszf9HrAX8+9DCaiKscn2Lz+ON/opFKFRAQngCJ9swBc27twavUxx4qNzOVJLdH8oGhCdl5DA4mgGGDWZz463X0HzagGUpi/RfME26uQnTkyK8eErL2yac+1VmA/QOx0RkYlrZ/pIEywkZPWusjJepCm9nlZGylaBr2mDpk8Kea+7IytO6sefiBwjX1RiqmnjnszO3jb/w5s0giUItWuFmDr14sOaFmj6wQB643eSGi42LSPG+FMea1RwUupyEPeLZq/aoJ0jmewGLv4=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-a11y-announcer/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/README.md b/catapult/third_party/polymer/components/iron-a11y-announcer/README.md
new file mode 100644
index 00000000..8b997a30
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/README.md
@@ -0,0 +1,55 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-a11y-announcer.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-a11y-announcer.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-a11y-announcer)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-a11y-announcer)_
+
+
+##&lt;iron-a11y-announcer&gt;
+
+`iron-a11y-announcer` is a singleton element that is intended to add a11y
+to features that require on-demand announcement from screen readers. In
+order to make use of the announcer, it is best to request its availability
+in the announcing element.
+
+Example:
+
+```javascript
+Polymer({
+
+ is: 'x-chatty',
+
+ attached: function() {
+ // This will create the singleton element if it has not
+ // been created yet:
+ Polymer.IronA11yAnnouncer.requestAvailability();
+ }
+});
+```
+
+After the `iron-a11y-announcer` has been made available, elements can
+make announces by firing bubbling `iron-announce` events.
+
+Example:
+
+```javascript
+this.fire('iron-announce', {
+ text: 'This is an announcement!'
+}, { bubbles: true });
+```
+
+Note: announcements are only audible if you have a screen reader enabled.
+
+
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/bower.json b/catapult/third_party/polymer/components/iron-a11y-announcer/bower.json
new file mode 100644
index 00000000..8e963018
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "iron-a11y-announcer",
+ "version": "1.0.6",
+ "description": "A singleton element that simplifies announcing text to screen readers.",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "a11y",
+ "live"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-a11y-announcer.git"
+ },
+ "main": "iron-a11y-announcer.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "dependencies": {
+ "polymer": "polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-button": "polymerelements/paper-button#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/demo/index.html b/catapult/third_party/polymer/components/iron-a11y-announcer/demo/index.html
new file mode 100644
index 00000000..0dd286ac
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/demo/index.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>iron-a11y-announcer demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="x-announces.html">
+
+</head>
+<body>
+ <div class="horizontal-section-container">
+ <div>
+ <div class="vertical-section">
+ <span>Note: in order to hear the announcements, be sure to turn on your favorite screen reader!</span>
+ <x-announces>Hello, my name is Ava.</x-announces>
+ <x-announces>This true sentence is false.</x-announces>
+ <x-announces>Are you paying attention?</x-announces>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/demo/x-announces.html b/catapult/third_party/polymer/components/iron-a11y-announcer/demo/x-announces.html
new file mode 100644
index 00000000..15c43c0d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/demo/x-announces.html
@@ -0,0 +1,60 @@
+<!--
+@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
+-->
+<!--
+Copyright (c) 2015 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
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../paper-button/paper-button.html">
+<link rel="import" href="../iron-a11y-announcer.html">
+
+<dom-module id="x-announces">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ padding: 1em 0;
+ }
+
+ paper-button {
+ background: #4285f4;
+ color: #fff;
+ }
+ </style>
+
+ <paper-button on-tap="_onTapAnnounce" raised>Announce</paper-button>
+ <span id="content" aria-hidden="true">
+ <content></content>
+ </span>
+ </template>
+ <script>
+ Polymer({
+ is: 'x-announces',
+
+ attached: function() {
+ Polymer.IronA11yAnnouncer.requestAvailability();
+ },
+
+ _onTapAnnounce: function() {
+ this.fire('iron-announce', {
+ text: this.$.content.textContent.trim()
+ }, {
+ bubbles: true
+ });
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/index.html b/catapult/third_party/polymer/components/iron-a11y-announcer/index.html
new file mode 100644
index 00000000..1f8889a8
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-a11y-announcer</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/iron-a11y-announcer.html b/catapult/third_party/polymer/components/iron-a11y-announcer/iron-a11y-announcer.html
new file mode 100644
index 00000000..e2bec4e6
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/iron-a11y-announcer.html
@@ -0,0 +1,122 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!--
+`iron-a11y-announcer` is a singleton element that is intended to add a11y
+to features that require on-demand announcement from screen readers. In
+order to make use of the announcer, it is best to request its availability
+in the announcing element.
+
+Example:
+
+ Polymer({
+
+ is: 'x-chatty',
+
+ attached: function() {
+ // This will create the singleton element if it has not
+ // been created yet:
+ Polymer.IronA11yAnnouncer.requestAvailability();
+ }
+ });
+
+After the `iron-a11y-announcer` has been made available, elements can
+make announces by firing bubbling `iron-announce` events.
+
+Example:
+
+ this.fire('iron-announce', {
+ text: 'This is an announcement!'
+ }, { bubbles: true });
+
+Note: announcements are only audible if you have a screen reader enabled.
+
+@group Iron Elements
+@demo demo/index.html
+-->
+
+<dom-module id="iron-a11y-announcer">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ position: fixed;
+ clip: rect(0px,0px,0px,0px);
+ }
+ </style>
+ <div aria-live$="[[mode]]">[[_text]]</div>
+ </template>
+</dom-module>
+<script>
+'use strict';
+(function() {
+ 'use strict';
+ Polymer.IronA11yAnnouncer = Polymer({
+ is: 'iron-a11y-announcer',
+
+ properties: {
+
+ /**
+ * The value of mode is used to set the `aria-live` attribute
+ * for the element that will be announced. Valid values are: `off`,
+ * `polite` and `assertive`.
+ */
+ mode: {
+ type: String,
+ value: 'polite'
+ },
+
+ _text: {
+ type: String,
+ value: ''
+ }
+ },
+
+ created: function() {
+ if (!Polymer.IronA11yAnnouncer.instance) {
+ Polymer.IronA11yAnnouncer.instance = this;
+ }
+
+ document.body.addEventListener('iron-announce', this._onIronAnnounce.bind(this));
+ },
+
+ /**
+ * Cause a text string to be announced by screen readers.
+ *
+ * @param {string} text The text that should be announced.
+ */
+ announce: function(text) {
+ this._text = '';
+ this.async(function() {
+ this._text = text;
+ }, 100);
+ },
+
+ _onIronAnnounce: function(event) {
+ if (event.detail && event.detail.text) {
+ this.announce(event.detail.text);
+ }
+ }
+ });
+
+ Polymer.IronA11yAnnouncer.instance = null;
+
+ Polymer.IronA11yAnnouncer.requestAvailability = function() {
+ if (!Polymer.IronA11yAnnouncer.instance) {
+ Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y-announcer');
+ }
+
+ document.body.appendChild(Polymer.IronA11yAnnouncer.instance);
+ };
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/test/index.html b/catapult/third_party/polymer/components/iron-a11y-announcer/test/index.html
new file mode 100644
index 00000000..4307eb38
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="utf-8">
+ <title>iron-a11y-announcer tests</title>
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'iron-a11y-announcer.html',
+ 'iron-a11y-announcer.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-announcer/test/iron-a11y-announcer.html b/catapult/third_party/polymer/components/iron-a11y-announcer/test/iron-a11y-announcer.html
new file mode 100644
index 00000000..510bf6f9
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-announcer/test/iron-a11y-announcer.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+ <title>iron-a11y-announcer</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../iron-a11y-announcer.html">
+</head>
+<body>
+
+ <test-fixture id="Announcer">
+ <template>
+ <iron-a11y-announcer></iron-a11y-announcer>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('<iron-a11y-announcer>', function() {
+ var announcer;
+
+ setup(function() {
+ announcer = fixture('Announcer');
+ });
+
+ test('announces when there is an iron-announce event', function() {
+ var event = new CustomEvent('iron-announce', {
+ bubbles: true,
+ detail: {
+ text: 'foo'
+ }
+ });
+
+ sinon.spy(announcer, 'announce');
+
+ document.body.dispatchEvent(event);
+
+ expect(announcer.announce.callCount).to.be.equal(1);
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.bower.json b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.bower.json
new file mode 100644
index 00000000..64a15f03
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.bower.json
@@ -0,0 +1,43 @@
+{
+ "name": "iron-a11y-keys-behavior",
+ "version": "1.1.9",
+ "description": "A behavior that enables keybindings for greater a11y.",
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "a11y",
+ "input"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-a11y-keys-behavior.git"
+ },
+ "main": "iron-a11y-keys-behavior.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "paper-styles": "PolymerElements/paper-styles#^1.0.2",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-a11y-keys-behavior",
+ "_release": "1.1.9",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.9",
+ "commit": "26243e0f8687b8ea3d95eed3cefb7661a388dbce"
+ },
+ "_source": "https://github.com/PolymerElements/iron-a11y-keys-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-a11y-keys-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..7d936048
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-a11y-keys-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.gitignore b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.travis.yml
new file mode 100644
index 00000000..26583689
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/.travis.yml
@@ -0,0 +1,25 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ XJ31r/5USVGZRtziCLfr8qM1pJKKQMUN1AeYbCdDFEc6i293WxZneR8PwUVhvyptu+qdyd28uy24sH+Ob7kShFbZTUaif5P4gqHPekrYToI0aHyhmVX7C1LmT7nEL8IcT62NhUwh+83eHTAdodkXgnhfQhPn9FHV24Dkvwm8OKhhzEhtTgUGVuGX9j9FyNV6n1+gf4X3Zq63+NkEUh5vpolpue4W7ul2u0sf4l0fzg9pvKPCmywUwX2i7wwAEf3CJghMu2xup54OzXTEkjjSou/ebt1ZnxaUNV1+dblfUne0v9wTD0dPF8H3DwgewwzcZSbOZmj6lFVHRzmLzWcRJOEKdDrpJkjpg7HIhNPGCKDUcNylekafqi7ezhzrkzFwkh6JCdAj7He4mv/X/OUDNjNCClB7Ms/+WPZwtACvIcR2/pvgZ+1PHbIkbIInyAe6iVMMR0oUecei/X+d04DH7iW7rrODVEu6qdibsJki0R0lR2184rrDO9pGek4rLu9sUQBDNgEM6ZLEXXByO8lpG4xStRdkg0/uR5i1/Q8kux4gIJ9yV8WLANkS8NVlmuJgIi6kbh5n4VVKaihGhbBUuTt2aL7fLnH2I6YRwjyNI9TOIRxwk4afppFYUuq6Fv+nfPcdqDOi5Y2AOXLJ3Yvco0+H57nXe/Ny29gFVW4Kftg=
+ - secure: >-
+ huEi/Ja2qnLatb7EJ4Jdc/XAeKphhdH6G+px7/XZY33oDawjStxakx0N/MpT0LPE1BdEWOYTzc17CzKv9R2L3ybWksqXyv/Zs+1NMTmpEAS/54Sk4E61aE3nrV5cfS2R8dBGbJhFoH1W237BDsbw9A4XhsTvhxlIsluWsZgeurbleGg+DgAmg8KlHGRddsfBFgXEk+Khhj6KPsbgPUiWhXpdnXKBPJKF7fJEAbsGR4aFK2eFbYd1OAgJg2Aye0n93IHe+SsxcKRUYteg6UK9V8fk7q5PBlvaodly4F3gH82l+zbnhcTFVW+qN0s6xDBTQzsQ55eTlO3pEezIo3u/1Lq41Yoe6scEkLs63pSYqoB3kakbhLMDJAen080ggdNg9evqvgyznKFYM7sqEcPu+KxHd043DyLTTW11y9lZ/hV3xSTdG4W8mV7+ILbIi54wMaYAcWSGMTOVM0JC/KDoVjze3tzDmfcZwiutLPBFgfrkfJQf3fyqcgvhoLKtHaWHI+76XDsXwEOS2Q5OX9oDtjoZaZ7r8Gp4dqwaKYceOrlsLbaZOLh5nJ4WnDbf4AqZkeM22QWWIfUN6aK+yhsDpQ/d+xJ+/WFENDADrMEKp0Lf3CkzAAcpHp3u65B9qsqweD5/5Je9t0GsA/NvK2xCasnNz6inYy4tAx9i4NWPcOY=
+node_js: stable
+addons:
+ firefox: '46.0'
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/README.md b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/README.md
new file mode 100644
index 00000000..b0949871
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/README.md
@@ -0,0 +1,58 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-a11y-keys-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-a11y-keys-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-a11y-keys-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-a11y-keys-behavior)_
+
+
+##Polymer.IronA11yKeysBehavior
+
+`Polymer.IronA11yKeysBehavior` provides a normalized interface for processing
+keyboard commands that pertain to [WAI-ARIA best practices](http://www.w3.org/TR/wai-aria-practices/#kbd_general_binding).
+The element takes care of browser differences with respect to Keyboard events
+and uses an expressive syntax to filter key presses.
+
+Use the `keyBindings` prototype property to express what combination of keys
+will trigger the callback. A key binding has the format
+`"KEY+MODIFIER:EVENT": "callback"` (`"KEY": "callback"` or
+`"KEY:EVENT": "callback"` are valid as well). Some examples:
+
+```javascript
+ keyBindings: {
+ 'space': '_onKeydown', // same as 'space:keydown'
+ 'shift+tab': '_onKeydown',
+ 'enter:keypress': '_onKeypress',
+ 'esc:keyup': '_onKeyup'
+ }
+```
+
+The callback will receive with an event containing the following information in `event.detail`:
+
+```javascript
+ _onKeydown: function(event) {
+ console.log(event.detail.combo); // KEY+MODIFIER, e.g. "shift+tab"
+ console.log(event.detail.key); // KEY only, e.g. "tab"
+ console.log(event.detail.event); // EVENT, e.g. "keydown"
+ console.log(event.detail.keyboardEvent); // the original KeyboardEvent
+ }
+```
+
+Use the `keyEventTarget` attribute to set up event handlers on a specific
+node.
+
+See the [demo source code](https://github.com/PolymerElements/iron-a11y-keys-behavior/blob/master/demo/x-key-aware.html)
+for an example.
+
+
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/bower.json b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/bower.json
new file mode 100644
index 00000000..9ad8c923
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "iron-a11y-keys-behavior",
+ "version": "1.1.9",
+ "description": "A behavior that enables keybindings for greater a11y.",
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "a11y",
+ "input"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-a11y-keys-behavior.git"
+ },
+ "main": "iron-a11y-keys-behavior.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "paper-styles": "PolymerElements/paper-styles#^1.0.2",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/index.html
new file mode 100644
index 00000000..2c3fec7c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Iron A11y Keys Behavior demo</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="x-key-aware.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+</head>
+<body>
+ <div class="vertical-section vertical-section-container centered">
+ <x-key-aware></x-key-aware>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/x-key-aware.html b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/x-key-aware.html
new file mode 100644
index 00000000..5914bb99
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/demo/x-key-aware.html
@@ -0,0 +1,105 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../paper-styles/color.html">
+<link rel="import" href="../iron-a11y-keys-behavior.html">
+
+<dom-module id="x-key-aware">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ }
+
+ pre {
+ color: var(--google-blue-700);
+ }
+
+ .keys {
+ line-height: 25px;
+ }
+
+ .keys span {
+ cursor: default;
+ background-color: var(--google-grey-100);
+ border: 1px solid var(--google-grey-300);
+ padding: 1px 5px;
+ border-radius: 5px;
+ }
+ </style>
+
+ <h4>Press any of these keys</h4>
+ <input type="checkbox" checked="{{preventDefault::change}}"> prevent default = {{preventDefault}}
+ <p class="keys">
+ <template is="dom-repeat" items="[[boundKeys]]">
+ <span>{{item}}</span>
+ </template>
+ </p>
+ <pre>[[pressed]]</pre>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'x-key-aware',
+
+ behaviors: [
+ Polymer.IronA11yKeysBehavior
+ ],
+
+ properties: {
+ pressed: {
+ type: String,
+ readOnly: true,
+ value: ''
+ },
+
+ boundKeys: {
+ type: Array,
+ value: function() {
+ return Object.keys(this.keyBindings).join(' ').split(' ');
+ }
+ },
+
+ preventDefault: {
+ type: Boolean,
+ value: true,
+ notify: true
+ },
+
+ keyEventTarget: {
+ type: Object,
+ value: function() {
+ return document.body;
+ }
+ }
+ },
+
+ keyBindings: {
+ '* pageup pagedown left right down up home end space enter @ ~ " $ ? ! \\ + : # backspace': '_updatePressed',
+ 'a': '_updatePressed',
+ 'shift+a alt+a': '_updatePressed',
+ 'shift+tab shift+space': '_updatePressed'
+ },
+
+ _updatePressed: function(event) {
+ console.log(event.detail);
+
+ if (this.preventDefault) {
+ event.preventDefault();
+ }
+ this._setPressed(
+ this.pressed + event.detail.combo + ' pressed!\n'
+ );
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/index.html b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/index.html
new file mode 100644
index 00000000..c53ba6cf
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-a11y-keys-behavior</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
new file mode 100644
index 00000000..840d3571
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
@@ -0,0 +1,496 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+ (function() {
+ 'use strict';
+
+ /**
+ * Chrome uses an older version of DOM Level 3 Keyboard Events
+ *
+ * Most keys are labeled as text, but some are Unicode codepoints.
+ * Values taken from: http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html#KeySet-Set
+ */
+ var KEY_IDENTIFIER = {
+ 'U+0008': 'backspace',
+ 'U+0009': 'tab',
+ 'U+001B': 'esc',
+ 'U+0020': 'space',
+ 'U+007F': 'del'
+ };
+
+ /**
+ * Special table for KeyboardEvent.keyCode.
+ * KeyboardEvent.keyIdentifier is better, and KeyBoardEvent.key is even better
+ * than that.
+ *
+ * Values from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode#Value_of_keyCode
+ */
+ var KEY_CODE = {
+ 8: 'backspace',
+ 9: 'tab',
+ 13: 'enter',
+ 27: 'esc',
+ 33: 'pageup',
+ 34: 'pagedown',
+ 35: 'end',
+ 36: 'home',
+ 32: 'space',
+ 37: 'left',
+ 38: 'up',
+ 39: 'right',
+ 40: 'down',
+ 46: 'del',
+ 106: '*'
+ };
+
+ /**
+ * MODIFIER_KEYS maps the short name for modifier keys used in a key
+ * combo string to the property name that references those same keys
+ * in a KeyboardEvent instance.
+ */
+ var MODIFIER_KEYS = {
+ 'shift': 'shiftKey',
+ 'ctrl': 'ctrlKey',
+ 'alt': 'altKey',
+ 'meta': 'metaKey'
+ };
+
+ /**
+ * KeyboardEvent.key is mostly represented by printable character made by
+ * the keyboard, with unprintable keys labeled nicely.
+ *
+ * However, on OS X, Alt+char can make a Unicode character that follows an
+ * Apple-specific mapping. In this case, we fall back to .keyCode.
+ */
+ var KEY_CHAR = /[a-z0-9*]/;
+
+ /**
+ * Matches a keyIdentifier string.
+ */
+ var IDENT_CHAR = /U\+/;
+
+ /**
+ * Matches arrow keys in Gecko 27.0+
+ */
+ var ARROW_KEY = /^arrow/;
+
+ /**
+ * Matches space keys everywhere (notably including IE10's exceptional name
+ * `spacebar`).
+ */
+ var SPACE_KEY = /^space(bar)?/;
+
+ /**
+ * Matches ESC key.
+ *
+ * Value from: http://w3c.github.io/uievents-key/#key-Escape
+ */
+ var ESC_KEY = /^escape$/;
+
+ /**
+ * Transforms the key.
+ * @param {string} key The KeyBoardEvent.key
+ * @param {Boolean} [noSpecialChars] Limits the transformation to
+ * alpha-numeric characters.
+ */
+ function transformKey(key, noSpecialChars) {
+ var validKey = '';
+ if (key) {
+ var lKey = key.toLowerCase();
+ if (lKey === ' ' || SPACE_KEY.test(lKey)) {
+ validKey = 'space';
+ } else if (ESC_KEY.test(lKey)) {
+ validKey = 'esc';
+ } else if (lKey.length == 1) {
+ if (!noSpecialChars || KEY_CHAR.test(lKey)) {
+ validKey = lKey;
+ }
+ } else if (ARROW_KEY.test(lKey)) {
+ validKey = lKey.replace('arrow', '');
+ } else if (lKey == 'multiply') {
+ // numpad '*' can map to Multiply on IE/Windows
+ validKey = '*';
+ } else {
+ validKey = lKey;
+ }
+ }
+ return validKey;
+ }
+
+ function transformKeyIdentifier(keyIdent) {
+ var validKey = '';
+ if (keyIdent) {
+ if (keyIdent in KEY_IDENTIFIER) {
+ validKey = KEY_IDENTIFIER[keyIdent];
+ } else if (IDENT_CHAR.test(keyIdent)) {
+ keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16);
+ validKey = String.fromCharCode(keyIdent).toLowerCase();
+ } else {
+ validKey = keyIdent.toLowerCase();
+ }
+ }
+ return validKey;
+ }
+
+ function transformKeyCode(keyCode) {
+ var validKey = '';
+ if (Number(keyCode)) {
+ if (keyCode >= 65 && keyCode <= 90) {
+ // ascii a-z
+ // lowercase is 32 offset from uppercase
+ validKey = String.fromCharCode(32 + keyCode);
+ } else if (keyCode >= 112 && keyCode <= 123) {
+ // function keys f1-f12
+ validKey = 'f' + (keyCode - 112);
+ } else if (keyCode >= 48 && keyCode <= 57) {
+ // top 0-9 keys
+ validKey = String(keyCode - 48);
+ } else if (keyCode >= 96 && keyCode <= 105) {
+ // num pad 0-9
+ validKey = String(keyCode - 96);
+ } else {
+ validKey = KEY_CODE[keyCode];
+ }
+ }
+ return validKey;
+ }
+
+ /**
+ * Calculates the normalized key for a KeyboardEvent.
+ * @param {KeyboardEvent} keyEvent
+ * @param {Boolean} [noSpecialChars] Set to true to limit keyEvent.key
+ * transformation to alpha-numeric chars. This is useful with key
+ * combinations like shift + 2, which on FF for MacOS produces
+ * keyEvent.key = @
+ * To get 2 returned, set noSpecialChars = true
+ * To get @ returned, set noSpecialChars = false
+ */
+ function normalizedKeyForEvent(keyEvent, noSpecialChars) {
+ // Fall back from .key, to .detail.key for artifical keyboard events,
+ // and then to deprecated .keyIdentifier and .keyCode.
+ if (keyEvent.key) {
+ return transformKey(keyEvent.key, noSpecialChars);
+ }
+ if (keyEvent.detail && keyEvent.detail.key) {
+ return transformKey(keyEvent.detail.key, noSpecialChars);
+ }
+ return transformKeyIdentifier(keyEvent.keyIdentifier) ||
+ transformKeyCode(keyEvent.keyCode) || '';
+ }
+
+ function keyComboMatchesEvent(keyCombo, event) {
+ // For combos with modifiers we support only alpha-numeric keys
+ var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers);
+ return keyEvent === keyCombo.key &&
+ (!keyCombo.hasModifiers || (
+ !!event.shiftKey === !!keyCombo.shiftKey &&
+ !!event.ctrlKey === !!keyCombo.ctrlKey &&
+ !!event.altKey === !!keyCombo.altKey &&
+ !!event.metaKey === !!keyCombo.metaKey)
+ );
+ }
+
+ function parseKeyComboString(keyComboString) {
+ if (keyComboString.length === 1) {
+ return {
+ combo: keyComboString,
+ key: keyComboString,
+ event: 'keydown'
+ };
+ }
+ return keyComboString.split('+').reduce(function(parsedKeyCombo, keyComboPart) {
+ var eventParts = keyComboPart.split(':');
+ var keyName = eventParts[0];
+ var event = eventParts[1];
+
+ if (keyName in MODIFIER_KEYS) {
+ parsedKeyCombo[MODIFIER_KEYS[keyName]] = true;
+ parsedKeyCombo.hasModifiers = true;
+ } else {
+ parsedKeyCombo.key = keyName;
+ parsedKeyCombo.event = event || 'keydown';
+ }
+
+ return parsedKeyCombo;
+ }, {
+ combo: keyComboString.split(':').shift()
+ });
+ }
+
+ function parseEventString(eventString) {
+ return eventString.trim().split(' ').map(function(keyComboString) {
+ return parseKeyComboString(keyComboString);
+ });
+ }
+
+ /**
+ * `Polymer.IronA11yKeysBehavior` provides a normalized interface for processing
+ * keyboard commands that pertain to [WAI-ARIA best practices](http://www.w3.org/TR/wai-aria-practices/#kbd_general_binding).
+ * The element takes care of browser differences with respect to Keyboard events
+ * and uses an expressive syntax to filter key presses.
+ *
+ * Use the `keyBindings` prototype property to express what combination of keys
+ * will trigger the callback. A key binding has the format
+ * `"KEY+MODIFIER:EVENT": "callback"` (`"KEY": "callback"` or
+ * `"KEY:EVENT": "callback"` are valid as well). Some examples:
+ *
+ * keyBindings: {
+ * 'space': '_onKeydown', // same as 'space:keydown'
+ * 'shift+tab': '_onKeydown',
+ * 'enter:keypress': '_onKeypress',
+ * 'esc:keyup': '_onKeyup'
+ * }
+ *
+ * The callback will receive with an event containing the following information in `event.detail`:
+ *
+ * _onKeydown: function(event) {
+ * console.log(event.detail.combo); // KEY+MODIFIER, e.g. "shift+tab"
+ * console.log(event.detail.key); // KEY only, e.g. "tab"
+ * console.log(event.detail.event); // EVENT, e.g. "keydown"
+ * console.log(event.detail.keyboardEvent); // the original KeyboardEvent
+ * }
+ *
+ * Use the `keyEventTarget` attribute to set up event handlers on a specific
+ * node.
+ *
+ * See the [demo source code](https://github.com/PolymerElements/iron-a11y-keys-behavior/blob/master/demo/x-key-aware.html)
+ * for an example.
+ *
+ * @demo demo/index.html
+ * @polymerBehavior
+ */
+ Polymer.IronA11yKeysBehavior = {
+ properties: {
+ /**
+ * The EventTarget that will be firing relevant KeyboardEvents. Set it to
+ * `null` to disable the listeners.
+ * @type {?EventTarget}
+ */
+ keyEventTarget: {
+ type: Object,
+ value: function() {
+ return this;
+ }
+ },
+
+ /**
+ * If true, this property will cause the implementing element to
+ * automatically stop propagation on any handled KeyboardEvents.
+ */
+ stopKeyboardEventPropagation: {
+ type: Boolean,
+ value: false
+ },
+
+ _boundKeyHandlers: {
+ type: Array,
+ value: function() {
+ return [];
+ }
+ },
+
+ // We use this due to a limitation in IE10 where instances will have
+ // own properties of everything on the "prototype".
+ _imperativeKeyBindings: {
+ type: Object,
+ value: function() {
+ return {};
+ }
+ }
+ },
+
+ observers: [
+ '_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)'
+ ],
+
+
+ /**
+ * To be used to express what combination of keys will trigger the relative
+ * callback. e.g. `keyBindings: { 'esc': '_onEscPressed'}`
+ * @type {!Object}
+ */
+ keyBindings: {},
+
+ registered: function() {
+ this._prepKeyBindings();
+ },
+
+ attached: function() {
+ this._listenKeyEventListeners();
+ },
+
+ detached: function() {
+ this._unlistenKeyEventListeners();
+ },
+
+ /**
+ * Can be used to imperatively add a key binding to the implementing
+ * element. This is the imperative equivalent of declaring a keybinding
+ * in the `keyBindings` prototype property.
+ */
+ addOwnKeyBinding: function(eventString, handlerName) {
+ this._imperativeKeyBindings[eventString] = handlerName;
+ this._prepKeyBindings();
+ this._resetKeyEventListeners();
+ },
+
+ /**
+ * When called, will remove all imperatively-added key bindings.
+ */
+ removeOwnKeyBindings: function() {
+ this._imperativeKeyBindings = {};
+ this._prepKeyBindings();
+ this._resetKeyEventListeners();
+ },
+
+ /**
+ * Returns true if a keyboard event matches `eventString`.
+ *
+ * @param {KeyboardEvent} event
+ * @param {string} eventString
+ * @return {boolean}
+ */
+ keyboardEventMatchesKeys: function(event, eventString) {
+ var keyCombos = parseEventString(eventString);
+ for (var i = 0; i < keyCombos.length; ++i) {
+ if (keyComboMatchesEvent(keyCombos[i], event)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ _collectKeyBindings: function() {
+ var keyBindings = this.behaviors.map(function(behavior) {
+ return behavior.keyBindings;
+ });
+
+ if (keyBindings.indexOf(this.keyBindings) === -1) {
+ keyBindings.push(this.keyBindings);
+ }
+
+ return keyBindings;
+ },
+
+ _prepKeyBindings: function() {
+ this._keyBindings = {};
+
+ this._collectKeyBindings().forEach(function(keyBindings) {
+ for (var eventString in keyBindings) {
+ this._addKeyBinding(eventString, keyBindings[eventString]);
+ }
+ }, this);
+
+ for (var eventString in this._imperativeKeyBindings) {
+ this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]);
+ }
+
+ // Give precedence to combos with modifiers to be checked first.
+ for (var eventName in this._keyBindings) {
+ this._keyBindings[eventName].sort(function (kb1, kb2) {
+ var b1 = kb1[0].hasModifiers;
+ var b2 = kb2[0].hasModifiers;
+ return (b1 === b2) ? 0 : b1 ? -1 : 1;
+ })
+ }
+ },
+
+ _addKeyBinding: function(eventString, handlerName) {
+ parseEventString(eventString).forEach(function(keyCombo) {
+ this._keyBindings[keyCombo.event] =
+ this._keyBindings[keyCombo.event] || [];
+
+ this._keyBindings[keyCombo.event].push([
+ keyCombo,
+ handlerName
+ ]);
+ }, this);
+ },
+
+ _resetKeyEventListeners: function() {
+ this._unlistenKeyEventListeners();
+
+ if (this.isAttached) {
+ this._listenKeyEventListeners();
+ }
+ },
+
+ _listenKeyEventListeners: function() {
+ if (!this.keyEventTarget) {
+ return;
+ }
+ Object.keys(this._keyBindings).forEach(function(eventName) {
+ var keyBindings = this._keyBindings[eventName];
+ var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings);
+
+ this._boundKeyHandlers.push([this.keyEventTarget, eventName, boundKeyHandler]);
+
+ this.keyEventTarget.addEventListener(eventName, boundKeyHandler);
+ }, this);
+ },
+
+ _unlistenKeyEventListeners: function() {
+ var keyHandlerTuple;
+ var keyEventTarget;
+ var eventName;
+ var boundKeyHandler;
+
+ while (this._boundKeyHandlers.length) {
+ // My kingdom for block-scope binding and destructuring assignment..
+ keyHandlerTuple = this._boundKeyHandlers.pop();
+ keyEventTarget = keyHandlerTuple[0];
+ eventName = keyHandlerTuple[1];
+ boundKeyHandler = keyHandlerTuple[2];
+
+ keyEventTarget.removeEventListener(eventName, boundKeyHandler);
+ }
+ },
+
+ _onKeyBindingEvent: function(keyBindings, event) {
+ if (this.stopKeyboardEventPropagation) {
+ event.stopPropagation();
+ }
+
+ // if event has been already prevented, don't do anything
+ if (event.defaultPrevented) {
+ return;
+ }
+
+ for (var i = 0; i < keyBindings.length; i++) {
+ var keyCombo = keyBindings[i][0];
+ var handlerName = keyBindings[i][1];
+ if (keyComboMatchesEvent(keyCombo, event)) {
+ this._triggerKeyHandler(keyCombo, handlerName, event);
+ // exit the loop if eventDefault was prevented
+ if (event.defaultPrevented) {
+ return;
+ }
+ }
+ }
+ },
+
+ _triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) {
+ var detail = Object.create(keyCombo);
+ detail.keyboardEvent = keyboardEvent;
+ var event = new CustomEvent(keyCombo.event, {
+ detail: detail,
+ cancelable: true
+ });
+ this[handlerName].call(this, event);
+ if (event.defaultPrevented) {
+ keyboardEvent.preventDefault();
+ }
+ }
+ };
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/test/basic-test.html b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/test/basic-test.html
new file mode 100644
index 00000000..d6947aca
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/test/basic-test.html
@@ -0,0 +1,445 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>iron-a11y-keys</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../iron-a11y-keys-behavior.html">
+</head>
+<body>
+ <test-fixture id="BasicKeys">
+ <template>
+ <x-a11y-basic-keys></x-a11y-basic-keys>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="NonPropagatingKeys">
+ <template>
+ <x-a11y-basic-keys stop-keyboard-event-propagation></x-a11y-basic-keys>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ComboKeys">
+ <template>
+ <x-a11y-combo-keys></x-a11y-combo-keys>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="AlternativeEventKeys">
+ <template>
+ <x-a11y-alternate-event-keys></x-a11y-alternate-event-keys>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="BehaviorKeys">
+ <template>
+ <x-a11y-behavior-keys></x-a11y-behavior-keys>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="PreventKeys">
+ <template>
+ <x-a11y-prevent-keys></x-a11y-prevent-keys>
+ </template>
+ </test-fixture>
+
+ <script>
+suite('Polymer.IronA11yKeysBehavior', function() {
+ var keys;
+
+ suiteSetup(function() {
+ var KeysTestBehavior = [Polymer.IronA11yKeysBehavior, {
+ properties: {
+ keyCount: {
+ type: Number,
+ value: 0
+ }
+ },
+
+ _keyHandler: function(event) {
+ this.keyCount++;
+ this.lastEvent = event;
+ },
+
+ // Same as _keyHandler, used to distinguish who's called before who.
+ _keyHandler2: function(event) {
+ this.keyCount++;
+ this.lastEvent = event;
+ },
+
+ _preventDefaultHandler: function(event) {
+ event.preventDefault();
+ this.keyCount++;
+ this.lastEvent = event;
+ }
+ }];
+
+ Polymer({
+ is: 'x-a11y-basic-keys',
+
+ behaviors: [
+ KeysTestBehavior
+ ],
+
+ keyBindings: {
+ 'space': '_keyHandler',
+ '@': '_keyHandler',
+ 'esc': '_keyHandler'
+ }
+ });
+
+ Polymer({
+ is: 'x-a11y-combo-keys',
+
+ behaviors: [
+ KeysTestBehavior
+ ],
+
+ keyBindings: {
+ 'enter': '_keyHandler2',
+ 'ctrl+shift+a shift+enter': '_keyHandler'
+ }
+ });
+
+ Polymer({
+ is: 'x-a11y-alternate-event-keys',
+
+ behaviors: [
+ KeysTestBehavior
+ ],
+
+ keyBindings: {
+ 'space:keyup': '_keyHandler'
+ }
+ });
+
+ var XA11yBehavior = {
+ keyBindings: {
+ 'enter': '_keyHandler'
+ }
+ };
+
+ Polymer({
+ is: 'x-a11y-behavior-keys',
+
+ behaviors: [
+ KeysTestBehavior,
+ XA11yBehavior
+ ],
+
+ keyBindings: {
+ 'enter': '_keyHandler'
+ }
+ });
+
+ Polymer({
+ is: 'x-a11y-prevent-keys',
+
+ behaviors: [
+ KeysTestBehavior,
+ XA11yBehavior
+ ],
+
+ keyBindings: {
+ 'space a': '_keyHandler',
+ 'enter shift+a': '_preventDefaultHandler'
+ }
+ });
+ });
+
+ suite('basic keys', function() {
+ setup(function() {
+ keys = fixture('BasicKeys');
+ });
+
+ test('trigger the handler when the specified key is pressed', function() {
+ MockInteractions.pressSpace(keys);
+
+ expect(keys.keyCount).to.be.equal(1);
+ });
+
+ test('keyEventTarget can be null, and disables listeners', function() {
+ keys.keyEventTarget = null;
+ MockInteractions.pressSpace(keys);
+
+ expect(keys.keyCount).to.be.equal(0);
+ });
+
+ test('trigger the handler when the specified key is pressed together with a modifier', function() {
+ var event = new CustomEvent('keydown');
+ event.ctrlKey = true;
+ event.keyCode = event.code = 32;
+ keys.dispatchEvent(event);
+ expect(keys.keyCount).to.be.equal(1);
+ });
+
+ test('handles special character @', function() {
+ MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], '@');
+
+ expect(keys.keyCount).to.be.equal(1);
+ });
+
+ test('handles variations of Esc key', function() {
+ MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], 'Esc');
+ expect(keys.keyCount).to.be.equal(1);
+
+ MockInteractions.pressAndReleaseKeyOn(keys, undefined, [], 'Escape');
+ expect(keys.keyCount).to.be.equal(2);
+
+ MockInteractions.pressAndReleaseKeyOn(keys, 27, [], '');
+ expect(keys.keyCount).to.be.equal(3);
+ });
+
+ test('do not trigger the handler for non-specified keys', function() {
+ MockInteractions.pressEnter(keys);
+
+ expect(keys.keyCount).to.be.equal(0);
+ });
+
+ test('can have bindings added imperatively', function() {
+ keys.addOwnKeyBinding('enter', '_keyHandler');
+
+ MockInteractions.pressEnter(keys);
+ expect(keys.keyCount).to.be.equal(1);
+
+ MockInteractions.pressSpace(keys);
+ expect(keys.keyCount).to.be.equal(2);
+ });
+
+ test('can remove imperatively added bindings', function() {
+ keys.addOwnKeyBinding('enter', '_keyHandler');
+ keys.removeOwnKeyBindings();
+
+ MockInteractions.pressEnter(keys);
+ expect(keys.keyCount).to.be.equal(0);
+
+ MockInteractions.pressSpace(keys);
+ expect(keys.keyCount).to.be.equal(1);
+ });
+
+ test('allows propagation beyond the key combo handler', function() {
+ var keySpy = sinon.spy();
+ document.addEventListener('keydown', keySpy);
+
+ MockInteractions.pressEnter(keys);
+
+ expect(keySpy.callCount).to.be.equal(1);
+ });
+
+ suite('edge cases', function() {
+ test('knows that `spacebar` is the same as `space`', function() {
+ var event = new CustomEvent('keydown');
+ event.key = 'spacebar';
+ expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true);
+ });
+
+ test('handles `+`', function() {
+ var event = new CustomEvent('keydown');
+ event.key = '+';
+ expect(keys.keyboardEventMatchesKeys(event, '+')).to.be.equal(true);
+ });
+
+ test('handles `:`', function() {
+ var event = new CustomEvent('keydown');
+ event.key = ':';
+ expect(keys.keyboardEventMatchesKeys(event, ':')).to.be.equal(true);
+ });
+
+ test('handles ` ` (space)', function() {
+ var event = new CustomEvent('keydown');
+ event.key = ' ';
+ expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true);
+ });
+ });
+
+ suite('matching keyboard events to keys', function() {
+ test('can be done imperatively', function() {
+ var event = new CustomEvent('keydown');
+ event.keyCode = 65;
+ expect(keys.keyboardEventMatchesKeys(event, 'a')).to.be.equal(true);
+ });
+
+ test('can be done with a provided keyboardEvent', function() {
+ var event;
+ MockInteractions.pressSpace(keys);
+ event = keys.lastEvent;
+
+ expect(event.detail.keyboardEvent).to.be.okay;
+ expect(keys.keyboardEventMatchesKeys(event, 'space')).to.be.equal(true);
+ });
+
+ test('can handle variations in arrow key names', function() {
+ var event = new CustomEvent('keydown');
+ event.key = 'up';
+ expect(keys.keyboardEventMatchesKeys(event, 'up')).to.be.equal(true);
+ event.key = 'ArrowUp';
+ expect(keys.keyboardEventMatchesKeys(event, 'up')).to.be.equal(true);
+ });
+ });
+
+ suite('matching keyboard events to top row and number pad digit keys', function() {
+ test('top row can be done imperatively', function() {
+ var event = new CustomEvent('keydown');
+ event.keyCode = 49;
+ expect(keys.keyboardEventMatchesKeys(event, '1')).to.be.equal(true);
+ });
+
+ test('number pad digits can be done imperatively', function() {
+ var event = new CustomEvent('keydown');
+ event.keyCode = 97;
+ expect(keys.keyboardEventMatchesKeys(event, '1')).to.be.equal(true);
+ });
+ });
+ });
+
+ suite('combo keys', function() {
+ setup(function() {
+ keys = fixture('ComboKeys');
+ });
+
+ test('trigger the handler when the combo is pressed', function() {
+ var event = new CustomEvent('keydown');
+
+ event.ctrlKey = true;
+ event.shiftKey = true;
+ event.keyCode = event.code = 65;
+
+ keys.dispatchEvent(event);
+
+ expect(keys.keyCount).to.be.equal(1);
+ });
+
+ test('check if KeyBoardEvent.key is alpha-numberic', function() {
+ var event = new CustomEvent('keydown');
+
+ event.ctrlKey = true;
+ event.shiftKey = true;
+ event.key = 'A';
+
+ keys.dispatchEvent(event);
+
+ expect(keys.keyCount).to.be.equal(1);
+ });
+
+ test('trigger also bindings without modifiers', function() {
+ var event = new CustomEvent('keydown');
+ // Combo `shift+enter`.
+ event.shiftKey = true;
+ event.keyCode = event.code = 13;
+ keys.dispatchEvent(event);
+ expect(keys.keyCount).to.be.equal(2);
+ });
+
+ test('give precendence to combos with modifiers', function() {
+ var enterSpy = sinon.spy(keys, '_keyHandler2');
+ var shiftEnterSpy = sinon.spy(keys, '_keyHandler');
+ var event = new CustomEvent('keydown');
+ // Combo `shift+enter`.
+ event.shiftKey = true;
+ event.keyCode = event.code = 13;
+ keys.dispatchEvent(event);
+ expect(enterSpy.called).to.be.true;
+ expect(shiftEnterSpy.called).to.be.true;
+ expect(enterSpy.calledAfter(shiftEnterSpy)).to.be.true;
+ });
+
+ });
+
+ suite('alternative event keys', function() {
+ setup(function() {
+ keys = fixture('AlternativeEventKeys');
+ });
+
+ test('trigger on the specified alternative keyboard event', function() {
+ MockInteractions.keyDownOn(keys, 32);
+
+ expect(keys.keyCount).to.be.equal(0);
+
+ MockInteractions.keyUpOn(keys, 32);
+
+ expect(keys.keyCount).to.be.equal(1);
+ });
+ });
+
+ suite('behavior keys', function() {
+ setup(function() {
+ keys = fixture('BehaviorKeys');
+ });
+
+ test('bindings in other behaviors are transitive', function() {
+ MockInteractions.pressEnter(keys);
+ expect(keys.keyCount).to.be.equal(2);
+ });
+ });
+
+ suite('stopping propagation automatically', function() {
+ setup(function() {
+ keys = fixture('NonPropagatingKeys');
+ });
+
+ test('does not propagate key events beyond the combo handler', function() {
+ var keySpy = sinon.spy();
+
+ document.addEventListener('keydown', keySpy);
+
+ MockInteractions.pressEnter(keys);
+
+ expect(keySpy.callCount).to.be.equal(0);
+ });
+ });
+
+ suite('prevent default behavior of event', function() {
+ setup(function() {
+ keys = fixture('PreventKeys');
+ });
+
+ test('`defaultPrevented` is correctly set', function() {
+ MockInteractions.pressEnter(keys);
+ expect(keys.lastEvent.defaultPrevented).to.be.equal(true);
+ });
+
+ test('only 1 handler is invoked', function() {
+ var aSpy = sinon.spy(keys, '_keyHandler');
+ var shiftASpy = sinon.spy(keys, '_preventDefaultHandler');
+ var event = new CustomEvent('keydown', {
+ cancelable: true
+ });
+ // Combo `shift+a`.
+ event.shiftKey = true;
+ event.keyCode = event.code = 65;
+ keys.dispatchEvent(event);
+
+ expect(keys.keyCount).to.be.equal(1);
+ expect(shiftASpy.called).to.be.true;
+ expect(aSpy.called).to.be.false;
+ });
+ });
+
+ suite('remove key behavior with null target', function () {
+ test('add and remove a iron-a11y-keys-behavior', function () {
+ var element = document.createElement('x-a11y-basic-keys');
+ element.keyEventTarget = null;
+ document.body.appendChild(element);
+ document.body.removeChild(element);
+ });
+ });
+
+});
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-a11y-keys-behavior/test/index.html b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/test/index.html
new file mode 100755
index 00000000..d00850a0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-a11y-keys-behavior/test/index.html
@@ -0,0 +1,28 @@
+<!--
+ @license
+ Copyright (c) 2014 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
+--><!DOCTYPE html><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>Tests</title>
+
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+
+ <body>
+ <script>
+ // Load and run all tests (.html, .js) as one suite:
+ WCT.loadSuites([
+ 'basic-test.html',
+ 'basic-test.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-ajax/.bower.json b/catapult/third_party/polymer/components/iron-ajax/.bower.json
new file mode 100644
index 00000000..7b963453
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/.bower.json
@@ -0,0 +1,65 @@
+{
+ "name": "iron-ajax",
+ "version": "2.0.5",
+ "description": "Makes it easy to make ajax calls and parse the response",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "ajax"
+ ],
+ "main": "iron-ajax.html",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-ajax.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-ajax",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2",
+ "iron-image": "PolymerElements/iron-image#1 - 2",
+ "promise-polyfill": "PolymerLabs/promise-polyfill#1 - 2",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^3.0.0-rc.1",
+ "web-component-tester": "^6.0.0"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "promise-polyfill": "PolymerLabs/promise-polyfill#^1.0.0",
+ "polymer": "Polymer/polymer#^1.9"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-image": "PolymerElements/iron-image#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "resolutions": {
+ "promise-polyfill": "^1.0.0",
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ },
+ "_release": "2.0.5",
+ "_resolution": {
+ "type": "version",
+ "tag": "v2.0.5",
+ "commit": "536c76a4206e08a16b364930855e46f58ce3565d"
+ },
+ "_source": "https://github.com/PolymerElements/iron-ajax.git",
+ "_target": "^2.0.0",
+ "_originalSource": "PolymerElements/iron-ajax"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-ajax/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-ajax/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..e73ee265
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-ajax/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/zeriha/2/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-ajax/.gitignore b/catapult/third_party/polymer/components/iron-ajax/.gitignore
new file mode 100644
index 00000000..1b3dfde0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/.gitignore
@@ -0,0 +1,2 @@
+bower_components*
+bower-*.json \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-ajax/.travis.yml b/catapult/third_party/polymer/components/iron-ajax/.travis.yml
new file mode 100644
index 00000000..e0f2d61c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/.travis.yml
@@ -0,0 +1,26 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g polymer-cli
+ - polymer install --variants
+env:
+ global:
+ - secure: >-
+ HWwX1XiMfkZkC5XzAz6HkMJcFR+O1DZ+g9hnogZ0e3zdjqZ8VFkCsx0glJLA4TIPdWDfq93gRmeGtbepIx0x4BVOWX8rqoIjb9fbNJDU4ItZsbOvDLInFqKevdq+/klDavVfbR4hNeaHgDmcIEa/IolDU0qIrIdefBfZLOQ0ajM=
+ - secure: >-
+ hBkZz41jJfEP+Z8Uwcw3/EWDLLBzKT+WKY/XI7ubT/bxlXn95RJqY+j1pBX7+fHfLrUfjRmQHTsqkkST2JwRGAZoZpBX+lmUuNRyMoYsDKMl+CYinhSNTVVGSv7JPt5Ls5fktfzIt6Qd97vBy25hcCGKB6HBy+ppfP2DuII4uC8=
+node_js: stable
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+ - xvfb-run polymer test
+ - >-
+ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then polymer test -s 'default';
+ fi
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-ajax/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-ajax/CONTRIBUTING.md
new file mode 100644
index 00000000..543fda4f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/zeriha/2/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/zeriha/2/edit?html,output](https://jsbin.com/zeriha/2/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-ajax/README.md b/catapult/third_party/polymer/components/iron-ajax/README.md
new file mode 100644
index 00000000..7a1a3697
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/README.md
@@ -0,0 +1,111 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-ajax.html iron-request.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-ajax.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-ajax)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-ajax)_
+
+## Changes in 2.0
+
+* Promise polyfill is now a dev dependency and no longer shipped with `iron-ajax`.
+
+ `iron-ajax` uses the `Promise` API, which is not yet supported in all browsers.
+
+ The 1.x version of `iron-ajax` automatically loaded the promise polyfill. This
+ forced the application to include the polyfill, whether or not it was needed.
+
+ When using `iron-ajax` 2.x with Polymer 1.x, you must provide your own Promise polyfill,
+ if needed. For example, you could use the promise polyfill by installing it in your project:
+
+ bower install --save PolymerLabs/promise-polyfill#1 - 2
+
+ Then your app should include the promise polyfill before loading `iron-ajax`:
+
+ <link rel="import" href="bower_components/promise-polyfill/promise-polyfill-lite.html">
+
+ You can use a different promise polyfill if you need a more fully-featured implementation of
+ Promise.
+
+ For Polymer 2.x, you **do not need to provide your own Promise polyfill if you are using
+ the web components polyfills.** Because the web components v1 APIs depend on `Promise`,
+ a promise polyfill is loaded when needed by the v1 polyfills (`web-components-lite.js` or
+ `webcomponents-loader.js`).
+
+* New optional error information.
+
+ The `generateRequest` method returns an `iron-request` element representing the
+ request, and the request element provides a `completes` property, which is a
+ promise that completes when the request either succeeds or fails.
+
+ This version includes a new flag, `rejectWithRequest`, that modifies the error handling
+ of the `completes` promise. By default, when the promise is rejected (because the request
+ failed), the rejection callback only receives an `Error` object describing the failure.
+
+ With `rejectWithRequest` set to true, the callback receives an object with two keys, `error`,
+ the error message, and `request`, the original request that the error is related to:
+
+ let request = ironAjaxElement.generateRequest();
+ request.completes.then(function(req) {
+ // succesful request, argument is iron-request element
+ ...
+ }, function(rejected) {
+ // failed request, argument is an object
+ let req = rejected.request;
+ let error = rejected.error;
+ ...
+ }
+ )
+
+ Because this change could break existing code, `rejectWithRequest` is false by default,
+ however, in the next major release, this option will be removed and the new behavior made
+ the default.
+
+
+## &lt;iron-ajax&gt;
+
+The `iron-ajax` element exposes network request functionality.
+
+```html
+<iron-ajax
+ auto
+ url="https://www.googleapis.com/youtube/v3/search"
+ params='{"part":"snippet", "q":"polymer", "key": "YOUTUBE_API_KEY", "type": "video"}'
+ handle-as="json"
+ on-response="handleResponse"
+ debounce-duration="300"></iron-ajax>
+```
+
+With `auto` set to `true`, the element performs a request whenever
+its `url`, `params` or `body` properties are changed. Automatically generated
+requests will be debounced in the case that multiple attributes are changed
+sequentially.
+
+Note: The `params` attribute must be double quoted JSON.
+
+You can trigger a request explicitly by calling `generateRequest` on the
+element.
+
+
+
+## &lt;iron-request&gt;
+
+iron-request can be used to perform XMLHttpRequests.
+
+```html
+<iron-request id="xhr"></iron-request>
+...
+this.$.xhr.send({url: url, body: params});
+```
+
+
diff --git a/catapult/third_party/polymer/components/iron-ajax/bower.json b/catapult/third_party/polymer/components/iron-ajax/bower.json
new file mode 100644
index 00000000..fc9ae9d8
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/bower.json
@@ -0,0 +1,56 @@
+{
+ "name": "iron-ajax",
+ "version": "2.0.5",
+ "description": "Makes it easy to make ajax calls and parse the response",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "ajax"
+ ],
+ "main": "iron-ajax.html",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-ajax.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-ajax",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2",
+ "iron-image": "PolymerElements/iron-image#1 - 2",
+ "promise-polyfill": "PolymerLabs/promise-polyfill#1 - 2",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^3.0.0-rc.1",
+ "web-component-tester": "^6.0.0"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "promise-polyfill": "PolymerLabs/promise-polyfill#^1.0.0",
+ "polymer": "Polymer/polymer#^1.9"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-image": "PolymerElements/iron-image#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "resolutions": {
+ "promise-polyfill": "^1.0.0",
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-ajax/demo/index.html b/catapult/third_party/polymer/components/iron-ajax/demo/index.html
new file mode 100644
index 00000000..02c37b59
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/demo/index.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-ajax</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../iron-ajax.html">
+ <link rel="import" href="../../iron-image/iron-image.html">
+</head>
+<body unresolved>
+ <h1>Video Feed</h1>
+ <dom-module id="iron-ajax-demo">
+ <template>
+ <style>
+ iron-image {
+ background-color: lightgray;
+ margin: 1em;
+ }
+ .horizontal-section-container {
+ display: flex;
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ -ms-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+ }
+ .horizontal-section {
+ background-color: white;
+ padding: 24px;
+ margin-right: 24px;
+ min-width: 200px;
+ 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);
+ max-width: 300px;
+ margin-bottom: 24px;
+ }
+ </style>
+
+ <iron-ajax auto
+ url="https://www.googleapis.com/youtube/v3/search"
+ params='{"part":"snippet", "q":"polymer", "key": "AIzaSyAuecFZ9xJXbGDkQYWBmYrtzOGJD-iDIgI", "type": "video"}'
+ handle-as="json"
+ last-response="{{ajaxResponse}}"></iron-ajax>
+
+ <div class="horizontal-section-container">
+ <template is="dom-repeat" items="[[ajaxResponse.items]]">
+ <div class="horizontal-section">
+ <h2><a href="[[url(item.id.videoId)]]" target="_blank">[[item.snippet.title]]</a></h2>
+ <iron-image src="[[item.snippet.thumbnails.high.url]]" width="256" height="256" sizing="cover" preload fade></iron-image>
+ <p>[[item.snippet.description]]</p>
+ </div>
+ </template>
+ </div>
+ </template>
+
+ <script>
+ window.addEventListener('WebComponentsReady', function() {
+ Polymer({
+ is: 'iron-ajax-demo',
+
+ url: function(videoId) {
+ return 'https://www.youtube.com/watch?v=' + videoId;
+ }
+ });
+ })
+ </script>
+ </dom-module>
+
+ <iron-ajax-demo></iron-ajax-demo>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-ajax/hero.svg b/catapult/third_party/polymer/components/iron-ajax/hero.svg
new file mode 100644
index 00000000..6ee49f8d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/hero.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <path d="M42.1,54.6H30.9l-2.6,8.2h-4.5l10.9-32h3.8l10.6,32h-4.5L42.1,54.6z M32.1,50.9H41l-4.4-14.2L32.1,50.9z"/>
+ <path d="M69.1,30.8h4.4v22.7c0,1.5-0.3,2.9-0.8,4.1s-1.3,2.2-2.2,3.1c-0.9,0.8-2,1.5-3.3,1.9c-1.3,0.4-2.7,0.7-4.2,0.7
+ c-1.5,0-2.9-0.2-4.2-0.6c-1.3-0.4-2.4-1-3.3-1.7c-0.9-0.8-1.6-1.7-2.1-2.9s-0.7-2.5-0.7-4.1h4.4c0,2,0.5,3.4,1.5,4.4
+ s2.5,1.4,4.4,1.4c0.8,0,1.6-0.1,2.4-0.4c0.7-0.3,1.4-0.7,1.9-1.3s1-1.2,1.3-2s0.5-1.6,0.5-2.6V30.8z"/>
+ <path d="M96.1,54.6H84.9l-2.6,8.2h-4.5l10.9-32h3.8l10.6,32h-4.5L96.1,54.6z M86.1,50.9H95l-4.4-14.2L86.1,50.9z"/>
+ <path d="M117.7,43.2l7-12.3h5.3l-9.7,15.9l9.9,16.1h-5.2l-7.2-12.6l-7.2,12.6h-5.3l9.9-16.1l-9.7-15.9h5.3L117.7,43.2z"/>
+ </g>
+ <circle cx="25.7" cy="82.2" r="4"/>
+ <circle cx="130.7" cy="82.2" r="4"/>
+ <circle cx="52.7" cy="93.2" r="4"/>
+ <circle cx="104.7" cy="93.2" r="4"/>
+ <circle cx="156.7" cy="93.2" r="4"/>
+ <path d="M81.6,78.3c3.3,4.3-1.6,9.1-5.9,5.9C72.5,79.8,77.4,75,81.6,78.3z"/>
+ <polygon points="104.6,94.1 78.4,82.1 52.2,94.1 25.6,81.9 26.4,80.1 52.2,91.9 78.4,79.9 104.6,91.9 130.8,79.9 157.4,92.1
+ 156.6,93.9 130.8,82.1 "/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-ajax/index.html b/catapult/third_party/polymer/components/iron-ajax/index.html
new file mode 100644
index 00000000..41aca5cb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+
+ <title>iron-ajax</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-ajax/iron-ajax.html b/catapult/third_party/polymer/components/iron-ajax/iron-ajax.html
new file mode 100644
index 00000000..1a9dddf6
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/iron-ajax.html
@@ -0,0 +1,580 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="iron-request.html">
+
+<!--
+The `iron-ajax` element exposes network request functionality.
+
+ <iron-ajax
+ auto
+ url="https://www.googleapis.com/youtube/v3/search"
+ params='{"part":"snippet", "q":"polymer", "key": "YOUTUBE_API_KEY", "type": "video"}'
+ handle-as="json"
+ on-response="handleResponse"
+ debounce-duration="300"></iron-ajax>
+
+With `auto` set to `true`, the element performs a request whenever
+its `url`, `params` or `body` properties are changed. Automatically generated
+requests will be debounced in the case that multiple attributes are changed
+sequentially.
+
+Note: The `params` attribute must be double quoted JSON.
+
+You can trigger a request explicitly by calling `generateRequest` on the
+element.
+
+@demo demo/index.html
+@hero hero.svg
+-->
+
+<script>
+ 'use strict';
+
+ Polymer({
+
+ is: 'iron-ajax',
+
+ /**
+ * Fired before a request is sent.
+ *
+ * @event iron-ajax-presend
+ */
+
+ /**
+ * Fired when a request is sent.
+ *
+ * @event request
+ * @event iron-ajax-request
+ */
+
+ /**
+ * Fired when a response is received.
+ *
+ * @event response
+ * @event iron-ajax-response
+ */
+
+ /**
+ * Fired when an error is received.
+ *
+ * @event error
+ * @event iron-ajax-error
+ */
+
+ hostAttributes: {
+ hidden: true
+ },
+
+ properties: {
+ /**
+ * The URL target of the request.
+ */
+ url: {
+ type: String
+ },
+
+ /**
+ * An object that contains query parameters to be appended to the
+ * specified `url` when generating a request. If you wish to set the body
+ * content when making a POST request, you should use the `body` property
+ * instead.
+ */
+ params: {
+ type: Object,
+ value: function() {
+ return {};
+ }
+ },
+
+ /**
+ * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
+ * Default is 'GET'.
+ */
+ method: {
+ type: String,
+ value: 'GET'
+ },
+
+ /**
+ * HTTP request headers to send.
+ *
+ * Example:
+ *
+ * <iron-ajax
+ * auto
+ * url="http://somesite.com"
+ * headers='{"X-Requested-With": "XMLHttpRequest"}'
+ * handle-as="json"></iron-ajax>
+ *
+ * Note: setting a `Content-Type` header here will override the value
+ * specified by the `contentType` property of this element.
+ */
+ headers: {
+ type: Object,
+ value: function() {
+ return {};
+ }
+ },
+
+ /**
+ * Content type to use when sending data. If the `contentType` property
+ * is set and a `Content-Type` header is specified in the `headers`
+ * property, the `headers` property value will take precedence.
+ *
+ * Varies the handling of the `body` param.
+ */
+ contentType: {
+ type: String,
+ value: null
+ },
+
+ /**
+ * Body content to send with the request, typically used with "POST"
+ * requests.
+ *
+ * If body is a string it will be sent unmodified.
+ *
+ * If Content-Type is set to a value listed below, then
+ * the body will be encoded accordingly.
+ *
+ * * `content-type="application/json"`
+ * * body is encoded like `{"foo":"bar baz","x":1}`
+ * * `content-type="application/x-www-form-urlencoded"`
+ * * body is encoded like `foo=bar+baz&x=1`
+ *
+ * Otherwise the body will be passed to the browser unmodified, and it
+ * will handle any encoding (e.g. for FormData, Blob, ArrayBuffer).
+ *
+ * @type (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object)
+ */
+ body: {
+ type: Object,
+ value: null
+ },
+
+ /**
+ * Toggle whether XHR is synchronous or asynchronous. Don't change this
+ * to true unless You Know What You Are Doing™.
+ */
+ sync: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Specifies what data to store in the `response` property, and
+ * to deliver as `event.detail.response` in `response` events.
+ *
+ * One of:
+ *
+ * `text`: uses `XHR.responseText`.
+ *
+ * `xml`: uses `XHR.responseXML`.
+ *
+ * `json`: uses `XHR.responseText` parsed as JSON.
+ *
+ * `arraybuffer`: uses `XHR.response`.
+ *
+ * `blob`: uses `XHR.response`.
+ *
+ * `document`: uses `XHR.response`.
+ */
+ handleAs: {
+ type: String,
+ value: 'json'
+ },
+
+ /**
+ * Set the withCredentials flag on the request.
+ */
+ withCredentials: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set the timeout flag on the request.
+ */
+ timeout: {
+ type: Number,
+ value: 0
+ },
+
+ /**
+ * If true, automatically performs an Ajax request when either `url` or
+ * `params` changes.
+ */
+ auto: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, error messages will automatically be logged to the console.
+ */
+ verbose: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The most recent request made by this iron-ajax element.
+ */
+ lastRequest: {
+ type: Object,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * True while lastRequest is in flight.
+ */
+ loading: {
+ type: Boolean,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * lastRequest's response.
+ *
+ * Note that lastResponse and lastError are set when lastRequest finishes,
+ * so if loading is true, then lastResponse and lastError will correspond
+ * to the result of the previous request.
+ *
+ * The type of the response is determined by the value of `handleAs` at
+ * the time that the request was generated.
+ *
+ * @type {Object}
+ */
+ lastResponse: {
+ type: Object,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * lastRequest's error, if any.
+ *
+ * @type {Object}
+ */
+ lastError: {
+ type: Object,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * An Array of all in-flight requests originating from this iron-ajax
+ * element.
+ */
+ activeRequests: {
+ type: Array,
+ notify: true,
+ readOnly: true,
+ value: function() {
+ return [];
+ }
+ },
+
+ /**
+ * Length of time in milliseconds to debounce multiple automatically generated requests.
+ */
+ debounceDuration: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
+ * Prefix to be stripped from a JSON response before parsing it.
+ *
+ * In order to prevent an attack using CSRF with Array responses
+ * (http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/)
+ * many backends will mitigate this by prefixing all JSON response bodies
+ * with a string that would be nonsensical to a JavaScript parser.
+ *
+ */
+ jsonPrefix: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * By default, iron-ajax's events do not bubble. Setting this attribute will cause its
+ * request and response events as well as its iron-ajax-request, -response, and -error
+ * events to bubble to the window object. The vanilla error event never bubbles when
+ * using shadow dom even if this.bubbles is true because a scoped flag is not passed with
+ * it (first link) and because the shadow dom spec did not used to allow certain events,
+ * including events named error, to leak outside of shadow trees (second link).
+ * https://www.w3.org/TR/shadow-dom/#scoped-flag
+ * https://www.w3.org/TR/2015/WD-shadow-dom-20151215/#events-that-are-not-leaked-into-ancestor-trees
+ */
+ bubbles: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Changes the [`completes`](iron-request#property-completes) promise chain
+ * from `generateRequest` to reject with an object
+ * containing the original request, as well an error message.
+ * If false (default), the promise rejects with an error message only.
+ */
+ rejectWithRequest: {
+ type: Boolean,
+ value: false
+ },
+
+ _boundHandleResponse: {
+ type: Function,
+ value: function() {
+ return this._handleResponse.bind(this);
+ }
+ }
+ },
+
+ observers: [
+ '_requestOptionsChanged(url, method, params.*, headers, contentType, ' +
+ 'body, sync, handleAs, jsonPrefix, withCredentials, timeout, auto)'
+ ],
+
+ /**
+ * The query string that should be appended to the `url`, serialized from
+ * the current value of `params`.
+ *
+ * @return {string}
+ */
+ get queryString () {
+ var queryParts = [];
+ var param;
+ var value;
+
+ for (param in this.params) {
+ value = this.params[param];
+ param = window.encodeURIComponent(param);
+
+ if (Array.isArray(value)) {
+ for (var i = 0; i < value.length; i++) {
+ queryParts.push(param + '=' + window.encodeURIComponent(value[i]));
+ }
+ } else if (value !== null) {
+ queryParts.push(param + '=' + window.encodeURIComponent(value));
+ } else {
+ queryParts.push(param);
+ }
+ }
+
+ return queryParts.join('&');
+ },
+
+ /**
+ * The `url` with query string (if `params` are specified), suitable for
+ * providing to an `iron-request` instance.
+ *
+ * @return {string}
+ */
+ get requestUrl() {
+ var queryString = this.queryString;
+ var url = this.url || '';
+
+ if (queryString) {
+ var bindingChar = url.indexOf('?') >= 0 ? '&' : '?';
+ return url + bindingChar + queryString;
+ }
+
+ return url;
+ },
+
+ /**
+ * An object that maps header names to header values, first applying the
+ * the value of `Content-Type` and then overlaying the headers specified
+ * in the `headers` property.
+ *
+ * @return {Object}
+ */
+ get requestHeaders() {
+ var headers = {};
+ var contentType = this.contentType;
+ if (contentType == null && (typeof this.body === 'string')) {
+ contentType = 'application/x-www-form-urlencoded';
+ }
+ if (contentType) {
+ headers['content-type'] = contentType;
+ }
+ var header;
+
+ if (typeof this.headers === 'object') {
+ for (header in this.headers) {
+ headers[header] = this.headers[header].toString();
+ }
+ }
+
+ return headers;
+ },
+
+ /**
+ * Request options suitable for generating an `iron-request` instance based
+ * on the current state of the `iron-ajax` instance's properties.
+ *
+ * @return {{
+ * url: string,
+ * method: (string|undefined),
+ * async: (boolean|undefined),
+ * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object),
+ * headers: (Object|undefined),
+ * handleAs: (string|undefined),
+ * jsonPrefix: (string|undefined),
+ * withCredentials: (boolean|undefined)}}
+ */
+ toRequestOptions: function() {
+ return {
+ url: this.requestUrl || '',
+ method: this.method,
+ headers: this.requestHeaders,
+ body: this.body,
+ async: !this.sync,
+ handleAs: this.handleAs,
+ jsonPrefix: this.jsonPrefix,
+ withCredentials: this.withCredentials,
+ timeout: this.timeout,
+ rejectWithRequest: this.rejectWithRequest,
+ };
+ },
+
+ /**
+ * Performs an AJAX request to the specified URL.
+ *
+ * @return {!IronRequestElement}
+ */
+ generateRequest: function() {
+ var request = /** @type {!IronRequestElement} */ (document.createElement('iron-request'));
+ var requestOptions = this.toRequestOptions();
+
+ this.push('activeRequests', request);
+
+ request.completes.then(
+ this._boundHandleResponse
+ ).catch(
+ this._handleError.bind(this, request)
+ ).then(
+ this._discardRequest.bind(this, request)
+ );
+
+ var evt = this.fire('iron-ajax-presend', {
+ request: request,
+ options: requestOptions
+ }, {bubbles: this.bubbles, cancelable: true});
+
+ if (evt.defaultPrevented) {
+ request.abort();
+ request.rejectCompletes(request);
+ return request;
+ }
+
+ request.send(requestOptions);
+
+ this._setLastRequest(request);
+ this._setLoading(true);
+
+ this.fire('request', {
+ request: request,
+ options: requestOptions
+ }, {
+ bubbles: this.bubbles,
+ composed: true
+ });
+
+ this.fire('iron-ajax-request', {
+ request: request,
+ options: requestOptions
+ }, {
+ bubbles: this.bubbles,
+ composed: true
+ });
+
+ return request;
+ },
+
+ _handleResponse: function(request) {
+ if (request === this.lastRequest) {
+ this._setLastResponse(request.response);
+ this._setLastError(null);
+ this._setLoading(false);
+ }
+ this.fire('response', request, {
+ bubbles: this.bubbles,
+ composed: true
+ });
+ this.fire('iron-ajax-response', request, {
+ bubbles: this.bubbles,
+ composed: true
+ });
+ },
+
+ _handleError: function(request, error) {
+ if (this.verbose) {
+ Polymer.Base._error(error);
+ }
+
+ if (request === this.lastRequest) {
+ this._setLastError({
+ request: request,
+ error: error,
+ status: request.xhr.status,
+ statusText: request.xhr.statusText,
+ response: request.xhr.response
+ });
+ this._setLastResponse(null);
+ this._setLoading(false);
+ }
+
+ // Tests fail if this goes after the normal this.fire('error', ...)
+ this.fire('iron-ajax-error', {
+ request: request,
+ error: error
+ }, {
+ bubbles: this.bubbles,
+ composed: true
+ });
+
+ this.fire('error', {
+ request: request,
+ error: error
+ }, {
+ bubbles: this.bubbles,
+ composed: true
+ });
+ },
+
+ _discardRequest: function(request) {
+ var requestIndex = this.activeRequests.indexOf(request);
+
+ if (requestIndex > -1) {
+ this.splice('activeRequests', requestIndex, 1);
+ }
+ },
+
+ _requestOptionsChanged: function() {
+ this.debounce('generate-request', function() {
+ if (this.url == null) {
+ return;
+ }
+
+ if (this.auto) {
+ this.generateRequest();
+ }
+ }, this.debounceDuration);
+ },
+
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-ajax/iron-request.html b/catapult/third_party/polymer/components/iron-ajax/iron-request.html
new file mode 100644
index 00000000..0bb3b7f6
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/iron-request.html
@@ -0,0 +1,471 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!--
+iron-request can be used to perform XMLHttpRequests.
+
+ <iron-request id="xhr"></iron-request>
+ ...
+ this.$.xhr.send({url: url, body: params});
+-->
+<script>
+ 'use strict';
+
+ Polymer({
+ is: 'iron-request',
+
+ hostAttributes: {
+ hidden: true
+ },
+
+ properties: {
+
+ /**
+ * A reference to the XMLHttpRequest instance used to generate the
+ * network request.
+ *
+ * @type {XMLHttpRequest}
+ */
+ xhr: {
+ type: Object,
+ notify: true,
+ readOnly: true,
+ value: function() {
+ return new XMLHttpRequest();
+ }
+ },
+
+ /**
+ * A reference to the parsed response body, if the `xhr` has completely
+ * resolved.
+ *
+ * @type {*}
+ * @default null
+ */
+ response: {
+ type: Object,
+ notify: true,
+ readOnly: true,
+ value: function() {
+ return null;
+ }
+ },
+
+ /**
+ * A reference to the status code, if the `xhr` has completely resolved.
+ */
+ status: {
+ type: Number,
+ notify: true,
+ readOnly: true,
+ value: 0
+ },
+
+ /**
+ * A reference to the status text, if the `xhr` has completely resolved.
+ */
+ statusText: {
+ type: String,
+ notify: true,
+ readOnly: true,
+ value: ''
+ },
+
+ /**
+ * A promise that resolves when the `xhr` response comes back, or rejects
+ * if there is an error before the `xhr` completes.
+ * The resolve callback is called with the original request as an argument.
+ * By default, the reject callback is called with an `Error` as an argument.
+ * If `rejectWithRequest` is true, the reject callback is called with an
+ * object with two keys: `request`, the original request, and `error`, the
+ * error object.
+ *
+ * @type {Promise}
+ */
+ completes: {
+ type: Object,
+ readOnly: true,
+ notify: true,
+ value: function() {
+ return new Promise(function(resolve, reject) {
+ this.resolveCompletes = resolve;
+ this.rejectCompletes = reject;
+ }.bind(this));
+ }
+ },
+
+ /**
+ * An object that contains progress information emitted by the XHR if
+ * available.
+ *
+ * @default {}
+ */
+ progress: {
+ type: Object,
+ notify: true,
+ readOnly: true,
+ value: function() {
+ return {};
+ }
+ },
+
+ /**
+ * Aborted will be true if an abort of the request is attempted.
+ */
+ aborted: {
+ type: Boolean,
+ notify: true,
+ readOnly: true,
+ value: false,
+ },
+
+ /**
+ * Errored will be true if the browser fired an error event from the
+ * XHR object (mainly network errors).
+ */
+ errored: {
+ type: Boolean,
+ notify: true,
+ readOnly: true,
+ value: false
+ },
+
+ /**
+ * TimedOut will be true if the XHR threw a timeout event.
+ */
+ timedOut: {
+ type: Boolean,
+ notify: true,
+ readOnly: true,
+ value: false
+ }
+ },
+
+ /**
+ * Succeeded is true if the request succeeded. The request succeeded if it
+ * loaded without error, wasn't aborted, and the status code is ≥ 200, and
+ * < 300, or if the status code is 0.
+ *
+ * The status code 0 is accepted as a success because some schemes - e.g.
+ * file:// - don't provide status codes.
+ *
+ * @return {boolean}
+ */
+ get succeeded() {
+ if (this.errored || this.aborted || this.timedOut) {
+ return false;
+ }
+ var status = this.xhr.status || 0;
+
+ // Note: if we are using the file:// protocol, the status code will be 0
+ // for all outcomes (successful or otherwise).
+ return status === 0 ||
+ (status >= 200 && status < 300);
+ },
+
+ /**
+ * Sends an HTTP request to the server and returns a promise (see the `completes`
+ * property for details).
+ *
+ * The handling of the `body` parameter will vary based on the Content-Type
+ * header. See the docs for iron-ajax's `body` property for details.
+ *
+ * @param {{
+ * url: string,
+ * method: (string|undefined),
+ * async: (boolean|undefined),
+ * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object),
+ * headers: (Object|undefined),
+ * handleAs: (string|undefined),
+ * jsonPrefix: (string|undefined),
+ * withCredentials: (boolean|undefined),
+ * timeout: (Number|undefined),
+ * rejectWithRequest: (boolean|undefined)}} options -
+ * - url The url to which the request is sent.
+ * - method The HTTP method to use, default is GET.
+ * - async By default, all requests are sent asynchronously. To send synchronous requests,
+ * set to false.
+ * - body The content for the request body for POST method.
+ * - headers HTTP request headers.
+ * - handleAs The response type. Default is 'text'.
+ * - withCredentials Whether or not to send credentials on the request. Default is false.
+ * - timeout - Timeout for request, in milliseconds.
+ * - rejectWithRequest Set to true to include the request object with promise rejections.
+ * @return {Promise}
+ */
+ send: function(options) {
+ var xhr = this.xhr;
+
+ if (xhr.readyState > 0) {
+ return null;
+ }
+
+ xhr.addEventListener('progress', function(progress) {
+ this._setProgress({
+ lengthComputable: progress.lengthComputable,
+ loaded: progress.loaded,
+ total: progress.total
+ });
+ }.bind(this));
+
+ xhr.addEventListener('error', function(error) {
+ this._setErrored(true);
+ this._updateStatus();
+ var response = options.rejectWithRequest ? {
+ error: error,
+ request: this
+ } : error;
+ this.rejectCompletes(response);
+ }.bind(this));
+
+ xhr.addEventListener('timeout', function(error) {
+ this._setTimedOut(true);
+ this._updateStatus();
+ var response = options.rejectWithRequest ? {
+ error: error,
+ request: this
+ } : error;
+ this.rejectCompletes(response);
+ }.bind(this));
+
+ xhr.addEventListener('abort', function() {
+ this._setAborted(true);
+ this._updateStatus();
+ var error = new Error('Request aborted.');
+ var response = options.rejectWithRequest ? {
+ error: error,
+ request: this
+ } : error;
+ this.rejectCompletes(response);
+ }.bind(this));
+
+ // Called after all of the above.
+ xhr.addEventListener('loadend', function() {
+ this._updateStatus();
+ this._setResponse(this.parseResponse());
+
+ if (!this.succeeded) {
+ var error = new Error('The request failed with status code: ' + this.xhr.status);
+ var response = options.rejectWithRequest ? {
+ error: error,
+ request: this
+ } : error;
+ this.rejectCompletes(response);
+ return;
+ }
+
+ this.resolveCompletes(this);
+ }.bind(this));
+
+ this.url = options.url;
+ xhr.open(
+ options.method || 'GET',
+ options.url,
+ options.async !== false
+ );
+
+ var acceptType = {
+ 'json': 'application/json',
+ 'text': 'text/plain',
+ 'html': 'text/html',
+ 'xml': 'application/xml',
+ 'arraybuffer': 'application/octet-stream'
+ }[options.handleAs];
+ var headers = options.headers || Object.create(null);
+ var newHeaders = Object.create(null);
+ for (var key in headers) {
+ newHeaders[key.toLowerCase()] = headers[key];
+ }
+ headers = newHeaders;
+
+ if (acceptType && !headers['accept']) {
+ headers['accept'] = acceptType;
+ }
+ Object.keys(headers).forEach(function(requestHeader) {
+ if (/[A-Z]/.test(requestHeader)) {
+ Polymer.Base._error('Headers must be lower case, got', requestHeader);
+ }
+ xhr.setRequestHeader(
+ requestHeader,
+ headers[requestHeader]
+ );
+ }, this);
+
+ if (options.async !== false) {
+ if (options.async) {
+ xhr.timeout = options.timeout;
+ }
+
+ var handleAs = options.handleAs;
+
+ // If a JSON prefix is present, the responseType must be 'text' or the
+ // browser won’t be able to parse the response.
+ if (!!options.jsonPrefix || !handleAs) {
+ handleAs = 'text';
+ }
+
+ // In IE, `xhr.responseType` is an empty string when the response
+ // returns. Hence, caching it as `xhr._responseType`.
+ xhr.responseType = xhr._responseType = handleAs;
+
+ // Cache the JSON prefix, if it exists.
+ if (!!options.jsonPrefix) {
+ xhr._jsonPrefix = options.jsonPrefix;
+ }
+ }
+
+ xhr.withCredentials = !!options.withCredentials;
+
+
+ var body = this._encodeBodyObject(options.body, headers['content-type']);
+
+ xhr.send(
+ /** @type {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|
+ null|string|undefined} */
+ (body));
+
+ return this.completes;
+ },
+
+ /**
+ * Attempts to parse the response body of the XHR. If parsing succeeds,
+ * the value returned will be deserialized based on the `responseType`
+ * set on the XHR.
+ *
+ * @return {*} The parsed response,
+ * or undefined if there was an empty response or parsing failed.
+ */
+ parseResponse: function() {
+ var xhr = this.xhr;
+ var responseType = xhr.responseType || xhr._responseType;
+ var preferResponseText = !this.xhr.responseType;
+ var prefixLen = (xhr._jsonPrefix && xhr._jsonPrefix.length) || 0;
+
+ try {
+ switch (responseType) {
+ case 'json':
+ // If the xhr object doesn't have a natural `xhr.responseType`,
+ // we can assume that the browser hasn't parsed the response for us,
+ // and so parsing is our responsibility. Likewise if response is
+ // undefined, as there's no way to encode undefined in JSON.
+ if (preferResponseText || xhr.response === undefined) {
+ // Try to emulate the JSON section of the response body section of
+ // the spec: https://xhr.spec.whatwg.org/#response-body
+ // That is to say, we try to parse as JSON, but if anything goes
+ // wrong return null.
+ try {
+ return JSON.parse(xhr.responseText);
+ } catch (_) {
+ return null;
+ }
+ }
+
+ return xhr.response;
+ case 'xml':
+ return xhr.responseXML;
+ case 'blob':
+ case 'document':
+ case 'arraybuffer':
+ return xhr.response;
+ case 'text':
+ default: {
+ // If `prefixLen` is set, it implies the response should be parsed
+ // as JSON once the prefix of length `prefixLen` is stripped from
+ // it. Emulate the behavior above where null is returned on failure
+ // to parse.
+ if (prefixLen) {
+ try {
+ return JSON.parse(xhr.responseText.substring(prefixLen));
+ } catch (_) {
+ return null;
+ }
+ }
+ return xhr.responseText;
+ }
+ }
+ } catch (e) {
+ this.rejectCompletes(new Error('Could not parse response. ' + e.message));
+ }
+ },
+
+ /**
+ * Aborts the request.
+ */
+ abort: function() {
+ this._setAborted(true);
+ this.xhr.abort();
+ },
+
+ /**
+ * @param {*} body The given body of the request to try and encode.
+ * @param {?string} contentType The given content type, to infer an encoding
+ * from.
+ * @return {*} Either the encoded body as a string, if successful,
+ * or the unaltered body object if no encoding could be inferred.
+ */
+ _encodeBodyObject: function(body, contentType) {
+ if (typeof body == 'string') {
+ return body; // Already encoded.
+ }
+ var bodyObj = /** @type {Object} */ (body);
+ switch(contentType) {
+ case('application/json'):
+ return JSON.stringify(bodyObj);
+ case('application/x-www-form-urlencoded'):
+ return this._wwwFormUrlEncode(bodyObj);
+ }
+ return body;
+ },
+
+ /**
+ * @param {Object} object The object to encode as x-www-form-urlencoded.
+ * @return {string} .
+ */
+ _wwwFormUrlEncode: function(object) {
+ if (!object) {
+ return '';
+ }
+ var pieces = [];
+ Object.keys(object).forEach(function(key) {
+ // TODO(rictic): handle array values here, in a consistent way with
+ // iron-ajax params.
+ pieces.push(
+ this._wwwFormUrlEncodePiece(key) + '=' +
+ this._wwwFormUrlEncodePiece(object[key]));
+ }, this);
+ return pieces.join('&');
+ },
+
+ /**
+ * @param {*} str A key or value to encode as x-www-form-urlencoded.
+ * @return {string} .
+ */
+ _wwwFormUrlEncodePiece: function(str) {
+ // Spec says to normalize newlines to \r\n and replace %20 spaces with +.
+ // jQuery does this as well, so this is likely to be widely compatible.
+ if (str === null || str === undefined || !str.toString) {
+ return '';
+ }
+
+ return encodeURIComponent(str.toString().replace(/\r?\n/g, '\r\n'))
+ .replace(/%20/g, '+');
+ },
+
+ /**
+ * Updates the status code and status text.
+ */
+ _updateStatus: function() {
+ this._setStatus(this.xhr.status);
+ this._setStatusText((this.xhr.statusText === undefined) ? '' : this.xhr.statusText);
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-ajax/test/index.html b/catapult/third_party/polymer/components/iron-ajax/test/index.html
new file mode 100644
index 00000000..997b08f1
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/test/index.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'iron-request.html?wc-shadowdom=true&wc-ce=true',
+ 'iron-ajax.html?wc-shadowdom=true&wc-ce=true',
+ 'iron-request.html?dom=shadow',
+ 'iron-ajax.html?dom=shadow'
+ ]);
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-ajax/test/iron-ajax.html b/catapult/third_party/polymer/components/iron-ajax/test/iron-ajax.html
new file mode 100644
index 00000000..eae01718
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/test/iron-ajax.html
@@ -0,0 +1,968 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-ajax</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../iron-ajax.html">
+</head>
+<body>
+ <test-fixture id="TrivialGet">
+ <template>
+ <iron-ajax url="/responds_to_get_with_json"></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="ParamsGet">
+ <template>
+ <iron-ajax url="/responds_to_get_with_json"
+ params='{"a": "a"}'></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="AutoGet">
+ <template>
+ <iron-ajax auto url="/responds_to_get_with_json"></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="GetEcho">
+ <template>
+ <iron-ajax handle-as="json" url="/echoes_request_url"></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="TrivialPost">
+ <template>
+ <iron-ajax method="POST"
+ url="/responds_to_post_with_json"></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="DebouncedGet">
+ <template>
+ <iron-ajax auto
+ url="/responds_to_debounced_get_with_json"
+ debounce-duration="150"></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id='BlankUrl'>
+ <template>
+ <iron-ajax auto handle-as='text'></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="RealPost">
+ <template>
+ <iron-ajax method="POST" url="http://httpbin.org/post"></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="Delay">
+ <template>
+ <iron-ajax url="http://httpbin.org/delay/1"></iron-ajax>
+ </template>
+ </test-fixture>
+ <test-fixture id="Bubbles">
+ <template>
+ <iron-ajax url="http://httpbin.org/post" method="POST" bubbles></iron-ajax>
+ <iron-ajax url="/responds_to_get_with_502_error_json" bubbles></iron-ajax>
+ </template>
+ </test-fixture>
+ <script>
+ 'use strict';
+ suite('<iron-ajax>', function() {
+ var responseHeaders = {
+ json: { 'Content-Type': 'application/json' },
+ plain: { 'Content-Type': 'text/plain' }
+ };
+ var ajax;
+ var request;
+ var server;
+
+ function timePasses(ms) {
+ return new Promise(function(resolve) {
+ window.setTimeout(function() {
+ resolve();
+ }, ms);
+ });
+ }
+
+ setup(function() {
+ server = sinon.fakeServer.create();
+ server.respondWith(
+ 'GET',
+ /\/responds_to_get_with_json.*/,
+ [
+ 200,
+ responseHeaders.json,
+ '{"success":true}'
+ ]
+ );
+
+ server.respondWith(
+ 'POST',
+ '/responds_to_post_with_json',
+ [
+ 200,
+ responseHeaders.json,
+ '{"post_success":true}'
+ ]
+ );
+
+ server.respondWith(
+ 'GET',
+ '/responds_to_get_with_text',
+ [
+ 200,
+ responseHeaders.plain,
+ 'Hello World'
+ ]
+ );
+
+ server.respondWith(
+ 'GET',
+ '/responds_to_debounced_get_with_json',
+ [
+ 200,
+ responseHeaders.json,
+ '{"success": "true"}'
+ ]
+ );
+
+ server.respondWith(
+ 'GET',
+ '/responds_to_get_with_502_error_json',
+ [
+ 502,
+ responseHeaders.json,
+ '{"message": "an error has occurred"}'
+ ]
+ );
+
+ ajax = fixture('TrivialGet');
+ });
+
+ teardown(function() {
+ server.restore();
+ });
+
+ // Echo requests are responded to individually and on demand, unlike the
+ // others in this file which are responded to with server.respond(),
+ // which responds to all open requests.
+ // We don't use server.respondWith here because there's no way to use it
+ // and only respond to a subset of requests.
+ // This way we can test for delayed and out of order responses and
+ // distinquish them by their responses.
+ function respondToEchoRequest(request) {
+ request.respond(200, responseHeaders.json, JSON.stringify({
+ url: request.url
+ }));
+ }
+
+ suite('when making simple GET requests for JSON', function() {
+ test('has sane defaults that love you', function() {
+ request = ajax.generateRequest();
+
+ server.respond();
+
+ expect(request.response).to.be.ok;
+ expect(request.response).to.be.an('object');
+ expect(request.response.success).to.be.equal(true);
+ });
+
+ test('will be asynchronous by default', function() {
+ expect(ajax.toRequestOptions().async).to.be.eql(true);
+ });
+ });
+
+ suite('when setting custom headers', function() {
+ test('are present in the request headers', function() {
+ ajax.headers['custom-header'] = 'valid';
+ var options = ajax.toRequestOptions();
+
+ expect(options.headers).to.be.ok;
+ expect(options.headers['custom-header']).to.be.an('string');
+ expect(options.headers.hasOwnProperty('custom-header')).to.be.equal(
+ true);
+ });
+
+ test('non-objects in headers are not applied', function() {
+ ajax.headers = 'invalid';
+ var options = ajax.toRequestOptions();
+
+ expect(Object.keys(options.headers).length).to.be.equal(0);
+ });
+ });
+
+ suite('when url isn\'t set yet', function() {
+ test('we don\'t fire any automatic requests', function() {
+ expect(server.requests.length).to.be.equal(0);
+ ajax = fixture('BlankUrl');
+
+ return timePasses(1).then(function() {
+ // We don't make any requests.
+ expect(server.requests.length).to.be.equal(0);
+
+ // Explicitly asking for the request to fire works.
+ ajax.generateRequest();
+ expect(server.requests.length).to.be.equal(1);
+ server.requests = [];
+
+ // Explicitly setting url to '' works too.
+ ajax = fixture('BlankUrl');
+ ajax.url = '';
+ return timePasses(1);
+ }).then(function() {
+ expect(server.requests.length).to.be.equal(1);
+ });
+ });
+
+ test('requestUrl remains empty despite valid queryString', function() {
+ ajax = fixture('BlankUrl');
+ expect(ajax.url).to.be.equal(undefined);
+ expect(ajax.queryString).to.be.equal('');
+ expect(ajax.requestUrl).to.be.equal('');
+
+ ajax.params = {'a':'b', 'c':'d'};
+
+ expect(ajax.queryString).to.be.equal('a=b&c=d');
+ expect(ajax.requestUrl).to.be.equal('?a=b&c=d');
+ });
+
+ test('generateRequest works with empty URL and valid queryString', function() {
+ ajax = fixture('BlankUrl');
+ expect(ajax.url).to.be.equal(undefined);
+
+ ajax.generateRequest();
+ expect(server.requests[0].url).to.be.eql('');
+
+ ajax.params = {'a':'b', 'c':'d'};
+
+ ajax.generateRequest();
+ expect(server.requests[1].url).to.be.eql('?a=b&c=d');
+ });
+ });
+
+ suite('when properties are changed', function() {
+ test('generates simple-request elements that reflect the change', function() {
+ request = ajax.generateRequest();
+
+ expect(request.xhr.method).to.be.equal('GET');
+
+ ajax.method = 'POST';
+ ajax.url = '/responds_to_post_with_json';
+
+ request = ajax.generateRequest();
+
+ expect(request.xhr.method).to.be.equal('POST');
+ });
+ });
+
+ suite('when generating a request', function() {
+ test('yields an iron-request instance', function() {
+ var IronRequest = document.createElement('iron-request').constructor;
+
+ expect(ajax.generateRequest()).to.be.instanceOf(IronRequest);
+ });
+
+ test('correctly adds params to a URL that already has some', function() {
+ ajax.url += '?a=b';
+ ajax.params = {'c': 'd'};
+
+ expect(ajax.requestUrl).to.be.equal('/responds_to_get_with_json?a=b&c=d')
+ })
+
+ test('encodes params properly', function() {
+ ajax.params = {'a b,c': 'd e f'};
+
+ expect(ajax.queryString).to.be.equal('a%20b%2Cc=d%20e%20f');
+ });
+
+ test('encodes array params properly', function() {
+ ajax.params = {'a b': ['c','d e', 'f']};
+
+ expect(ajax.queryString).to.be.equal('a%20b=c&a%20b=d%20e&a%20b=f');
+ });
+
+ test('reflects the loading state in the `loading` property', function() {
+ var request = ajax.generateRequest();
+
+ expect(ajax.loading).to.be.equal(true);
+
+ server.respond();
+
+ return request.completes.then(function() {
+ return timePasses(1);
+ }).then(function() {
+ expect(ajax.loading).to.be.equal(false);
+ });
+ });
+
+ test('the `iron-ajax-presend` event gets fired', function() {
+ var spy = sinon.spy();
+ var windowSpy = sinon.spy();
+ ajax.addEventListener('iron-ajax-presend', spy);
+ window.addEventListener('iron-ajax-presend', windowSpy);
+
+ var request = ajax.generateRequest();
+
+ server.respond()
+
+ return request.completes.then(function() {
+ expect(spy).to.be.calledOnce;
+ expect(windowSpy).not.to.be.called;
+ });
+ });
+
+ test('the loading-changed event gets fired twice', function() {
+ var count = 0;
+ ajax.addEventListener('loading-changed', function() {
+ count++;
+ });
+
+ var request = ajax.generateRequest();
+
+ server.respond();
+
+ return request.completes.then(function() {
+ return timePasses(1);
+ }).then(function() {
+ expect(count).to.be.equal(2);
+ });
+ });
+ });
+
+ suite('when there are multiple requests', function() {
+ var requests;
+ var echoAjax;
+ var promiseAllComplete;
+
+ setup(function() {
+ echoAjax = fixture('GetEcho');
+ requests = [];
+
+ for (var i = 0; i < 3; ++i) {
+ echoAjax.params = {'order': i + 1};
+ requests.push(echoAjax.generateRequest());
+ }
+ var allPromises = requests.map(function(r){return r.completes});
+ promiseAllComplete = Promise.all(allPromises);
+ });
+
+ test('holds all requests in the `activeRequests` Array', function() {
+ expect(requests).to.deep.eql(echoAjax.activeRequests);
+ });
+
+ test('empties `activeRequests` when requests are completed', function() {
+ expect(echoAjax.activeRequests.length).to.be.equal(3);
+ for (var i = 0; i < 3; i++) {
+ respondToEchoRequest(server.requests[i]);
+ }
+ return promiseAllComplete.then(function() {
+ return timePasses(1);
+ }).then(function() {
+ expect(echoAjax.activeRequests.length).to.be.equal(0);
+ });
+ });
+
+ test('avoids race conditions with last response', function() {
+ expect(echoAjax.lastResponse).to.be.equal(undefined);
+
+ // Resolving the oldest request doesn't update lastResponse.
+ respondToEchoRequest(server.requests[0]);
+ return requests[0].completes.then(function() {
+ expect(echoAjax.lastResponse).to.be.equal(undefined);
+
+ // Resolving the most recent request does!
+ respondToEchoRequest(server.requests[2]);
+ return requests[2].completes;
+ }).then(function() {
+ expect(echoAjax.lastResponse).to.be.deep.eql(
+ {url: '/echoes_request_url?order=3'});
+
+
+ // Resolving an out of order stale request after does nothing!
+ respondToEchoRequest(server.requests[1]);
+ return requests[1].completes;
+ }).then(function() {
+ expect(echoAjax.lastResponse).to.be.deep.eql(
+ {url: '/echoes_request_url?order=3'});
+ });
+ });
+
+ test('`loading` is true while the last one is loading', function() {
+ expect(echoAjax.loading).to.be.equal(true);
+
+ respondToEchoRequest(server.requests[0]);
+ return requests[0].completes.then(function() {
+ // We're still loading because requests[2] is the most recently
+ // made request.
+ expect(echoAjax.loading).to.be.equal(true);
+
+ respondToEchoRequest(server.requests[2]);
+ return requests[2].completes;
+ }).then(function() {
+ // Now we're done loading.
+ expect(echoAjax.loading).to.be.eql(false);
+
+ // Resolving an out of order stale request after should have
+ // no effect.
+ respondToEchoRequest(server.requests[1]);
+ return requests[1].completes;
+ }).then(function() {
+ expect(echoAjax.loading).to.be.eql(false);
+ });
+ });
+ });
+
+ suite('when params are changed', function() {
+ test('generates a request that reflects the change', function() {
+ ajax = fixture('ParamsGet');
+ request = ajax.generateRequest();
+
+ expect(request.xhr.url).to.be.equal('/responds_to_get_with_json?a=a');
+
+ ajax.params = {b: 'b'};
+ request = ajax.generateRequest();
+
+ expect(request.xhr.url).to.be.equal('/responds_to_get_with_json?b=b');
+ });
+ });
+
+ suite('when `auto` is enabled', function() {
+ setup(function() {
+ ajax = fixture('AutoGet');
+ });
+
+ test('automatically generates new requests', function() {
+ return new Promise(function(resolve) {
+ ajax.addEventListener('request', function() {
+ resolve();
+ });
+ });
+ });
+
+ test('does not send requests if url is not a string', function() {
+ return new Promise(function(resolve, reject) {
+ ajax.addEventListener('request', function() {
+ reject('A request was generated but url is null!');
+ });
+
+ ajax.url = null;
+ ajax.handleAs = 'text';
+
+ Polymer.Base.async(function() {
+ resolve();
+ }, 1);
+ });
+ });
+
+ test('deduplicates multiple changes to a single request', function() {
+ return new Promise(function(resolve, reject) {
+ ajax.addEventListener('request', function() {
+ server.respond();
+ });
+
+ ajax.addEventListener('response', function() {
+ try {
+ expect(ajax.activeRequests.length).to.be.eql(1);
+ resolve()
+ } catch (e) {
+ reject(e);
+ }
+ });
+
+ ajax.handleas = 'text';
+ ajax.params = { foo: 'bar' };
+ ajax.headers = { 'X-Foo': 'Bar' };
+ });
+ });
+
+ test('automatically generates new request when a sub-property of params is changed', function(done) {
+ ajax.addEventListener('request', function() {
+ server.respond();
+ });
+
+ ajax.params = { foo: 'bar' };
+ ajax.addEventListener('response', function() {
+ ajax.addEventListener('request', function() {
+ done();
+ });
+
+ ajax.set('params.foo', 'xyz');
+ });
+ });
+ });
+
+ suite('the last response', function() {
+ setup(function() {
+ request = ajax.generateRequest();
+ server.respond();
+ });
+
+ test('is accessible as a readonly property', function() {
+ return request.completes.then(function(request) {
+ expect(ajax.lastResponse).to.be.equal(request.response);
+ });
+ });
+
+
+ test('updates with each new response', function() {
+ return request.completes.then(function(request) {
+
+ expect(request.response).to.be.an('object');
+ expect(ajax.lastResponse).to.be.equal(request.response);
+
+ ajax.handleAs = 'text';
+ request = ajax.generateRequest();
+ server.respond();
+
+ return request.completes;
+ }).then(function(request) {
+ expect(request.response).to.be.a('string');
+ expect(ajax.lastResponse).to.be.equal(request.response);
+ });
+ });
+ });
+
+ suite('when making POST requests', function() {
+ setup(function() {
+ ajax = fixture('TrivialPost');
+ });
+
+ test('POSTs the value of the `body` attribute', function() {
+ var requestBody = JSON.stringify({foo: 'bar'});
+
+ ajax.body = requestBody;
+ ajax.generateRequest();
+
+ expect(server.requests[0]).to.be.ok;
+ expect(server.requests[0].requestBody).to.be.equal(requestBody);
+ });
+
+ test('if `contentType` is set to form encode, the body is encoded',function() {
+ ajax.body = {foo: 'bar\nbip', 'biz bo': 'baz blar'};
+ ajax.contentType = 'application/x-www-form-urlencoded';
+ ajax.generateRequest();
+
+ expect(server.requests[0]).to.be.ok;
+ expect(server.requests[0].requestBody).to.be.equal(
+ 'foo=bar%0D%0Abip&biz+bo=baz+blar');
+ });
+
+ test('if `contentType` is json, the body is json encoded', function() {
+ var requestObj = {foo: 'bar', baz: [1,2,3]}
+ ajax.body = requestObj;
+ ajax.contentType = 'application/json';
+ ajax.generateRequest();
+
+ expect(server.requests[0]).to.be.ok;
+ expect(server.requests[0].requestBody).to.be.equal(
+ JSON.stringify(requestObj));
+ });
+
+ suite('the examples in the documentation work', function() {
+ test('json content, body attribute is an object', function() {
+ ajax.setAttribute('body', '{"foo": "bar baz", "x": 1}');
+ ajax.contentType = 'application/json';
+ ajax.generateRequest();
+
+ expect(server.requests[0]).to.be.ok;
+ expect(server.requests[0].requestBody).to.be.equal(
+ '{"foo":"bar baz","x":1}');
+ });
+
+ test('form content, body attribute is an object', function() {
+ ajax.setAttribute('body', '{"foo": "bar baz", "x": 1}');
+ ajax.contentType = 'application/x-www-form-urlencoded';
+ ajax.generateRequest();
+
+ expect(server.requests[0]).to.be.ok;
+ expect(server.requests[0].requestBody).to.be.equal(
+ 'foo=bar+baz&x=1');
+ });
+ });
+
+ suite('and `contentType` is explicitly set to form encode', function() {
+ test('we encode a custom object', function() {
+ function Foo(bar) { this.bar = bar };
+ var requestObj = new Foo('baz');
+ ajax.body = requestObj;
+ ajax.contentType = 'application/x-www-form-urlencoded';
+ ajax.generateRequest();
+
+ expect(server.requests[0]).to.be.ok;
+ expect(server.requests[0].requestBody).to.be.equal('bar=baz');
+ });
+ })
+
+ suite('and `contentType` isn\'t set', function() {
+ test('we don\'t try to encode an ArrayBuffer', function() {
+ var requestObj = new ArrayBuffer()
+ ajax.body = requestObj;
+ ajax.generateRequest();
+
+ expect(server.requests[0]).to.be.ok;
+ // We give the browser the ArrayBuffer directly, without trying
+ // to encode it.
+ expect(server.requests[0].requestBody).to.be.equal(requestObj);
+ });
+ })
+ });
+
+ suite('when debouncing requests', function() {
+ setup(function() {
+ ajax = fixture('DebouncedGet');
+ });
+
+ test('only requests a single resource', function() {
+ ajax._requestOptionsChanged();
+ expect(server.requests[0]).to.be.equal(undefined);
+ ajax._requestOptionsChanged();
+ return timePasses(200).then(function() {
+ expect(server.requests[0]).to.be.ok;
+ });
+ });
+ });
+
+ suite('when a response handler is bound', function() {
+ var responseHandler;
+
+ setup(function() {
+ responseHandler = sinon.spy();
+ ajax.addEventListener('response', responseHandler);
+ });
+
+ test('calls the handler after every response', function() {
+ ajax.generateRequest();
+ ajax.generateRequest();
+
+ server.respond();
+
+ return ajax.lastRequest.completes.then(function() {
+ expect(responseHandler.callCount).to.be.equal(2);
+ });
+ });
+ });
+
+ suite('when the response type is `json`', function() {
+ setup(function() {
+ server.restore();
+ });
+
+ test('finds the JSON on any platform', function() {
+ ajax.url = '../bower.json';
+ request = ajax.generateRequest();
+ return request.completes.then(function() {
+ expect(ajax.lastResponse).to.be.instanceOf(Object);
+ });
+ });
+ });
+
+ suite('when handleAs parameter is `text`', function() {
+
+ test('response type is string', function() {
+ ajax.url = '/responds_to_get_with_json';
+ ajax.handleAs = 'text';
+
+ request = ajax.generateRequest();
+ var promise = request.completes.then(function() {
+ expect(typeof(ajax.lastResponse)).to.be.equal('string');
+ });
+
+ expect(server.requests.length).to.be.equal(1);
+ expect(server.requests[0].requestHeaders['accept']).to.be.equal(
+ 'text/plain');
+ server.respond();
+
+ return promise;
+ });
+
+ });
+
+ suite('when a request fails', function() {
+ test('we give an error with useful details', function() {
+ ajax.url = '/responds_to_get_with_502_error_json';
+ ajax.handleAs = 'json';
+ var eventFired = false;
+ ajax.addEventListener('error', function(event) {
+ expect(event.detail.request).to.be.ok;
+ expect(event.detail.error).to.be.ok;
+ eventFired = true;
+ });
+ var request = ajax.generateRequest();
+ var promise = request.completes.then(function() {
+ throw new Error('Expected the request to fail!');
+ }, function(error) {
+ expect(error).to.be.instanceof(Error);
+ expect(request.succeeded).to.be.eq(false);
+ return timePasses(100);
+ }).then(function() {
+ expect(eventFired).to.be.eq(true);
+ expect(ajax.lastError).to.not.be.eq(null);
+ expect(ajax.lastError.status).to.be.eq(502);
+ expect(ajax.lastError.statusText).to.be.eq("Bad Gateway");
+ expect(ajax.lastError.response).to.be.ok;
+ });
+
+ server.respond();
+
+ return promise;
+ });
+
+ test('with rejectWithRequest the promise chain contains the request and error', function() {
+ ajax.url = '/responds_to_get_with_502_error_json';
+ ajax.handleAs = 'json';
+ ajax.rejectWithRequest = true;
+
+ var request = ajax.generateRequest();
+ var promise = request.completes.then(function() {
+ throw new Error('Expected the request to fail!');
+ }, function(resp) {
+ expect(resp.error).to.be.instanceof(Error);
+ expect(resp.request).to.deep.equal(request);
+ });
+
+ server.respond();
+
+ return promise;
+ });
+
+ test('we give a useful error even when the domain doesn\'t resolve', function() {
+ ajax.url = 'http://nonexistant.example.com/';
+ server.restore();
+ var eventFired = false;
+ ajax.addEventListener('error', function(event) {
+ expect(event.detail.request).to.be.ok;
+ expect(event.detail.error).to.be.ok;
+ eventFired = true;
+ });
+ var request = ajax.generateRequest();
+ var promise = request.completes.then(function() {
+ throw new Error('Expected the request to fail!');
+ }, function(error) {
+ expect(request.succeeded).to.be.eq(false);
+ expect(error).to.not.be.eq(null);
+ return timePasses(100);
+ }).then(function() {
+ expect(eventFired).to.be.eq(true);
+ expect(ajax.lastError).to.not.be.eq(null);
+ });
+
+ server.respond();
+
+ return promise;
+ });
+ });
+
+ suite('when handleAs parameter is `json`', function() {
+
+ test('response type is string', function() {
+ ajax.url = '/responds_to_get_with_json';
+ ajax.handleAs = 'json';
+
+ request = ajax.generateRequest();
+ var promise = request.completes.then(function() {
+ expect(typeof(ajax.lastResponse)).to.be.equal('object');
+ });
+
+ expect(server.requests.length).to.be.equal(1);
+ expect(server.requests[0].requestHeaders['accept']).to.be.equal(
+ 'application/json');
+
+ server.respond();
+
+ return promise;
+ });
+
+ });
+
+ suite('when making a POST over the wire', function() {
+ test('FormData is handled correctly', function() {
+ server.restore();
+ var requestBody = new FormData();
+ requestBody.append('a', 'foo');
+ requestBody.append('b', 'bar');
+
+ var ajax = fixture('RealPost');
+ ajax.body = requestBody;
+ return ajax.generateRequest().completes.then(function() {
+ expect(ajax.lastResponse.headers['Content-Type']).to.match(
+ /^multipart\/form-data; boundary=.*$/);
+
+ expect(ajax.lastResponse.form.a).to.be.equal('foo');
+ expect(ajax.lastResponse.form.b).to.be.equal('bar');
+ });
+ });
+
+ test('json is handled correctly', function() {
+ server.restore();
+ var ajax = fixture('RealPost');
+ ajax.body = JSON.stringify({a: 'foo', b: 'bar'});
+ ajax.contentType = 'application/json';
+ return ajax.generateRequest().completes.then(function() {
+ expect(ajax.lastResponse.headers['Content-Type']).to.match(
+ /^application\/json(;.*)?$/);
+ expect(ajax.lastResponse.json.a).to.be.equal('foo');
+ expect(ajax.lastResponse.json.b).to.be.equal('bar');
+ });
+ });
+
+ test('urlencoded data is handled correctly', function() {
+ server.restore();
+ var ajax = fixture('RealPost');
+ ajax.body = 'a=foo&b=bar';
+ return ajax.generateRequest().completes.then(function() {
+ expect(ajax.lastResponse.headers['Content-Type']).to.match(
+ /^application\/x-www-form-urlencoded(;.*)?$/);
+
+ expect(ajax.lastResponse.form.a).to.be.equal('foo');
+ expect(ajax.lastResponse.form.b).to.be.equal('bar');
+ });
+ });
+
+ test('xml is handled correctly', function() {
+ server.restore();
+ var ajax = fixture('RealPost');
+
+ var xmlDoc = document.implementation.createDocument(
+ null, "foo", null);
+ var node = xmlDoc.createElement("bar");
+ node.setAttribute("name" , "baz");
+ xmlDoc.documentElement.appendChild(node);
+ ajax.body = xmlDoc;
+ return ajax.generateRequest().completes.then(function() {
+ expect(ajax.lastResponse.headers['Content-Type']).to.match(
+ /^application\/xml(;.*)?$/);
+ expect(ajax.lastResponse.data).to.match(
+ /<foo\s*><bar\s+name="baz"\s*\/><\/foo\s*>/);
+ });
+ });
+ });
+
+ suite('when setting timeout', function() {
+ setup(function() {
+ server.restore();
+ });
+
+ test('it is present in the request xhr object', function() {
+ ajax.url = '/responds_to_get_with_json';
+ ajax.timeout = 5000; // 5 Seconds
+
+ request = ajax.generateRequest();
+ expect(request.xhr.timeout).to.be.equal(5000); // 5 Seconds
+ });
+
+ test('it fails once that timeout is reached', function() {
+ var ajax = fixture('Delay');
+ ajax.timeout = 1; // 1 Millisecond
+
+ request = ajax.generateRequest();
+ return request.completes.then(function() {
+ throw new Error('Expected the request to throw an error.');
+ }, function() {
+ expect(request.succeeded).to.be.equal(false);
+ expect(request.xhr.status).to.be.equal(0);
+ expect(request.timedOut).to.be.equal(true);
+ return timePasses(1);
+ }).then(function() {
+ expect(ajax.loading).to.be.equal(false);
+ expect(ajax.lastResponse).to.be.equal(null);
+ expect(ajax.lastError).to.not.be.equal(null);
+ });
+ });
+ });
+
+ suite('when using the bubbles attribute', function() {
+ test('the request and response events should bubble to window', function(done) {
+ server.restore();
+ var total = 0;
+ function incrementTotal() {
+ total++;
+ if (total === 5) {
+ done();
+ }
+ }
+ window.addEventListener('iron-ajax-presend', incrementTotal);
+ window.addEventListener('request', incrementTotal);
+ window.addEventListener('iron-ajax-request', incrementTotal);
+ window.addEventListener('response', incrementTotal);
+ window.addEventListener('iron-ajax-response', incrementTotal);
+ var ajax = fixture('Bubbles')[0];
+ ajax.generateRequest();
+ server.respond();
+ });
+
+ test('the request and error events should bubble to window', function(done) {
+ var total = 0;
+ function incrementTotal() {
+ total++;
+ if (total === 5) {
+ done();
+ }
+ }
+ window.addEventListener('iron-ajax-presend', incrementTotal);
+ window.addEventListener('request', incrementTotal);
+ window.addEventListener('iron-ajax-request', incrementTotal);
+ // NOTE(cdata): This needs to be capturing because Mocha + Firefox
+ // results in the error event being observed too early by the test
+ // runner and failing the test:
+ window.addEventListener('error', incrementTotal, true);
+ window.addEventListener('iron-ajax-error', incrementTotal);
+ var ajax = fixture('Bubbles')[1];
+ ajax.generateRequest();
+ server.respond();
+ });
+ });
+
+ suite('when handling the `iron-ajax-presend` event', function() {
+ setup(function() {
+ server.restore();
+ });
+
+ test('ability to cancel request', function() {
+ var requestSpy = sinon.spy();
+ var promiseSpy = sinon.spy();
+
+ ajax.addEventListener('iron-ajax-presend', function(e) {
+ e.preventDefault();
+ });
+ ajax.addEventListener('iron-ajax-request', requestSpy);
+
+ var request = ajax.generateRequest();
+
+ return request.completes.catch(function(request) {
+ expect(request.aborted).to.be.true;
+ promiseSpy();
+ }).then(function() {
+ expect(promiseSpy).to.be.calledOnce;
+ expect(requestSpy).not.to.be.called;
+ });
+ });
+
+ test('ability to modify the request options', function(done) {
+ ajax.addEventListener('iron-ajax-presend', function(e) {
+ e.detail.options.url += '/test';
+ e.detail.options.headers.authToken = 'a.b.c';
+ });
+ ajax.addEventListener('iron-ajax-request', function(e) {
+ expect(e.detail.options.url).to.equal('/responds_to_get_with_json/test');
+ expect(e.detail.options.headers.authToken).to.equal('a.b.c');
+ done();
+ });
+
+ ajax.generateRequest();
+ });
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-ajax/test/iron-request.html b/catapult/third_party/polymer/components/iron-ajax/test/iron-request.html
new file mode 100644
index 00000000..6161bb5f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-ajax/test/iron-request.html
@@ -0,0 +1,368 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-request</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../iron-request.html">
+</head>
+<body>
+ <test-fixture id="TrivialRequest">
+ <template>
+ <iron-request></iron-request>
+ </template>
+ </test-fixture>
+ <script>
+ suite('<iron-request>', function() {
+ var jsonResponseHeaders;
+ var successfulRequestOptions;
+ var request;
+ var server;
+
+ setup(function() {
+ jsonResponseHeaders = {
+ 'Content-Type': 'application/json'
+ };
+ server = sinon.fakeServer.create();
+ server.respondWith('GET', '/responds_to_get_with_json', [
+ 200,
+ jsonResponseHeaders,
+ '{"success":true}'
+ ]);
+
+ server.respondWith('GET', '/responds_to_get_with_prefixed_json', [
+ 200,
+ jsonResponseHeaders,
+ '])}while(1);</x>{"success":true}'
+ ]);
+
+ server.respondWith('GET', '/responds_to_get_with_500', [
+ 500,
+ {},
+ ''
+ ]);
+
+ server.respondWith('GET', '/responds_to_get_with_100', [
+ 100,
+ {},
+ ''
+ ]);
+
+ server.respondWith('GET', '/responds_to_get_with_0', [
+ 0,
+ jsonResponseHeaders,
+ '{"success":true}'
+ ]);
+
+
+ request = fixture('TrivialRequest');
+ successfulRequestOptions = {
+ url: '/responds_to_get_with_json'
+ };
+
+ synchronousSuccessfulRequestOptions = {
+ url: '/responds_to_get_with_json',
+ async: false,
+ timeout: 100
+ };
+
+ asynchronousSuccessfulRequestOptions = {
+ url: '/responds_to_get_with_json',
+ async: true,
+ timeout: 100
+ };
+ });
+
+ teardown(function() {
+ server.restore();
+ });
+
+ suite('basic usage', function() {
+ test('creates network requests, requiring only `url`', function() {
+ request.send(successfulRequestOptions);
+
+ server.respond();
+
+ expect(request.response).to.be.ok;
+ });
+
+ test('timeout not set if synchronous', function() {
+ request.send(synchronousSuccessfulRequestOptions);
+
+ expect(request.xhr.async).to.be.eql(false);
+ expect(request.xhr.timeout).to.be.eql(undefined);
+ });
+
+ test('timeout set if asynchronous', function() {
+ request.send(asynchronousSuccessfulRequestOptions);
+
+ expect(request.xhr.async).to.be.eql(true);
+ expect(request.xhr.timeout).to.be.eql(100);
+ });
+
+ test('sets async to true by default', function() {
+ request.send(successfulRequestOptions);
+ expect(request.xhr.async).to.be.eql(true);
+ });
+
+ test('can be aborted', function() {
+ request.send(successfulRequestOptions);
+
+ request.abort();
+
+ server.respond();
+
+ return request.completes.then(function() {
+ throw new Error('Request did not abort appropriately!');
+ }).catch(function(e) {
+ expect(request.response).to.not.be.ok;
+ });
+ });
+
+ test('can be aborted with request element', function() {
+ var options = {
+ url: successfulRequestOptions.url,
+ rejectWithRequest: true
+ };
+ request.send(options);
+
+ request.abort();
+
+ server.respond();
+
+ return request.completes.then(function() {
+ throw new Error('Request did not abort appropriately!');
+ }).catch(function(e) {
+ expect(e.error).to.be.instanceof(Error);
+ expect(e.request).to.deep.equal(request);
+ });
+ });
+
+ test('default responseType is text', function() {
+ request.send(successfulRequestOptions);
+ server.respond();
+
+ return request.completes.then(function() {
+ expect(request.response).to.be.an('string')
+ });
+ });
+
+ test('default responseType of text is not applied, when async is false', function() {
+ var options = Object.create(successfulRequestOptions);
+ options.async = false;
+
+ request.send(options);
+ server.respond();
+
+ return request.completes.then(function() {
+ expect(request.xhr.responseType).to.be.empty;
+ });
+ });
+
+ test('responseType can be configured via handleAs option', function() {
+ var options = Object.create(successfulRequestOptions);
+ options.handleAs = 'json';
+
+ request.send(options);
+ expect(server.requests.length).to.be.equal(1);
+ expect(server.requests[0].requestHeaders['accept']).to.be.equal(
+ 'application/json');
+ server.respond();
+
+ return request.completes.then(function() {
+ expect(request.response).to.be.an('object');
+ });
+ });
+
+ test('setting jsonPrefix correctly strips it from the response', function() {
+ var options = {
+ url: '/responds_to_get_with_prefixed_json',
+ handleAs: 'json',
+ jsonPrefix: '])}while(1);</x>'
+ };
+
+ request.send(options);
+ expect(server.requests.length).to.be.equal(1);
+ expect(server.requests[0].requestHeaders['accept']).to.be.equal(
+ 'application/json');
+ server.respond();
+
+ return request.completes.then(function() {
+ expect(request.response).to.deep.eq({success: true});
+ });
+ });
+
+ test('responseType cannot be configured via handleAs option, when async is false', function() {
+ var options = Object.create(successfulRequestOptions);
+ options.handleAs = 'json';
+ options.async = false;
+
+ request.send(options);
+ expect(server.requests.length).to.be.equal(1);
+ expect(server.requests[0].requestHeaders['accept']).to.be.equal(
+ 'application/json');
+ server.respond();
+
+ return request.completes.then(function() {
+ expect(request.response).to.be.a('string');
+ });
+ });
+
+ test('headers are sent up', function() {
+ var options = Object.create(successfulRequestOptions);
+ options.headers = {
+ 'foo': 'bar',
+ 'accept': 'this should override the default'
+ };
+ request.send(options);
+ expect(server.requests.length).to.be.equal(1);
+ var fakeXhr = server.requests[0]
+ expect(fakeXhr.requestHeaders['foo']).to.be.equal(
+ 'bar');
+ expect(fakeXhr.requestHeaders['accept']).to.be.equal(
+ 'this should override the default');
+ });
+
+ test('headers are deduped by lowercasing', function() {
+ var options = Object.create(successfulRequestOptions);
+ options.headers = {
+ 'foo': 'bar',
+ 'Foo': 'bar',
+ 'fOo': 'bar',
+ 'Accept': 'this should also override the default'
+ };
+ request.send(options);
+ expect(server.requests.length).to.be.equal(1);
+ var fakeXhr = server.requests[0]
+ expect(Object.keys(fakeXhr.requestHeaders).length).to.be.equal(2);
+ expect(fakeXhr.requestHeaders['foo']).to.be.equal(
+ 'bar');
+ expect(fakeXhr.requestHeaders['accept']).to.be.equal(
+ 'this should also override the default');
+ });
+ });
+
+ suite('special cases', function() {
+ test('treats status code 0 as success, though the outcome is ambiguous', function() {
+ // Note: file:// status code will probably be 0 no matter what happened.
+ request.send({
+ url: '/responds_to_get_with_0'
+ });
+
+ server.respond();
+
+ expect(request.succeeded).to.be.equal(true);
+ });
+
+ test('special form characters', function() {
+ var testCases = [
+ {
+ test: null,
+ answer: ''
+ },
+ {
+ test: undefined,
+ answer: ''
+ },
+ {
+ test: NaN,
+ answer: 'NaN'
+ },
+ {
+ test: new String('\n\r\n\r'),
+ answer: '%0D%0A%0D%0A%0D' // \r\n\r\n\r
+ },
+ {
+ test: 0,
+ answer: '0'
+ },
+ {
+ test: new String('hello world'),
+ answer: 'hello+world'
+ }
+ ];
+
+ var testCase;
+ for (var i = 0; i < testCases.length; i++) {
+ testCase = testCases[i];
+ var encoded = request._wwwFormUrlEncodePiece(testCase.test);
+
+ expect(encoded).to.be.equal(testCase.answer);
+ }
+ });
+ });
+
+ suite('errors', function() {
+ test('treats status codes between 1 and 199 as errors', function() {
+ request.send({
+ url: '/responds_to_get_with_100'
+ });
+
+ server.respond();
+
+ expect(request.succeeded).to.be.equal(false);
+ });
+
+ test('treats status codes between 300 and ∞ as errors', function() {
+ request.send({
+ url: '/responds_to_get_with_500'
+ });
+
+ server.respond();
+
+ expect(request.succeeded).to.be.equal(false);
+ });
+ });
+
+ suite('status codes', function() {
+ test('status and statusText is set after a ambiguous request', function() {
+ request.send({
+ url: '/responds_to_get_with_0'
+ });
+
+ server.respond();
+
+ expect(request.status).to.be.equal(0);
+ expect(request.statusText).to.be.equal('');
+ });
+
+ test('status and statusText is set after a request that succeeded', function() {
+ request.send({
+ url: '/responds_to_get_with_json'
+ });
+
+ server.respond();
+
+ expect(request.status).to.be.equal(200);
+ expect(request.statusText).to.be.equal('OK');
+ });
+
+ test('status and statusText is set after a request that failed', function() {
+ request.send({
+ url: '/responds_to_get_with_500'
+ });
+
+ server.respond();
+
+ expect(request.status).to.be.equal(500);
+ expect(request.statusText).to.be.equal('Internal Server Error');
+ });
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/.bower.json b/catapult/third_party/polymer/components/iron-autogrow-textarea/.bower.json
new file mode 100644
index 00000000..4d0f8b88
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/.bower.json
@@ -0,0 +1,48 @@
+{
+ "name": "iron-autogrow-textarea",
+ "version": "1.0.15",
+ "description": "A textarea element that automatically grows with input",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "input",
+ "textarea"
+ ],
+ "main": "iron-autogrow-textarea.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-autogrow-textarea.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-autogrow-textarea",
+ "ignore": [],
+ "dependencies": {
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.0.15",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.15",
+ "commit": "e75057555b347185f498d400127082fb1a812bda"
+ },
+ "_source": "https://github.com/PolymerElements/iron-autogrow-textarea.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-autogrow-textarea"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-autogrow-textarea/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..0b70c29d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-autogrow-textarea/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/.gitignore b/catapult/third_party/polymer/components/iron-autogrow-textarea/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/.travis.yml b/catapult/third_party/polymer/components/iron-autogrow-textarea/.travis.yml
new file mode 100644
index 00000000..be2553d6
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ TqFz1Cdu4BtLCHYLFPES0q+6wPfCnj9bnTfven9LIU+gtek70FPDT3UlnviNwp5ob3o0sbLgsLWG5OkCsRHli+HCgPEVDazSrghfwaT9mL+h/DlM/PVB079VDdIKvZM6L7xEF0zPv7t26kljBRQcZJ81O3K7h1mNjeBj6sDXHzE=
+ - secure: >-
+ ihYM52Uu3H7FGU+x+f+hzMcWt00B7l6GZOtxzT1xYx4hb4E9/WwXYR5z9bqN+s5p10yf3FG64zbmMTuJeBvknDpSyBt/vKP+QBBZT0hxv05GifS38hiSoYT3HBxEpwhYdpjlsSJtIbKfYGR3xIXutRzRrRKSYigcrQNX83MGWFY=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-autogrow-textarea/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/README.md b/catapult/third_party/polymer/components/iron-autogrow-textarea/README.md
new file mode 100644
index 00000000..8a3363ca
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/README.md
@@ -0,0 +1,41 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-autogrow-textarea.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-autogrow-textarea.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-autogrow-textarea)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-autogrow-textarea)_
+
+
+##&lt;iron-autogrow-textarea&gt;
+
+`iron-autogrow-textarea` is an element containing a textarea that grows in height as more
+lines of input are entered. Unless an explicit height or the `maxRows` property is set, it will
+never scroll.
+
+Example:
+
+```html
+<iron-autogrow-textarea></iron-autogrow-textarea>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--iron-autogrow-textarea` | Mixin applied to the textarea | `{}` |
+| `--iron-autogrow-textarea-placeholder` | Mixin applied to the textarea placeholder | `{}` |
+
+
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/bower.json b/catapult/third_party/polymer/components/iron-autogrow-textarea/bower.json
new file mode 100644
index 00000000..3abdfa30
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/bower.json
@@ -0,0 +1,39 @@
+{
+ "name": "iron-autogrow-textarea",
+ "version": "1.0.15",
+ "description": "A textarea element that automatically grows with input",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "input",
+ "textarea"
+ ],
+ "main": "iron-autogrow-textarea.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-autogrow-textarea.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-autogrow-textarea",
+ "ignore": [],
+ "dependencies": {
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/demo/index.html b/catapult/third_party/polymer/components/iron-autogrow-textarea/demo/index.html
new file mode 100644
index 00000000..525d90a4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/demo/index.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>iron-autogrow-textarea demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../iron-autogrow-textarea.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ iron-autogrow-textarea {
+ display: block;
+ width: 200px;
+ margin: 5px 0;
+ }
+
+ textarea {
+ width: 200px;
+ }
+
+ .vertical-section {
+ box-sizing: border-box;
+ width: 400px;
+ margin: 0;
+ }
+ </style>
+ </head>
+ <body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>An iron-autogrow-textarea grows automatically as more text is entered</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <iron-autogrow-textarea></iron-autogrow-textarea>
+ </template>
+ </demo-snippet>
+
+ <h3>The maximum height can be controlled either through the <i>max-rows</i>
+ property, or through a fixed max height</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <iron-autogrow-textarea max-rows="4" placeholder="scrolls after 4 rows"></iron-autogrow-textarea>
+ <iron-autogrow-textarea style="max-height: 50px;" placeholder="scrolls after 50px"></iron-autogrow-textarea>
+ </template>
+ </demo-snippet>
+
+ <h3>The initial height can also be controlled using the <i>rows</i> property,
+ or through a fixed height</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <iron-autogrow-textarea rows="4" placeholder="start with 4 rows"></iron-autogrow-textarea>
+ <iron-autogrow-textarea style="height: 50px;"></iron-autogrow-textarea>
+ </template>
+ </demo-snippet>
+
+ <h3>Example of updating the value imperatively</h3>
+ <!-- TODO: replace this with a demo-snippet when https://github.com/webcomponents/webcomponentsjs/issues/362
+ is fixed -->
+ <div class="example">
+ <template is="dom-bind">
+ <div class="vertical-section">
+ <iron-autogrow-textarea bind-value="{{bindValue}}" id="a1"></iron-autogrow-textarea>
+ <br>
+ <code>bind-value</code>: <span>[[bindValue]]</span>
+ <p on-click="setValue">
+ Imperatively changing <code>bind-value</code> will also update
+ <code>textarea.value</code>:<br>
+ <textarea></textarea>
+ <button value="bindValue">set</button>
+ <br><br>
+
+ Imperatively updating <code>textarea.value</code> will update
+ the display, but not update <code>bind-value</code>:<br>
+ <textarea></textarea>
+ <button value="value">set</button>
+ </p>
+ </div>
+ </template>
+ </div>
+ </div>
+ <script>
+ var scope = document.querySelector('template[is=dom-bind]');
+
+ scope.setValue = function(event) {
+ if (!(event.target instanceof HTMLButtonElement)) {
+ return;
+ }
+ var inputValue = event.target.previousElementSibling.value;
+ if (event.target.value == "bindValue") {
+ document.querySelector('#a1').bindValue = inputValue;
+ } else {
+ document.querySelector('#a1').textarea.value = inputValue;
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/hero.svg b/catapult/third_party/polymer/components/iron-autogrow-textarea/hero.svg
new file mode 100755
index 00000000..ef7e97e7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/hero.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <path d="M140,47c-3,0-4.7-2.4-6.2-4.4c-1.3-1.9-2.4-3.6-4.7-3.6c-2.3,0-3.4,1.7-4.7,3.6c-1.5,2.1-3.1,4.4-6.4,4.4
+ c-3.3,0-4.9-2.4-6.4-4.4c-1.3-1.9-2.5-3.6-4.8-3.6c-2.3,0-3.4,1.7-4.8,3.6c-1.5,2.1-3.1,4.4-6.4,4.4s-5.2-2.4-6.7-4.4
+ c-1.3-1.9-2-3.6-5-3.6v-2c4,0,5.2,2.4,6.7,4.4c1.3,1.9,2.6,3.6,4.9,3.6c2.3,0,3.5-1.7,4.8-3.6c1.5-2.1,3.1-4.4,6.5-4.4
+ s5,2.4,6.4,4.4c1.3,1.9,2.5,3.6,4.8,3.6c2.3,0,3.4-1.7,4.8-3.6c1.5-2.1,3.1-4.4,6.4-4.4c3.3,0,4.7,2.4,6.2,4.4
+ c1.3,1.9,2.5,3.6,4.5,3.6V47z"/>
+ <path d="M140,65c-3,0-4.7-2.4-6.2-4.4c-1.3-1.9-2.4-3.6-4.7-3.6c-2.3,0-3.4,1.7-4.7,3.6c-1.5,2.1-3.1,4.4-6.4,4.4
+ c-3.3,0-4.9-2.4-6.4-4.4c-1.3-1.9-2.5-3.6-4.8-3.6c-2.3,0-3.4,1.7-4.8,3.6c-1.5,2.1-3.1,4.4-6.4,4.4s-5.2-2.4-6.7-4.4
+ c-1.3-1.9-2-3.6-5-3.6v-2c4,0,5.2,2.4,6.7,4.4c1.3,1.9,2.6,3.6,4.9,3.6c2.3,0,3.5-1.7,4.8-3.6c1.5-2.1,3.1-4.4,6.5-4.4
+ s5,2.4,6.4,4.4c1.3,1.9,2.5,3.6,4.8,3.6c2.3,0,3.4-1.7,4.8-3.6c1.5-2.1,3.1-4.4,6.4-4.4c3.3,0,4.7,2.4,6.2,4.4
+ c1.3,1.9,2.5,3.6,4.5,3.6V65z"/>
+ <path d="M140,83c-3,0-4.7-2.4-6.2-4.4c-1.3-1.9-2.4-3.6-4.7-3.6c-2.3,0-3.4,1.7-4.7,3.6c-1.5,2.1-3.1,4.4-6.4,4.4
+ c-3.3,0-4.9-2.4-6.4-4.4c-1.3-1.9-2.5-3.6-4.8-3.6c-2.3,0-3.4,1.7-4.8,3.6c-1.5,2.1-3.1,4.4-6.4,4.4s-5.2-2.4-6.7-4.4
+ c-1.3-1.9-2-3.6-5-3.6v-2c4,0,5.2,2.4,6.7,4.4c1.3,1.9,2.6,3.6,4.9,3.6c2.3,0,3.5-1.7,4.8-3.6c1.5-2.1,3.1-4.4,6.5-4.4
+ s5,2.4,6.4,4.4c1.3,1.9,2.5,3.6,4.8,3.6c2.3,0,3.4-1.7,4.8-3.6c1.5-2.1,3.1-4.4,6.4-4.4c3.3,0,4.7,2.4,6.2,4.4
+ c1.3,1.9,2.5,3.6,4.5,3.6V83z"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+ <path d="M151,102H73V24h78V102z M75,100h74V26H75V100z"/>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/index.html b/catapult/third_party/polymer/components/iron-autogrow-textarea/index.html
new file mode 100644
index 00000000..3be2964f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-autogrow-textarea</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+ </head>
+ <body>
+
+ <iron-component-page></iron-component-page>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/iron-autogrow-textarea.html b/catapult/third_party/polymer/components/iron-autogrow-textarea/iron-autogrow-textarea.html
new file mode 100644
index 00000000..b70d6ae5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/iron-autogrow-textarea.html
@@ -0,0 +1,373 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html">
+<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
+
+<!--
+`iron-autogrow-textarea` is an element containing a textarea that grows in height as more
+lines of input are entered. Unless an explicit height or the `maxRows` property is set, it will
+never scroll.
+
+Example:
+
+ <iron-autogrow-textarea></iron-autogrow-textarea>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--iron-autogrow-textarea` | Mixin applied to the textarea | `{}`
+`--iron-autogrow-textarea-placeholder` | Mixin applied to the textarea placeholder | `{}`
+
+@group Iron Elements
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="iron-autogrow-textarea">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ position: relative;
+ width: 400px;
+ border: 1px solid;
+ padding: 2px;
+ -moz-appearance: textarea;
+ -webkit-appearance: textarea;
+ overflow: hidden;
+ }
+
+ .mirror-text {
+ visibility: hidden;
+ word-wrap: break-word;
+ }
+
+ .fit {
+ @apply(--layout-fit);
+ }
+
+ textarea {
+ position: relative;
+ outline: none;
+ border: none;
+ resize: none;
+ background: inherit;
+ color: inherit;
+ /* see comments in template */
+ width: 100%;
+ height: 100%;
+ font-size: inherit;
+ font-family: inherit;
+ line-height: inherit;
+ text-align: inherit;
+ @apply(--iron-autogrow-textarea);
+ }
+
+ ::content textarea:invalid {
+ box-shadow: none;
+ }
+
+ textarea::-webkit-input-placeholder {
+ @apply(--iron-autogrow-textarea-placeholder);
+ }
+
+ textarea:-moz-placeholder {
+ @apply(--iron-autogrow-textarea-placeholder);
+ }
+
+ textarea::-moz-placeholder {
+ @apply(--iron-autogrow-textarea-placeholder);
+ }
+
+ textarea:-ms-input-placeholder {
+ @apply(--iron-autogrow-textarea-placeholder);
+ }
+ </style>
+
+ <!-- the mirror sizes the input/textarea so it grows with typing -->
+ <!-- use &#160; instead &nbsp; of to allow this element to be used in XHTML -->
+ <div id="mirror" class="mirror-text" aria-hidden="true">&#160;</div>
+
+ <!-- size the input/textarea with a div, because the textarea has intrinsic size in ff -->
+ <div class="textarea-container fit">
+ <textarea id="textarea"
+ name$="[[name]]"
+ autocomplete$="[[autocomplete]]"
+ autofocus$="[[autofocus]]"
+ inputmode$="[[inputmode]]"
+ placeholder$="[[placeholder]]"
+ readonly$="[[readonly]]"
+ required$="[[required]]"
+ disabled$="[[disabled]]"
+ rows$="[[rows]]"
+ minlength$="[[minlength]]"
+ maxlength$="[[maxlength]]"></textarea>
+ </div>
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'iron-autogrow-textarea',
+
+ behaviors: [
+ Polymer.IronFormElementBehavior,
+ Polymer.IronValidatableBehavior,
+ Polymer.IronControlState
+ ],
+
+ properties: {
+
+ /**
+ * Use this property instead of `value` for two-way data binding.
+ * This property will be deprecated in the future. Use `value` instead.
+ * @type {string|number}
+ */
+ bindValue: {
+ observer: '_bindValueChanged',
+ type: String
+ },
+
+ /**
+ * The initial number of rows.
+ *
+ * @attribute rows
+ * @type number
+ * @default 1
+ */
+ rows: {
+ type: Number,
+ value: 1,
+ observer: '_updateCached'
+ },
+
+ /**
+ * The maximum number of rows this element can grow to until it
+ * scrolls. 0 means no maximum.
+ *
+ * @attribute maxRows
+ * @type number
+ * @default 0
+ */
+ maxRows: {
+ type: Number,
+ value: 0,
+ observer: '_updateCached'
+ },
+
+ /**
+ * Bound to the textarea's `autocomplete` attribute.
+ */
+ autocomplete: {
+ type: String,
+ value: 'off'
+ },
+
+ /**
+ * Bound to the textarea's `autofocus` attribute.
+ */
+ autofocus: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Bound to the textarea's `inputmode` attribute.
+ */
+ inputmode: {
+ type: String
+ },
+
+ /**
+ * Bound to the textarea's `placeholder` attribute.
+ */
+ placeholder: {
+ type: String
+ },
+
+ /**
+ * Bound to the textarea's `readonly` attribute.
+ */
+ readonly: {
+ type: String
+ },
+
+ /**
+ * Set to true to mark the textarea as required.
+ */
+ required: {
+ type: Boolean
+ },
+
+ /**
+ * The minimum length of the input value.
+ */
+ minlength: {
+ type: Number
+ },
+
+ /**
+ * The maximum length of the input value.
+ */
+ maxlength: {
+ type: Number
+ }
+
+ },
+
+ listeners: {
+ 'input': '_onInput'
+ },
+
+ observers: [
+ '_onValueChanged(value)'
+ ],
+
+ /**
+ * Returns the underlying textarea.
+ * @type HTMLTextAreaElement
+ */
+ get textarea() {
+ return this.$.textarea;
+ },
+
+ /**
+ * Returns textarea's selection start.
+ * @type Number
+ */
+ get selectionStart() {
+ return this.$.textarea.selectionStart;
+ },
+
+ /**
+ * Returns textarea's selection end.
+ * @type Number
+ */
+ get selectionEnd() {
+ return this.$.textarea.selectionEnd;
+ },
+
+ /**
+ * Sets the textarea's selection start.
+ */
+ set selectionStart(value) {
+ this.$.textarea.selectionStart = value;
+ },
+
+ /**
+ * Sets the textarea's selection end.
+ */
+ set selectionEnd(value) {
+ this.$.textarea.selectionEnd = value;
+ },
+
+ attached: function() {
+ /* iOS has an arbitrary left margin of 3px that isn't present
+ * in any other browser, and means that the paper-textarea's cursor
+ * overlaps the label.
+ * See https://github.com/PolymerElements/paper-input/issues/468.
+ */
+ var IS_IOS = navigator.userAgent.match(/iP(?:[oa]d|hone)/);
+ if (IS_IOS) {
+ this.$.textarea.style.marginLeft = '-3px';
+ }
+ },
+
+ /**
+ * Returns true if `value` is valid. The validator provided in `validator`
+ * will be used first, if it exists; otherwise, the `textarea`'s validity
+ * is used.
+ * @return {boolean} True if the value is valid.
+ */
+ validate: function() {
+ // Empty, non-required input is valid.
+ if (!this.required && this.value == '') {
+ this.invalid = false;
+ return true;
+ }
+
+ var valid;
+ if (this.hasValidator()) {
+ valid = Polymer.IronValidatableBehavior.validate.call(this, this.value);
+ } else {
+ valid = this.$.textarea.validity.valid;
+ this.invalid = !valid;
+ }
+ this.fire('iron-input-validate');
+ return valid;
+ },
+
+ _bindValueChanged: function() {
+ var textarea = this.textarea;
+ if (!textarea) {
+ return;
+ }
+
+ // If the bindValue changed manually, then we need to also update
+ // the underlying textarea's value. Otherwise this change was probably
+ // generated from the _onInput handler, and the two values are already
+ // the same.
+ if (textarea.value !== this.bindValue) {
+ textarea.value = !(this.bindValue || this.bindValue === 0) ? '' : this.bindValue;
+ }
+
+ this.value = this.bindValue;
+ this.$.mirror.innerHTML = this._valueForMirror();
+ // manually notify because we don't want to notify until after setting value
+ this.fire('bind-value-changed', {value: this.bindValue});
+ },
+
+ _onInput: function(event) {
+ this.bindValue = event.path ? event.path[0].value : event.target.value;
+ },
+
+ _constrain: function(tokens) {
+ var _tokens;
+ tokens = tokens || [''];
+ // Enforce the min and max heights for a multiline input to avoid measurement
+ if (this.maxRows > 0 && tokens.length > this.maxRows) {
+ _tokens = tokens.slice(0, this.maxRows);
+ } else {
+ _tokens = tokens.slice(0);
+ }
+ while (this.rows > 0 && _tokens.length < this.rows) {
+ _tokens.push('');
+ }
+ // Use &#160; instead &nbsp; of to allow this element to be used in XHTML.
+ return _tokens.join('<br/>') + '&#160;';
+ },
+
+ _valueForMirror: function() {
+ var input = this.textarea;
+ if (!input) {
+ return;
+ }
+ this.tokens = (input && input.value) ? input.value.replace(/&/gm, '&amp;').replace(/"/gm, '&quot;').replace(/'/gm, '&#39;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;').split('\n') : [''];
+ return this._constrain(this.tokens);
+ },
+
+ _updateCached: function() {
+ this.$.mirror.innerHTML = this._constrain(this.tokens);
+ },
+
+ _onValueChanged: function() {
+ this.bindValue = this.value;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/test/basic.html b/catapult/third_party/polymer/components/iron-autogrow-textarea/test/basic.html
new file mode 100644
index 00000000..51f3abab
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/test/basic.html
@@ -0,0 +1,190 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>iron-autogrow-textarea tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+ <link rel="import" href="../iron-autogrow-textarea.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <iron-autogrow-textarea></iron-autogrow-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="has-bindValue">
+ <template>
+ <iron-autogrow-textarea bind-value="foobar"></iron-autogrow-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="has-value">
+ <template>
+ <iron-autogrow-textarea value="foobar"></iron-autogrow-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="rows">
+ <template>
+ <iron-autogrow-textarea rows="3"></iron-autogrow-textarea>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('setting bindValue sets textarea value', function() {
+ var autogrow = fixture('basic');
+ var textarea = autogrow.textarea;
+
+ autogrow.bindValue = 'batman';
+ assert.equal(textarea.value, autogrow.bindValue, 'textarea value equals to bindValue');
+ });
+
+ test('can set an initial bindValue', function() {
+ var autogrow = fixture('has-bindValue');
+ assert.equal(autogrow.textarea.value, 'foobar', 'textarea value equals to initial bindValue');
+ assert.equal(autogrow.value, 'foobar', 'value equals to initial bindValue');
+ });
+
+ test('can set an initial value', function() {
+ var autogrow = fixture('has-value');
+ assert.equal(autogrow.textarea.value, 'foobar', 'textarea value equals to initial bindValue');
+ assert.equal(autogrow.bindValue, 'foobar', 'textarea bindValue equals to initial value');
+ });
+
+ test('can update the value', function() {
+ var autogrow = fixture('has-bindValue');
+ assert.equal(autogrow.textarea.value, 'foobar', 'textarea value equals to initial bindValue');
+ autogrow.value = 'batman';
+ assert.equal(autogrow.textarea.value, 'batman', 'textarea value is updated');
+ assert.equal(autogrow.bindValue, 'batman', 'bindValue is updated');
+ assert.equal(autogrow.value, 'batman', 'value is updated');
+ });
+
+ test('can update the bindValue', function() {
+ var autogrow = fixture('has-bindValue');
+ assert.equal(autogrow.textarea.value, 'foobar', 'textarea value equals to initial bindValue');
+ autogrow.bindValue = 'batman';
+ assert.equal(autogrow.textarea.value, 'batman', 'textarea value is updated');
+ assert.equal(autogrow.bindValue, 'batman', 'bindValue is updated');
+ assert.equal(autogrow.value, 'batman', 'value is updated');
+ });
+
+ test('can set an initial number of rows', function() {
+ var autogrow = fixture("rows");
+ assert.equal(autogrow.textarea.rows, 3, 'textarea has rows=3');
+ });
+
+ test('adding rows grows the textarea', function() {
+ var autogrow = fixture('basic');
+ var initialHeight = autogrow.offsetHeight;
+
+ autogrow.bindValue = 'batman\nand\nrobin';
+ var finalHeight = autogrow.offsetHeight
+ assert.isTrue(finalHeight > initialHeight);
+ });
+
+ test('removing rows shrinks the textarea', function() {
+ var autogrow = fixture('basic');
+ autogrow.bindValue = 'batman\nand\nrobin';
+ var initialHeight = autogrow.offsetHeight;
+
+ autogrow.bindValue = 'batman';
+ var finalHeight = autogrow.offsetHeight
+ assert.isTrue(finalHeight < initialHeight);
+ });
+
+ test('an undefined bindValue is the empty string', function() {
+ var autogrow = fixture('basic');
+ var initialHeight = autogrow.offsetHeight;
+
+ autogrow.bindValue = 'batman\nand\nrobin';
+ var finalHeight = autogrow.offsetHeight;
+ assert.isTrue(finalHeight > initialHeight);
+
+ autogrow.bindValue = undefined;
+ assert.equal(autogrow.offsetHeight, initialHeight);
+ assert.equal(autogrow.textarea.value, '');
+ });
+
+ test('textarea selection works', function() {
+ var autogrow = fixture('basic');
+ var textarea = autogrow.textarea;
+ autogrow.bindValue = 'batman\nand\nrobin';
+
+ autogrow.selectionStart = 3;
+ autogrow.selectionEnd = 5;
+
+ assert.equal(textarea.selectionStart, 3);
+ assert.equal(textarea.selectionEnd, 5);
+ });
+ });
+
+ suite('focus/blur events', function() {
+ var input;
+
+ setup(function() {
+ input = fixture('basic');
+ });
+
+ test('focus/blur events fired on host element', function(done) {
+ var nFocusEvents = 0;
+ var nBlurEvents = 0;
+ input.addEventListener('focus', function() {
+ nFocusEvents += 1;
+ // setTimeout to wait for potentially more, erroneous events
+ setTimeout(function() {
+ assert.equal(nFocusEvents, 1, 'one focus event fired');
+ MockInteractions.blur(input.textarea);
+ });
+ });
+ input.addEventListener('blur', function() {
+ nBlurEvents += 1;
+ // setTimeout to wait for potentially more, erroneous events
+ setTimeout(function() {
+ assert.equal(nBlurEvents, 1, 'one blur event fired');
+ done();
+ });
+ });
+ MockInteractions.focus(input.textarea);
+ });
+
+ });
+
+ suite('validation', function() {
+ test('a required textarea with no text is invalid', function() {
+ var input = fixture('basic');
+ input.required = true;
+ assert.isFalse(input.validate());
+
+ input.bindValue = 'batman';
+ assert.isTrue(input.validate());
+ });
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-autogrow-textarea/test/index.html b/catapult/third_party/polymer/components/iron-autogrow-textarea/test/index.html
new file mode 100644
index 00000000..c4c52159
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-autogrow-textarea/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2014 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>iron-autogrow-textarea tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/.bower.json b/catapult/third_party/polymer/components/iron-behaviors/.bower.json
new file mode 100644
index 00000000..35e00aca
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/.bower.json
@@ -0,0 +1,42 @@
+{
+ "name": "iron-behaviors",
+ "version": "1.0.18",
+ "description": "Provides a set of behaviors for the iron elements",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-behaviors.git"
+ },
+ "main": [
+ "iron-button-state.html",
+ "iron-control-state.html"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.2.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "paper-input": "polymerelements/paper-input#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-behaviors",
+ "_release": "1.0.18",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.18",
+ "commit": "7c07f18d747aee2c31330511ae8f780554ff98d8"
+ },
+ "_source": "https://github.com/PolymerElements/iron-behaviors.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-behaviors"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-behaviors/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-behaviors/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..1cc85c0c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-behaviors/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-behaviors/.gitignore b/catapult/third_party/polymer/components/iron-behaviors/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-behaviors/.travis.yml b/catapult/third_party/polymer/components/iron-behaviors/.travis.yml
new file mode 100644
index 00000000..7b08b65a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ H49pcRc5C6G+ti/ehtT74GZdsUsM/xCvEVJBmKq8rpck7s18R6BbH37RkF2XgYfO4rVa1Bl4KU4Wf5S6aIDYzdaq/phGtFQ04NmDYGbmBhRjwfgxlW4dJ7mkXqXCvNZkxJtAJpgzgVG+xu/I6GsO1Lp4VjGENvVYSsrkGIlSA34=
+ - secure: >-
+ Zq+hvOlL1RmTtMfAtO3bxqYnB7X6MY199cVCKo2J/EbsMvOHII1JvEU1+s2/UG9tgoiXkd7N2OfFOivlbQ75BDIwtvkq32KZNrUEC6vRGhbMBc8JCKkdFB/XHh1mNhQcn6Js656PhZIj2WteZYMSGYDUj7KcBBMacRZQKWuB0OU=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-behaviors/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-behaviors/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-behaviors/README.md b/catapult/third_party/polymer/components/iron-behaviors/README.md
new file mode 100644
index 00000000..0a0629ec
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/README.md
@@ -0,0 +1,22 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-button-state.html iron-control-state.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-behaviors.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-behaviors)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-behaviors)_
+
+
+<!-- No docs for Polymer.IronButtonState found. -->
+
+<!-- No docs for Polymer.IronControlState found. -->
diff --git a/catapult/third_party/polymer/components/iron-behaviors/bower.json b/catapult/third_party/polymer/components/iron-behaviors/bower.json
new file mode 100644
index 00000000..68cf080b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/bower.json
@@ -0,0 +1,32 @@
+{
+ "name": "iron-behaviors",
+ "version": "1.0.18",
+ "description": "Provides a set of behaviors for the iron elements",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-behaviors.git"
+ },
+ "main": [
+ "iron-button-state.html",
+ "iron-control-state.html"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.2.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "paper-input": "polymerelements/paper-input#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-behaviors/demo/index.html b/catapult/third_party/polymer/components/iron-behaviors/demo/index.html
new file mode 100644
index 00000000..51fe2d64
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/demo/index.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>simple-button</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link href="../../paper-styles/demo-pages.html" rel="import">
+ <link href="simple-button.html" rel="import">
+
+ <style>
+
+ .vertical-section {
+ text-align: center;
+ }
+
+ </style>
+
+</head>
+<body>
+ <div class="vertical-section vertical-section-container centered">
+ <h3>Normal</h3>
+
+ <simple-button tabindex="0">Hello World</simple-button>
+
+ <h3>Toggles</h3>
+
+ <simple-button toggles tabindex="0">Hello World</simple-button>
+
+ <h3>Disabled</h3>
+
+ <simple-button disabled tabindex="0">Hello World</simple-button>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/demo/simple-button.html b/catapult/third_party/polymer/components/iron-behaviors/demo/simple-button.html
new file mode 100644
index 00000000..bfa6a2b3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/demo/simple-button.html
@@ -0,0 +1,66 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-button-state.html">
+<link rel="import" href="../iron-control-state.html">
+
+<dom-module id="simple-button">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ background-color: #4285F4;
+ color: #fff;
+ min-height: 8px;
+ min-width: 8px;
+ padding: 16px;
+ text-transform: uppercase;
+ border-radius: 3px;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ }
+
+ :host([disabled]) {
+ opacity: 0.3;
+ pointer-events: none;
+ }
+
+ :host([active]),
+ :host([pressed]) {
+ background-color: #3367D6;
+ box-shadow: inset 0 3px 5px rgba(0,0,0,.2);
+ }
+ </style>
+
+ <content></content>
+
+ </template>
+
+ <script>
+
+ Polymer({
+
+ behaviors: [
+ Polymer.IronControlState,
+ Polymer.IronButtonState
+ ],
+
+ hostAttributes: {
+ role: 'button'
+ }
+ });
+
+ </script>
+
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/index.html b/catapult/third_party/polymer/components/iron-behaviors/index.html
new file mode 100644
index 00000000..220deb00
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>Iron Behaviors</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page src="iron-button-state.html"></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/iron-button-state.html b/catapult/third_party/polymer/components/iron-behaviors/iron-button-state.html
new file mode 100644
index 00000000..c5221b8e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/iron-button-state.html
@@ -0,0 +1,236 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="iron-control-state.html">
+
+<script>
+
+ /**
+ * @demo demo/index.html
+ * @polymerBehavior Polymer.IronButtonState
+ */
+ Polymer.IronButtonStateImpl = {
+
+ properties: {
+
+ /**
+ * If true, the user is currently holding down the button.
+ */
+ pressed: {
+ type: Boolean,
+ readOnly: true,
+ value: false,
+ reflectToAttribute: true,
+ observer: '_pressedChanged'
+ },
+
+ /**
+ * If true, the button toggles the active state with each tap or press
+ * of the spacebar.
+ */
+ toggles: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+
+ /**
+ * If true, the button is a toggle and is currently in the active state.
+ */
+ active: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ reflectToAttribute: true
+ },
+
+ /**
+ * True if the element is currently being pressed by a "pointer," which
+ * is loosely defined as mouse or touch input (but specifically excluding
+ * keyboard input).
+ */
+ pointerDown: {
+ type: Boolean,
+ readOnly: true,
+ value: false
+ },
+
+ /**
+ * True if the input device that caused the element to receive focus
+ * was a keyboard.
+ */
+ receivedFocusFromKeyboard: {
+ type: Boolean,
+ readOnly: true
+ },
+
+ /**
+ * The aria attribute to be set if the button is a toggle and in the
+ * active state.
+ */
+ ariaActiveAttribute: {
+ type: String,
+ value: 'aria-pressed',
+ observer: '_ariaActiveAttributeChanged'
+ }
+ },
+
+ listeners: {
+ down: '_downHandler',
+ up: '_upHandler',
+ tap: '_tapHandler'
+ },
+
+ observers: [
+ '_focusChanged(focused)',
+ '_activeChanged(active, ariaActiveAttribute)'
+ ],
+
+ keyBindings: {
+ 'enter:keydown': '_asyncClick',
+ 'space:keydown': '_spaceKeyDownHandler',
+ 'space:keyup': '_spaceKeyUpHandler',
+ },
+
+ _mouseEventRe: /^mouse/,
+
+ _tapHandler: function() {
+ if (this.toggles) {
+ // a tap is needed to toggle the active state
+ this._userActivate(!this.active);
+ } else {
+ this.active = false;
+ }
+ },
+
+ _focusChanged: function(focused) {
+ this._detectKeyboardFocus(focused);
+
+ if (!focused) {
+ this._setPressed(false);
+ }
+ },
+
+ _detectKeyboardFocus: function(focused) {
+ this._setReceivedFocusFromKeyboard(!this.pointerDown && focused);
+ },
+
+ // to emulate native checkbox, (de-)activations from a user interaction fire
+ // 'change' events
+ _userActivate: function(active) {
+ if (this.active !== active) {
+ this.active = active;
+ this.fire('change');
+ }
+ },
+
+ _downHandler: function(event) {
+ this._setPointerDown(true);
+ this._setPressed(true);
+ this._setReceivedFocusFromKeyboard(false);
+ },
+
+ _upHandler: function() {
+ this._setPointerDown(false);
+ this._setPressed(false);
+ },
+
+ /**
+ * @param {!KeyboardEvent} event .
+ */
+ _spaceKeyDownHandler: function(event) {
+ var keyboardEvent = event.detail.keyboardEvent;
+ var target = Polymer.dom(keyboardEvent).localTarget;
+
+ // Ignore the event if this is coming from a focused light child, since that
+ // element will deal with it.
+ if (this.isLightDescendant(/** @type {Node} */(target)))
+ return;
+
+ keyboardEvent.preventDefault();
+ keyboardEvent.stopImmediatePropagation();
+ this._setPressed(true);
+ },
+
+ /**
+ * @param {!KeyboardEvent} event .
+ */
+ _spaceKeyUpHandler: function(event) {
+ var keyboardEvent = event.detail.keyboardEvent;
+ var target = Polymer.dom(keyboardEvent).localTarget;
+
+ // Ignore the event if this is coming from a focused light child, since that
+ // element will deal with it.
+ if (this.isLightDescendant(/** @type {Node} */(target)))
+ return;
+
+ if (this.pressed) {
+ this._asyncClick();
+ }
+ this._setPressed(false);
+ },
+
+ // trigger click asynchronously, the asynchrony is useful to allow one
+ // event handler to unwind before triggering another event
+ _asyncClick: function() {
+ this.async(function() {
+ this.click();
+ }, 1);
+ },
+
+ // any of these changes are considered a change to button state
+
+ _pressedChanged: function(pressed) {
+ this._changedButtonState();
+ },
+
+ _ariaActiveAttributeChanged: function(value, oldValue) {
+ if (oldValue && oldValue != value && this.hasAttribute(oldValue)) {
+ this.removeAttribute(oldValue);
+ }
+ },
+
+ _activeChanged: function(active, ariaActiveAttribute) {
+ if (this.toggles) {
+ this.setAttribute(this.ariaActiveAttribute,
+ active ? 'true' : 'false');
+ } else {
+ this.removeAttribute(this.ariaActiveAttribute);
+ }
+ this._changedButtonState();
+ },
+
+ _controlStateChanged: function() {
+ if (this.disabled) {
+ this._setPressed(false);
+ } else {
+ this._changedButtonState();
+ }
+ },
+
+ // provide hook for follow-on behaviors to react to button-state
+
+ _changedButtonState: function() {
+ if (this._buttonStateChanged) {
+ this._buttonStateChanged(); // abstract
+ }
+ }
+
+ };
+
+ /** @polymerBehavior */
+ Polymer.IronButtonState = [
+ Polymer.IronA11yKeysBehavior,
+ Polymer.IronButtonStateImpl
+ ];
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/iron-control-state.html b/catapult/third_party/polymer/components/iron-behaviors/iron-control-state.html
new file mode 100644
index 00000000..f34d0579
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/iron-control-state.html
@@ -0,0 +1,110 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+
+ /**
+ * @demo demo/index.html
+ * @polymerBehavior
+ */
+ Polymer.IronControlState = {
+
+ properties: {
+
+ /**
+ * If true, the element currently has focus.
+ */
+ focused: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ readOnly: true,
+ reflectToAttribute: true
+ },
+
+ /**
+ * If true, the user cannot interact with this element.
+ */
+ disabled: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ observer: '_disabledChanged',
+ reflectToAttribute: true
+ },
+
+ _oldTabIndex: {
+ type: Number
+ },
+
+ _boundFocusBlurHandler: {
+ type: Function,
+ value: function() {
+ return this._focusBlurHandler.bind(this);
+ }
+ }
+
+ },
+
+ observers: [
+ '_changedControlState(focused, disabled)'
+ ],
+
+ ready: function() {
+ this.addEventListener('focus', this._boundFocusBlurHandler, true);
+ this.addEventListener('blur', this._boundFocusBlurHandler, true);
+ },
+
+ _focusBlurHandler: function(event) {
+ // NOTE(cdata): if we are in ShadowDOM land, `event.target` will
+ // eventually become `this` due to retargeting; if we are not in
+ // ShadowDOM land, `event.target` will eventually become `this` due
+ // to the second conditional which fires a synthetic event (that is also
+ // handled). In either case, we can disregard `event.path`.
+
+ if (event.target === this) {
+ this._setFocused(event.type === 'focus');
+ } else if (!this.shadowRoot) {
+ var target = /** @type {Node} */(Polymer.dom(event).localTarget);
+ if (!this.isLightDescendant(target)) {
+ this.fire(event.type, {sourceEvent: event}, {
+ node: this,
+ bubbles: event.bubbles,
+ cancelable: event.cancelable
+ });
+ }
+ }
+ },
+
+ _disabledChanged: function(disabled, old) {
+ this.setAttribute('aria-disabled', disabled ? 'true' : 'false');
+ this.style.pointerEvents = disabled ? 'none' : '';
+ if (disabled) {
+ this._oldTabIndex = this.tabIndex;
+ this._setFocused(false);
+ this.tabIndex = -1;
+ this.blur();
+ } else if (this._oldTabIndex !== undefined) {
+ this.tabIndex = this._oldTabIndex;
+ }
+ },
+
+ _changedControlState: function() {
+ // _controlStateChanged is abstract, follow-on behaviors may implement it
+ if (this._controlStateChanged) {
+ this._controlStateChanged();
+ }
+ }
+
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/test/active-state.html b/catapult/third_party/polymer/components/iron-behaviors/test/active-state.html
new file mode 100644
index 00000000..e1ed4791
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/test/active-state.html
@@ -0,0 +1,290 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>active-state</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+ <link rel="import" href="test-elements.html">
+ <link rel="import" href="../../paper-input/paper-input.html">
+</head>
+<body>
+ <test-fixture id="TrivialActiveState">
+ <template>
+ <test-button></test-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ToggleActiveState">
+ <template>
+ <test-button toggles></test-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ButtonWithNativeInput">
+ <template>
+ <test-light-dom><input id="input"></test-light-dom>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ButtonWithPaperInput">
+ <template>
+ <test-light-dom><paper-input id="input"></paper-input></test-light-dom>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('active-state', function() {
+ var activeTarget;
+
+ setup(function() {
+ activeTarget = fixture('TrivialActiveState');
+ });
+
+ suite('active state with toggles attribute', function() {
+ setup(function() {
+ activeTarget = fixture('ToggleActiveState');
+ });
+
+ suite('when down', function() {
+ test('is pressed', function() {
+ MockInteractions.down(activeTarget);
+ expect(activeTarget.hasAttribute('pressed')).to.be.eql(true);
+ });
+ });
+
+ suite('when clicked', function() {
+ test('is activated', function(done) {
+ MockInteractions.downAndUp(activeTarget, function() {
+ try {
+ expect(activeTarget.hasAttribute('active')).to.be.eql(true);
+ expect(activeTarget.hasAttribute('aria-pressed')).to.be.eql(true);
+ expect(activeTarget.getAttribute('aria-pressed')).to.be.eql('true');
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ });
+
+ test('is deactivated by a subsequent click', function(done) {
+ MockInteractions.downAndUp(activeTarget, function() {
+ MockInteractions.downAndUp(activeTarget, function() {
+ try {
+ expect(activeTarget.hasAttribute('active')).to.be.eql(false);
+ expect(activeTarget.hasAttribute('aria-pressed')).to.be.eql(true);
+ expect(activeTarget.getAttribute('aria-pressed')).to.be.eql('false');
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ });
+ });
+
+ test('the correct aria attribute is set', function(done) {
+ activeTarget.ariaActiveAttribute = 'aria-checked';
+ MockInteractions.downAndUp(activeTarget, function() {
+ try {
+ expect(activeTarget.hasAttribute('active')).to.be.eql(true);
+ expect(activeTarget.hasAttribute('aria-checked')).to.be.eql(true);
+ expect(activeTarget.getAttribute('aria-checked')).to.be.eql('true');
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ });
+
+ test('the aria attribute is updated correctly', function(done) {
+ activeTarget.ariaActiveAttribute = 'aria-checked';
+ MockInteractions.downAndUp(activeTarget, function() {
+ try {
+ expect(activeTarget.hasAttribute('active')).to.be.eql(true);
+ expect(activeTarget.hasAttribute('aria-checked')).to.be.eql(true);
+ expect(activeTarget.getAttribute('aria-checked')).to.be.eql('true');
+
+ activeTarget.ariaActiveAttribute = 'aria-pressed';
+ expect(activeTarget.hasAttribute('aria-checked')).to.be.eql(false);
+ expect(activeTarget.hasAttribute('aria-pressed')).to.be.eql(true);
+ expect(activeTarget.getAttribute('aria-pressed')).to.be.eql('true');
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ });
+ });
+
+ suite('on blur', function() {
+ test('the pressed property becomes false', function() {
+ MockInteractions.focus(activeTarget);
+ MockInteractions.down(activeTarget);
+ expect(activeTarget.hasAttribute('pressed')).to.be.eql(true);
+ MockInteractions.blur(activeTarget);
+ expect(activeTarget.hasAttribute('pressed')).to.be.eql(false);
+ });
+ });
+ });
+
+ suite('without toggles attribute', function() {
+ suite('when mouse is down', function() {
+ test('does not get an active attribute', function() {
+ expect(activeTarget.hasAttribute('active')).to.be.eql(false);
+ MockInteractions.down(activeTarget);
+ expect(activeTarget.hasAttribute('active')).to.be.eql(false);
+ });
+ });
+
+ suite('when mouse is up', function() {
+ test('does not get an active attribute', function() {
+ MockInteractions.down(activeTarget);
+ expect(activeTarget.hasAttribute('active')).to.be.eql(false);
+ MockInteractions.up(activeTarget);
+ expect(activeTarget.hasAttribute('active')).to.be.eql(false);
+ });
+ });
+ });
+
+ suite('when space is pressed', function() {
+ test('triggers a click event', function(done) {
+ activeTarget.addEventListener('click', function() {
+ done();
+ });
+ MockInteractions.pressSpace(activeTarget);
+ });
+
+ test('only triggers click after the key is released', function(done) {
+ var keyupTriggered = false;
+
+ activeTarget.addEventListener('keyup', function() {
+ keyupTriggered = true;
+ });
+
+ activeTarget.addEventListener('click', function() {
+ try {
+ expect(keyupTriggered).to.be.eql(true);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+
+ MockInteractions.pressSpace(activeTarget);
+ });
+ });
+
+ suite('when enter is pressed', function() {
+ test('triggers a click event', function(done) {
+ activeTarget.addEventListener('click', function() {
+ done();
+ });
+
+ MockInteractions.pressEnter(activeTarget);
+ });
+
+ test('only triggers click before the key is released', function(done) {
+ var keyupTriggered = false;
+
+ activeTarget.addEventListener('keyup', function() {
+ keyupTriggered = true;
+ });
+
+ activeTarget.addEventListener('click', function() {
+ try {
+ expect(keyupTriggered).to.be.eql(false);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+
+ MockInteractions.pressEnter(activeTarget);
+ });
+ });
+
+ suite('nested native input inside button', function() {
+ test('space in light child input does not trigger a button click event', function(done) {
+ var item = fixture('ButtonWithNativeInput');
+ var input = item.querySelector('#input');
+
+ var itemClickHandler = sinon.spy();
+ item.addEventListener('click', itemClickHandler);
+
+ input.focus();
+ MockInteractions.pressSpace(input);
+ Polymer.Base.async(function(){
+ expect(itemClickHandler.callCount).to.be.equal(0);
+ done();
+ }, 1);
+ });
+
+ test('space in button triggers a button click event', function(done) {
+ var item = fixture('ButtonWithNativeInput');
+ var input = item.querySelector('#input');
+
+ var itemClickHandler = sinon.spy();
+ item.addEventListener('click', itemClickHandler);
+
+ MockInteractions.pressSpace(item);
+
+ Polymer.Base.async(function(){
+ // You need two ticks, one for the MockInteractions event, and one
+ // for the button event.
+ Polymer.Base.async(function(){
+ expect(itemClickHandler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ }, 1);
+ });
+ });
+
+ suite('nested paper-input inside button', function() {
+ test('space in light child input does not trigger a button click event', function(done) {
+ var item = fixture('ButtonWithPaperInput');
+ var input = item.querySelector('#input');
+
+ var itemClickHandler = sinon.spy();
+ item.addEventListener('click', itemClickHandler);
+
+ input.focus();
+ MockInteractions.pressSpace(input);
+ Polymer.Base.async(function(){
+ expect(itemClickHandler.callCount).to.be.equal(0);
+ done();
+ }, 1);
+ });
+
+ test('space in button triggers a button click event', function(done) {
+ var item = fixture('ButtonWithPaperInput');
+ var input = item.querySelector('#input');
+
+ var itemClickHandler = sinon.spy();
+ item.addEventListener('click', itemClickHandler);
+
+ MockInteractions.pressSpace(item);
+ Polymer.Base.async(function(){
+ // You need two ticks, one for the MockInteractions event, and one
+ // for the button event.
+ Polymer.Base.async(function(){
+ expect(itemClickHandler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ }, 1);
+ });
+ });
+
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/test/disabled-state.html b/catapult/third_party/polymer/components/iron-behaviors/test/disabled-state.html
new file mode 100644
index 00000000..b342cfd1
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/test/disabled-state.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>disabled-state</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="test-elements.html">
+</head>
+<body>
+
+ <test-fixture id="TrivialDisabledState">
+ <template>
+ <test-control></test-control>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="InitiallyDisabledState">
+ <template>
+ <test-control disabled></test-control>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('disabled-state', function() {
+ var disableTarget;
+
+ suite('a trivial disabled state', function() {
+ setup(function() {
+ disableTarget = fixture('TrivialDisabledState');
+ });
+
+ suite('when disabled is true', function() {
+ test('receives a disabled attribute', function() {
+ disableTarget.disabled = true;
+ expect(disableTarget.hasAttribute('disabled')).to.be.eql(true);
+ });
+
+ test('receives an appropriate aria attribute', function() {
+ disableTarget.disabled = true;
+ expect(disableTarget.getAttribute('aria-disabled')).to.be.eql('true');
+ });
+ });
+
+ suite('when disabled is false', function() {
+ test('loses the disabled attribute', function() {
+ disableTarget.disabled = true;
+ expect(disableTarget.hasAttribute('disabled')).to.be.eql(true);
+ disableTarget.disabled = false;
+ expect(disableTarget.hasAttribute('disabled')).to.be.eql(false);
+ });
+ });
+ });
+
+ suite('a state with an initially disabled target', function() {
+ setup(function() {
+ disableTarget = fixture('InitiallyDisabledState');
+ });
+
+ test('preserves the disabled attribute on target', function() {
+ expect(disableTarget.hasAttribute('disabled')).to.be.eql(true);
+ expect(disableTarget.disabled).to.be.eql(true);
+ });
+
+ test('adds `aria-disabled` to the target', function() {
+ expect(disableTarget.getAttribute('aria-disabled')).to.be.eql('true');
+ });
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/test/focused-state.html b/catapult/third_party/polymer/components/iron-behaviors/test/focused-state.html
new file mode 100644
index 00000000..3b75c704
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/test/focused-state.html
@@ -0,0 +1,161 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>focused-state</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+ <link rel="import" href="test-elements.html">
+</head>
+<body>
+
+ <test-fixture id="TrivialFocusedState">
+ <template>
+ <test-control tabindex="-1"></test-control>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="NestedFocusedState">
+ <template>
+ <nested-focusable></nested-focusable>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="LightDOM">
+ <template>
+ <test-light-dom>
+ <input id="input">
+ <nested-focusable></nested-focusable>
+ </test-light-dom>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('focused-state', function() {
+ var focusTarget;
+
+ setup(function() {
+ focusTarget = fixture('TrivialFocusedState');
+ });
+
+ suite('when is focused', function() {
+ test('receives a focused attribute', function() {
+ expect(focusTarget.hasAttribute('focused')).to.be.eql(false);
+ MockInteractions.focus(focusTarget);
+ expect(focusTarget.hasAttribute('focused')).to.be.eql(true);
+ });
+
+ test('focused property is true', function() {
+ expect(focusTarget.focused).to.not.be.eql(true);
+ MockInteractions.focus(focusTarget);
+ expect(focusTarget.focused).to.be.eql(true);
+ });
+ });
+
+ suite('when is blurred', function() {
+ test('loses the focused attribute', function() {
+ MockInteractions.focus(focusTarget);
+ expect(focusTarget.hasAttribute('focused')).to.be.eql(true);
+ MockInteractions.blur(focusTarget);
+ expect(focusTarget.hasAttribute('focused')).to.be.eql(false);
+ });
+
+ test('focused property is false', function() {
+ MockInteractions.focus(focusTarget);
+ expect(focusTarget.focused).to.be.eql(true);
+ MockInteractions.blur(focusTarget);
+ expect(focusTarget.focused).to.be.eql(false);
+ });
+ });
+
+ suite('when the focused state is disabled', function() {
+ test('will not be focusable', function() {
+ var blurSpy = sinon.spy(focusTarget, 'blur');
+ MockInteractions.focus(focusTarget);
+ focusTarget.disabled = true;
+ expect(focusTarget.getAttribute('tabindex')).to.be.eql('-1');
+ expect(blurSpy.called).to.be.eql(true);
+ });
+ });
+ });
+
+ suite('nested focusable', function() {
+ var focusable;
+
+ setup(function() {
+ focusable = fixture('NestedFocusedState');
+ });
+
+ test('focus/blur events fired on host element', function() {
+ var nFocusEvents = 0;
+ var nBlurEvents = 0;
+
+ focusable.addEventListener('focus', function() {
+ nFocusEvents += 1;
+ expect(focusable.focused).to.be.equal(true);
+ MockInteractions.blur(focusable.$.input);
+ });
+ focusable.addEventListener('blur', function() {
+ expect(focusable.focused).to.be.equal(false);
+ nBlurEvents += 1;
+ });
+
+ MockInteractions.focus(focusable.$.input);
+
+ expect(nBlurEvents).to.be.greaterThan(0);
+ expect(nFocusEvents).to.be.greaterThan(0);
+ });
+
+ });
+
+
+ suite('elements in the light dom', function() {
+ var lightDOM, input, lightDescendantShadowInput;
+
+ setup(function() {
+ lightDOM = fixture('LightDOM');
+ input = document.querySelector('#input');
+ lightDescendantShadowInput = Polymer.dom(lightDOM)
+ .querySelector('nested-focusable').$.input;
+ });
+
+ test('should not fire the focus event', function() {
+ var nFocusEvents = 0;
+
+ lightDOM.addEventListener('focus', function() {
+ nFocusEvents += 1;
+ });
+
+ MockInteractions.focus(input);
+
+ expect(nFocusEvents).to.be.equal(0);
+ });
+
+ test('should not fire the focus event from shadow descendants', function() {
+ var nFocusEvents = 0;
+
+ lightDOM.addEventListener('focus', function() {
+ nFocusEvents += 1;
+ });
+
+ MockInteractions.focus(lightDescendantShadowInput);
+
+ expect(nFocusEvents).to.be.equal(0);
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/test/index.html b/catapult/third_party/polymer/components/iron-behaviors/test/index.html
new file mode 100644
index 00000000..39e7f544
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/test/index.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="utf-8">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'focused-state.html',
+ 'active-state.html',
+ 'disabled-state.html',
+ 'focused-state.html?dom=shadow',
+ 'active-state.html?dom=shadow',
+ 'disabled-state.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-behaviors/test/test-elements.html b/catapult/third_party/polymer/components/iron-behaviors/test/test-elements.html
new file mode 100644
index 00000000..92f6da88
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-behaviors/test/test-elements.html
@@ -0,0 +1,91 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-control-state.html">
+<link rel="import" href="../iron-button-state.html">
+
+<script>
+
+ Polymer({
+
+ is: 'test-control',
+
+ behaviors: [
+ Polymer.IronControlState
+ ]
+
+ });
+
+</script>
+
+<script>
+
+ Polymer({
+
+ is: 'test-button',
+
+ behaviors: [
+ Polymer.IronControlState,
+ Polymer.IronButtonState
+ ],
+
+ _buttonStateChanged: function() {
+
+ }
+
+ });
+
+</script>
+
+<dom-module id="nested-focusable">
+
+ <template>
+ <input id="input">
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'nested-focusable',
+
+ behaviors: [
+ Polymer.IronControlState
+ ]
+
+ });
+
+</script>
+
+<dom-module id="test-light-dom">
+
+ <template>
+ <content select="*"></content>
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'test-light-dom',
+
+ behaviors: [
+ Polymer.IronControlState,
+ Polymer.IronButtonState
+ ]
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/.bower.json b/catapult/third_party/polymer/components/iron-checked-element-behavior/.bower.json
new file mode 100644
index 00000000..348a6b27
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/.bower.json
@@ -0,0 +1,43 @@
+{
+ "name": "iron-checked-element-behavior",
+ "version": "1.0.6",
+ "description": "Implements an element that has a checked attribute and can be added to a form",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "iron",
+ "behavior"
+ ],
+ "main": "iron-checked-element-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-checked-element-behavior.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-checked-element-behavior",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.0.6",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.6",
+ "commit": "be5295d0548b66966462c67bef3727b0529c658b"
+ },
+ "_source": "https://github.com/PolymerElements/iron-checked-element-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-checked-element-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-checked-element-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..2d520b47
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-checked-element-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/.gitignore b/catapult/third_party/polymer/components/iron-checked-element-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-checked-element-behavior/.travis.yml
new file mode 100644
index 00000000..55b3f6e0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ zj5WTkJrZAy1QK0j65jHEsHluT4B/PZl0u3iVEWnyd4CSUbHh3rS5NytZTA8wZ63DYnUcrdbhPeIQI3UKaVBUM2MO/K4MwsC2YTRpifpCLbC+wlRV9WRZNZ9CJ3hu6leshOBWHzZucUWAcKtyQRm66rAU+90ZaJcrBPC/xtgPG1n/Bm2aygr5IA36vJy80Zpwk1+yFmb08eu7jpzNVhcFor+VX0gBW3rxeX0kvzhHGn0bTLxKztNB56Oe+bzx6vU8ACjBcSylouOtPJVIk/iIh7AIDHDcpoZGzmwtVnwAV2mJQtu3V8hQ2kE8eXpDloBGID+AUfTF3YSOVQo34I1yv4T+FlB7uxWrgJo80X5IRXUe15+OoXqraZ25v736RPdTWPjV2b6cWHqdOa45wODkDY0KZ9SrmwZibF3vcfVOG21llW2jF/HC5FvDIiM7gv1ro5jsFbEIwv7ligh0KKa+TOZBphL4sEtMrpxR/zXLqBLXUbKL54A+AefHelxjDIqT3oKQylesrGZ58fEF7Fx5xDSYGJwhIEwTLsEQWIWw8sWB34yRtDBIPocqh8nNZ9pJBOdhK3oC5KK44IQnE44YrIzLRRinHzRVoywpUb5OJDxxSSvOwcqmTYbkFmRJMzfJCEj/EtYsEokmSFv8zIRRDetvenBTGouNK/VsU9xQpA=
+ - secure: >-
+ Q3xN3fRvTQuy/HKlkYFFnVrFo01r6Q8zgzHHK8yKNKio9T/BM0+iIMYP8mRY+qvCQxCboXuUawG6gsxxT2Zn/p7SgNZ+UHq7DcLmocqxmECdGfqra6Hy9Y7BZVYPWlWIADkNUjI0RfOz69/3o/TAFlt4Cnw3BX3ip0o0Rri5/jzj0Nn+xSF+onyMnpH2gQvUE77MHvCuWTPki3R86Bz8RRzbTwKYwVa0oVca7jzdPtOjqzsnz8k9X0HVwQEpHGjqgTP3lg8EotON0rac/ayWs3J3bk9ye5AfvdTCYcZnDcz8POAN6FeC2Xey7oqQO4N8vFagru88mC3AmN67ZYqwI0fmDEYre20lnXFAFBFzsiu4FvKMgFYi/C5tG5ngSR5XcXwLskax82Un7yYYRzVbyhFSxxWZCqIC4cJ5d+E0Wex4eEYDwAlZ9AFP/hRJ2qrBVUbzXeGVMkHlg4qtdUIzHRkMUmjvEWVzTvk0Xa2wD+S0mYPcBwQcscoZy9zqdJM4hfbG9IGi02/DyrTOL3IuNH8WHHvGWS4ajDhSRkGCV4I5VtGKQ/Y3RA/pU6AaI7tVcDhbxOyIPKuOXO6PWuZOiJhhPQ/kceMkUXeh0TmsDtSBpe5OKCnjsdSn02lTT5yYf4nlVIVczP9p6zBerXorzZk1/S3CRBtqYZPgRrefbCw=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-checked-element-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/README.md b/catapult/third_party/polymer/components/iron-checked-element-behavior/README.md
new file mode 100644
index 00000000..c65e58f0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/README.md
@@ -0,0 +1,27 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-checked-element-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-checked-element-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-checked-element-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-checked-element-behavior)_
+
+
+##Polymer.IronCheckedElementBehavior
+
+Use `Polymer.IronCheckedElementBehavior` to implement a custom element
+that has a `checked` property, which can be used for validation if the
+element is also `required`. Element instances implementing this behavior
+will also be registered for use in an `iron-form` element.
+
+
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/bower.json b/catapult/third_party/polymer/components/iron-checked-element-behavior/bower.json
new file mode 100644
index 00000000..bc14a74e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/bower.json
@@ -0,0 +1,34 @@
+{
+ "name": "iron-checked-element-behavior",
+ "version": "1.0.6",
+ "description": "Implements an element that has a checked attribute and can be added to a form",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "iron",
+ "behavior"
+ ],
+ "main": "iron-checked-element-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-checked-element-behavior.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-checked-element-behavior",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-checked-element-behavior/demo/index.html
new file mode 100644
index 00000000..788a64f0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/demo/index.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>iron-checked-element-behavior demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="simple-checkbox.html">
+</head>
+<body unresolved>
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Not required</h4>
+ <div class="horizontal-section">
+ <simple-checkbox></simple-checkbox>
+ </div>
+ </div>
+ <div>
+ <h4>Required</h4>
+ <div class="horizontal-section">
+ <simple-checkbox required></simple-checkbox>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/demo/simple-checkbox.html b/catapult/third_party/polymer/components/iron-checked-element-behavior/demo/simple-checkbox.html
new file mode 100644
index 00000000..24c5c125
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/demo/simple-checkbox.html
@@ -0,0 +1,65 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../paper-button/paper-button.html">
+<link rel="import" href="../iron-checked-element-behavior.html">
+
+<dom-module id="simple-checkbox">
+ <template>
+ <style>
+ :host {
+ display: block;
+ }
+
+ :host([invalid]) span {
+ color: red;
+ }
+
+ #labelText {
+ display: inline-block;
+ width: 100px;
+ }
+ </style>
+
+ <input type="checkbox" id="checkbox" on-tap="_onCheckTap">
+ <span id="labelText">{{label}}</span>
+ <paper-button raised on-click="_onClick">validate</paper-button>
+
+ </template>
+ <script>
+ Polymer({
+
+ is: 'simple-checkbox',
+
+ behaviors: [
+ Polymer.IronCheckedElementBehavior
+ ],
+
+ properties: {
+ label: {
+ type: String,
+ value: 'not validated'
+ }
+ },
+
+ _onCheckTap: function() {
+ this.checked = this.$.checkbox.checked;
+ },
+
+ _onClick: function() {
+ this.validate();
+ this.label = this.invalid ? 'is invalid' : 'is valid';
+ }
+ });
+
+ </script>
+
+ </dom-module>
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/index.html b/catapult/third_party/polymer/components/iron-checked-element-behavior/index.html
new file mode 100644
index 00000000..1975dc5a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-checked-element-behavior</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/iron-checked-element-behavior.html b/catapult/third_party/polymer/components/iron-checked-element-behavior/iron-checked-element-behavior.html
new file mode 100644
index 00000000..3ed2ffea
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/iron-checked-element-behavior.html
@@ -0,0 +1,120 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html">
+<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
+
+<script>
+
+ /**
+ * Use `Polymer.IronCheckedElementBehavior` to implement a custom element
+ * that has a `checked` property, which can be used for validation if the
+ * element is also `required`. Element instances implementing this behavior
+ * will also be registered for use in an `iron-form` element.
+ *
+ * @demo demo/index.html
+ * @polymerBehavior Polymer.IronCheckedElementBehavior
+ */
+ Polymer.IronCheckedElementBehaviorImpl = {
+
+ properties: {
+ /**
+ * Fired when the checked state changes.
+ *
+ * @event iron-change
+ */
+
+ /**
+ * Gets or sets the state, `true` is checked and `false` is unchecked.
+ */
+ checked: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ notify: true,
+ observer: '_checkedChanged'
+ },
+
+ /**
+ * If true, the button toggles the active state with each tap or press
+ * of the spacebar.
+ */
+ toggles: {
+ type: Boolean,
+ value: true,
+ reflectToAttribute: true
+ },
+
+ /* Overriden from Polymer.IronFormElementBehavior */
+ value: {
+ type: String,
+ value: 'on',
+ observer: '_valueChanged'
+ }
+ },
+
+ observers: [
+ '_requiredChanged(required)'
+ ],
+
+ created: function() {
+ // Used by `iron-form` to handle the case that an element with this behavior
+ // doesn't have a role of 'checkbox' or 'radio', but should still only be
+ // included when the form is serialized if `this.checked === true`.
+ this._hasIronCheckedElementBehavior = true;
+ },
+
+ /**
+ * Returns false if the element is required and not checked, and true otherwise.
+ * @param {*=} _value Ignored.
+ * @return {boolean} true if `required` is false or if `checked` is true.
+ */
+ _getValidity: function(_value) {
+ return this.disabled || !this.required || this.checked;
+ },
+
+ /**
+ * Update the aria-required label when `required` is changed.
+ */
+ _requiredChanged: function() {
+ if (this.required) {
+ this.setAttribute('aria-required', 'true');
+ } else {
+ this.removeAttribute('aria-required');
+ }
+ },
+
+ /**
+ * Fire `iron-changed` when the checked state changes.
+ */
+ _checkedChanged: function() {
+ this.active = this.checked;
+ this.fire('iron-change');
+ },
+
+ /**
+ * Reset value to 'on' if it is set to `undefined`.
+ */
+ _valueChanged: function() {
+ if (this.value === undefined || this.value === null) {
+ this.value = 'on';
+ }
+ }
+ };
+
+ /** @polymerBehavior Polymer.IronCheckedElementBehavior */
+ Polymer.IronCheckedElementBehavior = [
+ Polymer.IronFormElementBehavior,
+ Polymer.IronValidatableBehavior,
+ Polymer.IronCheckedElementBehaviorImpl
+ ];
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/test/basic.html b/catapult/third_party/polymer/components/iron-checked-element-behavior/test/basic.html
new file mode 100644
index 00000000..4c13dd8c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/test/basic.html
@@ -0,0 +1,152 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>iron-checked-element-behavior basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link href="../../test-fixture/test-fixture.html" rel="import">
+ <link href="../../iron-meta/iron-meta.html" rel="import">
+ <link href="simple-checkbox.html" rel="import">
+
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <simple-checkbox></simple-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="checked">
+ <template>
+ <simple-checkbox checked></simple-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="with-value">
+ <template>
+ <simple-checkbox value="batman"></simple-checkbox>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+ test('can be checked', function() {
+ var c = fixture('basic');
+ assert.isFalse(c.checked);
+ c.checked = true;
+ assert.isTrue(c.checked);
+ });
+
+ test('can be unchecked', function() {
+ var c = fixture('checked');
+ assert.isTrue(c.checked);
+ c.checked = false;
+ assert.isFalse(c.checked);
+ });
+
+ test('invalid if required and not checked', function() {
+ var c = fixture('basic');
+ c.required = true;
+ assert.isFalse(c.checked);
+ assert.isFalse(c.validate());
+ assert.isTrue(c.invalid);
+ });
+
+ test('valid if required and checked', function() {
+ var c = fixture('basic');
+ c.required = true;
+ c.checked = true;
+ assert.isTrue(c.checked);
+ assert.isTrue(c.validate());
+ assert.isFalse(c.invalid);
+ });
+
+ test('valid if not required and not checked', function() {
+ var c = fixture('basic');
+ assert.isFalse(c.checked);
+ assert.isTrue(c.validate());
+ assert.isFalse(c.invalid);
+ });
+
+ test('has a default value of "on", always', function() {
+ var c = fixture('basic');
+
+ assert.isTrue(c.value === 'on');
+
+ c.checked = true;
+ assert.isTrue(c.value === 'on');
+ });
+
+ test('does not stomp over user defined value when checked', function() {
+ var c = fixture('with-value');
+ assert.isTrue(c.value === 'batman');
+
+ c.checked = true;
+ assert.isTrue(c.value === 'batman');
+ });
+
+ test('value returns "on" when no explicit value is specified', function() {
+ var c = fixture('basic');
+
+ assert.equal(c.value, 'on', 'returns "on"');
+ });
+
+ test('value returns the value when an explicit value is set', function() {
+ var c = fixture('basic');
+
+ c.value = 'abc';
+ assert.equal(c.value, 'abc', 'returns "abc"');
+
+ c.value = '123';
+ assert.equal(c.value, '123', 'returns "123"');
+ });
+
+ test('value returns "on" when value is set to undefined', function() {
+ var c = fixture('basic');
+
+ c.value = 'abc';
+ assert.equal(c.value, 'abc', 'returns "abc"');
+
+ c.value = undefined;
+ assert.equal(c.value, 'on', 'returns "on"');
+ });
+ });
+
+ suite('a11y', function() {
+ test('setting `required` sets `aria-required=true`', function() {
+ var c = fixture('basic');
+ c.required = true;
+ assert.equal(c.getAttribute('aria-required'), 'true');
+ c.required = false;
+ assert.isFalse(c.hasAttribute('aria-required'));
+ });
+
+ test('setting `invalid` sets `aria-invalid=true`', function() {
+ var c = fixture('basic');
+ c.invalid = true;
+ assert.equal(c.getAttribute('aria-invalid'), 'true');
+ c.invalid = false;
+ assert.isFalse(c.hasAttribute('aria-invalid'));
+ });
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/test/index.html b/catapult/third_party/polymer/components/iron-checked-element-behavior/test/index.html
new file mode 100644
index 00000000..1ddeca33
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/test/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>iron-checked-element-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-checked-element-behavior/test/simple-checkbox.html b/catapult/third_party/polymer/components/iron-checked-element-behavior/test/simple-checkbox.html
new file mode 100644
index 00000000..95228fae
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-checked-element-behavior/test/simple-checkbox.html
@@ -0,0 +1,26 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-checked-element-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'simple-checkbox',
+
+ behaviors: [
+ Polymer.IronCheckedElementBehavior
+ ]
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-collapse/.bower.json b/catapult/third_party/polymer/components/iron-collapse/.bower.json
new file mode 100644
index 00000000..5b3054fb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/.bower.json
@@ -0,0 +1,43 @@
+{
+ "name": "iron-collapse",
+ "version": "1.3.0",
+ "description": "Provides a collapsable container",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "container"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/PolymerElements/iron-collapse"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-collapse",
+ "ignore": [],
+ "dependencies": {
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.5.0"
+ },
+ "devDependencies": {
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "iron-collapse.html",
+ "_release": "1.3.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.3.0",
+ "commit": "a6a66288669d59202b23125b70de3d4d91bd528e"
+ },
+ "_source": "https://github.com/PolymerElements/iron-collapse.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-collapse"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-collapse/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-collapse/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..544b51e7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-collapse/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-collapse/.gitignore b/catapult/third_party/polymer/components/iron-collapse/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-collapse/.travis.yml b/catapult/third_party/polymer/components/iron-collapse/.travis.yml
new file mode 100644
index 00000000..b9efff23
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ UKNKHUjbp8O3UiVmnCmmprLR573ZXak9znhIvy0Uvxpkga7DFvfKvYRFCXQI/eV2I9YOI1FgVjlPfH8Id1NOWdMLdNjAI7G5p4FWisgu36kfat853HDYemoxx0C9nqPwakPGCJ9duL1w0dwje2sEHndk1cdwkU3TMsgkawdOJbU=
+ - secure: >-
+ cWudj1yh2mzialubEqhSHBG/CPLtI9ZeUPaI1+N3hiPzvuxqGaEbkpzEmlZrdAfuy9axCQStg88dgKRSv+TdMbcDKpzQqfClxTf9baKgrSgzVoMReY0kjHumCJz1tIVIz74ggp/gdQVFyS9CiA82pHTnxDl/drwPIHa2S8ST02U=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-collapse/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-collapse/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-collapse/README.md b/catapult/third_party/polymer/components/iron-collapse/README.md
new file mode 100644
index 00000000..4fee0505
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/README.md
@@ -0,0 +1,66 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-collapse.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-collapse.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-collapse)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-collapse)_
+
+
+##&lt;iron-collapse&gt;
+
+`iron-collapse` creates a collapsible block of content. By default, the content
+will be collapsed. Use `opened` or `toggle()` to show/hide the content.
+
+```html
+<button on-click="toggle">toggle collapse</button>
+
+<iron-collapse id="collapse">
+ <div>Content goes here...</div>
+</iron-collapse>
+
+...
+
+toggle: function() {
+ this.$.collapse.toggle();
+}
+```
+
+`iron-collapse` adjusts the max-height/max-width of the collapsible element to show/hide
+the content. So avoid putting padding/margin/border on the collapsible directly,
+and instead put a div inside and style that.
+
+```html
+<style>
+ .collapse-content {
+ padding: 15px;
+ border: 1px solid #dedede;
+ }
+</style>
+
+<iron-collapse>
+ <div class="collapse-content">
+ <div>Content goes here...</div>
+ </div>
+</iron-collapse>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--iron-collapse-transition-duration` | Animation transition duration | `300ms` |
+
+
diff --git a/catapult/third_party/polymer/components/iron-collapse/bower.json b/catapult/third_party/polymer/components/iron-collapse/bower.json
new file mode 100644
index 00000000..a2e6b279
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/bower.json
@@ -0,0 +1,34 @@
+{
+ "name": "iron-collapse",
+ "version": "1.3.0",
+ "description": "Provides a collapsable container",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "container"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/PolymerElements/iron-collapse"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-collapse",
+ "ignore": [],
+ "dependencies": {
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.5.0"
+ },
+ "devDependencies": {
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "iron-collapse.html"
+}
diff --git a/catapult/third_party/polymer/components/iron-collapse/demo/index.html b/catapult/third_party/polymer/components/iron-collapse/demo/index.html
new file mode 100644
index 00000000..79a873c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/demo/index.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-collapse demo</title>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-collapse.html">
+ <link rel="import" href="../../paper-styles/shadow.html">
+ <link rel="stylesheet" href="../../paper-styles/demo.css">
+
+ <style is="custom-style">
+ .heading {
+ padding: 10px 15px;
+ margin-top: 20px;
+ background-color: #f3f3f3;
+ border: 1px solid #dedede;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ font-size: 18px;
+ cursor: pointer;
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ width: 100%;
+ text-align: left;
+ }
+
+ .content {
+ padding: 15px;
+ border: 1px solid #dedede;
+ border-top: none;
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+ @apply(--shadow-elevation-2dp);
+ }
+
+ #collapse3 {
+ max-height: 250px;
+ }
+
+ </style>
+
+ </head>
+ <body unresolved>
+ <template is="dom-bind">
+
+ <button class="heading" aria-expanded$="[[isExpanded(opened1)]]" aria-controls="collapse1" onclick="toggle('#collapse1')">Collapse #1</button>
+
+ <iron-collapse id="collapse1" tabindex="0" opened="{{opened1}}">
+ <div class="content">
+ <div>Lorem ipsum dolor sit amet, per in nusquam nominavi periculis, sit elit oportere ea, id minim maiestatis incorrupte duo. Dolorum verterem ad ius, his et nullam verterem. Eu alia debet usu, an doming tritani est. Vix ad ponderum petentium suavitate, eum eu tempor populo, graece sententiae constituam vim ex. Cu torquatos reprimique neglegentur nec, voluptua periculis has ut, at eos discere deleniti sensibus. Lorem ipsum dolor sit amet, per in nusquam nominavi periculis, sit elit oportere ea, id minim maiestatis incorrupte duo. Dolorum verterem ad ius, his et nullam verterem. Eu alia debet usu, an doming tritani est. Vix ad ponderum petentium suavitate, eum eu tempor populo, graece sententiae constituam vim ex. Cu torquatos reprimique neglegentur nec, voluptua periculis has ut, at eos discere deleniti sensibus.</div>
+ </div>
+ </iron-collapse>
+
+ <button class="heading" aria-expanded$="[[isExpanded(opened2)]]" aria-controls="collapse2" onclick="toggle('#collapse2')">Collapse #2</button>
+
+ <iron-collapse id="collapse2" tabindex="0" opened="{{opened2}}">
+ <div class="content">
+ <div>Pro saepe pertinax ei, ad pri animal labores suscipiantur. Modus commodo minimum eum te, vero utinam assueverit per eu, zril oportere suscipiantur pri te. Partem percipitur deterruisset ad sea, at eam suas luptatum dissentiunt. No error alienum pro, erant senserit ex mei, pri semper alterum no. Ut habemus menandri vulputate mea. Feugiat verterem ut sed. Dolores maiestatis id per. Pro saepe pertinax ei, ad pri animal labores suscipiantur. Modus commodo minimum eum te, vero utinam assueverit per eu, zril oportere suscipiantur pri te. Partem percipitur deterruisset ad sea, at eam suas luptatum dissentiunt. No error alienum pro, erant senserit ex mei, pri semper alterum no. Ut habemus menandri vulputate mea. Feugiat verterem ut sed. Dolores maiestatis id per.</div>
+
+ <button class="heading" aria-expanded$="[[isExpanded(opened3)]]" aria-controls="collapse3" onclick="toggle('#collapse3')">Collapse #3 (horizontal)</button>
+
+ <iron-collapse id="collapse3" tabindex="0" opened="{{opened3}}" horizontal>
+ <div class="content">
+ <div>Iisque perfecto dissentiet cum et, sit ut quot mandamus, ut vim tibique splendide instructior. Id nam odio natum malorum, tibique copiosae expetenda mel ea. Mea melius malorum ut. Ut nec tollit vocent timeam. Facer nonumy numquam id his, munere salutatus consequuntur eum et, eum cotidieque definitionem signiferumque id. Ei oblique graecis patrioque vis, et probatus dignissim inciderint vel. Sed id paulo erroribus, autem semper accusamus in mel. Iisque perfecto dissentiet cum et, sit ut quot mandamus, ut vim tibique splendide instructior. Id nam odio natum malorum, tibique copiosae expetenda mel ea. Mea melius malorum ut. Ut nec tollit vocent timeam. Facer nonumy numquam id his, munere salutatus consequuntur eum et, eum cotidieque definitionem signiferumque id. Ei oblique graecis patrioque vis, et probatus dignissim inciderint vel. Sed id paulo erroribus, autem semper accusamus in mel.</div>
+ </div>
+ </iron-collapse>
+
+ <button class="heading" aria-expanded$="[[isExpanded(opened4)]]" aria-controls="collapse4" onclick="toggle('#collapse4')">Collapse #4 (no animation)</button>
+
+ <iron-collapse id="collapse4" tabindex="0" opened="{{opened4}}" no-animation>
+ <div class="content">
+ <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
+ </div>
+ </iron-collapse>
+ </div>
+ </iron-collapse>
+</template>
+
+<script>
+
+ function toggle(selector) {
+ document.querySelector(selector).toggle();
+ }
+
+ document.querySelector('template[is=dom-bind]').isExpanded = function(opened) {
+ return String(opened);
+ };
+
+</script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-collapse/hero.svg b/catapult/third_party/polymer/components/iron-collapse/hero.svg
new file mode 100755
index 00000000..408ae52e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/hero.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+ <path display="inline" fill="none" d="M167.5,51.7c3.7-0.8,6.9,2.4,6.1,6.1c-0.4,1.9-1.9,3.4-3.8,3.8c-3.7,0.8-6.9-2.4-6.1-6.1
+ C164.2,53.6,165.7,52.1,167.5,51.7z"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <path d="M151,102H73V52h78V102z M75,100h74V54H75V100z"/>
+ <path d="M151,38H73V24h78V38z M75,36h74V26H75V36z"/>
+ <circle cx="171" cy="51" r="4"/>
+ <path d="M151,72v-2c10.5,0,19-8.5,19-19s-8.5-19-19-19v-2c11.6,0,21,9.4,21,21S162.6,72,151,72z"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-collapse/index.html b/catapult/third_party/polymer/components/iron-collapse/index.html
new file mode 100644
index 00000000..b5d20077
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/index.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-collapse</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+ </head>
+ <body>
+
+ <iron-component-page></iron-component-page>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-collapse/iron-collapse.html b/catapult/third_party/polymer/components/iron-collapse/iron-collapse.html
new file mode 100644
index 00000000..75a79e70
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/iron-collapse.html
@@ -0,0 +1,302 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+
+<!--
+`iron-collapse` creates a collapsible block of content. By default, the content
+will be collapsed. Use `opened` or `toggle()` to show/hide the content.
+
+ <button on-click="toggle">toggle collapse</button>
+
+ <iron-collapse id="collapse">
+ <div>Content goes here...</div>
+ </iron-collapse>
+
+ ...
+
+ toggle: function() {
+ this.$.collapse.toggle();
+ }
+
+`iron-collapse` adjusts the max-height/max-width of the collapsible element to show/hide
+the content. So avoid putting padding/margin/border on the collapsible directly,
+and instead put a div inside and style that.
+
+ <style>
+ .collapse-content {
+ padding: 15px;
+ border: 1px solid #dedede;
+ }
+ </style>
+
+ <iron-collapse>
+ <div class="collapse-content">
+ <div>Content goes here...</div>
+ </div>
+ </iron-collapse>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--iron-collapse-transition-duration` | Animation transition duration | `300ms`
+
+@group Iron Elements
+@hero hero.svg
+@demo demo/index.html
+@element iron-collapse
+-->
+
+<dom-module id="iron-collapse">
+
+ <template>
+
+ <style>
+ :host {
+ display: block;
+ transition-duration: var(--iron-collapse-transition-duration, 300ms);
+ overflow: visible;
+ }
+
+ :host(.iron-collapse-closed) {
+ display: none;
+ }
+
+ :host(:not(.iron-collapse-opened)) {
+ overflow: hidden;
+ }
+ </style>
+
+ <content></content>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'iron-collapse',
+
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ],
+
+ properties: {
+
+ /**
+ * If true, the orientation is horizontal; otherwise is vertical.
+ *
+ * @attribute horizontal
+ */
+ horizontal: {
+ type: Boolean,
+ value: false,
+ observer: '_horizontalChanged'
+ },
+
+ /**
+ * Set opened to true to show the collapse element and to false to hide it.
+ *
+ * @attribute opened
+ */
+ opened: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ observer: '_openedChanged'
+ },
+
+ /**
+ * When true, the element is transitioning its opened state. When false,
+ * the element has finished opening/closing.
+ *
+ * @attribute transitioning
+ */
+ transitioning: {
+ type: Boolean,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * Set noAnimation to true to disable animations.
+ *
+ * @attribute noAnimation
+ */
+ noAnimation: {
+ type: Boolean
+ },
+
+ /**
+ * Stores the desired size of the collapse body.
+ * @private
+ */
+ _desiredSize: {
+ type: String,
+ value: ''
+ }
+ },
+
+ get dimension() {
+ return this.horizontal ? 'width' : 'height';
+ },
+
+ /**
+ * `maxWidth` or `maxHeight`.
+ * @private
+ */
+ get _dimensionMax() {
+ return this.horizontal ? 'maxWidth' : 'maxHeight';
+ },
+
+ /**
+ * `max-width` or `max-height`.
+ * @private
+ */
+ get _dimensionMaxCss() {
+ return this.horizontal ? 'max-width' : 'max-height';
+ },
+
+ hostAttributes: {
+ role: 'group',
+ 'aria-hidden': 'true',
+ 'aria-expanded': 'false'
+ },
+
+ listeners: {
+ transitionend: '_onTransitionEnd'
+ },
+
+ /**
+ * Toggle the opened state.
+ *
+ * @method toggle
+ */
+ toggle: function() {
+ this.opened = !this.opened;
+ },
+
+ show: function() {
+ this.opened = true;
+ },
+
+ hide: function() {
+ this.opened = false;
+ },
+
+ /**
+ * Updates the size of the element.
+ * @param {string} size The new value for `maxWidth`/`maxHeight` as css property value, usually `auto` or `0px`.
+ * @param {boolean=} animated if `true` updates the size with an animation, otherwise without.
+ */
+ updateSize: function(size, animated) {
+ // Consider 'auto' as '', to take full size.
+ size = size === 'auto' ? '' : size;
+
+ var willAnimate = animated && !this.noAnimation &&
+ this.isAttached && this._desiredSize !== size;
+
+ this._desiredSize = size;
+
+ this._updateTransition(false);
+ // If we can animate, must do some prep work.
+ if (willAnimate) {
+ // Animation will start at the current size.
+ var startSize = this._calcSize();
+ // For `auto` we must calculate what is the final size for the animation.
+ // After the transition is done, _transitionEnd will set the size back to `auto`.
+ if (size === '') {
+ this.style[this._dimensionMax] = '';
+ size = this._calcSize();
+ }
+ // Go to startSize without animation.
+ this.style[this._dimensionMax] = startSize;
+ // Force layout to ensure transition will go. Set scrollTop to itself
+ // so that compilers won't remove it.
+ this.scrollTop = this.scrollTop;
+ // Enable animation.
+ this._updateTransition(true);
+ // If final size is the same as startSize it will not animate.
+ willAnimate = (size !== startSize);
+ }
+ // Set the final size.
+ this.style[this._dimensionMax] = size;
+ // If it won't animate, call transitionEnd to set correct classes.
+ if (!willAnimate) {
+ this._transitionEnd();
+ }
+ },
+
+ /**
+ * enableTransition() is deprecated, but left over so it doesn't break existing code.
+ * Please use `noAnimation` property instead.
+ *
+ * @method enableTransition
+ * @deprecated since version 1.0.4
+ */
+ enableTransition: function(enabled) {
+ Polymer.Base._warn('`enableTransition()` is deprecated, use `noAnimation` instead.');
+ this.noAnimation = !enabled;
+ },
+
+ _updateTransition: function(enabled) {
+ this.style.transitionDuration = (enabled && !this.noAnimation) ? '' : '0s';
+ },
+
+ _horizontalChanged: function() {
+ this.style.transitionProperty = this._dimensionMaxCss;
+ var otherDimension = this._dimensionMax === 'maxWidth' ? 'maxHeight' : 'maxWidth';
+ this.style[otherDimension] = '';
+ this.updateSize(this.opened ? 'auto' : '0px', false);
+ },
+
+ _openedChanged: function() {
+ this.setAttribute('aria-expanded', this.opened);
+ this.setAttribute('aria-hidden', !this.opened);
+
+ this._setTransitioning(true);
+ this.toggleClass('iron-collapse-closed', false);
+ this.toggleClass('iron-collapse-opened', false);
+ this.updateSize(this.opened ? 'auto' : '0px', true);
+
+ // Focus the current collapse.
+ if (this.opened) {
+ this.focus();
+ }
+ },
+
+ _transitionEnd: function() {
+ this.style[this._dimensionMax] = this._desiredSize;
+ this.toggleClass('iron-collapse-closed', !this.opened);
+ this.toggleClass('iron-collapse-opened', this.opened);
+ this._updateTransition(false);
+ this.notifyResize();
+ this._setTransitioning(false);
+ },
+
+ _onTransitionEnd: function(event) {
+ if (Polymer.dom(event).rootTarget === this) {
+ this._transitionEnd();
+ }
+ },
+
+ _calcSize: function() {
+ return this.getBoundingClientRect()[this.dimension] + 'px';
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-collapse/test/a11y.html b/catapult/third_party/polymer/components/iron-collapse/test/a11y.html
new file mode 100644
index 00000000..ddd3472b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/test/a11y.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-collapse-a11y</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-collapse.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="trivialCollapse">
+ <template>
+ <iron-collapse id="collapse" tabindex="0">
+ <div>
+ Forma temperiemque cornua sidera dissociata cornua recessit innabilis ligavit: solidumque coeptis nullus caelum sponte phoebe di regat mentisque tanta austro capacius amphitrite sui quin postquam semina fossae liquidum umor galeae coeptis caligine liberioris quin liquidum matutinis invasit posset: flexi glomeravit radiis certis invasit oppida postquam onerosior inclusum dominari opifex terris pace finxit quam aquae nunc sine altae auroram quam habentem homo totidemque scythiam in pondus ensis tegit caecoque poena lapidosos humanas coeperunt poena aetas totidem nec natura aethera locavit caelumque distinxit animalibus phoebe cingebant moderantum porrexerat terrae possedit sua sole diu summaque obliquis melioris orbem
+ </div>
+ </iron-collapse>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('a11y', function() {
+ var collapse;
+
+ setup(function () {
+ collapse = fixture('trivialCollapse');
+ });
+
+ test('aria attributes', function() {
+ assert.equal(collapse.getAttribute('role'), 'group');
+ assert.equal(collapse.getAttribute('aria-expanded'), 'false');
+ assert.equal(collapse.getAttribute('aria-hidden'), 'true');
+ });
+
+ test('set opened to true', function() {
+ collapse.opened = true;
+ assert.equal(collapse.getAttribute('aria-expanded'), 'true');
+ assert.equal(collapse.getAttribute('aria-hidden'), 'false');
+ });
+
+ test('focus the collapse when opened', function() {
+ assert.notEqual(document.activeElement, collapse);
+ collapse.opened = true;
+ assert.equal(document.activeElement, collapse);
+ });
+
+ a11ySuite('trivialCollapse');
+
+ });
+
+ </script>
+
+ </body>
+</html>
+
diff --git a/catapult/third_party/polymer/components/iron-collapse/test/basic.html b/catapult/third_party/polymer/components/iron-collapse/test/basic.html
new file mode 100644
index 00000000..c47686cc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/test/basic.html
@@ -0,0 +1,199 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-collapse-basic</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-collapse.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="test">
+ <template>
+ <iron-collapse opened>
+ <div style="height:100px;">
+ Lorem ipsum
+ </div>
+ </iron-collapse>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="empty">
+ <template>
+ <iron-collapse opened></iron-collapse>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ var collapse;
+ var collapseHeight;
+
+ setup(function() {
+ collapse = fixture('test');
+ collapseHeight = getComputedStyle(collapse).height;
+ });
+
+ test('opened attribute', function() {
+ assert.equal(collapse.opened, true);
+ });
+
+ test('animated by default', function() {
+ assert.isTrue(!collapse.noAnimation, '`noAnimation` is falsy');
+ });
+
+ test('not transitioning on attached', function() {
+ assert.isTrue(!collapse.transitioning, '`transitioning` is falsy');
+ });
+
+ test('horizontal attribute', function() {
+ assert.equal(collapse.horizontal, false);
+ });
+
+ test('default opened height', function() {
+ assert.equal(collapse.style.maxHeight, '');
+ });
+
+ test('set opened to false triggers animation', function(done) {
+ collapse.opened = false;
+ // Animation got enabled.
+ assert.notEqual(collapse.style.transitionDuration, '0s');
+ assert.equal(collapse.transitioning, true, 'transitioning became true');
+ collapse.addEventListener('transitionend', function() {
+ // Animation disabled.
+ assert.equal(collapse.style.transitionDuration, '0s');
+ assert.equal(collapse.transitioning, false, 'transitioning became false');
+ done();
+ });
+ });
+
+ test('transitioning updated only during transitions between open/close states', function() {
+ var spy = sinon.spy();
+ collapse.addEventListener('transitioning-changed', spy);
+ collapse.noAnimation = true;
+ assert.equal(spy.callCount, 0, 'transitioning not affected by noAnimation');
+ collapse.horizontal = true;
+ assert.equal(spy.callCount, 0, 'transitioning not affected by horizontal');
+ collapse.opened = false;
+ assert.equal(spy.callCount, 2, 'transitioning changed twice');
+ assert.equal(collapse.transitioning, false, 'transitioning is false');
+ });
+
+ test('enableTransition(false) disables animations', function() {
+ collapse.enableTransition(false);
+ assert.isTrue(collapse.noAnimation, '`noAnimation` is true');
+ // trying to animate the size update
+ collapse.updateSize('0px', true);
+ // Animation immediately disabled.
+ assert.equal(collapse.style.maxHeight, '0px');
+ });
+
+ test('set opened to false, then to true', function(done) {
+ // this listener will be triggered twice (every time `opened` changes)
+ collapse.addEventListener('transitionend', function() {
+ if (collapse.opened) {
+ // Check finalSize after animation is done.
+ assert.equal(collapse.style.height, '');
+ done();
+ } else {
+ // Check if size is still 0px.
+ assert.equal(collapse.style.maxHeight, '0px');
+ // Trigger 2nd toggle.
+ collapse.opened = true;
+ // Size should be immediately set.
+ assert.equal(collapse.style.maxHeight, collapseHeight);
+ }
+ });
+ // Trigger 1st toggle.
+ collapse.opened = false;
+ // Size should be immediately set.
+ assert.equal(collapse.style.maxHeight, '0px');
+ });
+
+ test('opened changes trigger iron-resize', function() {
+ var spy = sinon.stub();
+ collapse.addEventListener('iron-resize', spy);
+ // No animations for faster test.
+ collapse.noAnimation = true;
+ collapse.opened = false;
+ assert.isTrue(spy.calledOnce, 'iron-resize was fired');
+ });
+
+ test('overflow is hidden while animating', function(done) {
+ collapse.addEventListener('transitionend', function() {
+ // Should still be hidden.
+ assert.equal(getComputedStyle(collapse).overflow, 'hidden');
+ done();
+ });
+ assert.equal(getComputedStyle(collapse).overflow, 'visible');
+ collapse.opened = false;
+ // Immediately updated style.
+ assert.equal(getComputedStyle(collapse).overflow, 'hidden');
+ });
+
+ test('toggle horizontal updates size', function() {
+ collapse.horizontal = false;
+ assert.equal(collapse.style.width, '');
+ assert.equal(collapse.style.maxHeight, '');
+ assert.equal(collapse.style.transitionProperty, 'max-height');
+
+ collapse.horizontal = true;
+ assert.equal(collapse.style.maxWidth, '');
+ assert.equal(collapse.style.height, '');
+ assert.equal(collapse.style.transitionProperty, 'max-width');
+ });
+
+ test('change size with updateSize', function(done) {
+ collapse.addEventListener('transitionend', function() {
+ // size should be kept after transition
+ assert.equal(collapse.style.maxHeight, "123px");
+ done();
+ });
+ collapse.updateSize("123px", true);
+ });
+
+ });
+
+ suite('empty', function() {
+
+ var collapse;
+
+ setup(function() {
+ collapse = fixture('empty');
+ });
+
+ test('empty&opened shows dynamically loaded content', function(done) {
+ flush(function () {
+ collapse.toggle();
+ collapse.toggle();
+ assert.equal(collapse.style.maxHeight, '');
+ done();
+ });
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-collapse/test/flex.html b/catapult/third_party/polymer/components/iron-collapse/test/flex.html
new file mode 100644
index 00000000..6b109259
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/test/flex.html
@@ -0,0 +1,152 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-collapse-flex</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../iron-flex-layout/iron-flex-layout-classes.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-collapse.html">
+
+ <style is="custom-style" include="iron-flex">
+ </style>
+
+ </head>
+ <body>
+
+ <test-fixture id="test">
+ <template>
+ <div id="container" class="layout vertical" style="height: 200px">
+ <iron-collapse id="collapse" opened style="flex: 1 0 auto">
+ <div style="height:100px;">
+ Lorem ipsum
+ </div>
+ </iron-collapse>
+ </div>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('flex', function() {
+
+ var container;
+ var collapse;
+ var collapseHeight;
+
+ setup(function() {
+ container = fixture('test');
+ collapse = container.querySelector('iron-collapse');
+ collapseHeight = getComputedStyle(collapse).height;
+ });
+
+ test('default opened height', function() {
+ assert.equal(collapse.style.height, '');
+ });
+
+ test('set opened to false triggers animation', function(done) {
+ collapse.opened = false;
+ // Animation got enabled.
+ assert.notEqual(collapse.style.transitionDuration, '0s');
+ collapse.addEventListener('transitionend', function() {
+ // Animation disabled.
+ assert.equal(collapse.style.transitionDuration, '0s');
+ done();
+ });
+ });
+
+ test('enableTransition(false) disables animations', function() {
+ collapse.enableTransition(false);
+ assert.isTrue(collapse.noAnimation, '`noAnimation` is true');
+ // trying to animate the size update
+ collapse.updateSize('0px', true);
+ // Animation immediately disabled.
+ assert.equal(collapse.style.maxHeight, '0px');
+ });
+
+ test('set opened to false, then to true', function(done) {
+ // this listener will be triggered twice (every time `opened` changes)
+ collapse.addEventListener('transitionend', function() {
+ if (collapse.opened) {
+ // Check finalSize after animation is done.
+ assert.equal(collapse.style.maxHeight, '');
+ done();
+ } else {
+ // Check if size is still 0px.
+ assert.equal(collapse.style.maxHeight, '0px');
+ // Trigger 2nd toggle.
+ collapse.opened = true;
+ // Size should be immediately set.
+ assert.equal(collapse.style.maxHeight, collapseHeight);
+ }
+ });
+ // Trigger 1st toggle.
+ collapse.opened = false;
+ // Size should be immediately set.
+ assert.equal(collapse.style.maxHeight, '0px');
+ });
+
+ test('opened changes trigger iron-resize', function() {
+ var spy = sinon.stub();
+ collapse.addEventListener('iron-resize', spy);
+ // No animations for faster test.
+ collapse.noAnimation = true;
+ collapse.opened = false;
+ assert.isTrue(spy.calledOnce, 'iron-resize was fired');
+ });
+
+ test('overflow is hidden while animating', function(done) {
+ collapse.addEventListener('transitionend', function() {
+ // Should still be hidden.
+ assert.equal(getComputedStyle(collapse).overflow, 'hidden');
+ done();
+ });
+ assert.equal(getComputedStyle(collapse).overflow, 'visible');
+ collapse.opened = false;
+ // Immediately updated style.
+ assert.equal(getComputedStyle(collapse).overflow, 'hidden');
+ });
+
+ test('toggle horizontal updates size', function() {
+ collapse.horizontal = false;
+ assert.equal(collapse.style.width, '');
+ assert.equal(collapse.style.maxHeight, '');
+ assert.equal(collapse.style.transitionProperty, 'max-height');
+
+ collapse.horizontal = true;
+ assert.equal(collapse.style.maxWidth, '');
+ assert.equal(collapse.style.height, '');
+ assert.equal(collapse.style.transitionProperty, 'max-width');
+ });
+
+ test('change size with updateSize', function(done) {
+ collapse.addEventListener('transitionend', function() {
+ // size should be kept after transition
+ assert.equal(collapse.style.maxHeight, "123px");
+ done();
+ });
+ collapse.updateSize("123px", true);
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-collapse/test/horizontal.html b/catapult/third_party/polymer/components/iron-collapse/test/horizontal.html
new file mode 100644
index 00000000..61121b69
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/test/horizontal.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+ <title>iron-collapse-horizontal</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-collapse.html">
+ </head>
+ <body>
+
+ <test-fixture id="test">
+ <template>
+ <iron-collapse id="collapse" opened horizontal>
+ <div style="width:100px;">
+ Lorem ipsum
+ </div>
+ </iron-collapse>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('horizontal', function() {
+
+ var collapse;
+ var collapseWidth;
+
+ setup(function () {
+ collapse = fixture('test');
+ collapseWidth = getComputedStyle(collapse).width;
+ });
+
+ test('opened attribute', function() {
+ assert.equal(collapse.opened, true);
+ });
+
+ test('horizontal attribute', function() {
+ assert.equal(collapse.horizontal, true);
+ });
+
+ test('default opened width', function() {
+ assert.equal(collapse.style.width, '');
+ });
+
+ test('set opened to false, then to true', function(done) {
+ // This listener will be triggered twice (every time `opened` changes).
+ collapse.addEventListener('transitionend', function() {
+ if (collapse.opened) {
+ // Check finalSize after animation is done.
+ assert.equal(collapse.style.width, '');
+ done();
+ } else {
+ // Check if size is still 0px.
+ assert.equal(collapse.style.maxWidth, '0px');
+ // Trigger 2nd toggle.
+ collapse.opened = true;
+ // Size should be immediately set.
+ assert.equal(collapse.style.maxWidth, collapseWidth);
+ }
+ });
+ // Trigger 1st toggle.
+ collapse.opened = false;
+ // Size should be immediately set.
+ assert.equal(collapse.style.maxWidth, '0px');
+ });
+
+ test('change size with updateSize', function(done) {
+ collapse.addEventListener('transitionend', function() {
+ // size should be kept after transition
+ assert.equal(collapse.style.maxWidth, "123px");
+ done();
+ });
+ collapse.updateSize("123px", true);
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-collapse/test/index.html b/catapult/third_party/polymer/components/iron-collapse/test/index.html
new file mode 100644
index 00000000..5ccf5324
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/test/index.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'horizontal.html',
+ 'a11y.html',
+ 'nested.html',
+ 'flex.html',
+ 'basic.html?dom=shadow',
+ 'horizontal.html?dom=shadow',
+ 'a11y.html?dom=shadow',
+ 'nested.html?dom=shadow',
+ 'flex.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-collapse/test/nested.html b/catapult/third_party/polymer/components/iron-collapse/test/nested.html
new file mode 100644
index 00000000..ecfbc5ef
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-collapse/test/nested.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+ <title>iron-collapse-nested</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-collapse.html">
+ </head>
+ <body>
+
+ <test-fixture id="test">
+ <template>
+ <iron-collapse id="outer-collapse">
+ <div style="height:100px;">
+ Lorem ipsum
+ </div>
+
+ <iron-collapse id="inner-collapse-vertical">
+ <div style="height:100px;">
+ consectetur adipiscing elit
+ </div>
+ </iron-collapse>
+
+ <iron-collapse id="inner-collapse-horizontal" horizontal style="display: inline-block">
+ <div style="width:100px;">
+ consectetur adipiscing elit
+ </div>
+ </iron-collapse>
+ </iron-collapse>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('nested', function() {
+
+ var outerCollapse;
+ var innerCollapse;
+
+ setup(function () {
+ outerCollapse = fixture('test');
+ });
+
+ suite('vertical', function() {
+
+ setup(function () {
+ innerCollapse = outerCollapse.querySelector('#inner-collapse-vertical');
+ });
+
+ test('inner collapse default opened attribute', function() {
+ assert.equal(innerCollapse.opened, false);
+ });
+
+ test('inner collapse default style height', function() {
+ assert.equal(innerCollapse.style.maxHeight, '0px');
+ });
+
+ test('open inner collapse updates size without animation', function() {
+ innerCollapse.opened = true;
+
+ // Animation disabled
+ assert.equal(innerCollapse.style.transitionDuration, '0s');
+ });
+
+ test('open inner collapse then open outer collapse reveals inner collapse with expanded height', function() {
+ innerCollapse.opened = true;
+ outerCollapse.opened = true;
+
+ assert.equal(innerCollapse.getBoundingClientRect().height, 100);
+ });
+
+ test('notifyResize triggered only on element\'s animations', function(done) {
+ var spy = sinon.spy(outerCollapse, 'notifyResize');
+
+ outerCollapse.opened = true;
+ innerCollapse.opened = true;
+
+ setTimeout(function() {
+ assert.equal(spy.callCount, 1, 'notifyResize called once');
+ done();
+ }, 400);
+ });
+
+ });
+
+ suite('horizontal', function() {
+
+ setup(function () {
+ innerCollapse = outerCollapse.querySelector('#inner-collapse-horizontal');
+ });
+
+ test('inner collapse default style width', function() {
+ assert.equal(innerCollapse.style.maxWidth, '0px');
+ });
+
+ test('open inner collapse updates size without animation', function() {
+ innerCollapse.opened = true;
+
+ // Animation disabled
+ assert.equal(innerCollapse.style.transitionDuration, '0s');
+ });
+
+ test('open inner collapse then open outer collapse reveals inner collapse with expanded width', function() {
+ innerCollapse.opened = true;
+ outerCollapse.opened = true;
+
+ assert.equal(innerCollapse.getBoundingClientRect().width, 100);
+ });
+
+ });
+ });
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/.bower.json b/catapult/third_party/polymer/components/iron-dropdown/.bower.json
new file mode 100644
index 00000000..0f0841b4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/.bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "iron-dropdown",
+ "version": "1.5.6",
+ "description": "An unstyled element that works similarly to a native browser select",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer"
+ ],
+ "main": "iron-dropdown.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-dropdown"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-dropdown",
+ "dependencies": {
+ "polymer": "polymer/polymer#^1.1.0",
+ "iron-behaviors": "polymerelements/iron-behaviors#^1.0.0",
+ "iron-overlay-behavior": "polymerelements/iron-overlay-behavior#^1.8.6",
+ "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0",
+ "neon-animation": "polymerelements/neon-animation#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0",
+ "iron-image": "polymerelements/iron-image#^1.0.0"
+ },
+ "ignore": [],
+ "_release": "1.5.6",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.6",
+ "commit": "3507dcb983bc9d8dc9ad181daacfe0f4f8b97415"
+ },
+ "_source": "https://github.com/PolymerElements/iron-dropdown.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-dropdown"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-dropdown/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-dropdown/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..a1b71482
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-dropdown/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-dropdown/.gitignore b/catapult/third_party/polymer/components/iron-dropdown/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-dropdown/.travis.yml b/catapult/third_party/polymer/components/iron-dropdown/.travis.yml
new file mode 100644
index 00000000..054dfac3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ YMlrRxFp8QBJ9KfvrPru2sIaHm5GeVHsZ/wE4qm5ntdsoqQlsJXRYYhlE2jPSHte+2VrOnFItjCsoqG6myeCbMrDS+/ZAZd+PWNdxbSs0DiGVMJWtrd8PwhKeOoVYzJleTwU0Uj2mF/mKwoC6aNgf56V9XzC2mkooRJX9cDbCkfddcIGoU/45zsPPMJV+BRVDcETw5g0PD+7NJBPtJP+d8TMO+jtdgXQ1V0vPJhkS7RaYZoDeljsml/Xc8Djb3zUttu5cFk4lXeKAKtFgFzxVsFqytxuYY2aSkMcq2/KxwexnnK/SCe5uQC014TjYAMzSs5A7l/ZBEzRGDhxKmkR2JHY3ktRCOFphqrQa9JoK3k+Tc28JeXJXTXNW7B5SODclN45ik5jQQ/FaJgRlideK07LsEObC9FjEoSd0U5rt75bzIhGuBQ6vr16JQLtN7+NvPRMsNqZRGHQysbW7SbfuLdWwmprn8kXaiQoensAnWlrrGoaPtPfKgA68YRQ/2cr9zZWFmMTzfDP6/Y8YSulj1mzqP2U8AZStXZ1vKN7IWY/ADzYrRG/nCqwd8gQpQoiAEgOfc+de5fIzLPRfLS8fI8sow9yZC8hE91JDatxOPvg/YCDGfJUQddCG+Z3LIBqY6RYKjXzCTCexGD6XWIJso8x5HEEWEQKArqT6vLM4gE=
+ - secure: >-
+ yNGht7DLQ56gIwWh+Wu/3spWaIo0qR/EXB7SDzGtE7Y1KLbgYjmGCnA+3Y+vQ8q9Smpv66FMi8ejbBnObb347806LykEG23OJWBbruUi3gGoImNqLmaj+iqxiaYzxqIWs7gwhpYMdNGFiu1T83eBLOLoVQqO1OIIVbH1Fk0MvHvbknY52VyOEaEjVBdnWAxTpDdgujzVaPBxaPCbD15nUFgaMCsBdR0SKtX0X+vVZRdu0VzHqLJPhUxrQJIxdAYc/OyEKki5pR93b0df+0P14NHxduWNtaAReBtMtpVsiLfaxiHdoWzjHdJ2g+x4DEoH1ynbs4v/8qWE1xLABcgHK2UeZYwTtk7KSA9ZcYSx7JxnO1mhseWdiCSKO4Y0bZuBcSsQ9O5skvIOhAd9bLdQHMe4bHSf8mhtv/Kw6IHmEqZ9YZ8TIk7YY4bYiLOKsN6q2kYbWO7UVeYBLul3mppiEWZ04JmbjVU5us1hq40RdSsc34/fW3KitLfVaYQlTWlZ/h9nx7tOQ2o6OOSHgRnD8qPpFM+xExqkj2FIgUWyecn8VKQ6FojgfOeVyEQ3WdPK17LdFxhKfHVod+Qufl6ArBsUMWctns4/jQTCJXL+VIPfZWQ/wrYmkIW7dEjA6NnTMBqhjBa37C1G8+Ak5kvSAcU4iAbjqA1jVv4RANRV5x8=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-dropdown/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-dropdown/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-dropdown/README.md b/catapult/third_party/polymer/components/iron-dropdown/README.md
new file mode 100644
index 00000000..6440ec68
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/README.md
@@ -0,0 +1,46 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-dropdown.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-dropdown.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-dropdown)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-dropdown)_
+
+
+##&lt;iron-dropdown&gt;
+
+`<iron-dropdown>` is a generalized element that is useful when you have
+hidden content (`.dropdown-content`) that is revealed due to some change in
+state that should cause it to do so.
+
+Note that this is a low-level element intended to be used as part of other
+composite elements that cause dropdowns to be revealed.
+
+Examples of elements that might be implemented using an `iron-dropdown`
+include comboboxes, menubuttons, selects. The list goes on.
+
+The `<iron-dropdown>` element exposes attributes that allow the position
+of the `.dropdown-content` relative to the `.dropdown-trigger` to be
+configured.
+
+```html
+<iron-dropdown horizontal-align="right" vertical-align="top">
+ <div class="dropdown-content">Hello!</div>
+</iron-dropdown>
+```
+
+In the above example, the `<div>` with class `.dropdown-content` will be
+hidden until the dropdown element has `opened` set to true, or when the `open`
+method is called on the element.
+
+
diff --git a/catapult/third_party/polymer/components/iron-dropdown/bower.json b/catapult/third_party/polymer/components/iron-dropdown/bower.json
new file mode 100644
index 00000000..2d9fce71
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/bower.json
@@ -0,0 +1,38 @@
+{
+ "name": "iron-dropdown",
+ "version": "1.5.6",
+ "description": "An unstyled element that works similarly to a native browser select",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer"
+ ],
+ "main": "iron-dropdown.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-dropdown"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-dropdown",
+ "dependencies": {
+ "polymer": "polymer/polymer#^1.1.0",
+ "iron-behaviors": "polymerelements/iron-behaviors#^1.0.0",
+ "iron-overlay-behavior": "polymerelements/iron-overlay-behavior#^1.8.6",
+ "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0",
+ "neon-animation": "polymerelements/neon-animation#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0",
+ "iron-image": "polymerelements/iron-image#^1.0.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-dropdown/demo/grow-height-animation.html b/catapult/third_party/polymer/components/iron-dropdown/demo/grow-height-animation.html
new file mode 100644
index 00000000..e2fdc448
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/demo/grow-height-animation.html
@@ -0,0 +1,36 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../neon-animation/neon-animation-behavior.html">
+
+<script>
+ Polymer({
+ is: 'expand-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ var height = node.getBoundingClientRect().height;
+
+ this._effect = new KeyframeEffect(node, [{
+ height: (height / 2) + 'px'
+ }, {
+ height: height + 'px'
+ }], this.timingFromConfig(config));
+
+ return this._effect;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/demo/index.html b/catapult/third_party/polymer/components/iron-dropdown/demo/index.html
new file mode 100644
index 00000000..3ddbaf20
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/demo/index.html
@@ -0,0 +1,160 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-dropdown</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-image/iron-image.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="x-select.html">
+ <style>
+
+ .dropdown-content {
+ background-color: white;
+ line-height: 20px;
+ border-radius: 3px;
+ box-shadow: 0px 2px 6px #ccc;
+ }
+
+ .random-content {
+ padding: 1.5em 2em;
+ max-width: 250px;
+ }
+
+ button {
+ border: 1px solid #ccc;
+ background-color: #eee;
+ padding: 1em;
+ border-radius: 3px;
+ cursor: pointer;
+ }
+
+ button:focus {
+ outline: none;
+ border-color: blue;
+ }
+
+ ul {
+ margin: 0;
+ padding: 0;
+ }
+
+ li {
+ display: block;
+ position: relative;
+ margin: 0;
+ padding: 0;
+ }
+
+ a {
+ display: block;
+ position: relative;
+ padding: 1em;
+ text-decoration: none;
+ }
+
+ li:not(:last-of-type) {
+ border-bottom: 1px solid #eee;
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+
+ iron-image {
+ padding: 1em;
+ background-color: #fff;
+ box-shadow: 0px 2px 6px #ccc;
+ border-radius: 3px;
+ }
+ </style>
+</head>
+<body>
+ <template is="dom-bind" id="Demo">
+ <div class="vertical-section vertical-section-container ">
+ <h1>iron-dropdown</h1>
+ <p>Examples of vanilla elements.</p>
+ <div>
+ <x-select>
+ <button class="dropdown-trigger">Basic</button>
+ <div class="dropdown-content random-content">
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </div>
+ </x-select>
+ <x-select>
+ <button class="dropdown-trigger">Overflowing</button>
+ <div class="dropdown-content random-content">
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
+ </div>
+ </x-select>
+ <x-select vertical-align="bottom">
+ <button class="dropdown-trigger">Bottom-left Aligned</button>
+ <div class="dropdown-content random-content">
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </div>
+ </x-select>
+ <x-select horizontal-align="right" vertical-align="top">
+ <button class="dropdown-trigger">Top-right Aligned</button>
+ <div class="dropdown-content random-content">
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </div>
+ </x-select>
+ <x-select horizontal-align="left" vertical-align="top">
+ <button class="dropdown-trigger"> Content</button>
+ <iron-image class="dropdown-content" src="../../iron-image/demo/polymer.svg"></iron-image>
+ </x-select>
+ <x-select horizontal-align="right" vertical-align="top">
+ <button class="dropdown-trigger">Unordered list</button>
+ <ul class="dropdown-content" tabindex="0">
+ <template is="dom-repeat" items="[[dinosaurs]]">
+ <li><a href="javascript:void(0)">[[item]]</a></li>
+ </template>
+ </ul>
+ </x-select>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ Demo.dinosaurs = [
+ 'allosaurus',
+ 'brontosaurus',
+ 'carcharodontosaurus',
+ 'diplodocus',
+ 'ekrixinatosaurus',
+ 'fukuiraptor',
+ 'gallimimus',
+ 'hadrosaurus',
+ 'iguanodon',
+ 'jainosaurus',
+ 'kritosaurus',
+ 'liaoceratops',
+ 'megalosaurus',
+ 'nemegtosaurus',
+ 'ornithomimus',
+ 'protoceratops',
+ 'quetecsaurus',
+ 'rajasaurus',
+ 'stegosaurus',
+ 'triceratops',
+ 'utahraptor',
+ 'vulcanodon',
+ 'wannanosaurus',
+ 'xenoceratops',
+ 'yandusaurus',
+ 'zephyrosaurus'
+ ];
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/demo/x-select.html b/catapult/third_party/polymer/components/iron-dropdown/demo/x-select.html
new file mode 100644
index 00000000..30d86127
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/demo/x-select.html
@@ -0,0 +1,80 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-dropdown.html">
+<link rel="import" href="../../neon-animation/neon-animations.html">
+<link rel="import" href="grow-height-animation.html">
+
+<dom-module id="x-select">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ margin: 1em;
+ }
+ </style>
+ <div on-tap="open">
+ <content select=".dropdown-trigger"></content>
+ </div>
+ <iron-dropdown id="dropdown"
+ vertical-align="[[verticalAlign]]"
+ horizontal-align="[[horizontalAlign]]"
+ disabled="[[disabled]]"
+ open-animation-config="[[openAnimationConfig]]"
+ close-animation-config="[[closeAnimationConfig]]">
+ <content select=".dropdown-content"></content>
+ </iron-dropdown>
+ </template>
+ <script>
+ Polymer({
+ is: 'x-select',
+
+ properties: {
+ verticalAlign: String,
+ horizontalAlign: String,
+ disabled: Boolean,
+ openAnimationConfig: {
+ type: Array,
+ value: function() {
+ return [{
+ name: 'fade-in-animation',
+ timing: {
+ delay: 150,
+ duration: 50
+ }
+ }, {
+ name: 'expand-animation',
+ timing: {
+ delay: 150,
+ duration: 200
+ }
+ }];
+ }
+ },
+
+ closeAnimationConfig: {
+ type: Array,
+ value: function() {
+ return [{
+ name: 'fade-out-animation',
+ timing: {
+ duration: 200
+ }
+ }];
+ }
+ }
+ },
+
+ open: function() {
+ this.$.dropdown.open();
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/index.html b/catapult/third_party/polymer/components/iron-dropdown/index.html
new file mode 100644
index 00000000..1d3d6cad
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+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
+-->
+<html>
+<head>
+
+ <title>iron-dropdown</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown-scroll-manager.html b/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown-scroll-manager.html
new file mode 100644
index 00000000..125bf2b3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown-scroll-manager.html
@@ -0,0 +1,372 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+ (function() {
+ 'use strict';
+ /**
+ * Used to calculate the scroll direction during touch events.
+ * @type {!Object}
+ */
+ var lastTouchPosition = {
+ pageX: 0,
+ pageY: 0
+ };
+ /**
+ * Used to avoid computing event.path and filter scrollable nodes (better perf).
+ * @type {?EventTarget}
+ */
+ var lastRootTarget = null;
+ /**
+ * @type {!Array<Node>}
+ */
+ var lastScrollableNodes = [];
+
+ var scrollEvents = [
+ // Modern `wheel` event for mouse wheel scrolling:
+ 'wheel',
+ // Older, non-standard `mousewheel` event for some FF:
+ 'mousewheel',
+ // IE:
+ 'DOMMouseScroll',
+ // Touch enabled devices
+ 'touchstart',
+ 'touchmove'
+ ];
+
+ /**
+ * The IronDropdownScrollManager is intended to provide a central source
+ * of authority and control over which elements in a document are currently
+ * allowed to scroll.
+ */
+
+ Polymer.IronDropdownScrollManager = {
+
+ /**
+ * The current element that defines the DOM boundaries of the
+ * scroll lock. This is always the most recently locking element.
+ */
+ get currentLockingElement() {
+ return this._lockingElements[this._lockingElements.length - 1];
+ },
+
+ /**
+ * Returns true if the provided element is "scroll locked", which is to
+ * say that it cannot be scrolled via pointer or keyboard interactions.
+ *
+ * @param {HTMLElement} element An HTML element instance which may or may
+ * not be scroll locked.
+ */
+ elementIsScrollLocked: function(element) {
+ var currentLockingElement = this.currentLockingElement;
+
+ if (currentLockingElement === undefined)
+ return false;
+
+ var scrollLocked;
+
+ if (this._hasCachedLockedElement(element)) {
+ return true;
+ }
+
+ if (this._hasCachedUnlockedElement(element)) {
+ return false;
+ }
+
+ scrollLocked = !!currentLockingElement &&
+ currentLockingElement !== element &&
+ !this._composedTreeContains(currentLockingElement, element);
+
+ if (scrollLocked) {
+ this._lockedElementCache.push(element);
+ } else {
+ this._unlockedElementCache.push(element);
+ }
+
+ return scrollLocked;
+ },
+
+ /**
+ * Push an element onto the current scroll lock stack. The most recently
+ * pushed element and its children will be considered scrollable. All
+ * other elements will not be scrollable.
+ *
+ * Scroll locking is implemented as a stack so that cases such as
+ * dropdowns within dropdowns are handled well.
+ *
+ * @param {HTMLElement} element The element that should lock scroll.
+ */
+ pushScrollLock: function(element) {
+ // Prevent pushing the same element twice
+ if (this._lockingElements.indexOf(element) >= 0) {
+ return;
+ }
+
+ if (this._lockingElements.length === 0) {
+ this._lockScrollInteractions();
+ }
+
+ this._lockingElements.push(element);
+
+ this._lockedElementCache = [];
+ this._unlockedElementCache = [];
+ },
+
+ /**
+ * Remove an element from the scroll lock stack. The element being
+ * removed does not need to be the most recently pushed element. However,
+ * the scroll lock constraints only change when the most recently pushed
+ * element is removed.
+ *
+ * @param {HTMLElement} element The element to remove from the scroll
+ * lock stack.
+ */
+ removeScrollLock: function(element) {
+ var index = this._lockingElements.indexOf(element);
+
+ if (index === -1) {
+ return;
+ }
+
+ this._lockingElements.splice(index, 1);
+
+ this._lockedElementCache = [];
+ this._unlockedElementCache = [];
+
+ if (this._lockingElements.length === 0) {
+ this._unlockScrollInteractions();
+ }
+ },
+
+ _lockingElements: [],
+
+ _lockedElementCache: null,
+
+ _unlockedElementCache: null,
+
+ _hasCachedLockedElement: function(element) {
+ return this._lockedElementCache.indexOf(element) > -1;
+ },
+
+ _hasCachedUnlockedElement: function(element) {
+ return this._unlockedElementCache.indexOf(element) > -1;
+ },
+
+ _composedTreeContains: function(element, child) {
+ // NOTE(cdata): This method iterates over content elements and their
+ // corresponding distributed nodes to implement a contains-like method
+ // that pierces through the composed tree of the ShadowDOM. Results of
+ // this operation are cached (elsewhere) on a per-scroll-lock basis, to
+ // guard against potentially expensive lookups happening repeatedly as
+ // a user scrolls / touchmoves.
+ var contentElements;
+ var distributedNodes;
+ var contentIndex;
+ var nodeIndex;
+
+ if (element.contains(child)) {
+ return true;
+ }
+
+ contentElements = Polymer.dom(element).querySelectorAll('content');
+
+ for (contentIndex = 0; contentIndex < contentElements.length; ++contentIndex) {
+
+ distributedNodes = Polymer.dom(contentElements[contentIndex]).getDistributedNodes();
+
+ for (nodeIndex = 0; nodeIndex < distributedNodes.length; ++nodeIndex) {
+
+ if (this._composedTreeContains(distributedNodes[nodeIndex], child)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ _scrollInteractionHandler: function(event) {
+ // Avoid canceling an event with cancelable=false, e.g. scrolling is in
+ // progress and cannot be interrupted.
+ if (event.cancelable && this._shouldPreventScrolling(event)) {
+ event.preventDefault();
+ }
+ // If event has targetTouches (touch event), update last touch position.
+ if (event.targetTouches) {
+ var touch = event.targetTouches[0];
+ lastTouchPosition.pageX = touch.pageX;
+ lastTouchPosition.pageY = touch.pageY;
+ }
+ },
+
+ _lockScrollInteractions: function() {
+ this._boundScrollHandler = this._boundScrollHandler ||
+ this._scrollInteractionHandler.bind(this);
+ for (var i = 0, l = scrollEvents.length; i < l; i++) {
+ // NOTE: browsers that don't support objects as third arg will
+ // interpret it as boolean, hence useCapture = true in this case.
+ document.addEventListener(scrollEvents[i], this._boundScrollHandler, {
+ capture: true,
+ passive: false
+ });
+ }
+ },
+
+ _unlockScrollInteractions: function() {
+ for (var i = 0, l = scrollEvents.length; i < l; i++) {
+ // NOTE: browsers that don't support objects as third arg will
+ // interpret it as boolean, hence useCapture = true in this case.
+ document.removeEventListener(scrollEvents[i], this._boundScrollHandler, {
+ capture: true,
+ passive: false
+ });
+ }
+ },
+
+ /**
+ * Returns true if the event causes scroll outside the current locking
+ * element, e.g. pointer/keyboard interactions, or scroll "leaking"
+ * outside the locking element when it is already at its scroll boundaries.
+ * @param {!Event} event
+ * @return {boolean}
+ * @private
+ */
+ _shouldPreventScrolling: function(event) {
+
+ // Update if root target changed. For touch events, ensure we don't
+ // update during touchmove.
+ var target = Polymer.dom(event).rootTarget;
+ if (event.type !== 'touchmove' && lastRootTarget !== target) {
+ lastRootTarget = target;
+ lastScrollableNodes = this._getScrollableNodes(Polymer.dom(event).path);
+ }
+
+ // Prevent event if no scrollable nodes.
+ if (!lastScrollableNodes.length) {
+ return true;
+ }
+ // Don't prevent touchstart event inside the locking element when it has
+ // scrollable nodes.
+ if (event.type === 'touchstart') {
+ return false;
+ }
+ // Get deltaX/Y.
+ var info = this._getScrollInfo(event);
+ // Prevent if there is no child that can scroll.
+ return !this._getScrollingNode(lastScrollableNodes, info.deltaX, info.deltaY);
+ },
+
+ /**
+ * Returns an array of scrollable nodes up to the current locking element,
+ * which is included too if scrollable.
+ * @param {!Array<Node>} nodes
+ * @return {Array<Node>} scrollables
+ * @private
+ */
+ _getScrollableNodes: function(nodes) {
+ var scrollables = [];
+ var lockingIndex = nodes.indexOf(this.currentLockingElement);
+ // Loop from root target to locking element (included).
+ for (var i = 0; i <= lockingIndex; i++) {
+ // Skip non-Element nodes.
+ if (nodes[i].nodeType !== Node.ELEMENT_NODE) {
+ continue;
+ }
+ var node = /** @type {!Element} */ (nodes[i]);
+ // Check inline style before checking computed style.
+ var style = node.style;
+ if (style.overflow !== 'scroll' && style.overflow !== 'auto') {
+ style = window.getComputedStyle(node);
+ }
+ if (style.overflow === 'scroll' || style.overflow === 'auto') {
+ scrollables.push(node);
+ }
+ }
+ return scrollables;
+ },
+
+ /**
+ * Returns the node that is scrolling. If there is no scrolling,
+ * returns undefined.
+ * @param {!Array<Node>} nodes
+ * @param {number} deltaX Scroll delta on the x-axis
+ * @param {number} deltaY Scroll delta on the y-axis
+ * @return {Node|undefined}
+ * @private
+ */
+ _getScrollingNode: function(nodes, deltaX, deltaY) {
+ // No scroll.
+ if (!deltaX && !deltaY) {
+ return;
+ }
+ // Check only one axis according to where there is more scroll.
+ // Prefer vertical to horizontal.
+ var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX);
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ var canScroll = false;
+ if (verticalScroll) {
+ // delta < 0 is scroll up, delta > 0 is scroll down.
+ canScroll = deltaY < 0 ? node.scrollTop > 0 :
+ node.scrollTop < node.scrollHeight - node.clientHeight;
+ } else {
+ // delta < 0 is scroll left, delta > 0 is scroll right.
+ canScroll = deltaX < 0 ? node.scrollLeft > 0 :
+ node.scrollLeft < node.scrollWidth - node.clientWidth;
+ }
+ if (canScroll) {
+ return node;
+ }
+ }
+ },
+
+ /**
+ * Returns scroll `deltaX` and `deltaY`.
+ * @param {!Event} event The scroll event
+ * @return {{deltaX: number, deltaY: number}} Object containing the
+ * x-axis scroll delta (positive: scroll right, negative: scroll left,
+ * 0: no scroll), and the y-axis scroll delta (positive: scroll down,
+ * negative: scroll up, 0: no scroll).
+ * @private
+ */
+ _getScrollInfo: function(event) {
+ var info = {
+ deltaX: event.deltaX,
+ deltaY: event.deltaY
+ };
+ // Already available.
+ if ('deltaX' in event) {
+ // do nothing, values are already good.
+ }
+ // Safari has scroll info in `wheelDeltaX/Y`.
+ else if ('wheelDeltaX' in event) {
+ info.deltaX = -event.wheelDeltaX;
+ info.deltaY = -event.wheelDeltaY;
+ }
+ // Firefox has scroll info in `detail` and `axis`.
+ else if ('axis' in event) {
+ info.deltaX = event.axis === 1 ? event.detail : 0;
+ info.deltaY = event.axis === 2 ? event.detail : 0;
+ }
+ // On mobile devices, calculate scroll direction.
+ else if (event.targetTouches) {
+ var touch = event.targetTouches[0];
+ // Touch moves from right to left => scrolling goes right.
+ info.deltaX = lastTouchPosition.pageX - touch.pageX;
+ // Touch moves from down to up => scrolling goes down.
+ info.deltaY = lastTouchPosition.pageY - touch.pageY;
+ }
+ return info;
+ }
+ };
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html b/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html
new file mode 100644
index 00000000..82290a3c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html
@@ -0,0 +1,353 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+<link rel="import" href="../iron-overlay-behavior/iron-overlay-behavior.html">
+<link rel="import" href="../neon-animation/neon-animation-runner-behavior.html">
+<link rel="import" href="../neon-animation/animations/opaque-animation.html">
+<link rel="import" href="iron-dropdown-scroll-manager.html">
+
+<!--
+`<iron-dropdown>` is a generalized element that is useful when you have
+hidden content (`.dropdown-content`) that is revealed due to some change in
+state that should cause it to do so.
+
+Note that this is a low-level element intended to be used as part of other
+composite elements that cause dropdowns to be revealed.
+
+Examples of elements that might be implemented using an `iron-dropdown`
+include comboboxes, menubuttons, selects. The list goes on.
+
+The `<iron-dropdown>` element exposes attributes that allow the position
+of the `.dropdown-content` relative to the `.dropdown-trigger` to be
+configured.
+
+ <iron-dropdown horizontal-align="right" vertical-align="top">
+ <div class="dropdown-content">Hello!</div>
+ </iron-dropdown>
+
+In the above example, the `<div>` with class `.dropdown-content` will be
+hidden until the dropdown element has `opened` set to true, or when the `open`
+method is called on the element.
+
+@demo demo/index.html
+-->
+
+<dom-module id="iron-dropdown">
+ <template>
+ <style>
+ :host {
+ position: fixed;
+ }
+
+ #contentWrapper ::content > * {
+ overflow: auto;
+ }
+
+ #contentWrapper.animating ::content > * {
+ overflow: hidden;
+ }
+ </style>
+
+ <div id="contentWrapper">
+ <content id="content" select=".dropdown-content"></content>
+ </div>
+ </template>
+
+ <script>
+ (function() {
+ 'use strict';
+
+ Polymer({
+ is: 'iron-dropdown',
+
+ behaviors: [
+ Polymer.IronControlState,
+ Polymer.IronA11yKeysBehavior,
+ Polymer.IronOverlayBehavior,
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ properties: {
+ /**
+ * The orientation against which to align the dropdown content
+ * horizontally relative to the dropdown trigger.
+ * Overridden from `Polymer.IronFitBehavior`.
+ */
+ horizontalAlign: {
+ type: String,
+ value: 'left',
+ reflectToAttribute: true
+ },
+
+ /**
+ * The orientation against which to align the dropdown content
+ * vertically relative to the dropdown trigger.
+ * Overridden from `Polymer.IronFitBehavior`.
+ */
+ verticalAlign: {
+ type: String,
+ value: 'top',
+ reflectToAttribute: true
+ },
+
+ /**
+ * An animation config. If provided, this will be used to animate the
+ * opening of the dropdown. Pass an Array for multiple animations.
+ * See `neon-animation` documentation for more animation configuration
+ * details.
+ */
+ openAnimationConfig: {
+ type: Object
+ },
+
+ /**
+ * An animation config. If provided, this will be used to animate the
+ * closing of the dropdown. Pass an Array for multiple animations.
+ * See `neon-animation` documentation for more animation configuration
+ * details.
+ */
+ closeAnimationConfig: {
+ type: Object
+ },
+
+ /**
+ * If provided, this will be the element that will be focused when
+ * the dropdown opens.
+ */
+ focusTarget: {
+ type: Object
+ },
+
+ /**
+ * Set to true to disable animations when opening and closing the
+ * dropdown.
+ */
+ noAnimations: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * By default, the dropdown will constrain scrolling on the page
+ * to itself when opened.
+ * Set to true in order to prevent scroll from being constrained
+ * to the dropdown when it opens.
+ */
+ allowOutsideScroll: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Callback for scroll events.
+ * @type {Function}
+ * @private
+ */
+ _boundOnCaptureScroll: {
+ type: Function,
+ value: function() {
+ return this._onCaptureScroll.bind(this);
+ }
+ }
+ },
+
+ listeners: {
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+
+ observers: [
+ '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)'
+ ],
+
+ /**
+ * The element that is contained by the dropdown, if any.
+ */
+ get containedElement() {
+ return Polymer.dom(this.$.content).getDistributedNodes()[0];
+ },
+
+ /**
+ * The element that should be focused when the dropdown opens.
+ * @deprecated
+ */
+ get _focusTarget() {
+ return this.focusTarget || this.containedElement;
+ },
+
+ ready: function() {
+ // Memoized scrolling position, used to block scrolling outside.
+ this._scrollTop = 0;
+ this._scrollLeft = 0;
+ // Used to perform a non-blocking refit on scroll.
+ this._refitOnScrollRAF = null;
+ },
+
+ attached: function () {
+ if (!this.sizingTarget || this.sizingTarget === this) {
+ this.sizingTarget = this.containedElement || this;
+ }
+ },
+
+ detached: function() {
+ this.cancelAnimation();
+ document.removeEventListener('scroll', this._boundOnCaptureScroll);
+ Polymer.IronDropdownScrollManager.removeScrollLock(this);
+ },
+
+ /**
+ * Called when the value of `opened` changes.
+ * Overridden from `IronOverlayBehavior`
+ */
+ _openedChanged: function() {
+ if (this.opened && this.disabled) {
+ this.cancel();
+ } else {
+ this.cancelAnimation();
+ this._updateAnimationConfig();
+ this._saveScrollPosition();
+ if (this.opened) {
+ document.addEventListener('scroll', this._boundOnCaptureScroll);
+ !this.allowOutsideScroll && Polymer.IronDropdownScrollManager.pushScrollLock(this);
+ } else {
+ document.removeEventListener('scroll', this._boundOnCaptureScroll);
+ Polymer.IronDropdownScrollManager.removeScrollLock(this);
+ }
+ Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments);
+ }
+ },
+
+ /**
+ * Overridden from `IronOverlayBehavior`.
+ */
+ _renderOpened: function() {
+ if (!this.noAnimations && this.animationConfig.open) {
+ this.$.contentWrapper.classList.add('animating');
+ this.playAnimation('open');
+ } else {
+ Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments);
+ }
+ },
+
+ /**
+ * Overridden from `IronOverlayBehavior`.
+ */
+ _renderClosed: function() {
+
+ if (!this.noAnimations && this.animationConfig.close) {
+ this.$.contentWrapper.classList.add('animating');
+ this.playAnimation('close');
+ } else {
+ Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments);
+ }
+ },
+
+ /**
+ * Called when animation finishes on the dropdown (when opening or
+ * closing). Responsible for "completing" the process of opening or
+ * closing the dropdown by positioning it or setting its display to
+ * none.
+ */
+ _onNeonAnimationFinish: function() {
+ this.$.contentWrapper.classList.remove('animating');
+ if (this.opened) {
+ this._finishRenderOpened();
+ } else {
+ this._finishRenderClosed();
+ }
+ },
+
+ _onCaptureScroll: function() {
+ if (!this.allowOutsideScroll) {
+ this._restoreScrollPosition();
+ } else {
+ this._refitOnScrollRAF && window.cancelAnimationFrame(this._refitOnScrollRAF);
+ this._refitOnScrollRAF = window.requestAnimationFrame(this.refit.bind(this));
+ }
+ },
+
+ /**
+ * Memoizes the scroll position of the outside scrolling element.
+ * @private
+ */
+ _saveScrollPosition: function() {
+ if (document.scrollingElement) {
+ this._scrollTop = document.scrollingElement.scrollTop;
+ this._scrollLeft = document.scrollingElement.scrollLeft;
+ } else {
+ // Since we don't know if is the body or html, get max.
+ this._scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
+ this._scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
+ }
+ },
+
+ /**
+ * Resets the scroll position of the outside scrolling element.
+ * @private
+ */
+ _restoreScrollPosition: function() {
+ if (document.scrollingElement) {
+ document.scrollingElement.scrollTop = this._scrollTop;
+ document.scrollingElement.scrollLeft = this._scrollLeft;
+ } else {
+ // Since we don't know if is the body or html, set both.
+ document.documentElement.scrollTop = this._scrollTop;
+ document.documentElement.scrollLeft = this._scrollLeft;
+ document.body.scrollTop = this._scrollTop;
+ document.body.scrollLeft = this._scrollLeft;
+ }
+ },
+
+ /**
+ * Constructs the final animation config from different properties used
+ * to configure specific parts of the opening and closing animations.
+ */
+ _updateAnimationConfig: function() {
+ // Update the animation node to be the containedElement.
+ var animationNode = this.containedElement;
+ var animations = [].concat(this.openAnimationConfig || []).concat(this.closeAnimationConfig || []);
+ for (var i = 0; i < animations.length; i++) {
+ animations[i].node = animationNode;
+ }
+ this.animationConfig = {
+ open: this.openAnimationConfig,
+ close: this.closeAnimationConfig
+ };
+ },
+
+ /**
+ * Updates the overlay position based on configured horizontal
+ * and vertical alignment.
+ */
+ _updateOverlayPosition: function() {
+ if (this.isAttached) {
+ // This triggers iron-resize, and iron-overlay-behavior will call refit if needed.
+ this.notifyResize();
+ }
+ },
+
+ /**
+ * Apply focus to focusTarget or containedElement
+ */
+ _applyFocus: function () {
+ var focusTarget = this.focusTarget || this.containedElement;
+ if (focusTarget && this.opened && !this.noAutoFocus) {
+ focusTarget.focus();
+ } else {
+ Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments);
+ }
+ }
+ });
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/test/index.html b/catapult/third_party/polymer/components/iron-dropdown/test/index.html
new file mode 100644
index 00000000..3507c6bc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/test/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>iron-dropdown tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'iron-dropdown.html',
+ 'iron-dropdown-scroll-manager.html',
+ 'iron-dropdown.html?dom=shadow',
+ 'iron-dropdown-scroll-manager.html?dom=shadow'
+ ]);
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown-scroll-manager.html b/catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown-scroll-manager.html
new file mode 100644
index 00000000..ffc06616
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown-scroll-manager.html
@@ -0,0 +1,187 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+ <meta charset="UTF-8">
+ <title>iron-dropdown-scroll-manager tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../iron-dropdown-scroll-manager.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="x-scrollable-element.html">
+</head>
+
+<body>
+
+ <test-fixture id="DOMSubtree">
+ <template>
+ <x-scrollable-element id="Parent"></x-scrollable-element>
+ </template>
+ </test-fixture>
+ <script>
+
+ suite('IronDropdownScrollManager', function() {
+ var parent;
+ var childOne;
+ var childTwo;
+ var grandChildOne;
+ var grandChildTwo;
+ var ancestor;
+
+ setup(function() {
+ parent = fixture('DOMSubtree');
+ childOne = parent.$$('#ChildOne');
+ childTwo = parent.$$('#ChildTwo');
+ grandChildOne = parent.$$('#GrandchildOne');
+ grandChildTwo = parent.$$('#GrandchildTwo');
+ ancestor = document.body;
+ });
+
+ suite('constraining scroll in the DOM', function() {
+ setup(function() {
+ Polymer.IronDropdownScrollManager.pushScrollLock(childOne);
+ });
+
+ teardown(function() {
+ Polymer.IronDropdownScrollManager.removeScrollLock(childOne);
+ });
+
+ test('recognizes sibling as locked', function() {
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(childTwo))
+ .to.be.equal(true);
+ });
+
+ test('recognizes neice as locked', function() {
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(grandChildTwo))
+ .to.be.equal(true);
+ });
+
+ test('recognizes parent as locked', function() {
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(parent))
+ .to.be.equal(true);
+ });
+
+ test('recognizes ancestor as locked', function() {
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(ancestor))
+ .to.be.equal(true);
+ });
+
+ test('recognizes locking child as unlocked', function() {
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(childOne))
+ .to.be.equal(false);
+ });
+
+ test('recognizes descendant of locking child as unlocked', function() {
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(grandChildOne))
+ .to.be.equal(false);
+ });
+
+ test('unlocks locked elements when there are no locking elements', function() {
+ Polymer.IronDropdownScrollManager.removeScrollLock(childOne);
+
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(parent))
+ .to.be.equal(false);
+ });
+
+ suite('various scroll events', function() {
+ var scrollEvents;
+ var events;
+
+ setup(function() {
+ scrollEvents = [
+ 'wheel',
+ 'mousewheel',
+ 'DOMMouseScroll',
+ 'touchmove'
+ ];
+
+ events = scrollEvents.map(function(scrollEvent) {
+ var event = new CustomEvent(scrollEvent, {
+ bubbles: true,
+ cancelable: true
+ });
+ event.deltaX = 0;
+ event.deltaY = 10;
+ return event;
+ });
+ });
+
+ test('prevents wheel events from locked elements', function() {
+ events.forEach(function(event) {
+ childTwo.dispatchEvent(event);
+ expect(event.defaultPrevented).to.be.eql(true);
+ });
+ });
+
+ test('allows wheel events when there are no locking elements', function() {
+ Polymer.IronDropdownScrollManager.removeScrollLock(childOne);
+ events.forEach(function(event) {
+ childTwo.dispatchEvent(event);
+ expect(event.defaultPrevented).to.be.eql(false);
+ });
+ });
+
+ test('allows wheel events from unlocked elements', function() {
+ events.forEach(function(event) {
+ childOne.dispatchEvent(event);
+ expect(event.defaultPrevented).to.be.eql(false);
+ });
+ });
+
+ test('touchstart is prevented if dispatched by an element outside the locking element', function() {
+ var event = new CustomEvent('touchstart', {
+ bubbles: true,
+ cancelable: true
+ });
+ childTwo.dispatchEvent(event);
+ expect(event.defaultPrevented).to.be.eql(true);
+ });
+
+ test('touchstart is not prevented if dispatched by an element inside the locking element', function() {
+ var event = new CustomEvent('touchstart', {
+ bubbles: true,
+ cancelable: true
+ });
+ grandChildOne.dispatchEvent(event);
+ expect(event.defaultPrevented).to.be.eql(false);
+ });
+
+ test('arrow keyboard events not prevented by manager', function() {
+ // Even if these events might cause scrolling, they should not be
+ // prevented because they might cause a11y issues (e.g. arrow keys
+ // used for navigating the content). iron-dropdown is capable of
+ // restoring the scroll position of the document if necessary.
+ var left = MockInteractions.keyboardEventFor('keydown', 37);
+ var up = MockInteractions.keyboardEventFor('keydown', 38);
+ var right = MockInteractions.keyboardEventFor('keydown', 39);
+ var down = MockInteractions.keyboardEventFor('keydown', 40);
+ grandChildOne.dispatchEvent(left);
+ grandChildOne.dispatchEvent(up);
+ grandChildOne.dispatchEvent(right);
+ grandChildOne.dispatchEvent(down);
+ expect(left.defaultPrevented).to.be.eql(false);
+ expect(up.defaultPrevented).to.be.eql(false);
+ expect(right.defaultPrevented).to.be.eql(false);
+ expect(down.defaultPrevented).to.be.eql(false);
+ });
+ });
+ });
+ });
+ </script>
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown.html b/catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown.html
new file mode 100644
index 00000000..a01b0001
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/test/iron-dropdown.html
@@ -0,0 +1,597 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+ <meta charset="UTF-8">
+ <title>iron-dropdown basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../iron-dropdown.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+
+</head>
+<style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+
+ .container {
+ display: block;
+ position: relative;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+
+ .positioned {
+ position: absolute;
+ top: 40px;
+ left: 40px;
+ }
+
+ .dropdown-content {
+ width: 50px;
+ height: 50px;
+ background-color: orange;
+ }
+
+ .big {
+ width: 3000px;
+ height: 3000px;
+ }
+</style>
+
+<body>
+
+ <test-fixture id="TrivialDropdown">
+ <template>
+ <iron-dropdown>
+ <div class="dropdown-content"></div>
+ </iron-dropdown>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="NonLockingDropdown">
+ <template>
+ <iron-dropdown allow-outside-scroll>
+ <div class="dropdown-content">I don't lock scroll!</div>
+ </iron-dropdown>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="AlignedDropdown">
+ <template>
+ <div class="container">
+ <iron-dropdown horizontal-align="right" vertical-align="top">
+ <div class="dropdown-content big"></div>
+ </iron-dropdown>
+ </div>
+ </template>
+ </test-fixture>
+
+ <!-- Absolutely position the dropdown so that it has enough space to move around -->
+ <test-fixture id="OffsetDropdownTopLeft">
+ <template>
+ <div class="container positioned">
+ <iron-dropdown>
+ <div class="dropdown-content"></div>
+ </iron-dropdown>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="OffsetDropdownBottomRight">
+ <template>
+ <div class="container positioned">
+ <iron-dropdown horizontal-align="right" vertical-align="bottom">
+ <div class="dropdown-content"></div>
+ </iron-dropdown>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="FocusableContentDropdown">
+ <template>
+ <iron-dropdown>
+ <div class="dropdown-content" tabindex="0">
+ <div class="subcontent" tabindex="0"></div>
+ </div>
+ </iron-dropdown>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="RTLDropdownLeft">
+ <template>
+ <div dir="rtl" class="container">
+ <iron-dropdown>
+ <div class="dropdown-content"></div>
+ </iron-dropdown>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="RTLDropdownRight">
+ <template>
+ <div dir="rtl" class="container">
+ <iron-dropdown horizontal-align="right">
+ <div class="dropdown-content"></div>
+ </iron-dropdown>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="sizingTarget">
+ <template>
+ <iron-dropdown>
+ <div class="dropdown-content">
+ <div class="subcontent"></div>
+ </div>
+ </iron-dropdown>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="EmptyDropdown">
+ <template>
+ <iron-dropdown></iron-dropdown>
+ </template>
+ </test-fixture>
+
+ <script>
+ function elementIsVisible(element) {
+ var contentRect = element.getBoundingClientRect();
+ var computedStyle = window.getComputedStyle(element);
+
+ return computedStyle.display !== 'none' &&
+ contentRect.width > 0 &&
+ contentRect.height > 0;
+ }
+
+ function runAfterOpen(overlay, callback) {
+ overlay.addEventListener('iron-overlay-opened', callback);
+ overlay.open();
+ }
+
+ function fireWheel(node, deltaX, deltaY) {
+ // IE 11 doesn't support WheelEvent, use CustomEvent.
+ var event = new CustomEvent('wheel', {
+ cancelable: true,
+ bubbles: true
+ });
+ event.deltaX = deltaX;
+ event.deltaY = deltaY;
+ node.dispatchEvent(event);
+ return event;
+ }
+
+ function dispatchScroll(target, scrollLeft, scrollTop) {
+ target.scrollLeft = scrollLeft;
+ target.scrollTop = scrollTop;
+ target.dispatchEvent(new CustomEvent('scroll', { bubbles:true } ));
+ }
+
+ suite('<iron-dropdown>', function() {
+ var dropdown;
+ var content;
+
+ suite('basic', function() {
+ setup(function() {
+ dropdown = fixture('TrivialDropdown');
+ content = Polymer.dom(dropdown).querySelector('.dropdown-content');
+ });
+
+ test('effectively hides the dropdown content', function() {
+ expect(elementIsVisible(content)).to.be.equal(false);
+ });
+
+ test('shows dropdown content when opened', function(done) {
+ runAfterOpen(dropdown, function() {
+ expect(elementIsVisible(content)).to.be.equal(true);
+ done();
+ });
+ });
+
+ test('hides dropdown content when outside is clicked', function(done) {
+ runAfterOpen(dropdown, function() {
+ expect(elementIsVisible(content)).to.be.equal(true);
+ dropdown.addEventListener('iron-overlay-closed', function() {
+ expect(elementIsVisible(content)).to.be.equal(false);
+ done();
+ });
+ MockInteractions.tap(dropdown.parentNode);
+ });
+ });
+
+ suite('when content is focusable', function() {
+ setup(function() {
+ dropdown = fixture('FocusableContentDropdown');
+ content = Polymer.dom(dropdown).querySelector('.dropdown-content');
+ });
+ test('focuses the content when opened', function(done) {
+ runAfterOpen(dropdown, function() {
+ expect(document.activeElement).to.be.equal(content);
+ done();
+ });
+ });
+
+ test('focuses a configured focus target', function(done) {
+ var focusableChild = Polymer.dom(content).querySelector('div[tabindex]');
+ dropdown.focusTarget = focusableChild;
+
+ runAfterOpen(dropdown, function() {
+ expect(document.activeElement).to.not.be.equal(content);
+ expect(document.activeElement).to.be.equal(focusableChild);
+ done();
+ });
+ });
+ });
+
+ suite('when dropdown is empty', function() {
+ test('keeps the sizingTarget default value', function() {
+ dropdown = fixture('EmptyDropdown');
+ expect(dropdown.sizingTarget).to.be.equal(dropdown);
+ });
+ });
+
+ suite('correct animationConfig setup', function() {
+ test('as objects', function() {
+ dropdown.openAnimationConfig = {
+ name: 'open-animation'
+ };
+ dropdown.closeAnimationConfig = {
+ name: 'close-animation'
+ };
+
+ dropdown.opened = true;
+
+ assert.deepEqual(dropdown.openAnimationConfig, {
+ name: 'open-animation',
+ node: content
+ }, 'open animation ok');
+
+ assert.deepEqual(dropdown.closeAnimationConfig, {
+ name: 'close-animation',
+ node: content
+ }, 'close animation ok');
+
+ assert.deepEqual(dropdown.animationConfig, {
+ open: dropdown.openAnimationConfig,
+ close: dropdown.closeAnimationConfig
+ }, 'animationConfig ok');
+ });
+
+ test('as arrays', function() {
+ dropdown.openAnimationConfig = [{
+ name: 'open-animation-1'
+ }, {
+ name: 'open-animation-2'
+ }];
+ dropdown.closeAnimationConfig = [{
+ name: 'close-animation-1'
+ }, {
+ name: 'close-animation-2'
+ }];
+
+ dropdown.opened = true;
+
+ assert.deepEqual(dropdown.openAnimationConfig, [{
+ name: 'open-animation-1',
+ node: content
+ }, {
+ name: 'open-animation-2',
+ node: content
+ }], 'open animation ok');
+
+ assert.deepEqual(dropdown.closeAnimationConfig, [{
+ name: 'close-animation-1',
+ node: content
+ }, {
+ name: 'close-animation-2',
+ node: content
+ }], 'close animation ok');
+
+ assert.deepEqual(dropdown.animationConfig, {
+ open: dropdown.openAnimationConfig,
+ close: dropdown.closeAnimationConfig
+ }, 'animationConfig ok');
+ });
+ });
+
+ });
+
+ suite('locking scroll', function() {
+
+ var bigDiv, scrollTarget;
+ suiteSetup(function() {
+ bigDiv = document.createElement('div');
+ bigDiv.classList.add('big');
+ document.body.appendChild(bigDiv);
+ // Need to discover if html or body is scrollable.
+ // Here we are sure the page is scrollable.
+ document.documentElement.scrollTop = 1;
+ if (document.documentElement.scrollTop === 1) {
+ document.documentElement.scrollTop = 0;
+ scrollTarget = document.documentElement;
+ } else {
+ scrollTarget = document.body;
+ }
+ });
+
+ suiteTeardown(function() {
+ document.body.removeChild(bigDiv);
+ });
+
+ setup(function() {
+ dropdown = fixture('TrivialDropdown');
+ });
+
+ teardown(function() {
+ dispatchScroll(scrollTarget, 0, 0);
+ });
+
+ test('should lock, only once', function(done) {
+ var openCount = 0;
+ runAfterOpen(dropdown, function() {
+ expect(Polymer.IronDropdownScrollManager._lockingElements.length)
+ .to.be.equal(1);
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(document.body))
+ .to.be.equal(true);
+ expect(fireWheel(document.body, 0, 10).defaultPrevented).to.be.equal(true);
+
+ if (openCount === 0) {
+ // This triggers a second `pushScrollLock` with the same element, however
+ // that should not add the element to the `_lockingElements` stack twice
+ dropdown.close();
+ dropdown.open();
+ } else {
+ done();
+ }
+ openCount++;
+ });
+ });
+
+ test('should lock scroll', function(done) {
+ runAfterOpen(dropdown, function() {
+ dispatchScroll(scrollTarget, 10, 10);
+ assert.equal(scrollTarget.scrollTop, 0, 'scrollTop ok');
+ assert.equal(scrollTarget.scrollLeft, 0, 'scrollLeft ok');
+ done();
+ });
+ });
+
+ test('can be disabled with `allowOutsideScroll`', function(done) {
+ dropdown.allowOutsideScroll = true;
+ runAfterOpen(dropdown, function() {
+ dispatchScroll(scrollTarget, 10, 10);
+ assert.equal(scrollTarget.scrollTop, 10, 'scrollTop ok');
+ assert.equal(scrollTarget.scrollLeft, 10, 'scrollLeft ok');
+ done();
+ });
+ });
+
+ });
+
+ suite('non locking scroll', function() {
+
+ setup(function() {
+ dropdown = fixture('NonLockingDropdown');
+ });
+
+ test('can be disabled with `allowOutsideScroll`', function(done) {
+ runAfterOpen(dropdown, function() {
+ expect(Polymer.IronDropdownScrollManager.elementIsScrollLocked(document.body))
+ .to.be.equal(false);
+ expect(fireWheel(document.body, 0, 10).defaultPrevented).to.be.equal(false);
+ done();
+ });
+ });
+ });
+
+ suite('aligned dropdown', function() {
+ var parent;
+ var parentRect;
+ var dropdownRect;
+
+ setup(function() {
+ parent = fixture('AlignedDropdown');
+ dropdown = parent.querySelector('iron-dropdown');
+ parentRect = parent.getBoundingClientRect();
+ });
+
+ test('can be re-aligned to the right and the top', function(done) {
+ runAfterOpen(dropdown, function() {
+ dropdownRect = dropdown.getBoundingClientRect();
+ assert.equal(dropdownRect.top, parentRect.top, 'top ok');
+ assert.equal(dropdownRect.left, 0, 'left ok');
+ assert.equal(dropdownRect.bottom, window.innerHeight, 'bottom ok');
+ assert.equal(dropdownRect.right, parentRect.right, 'right ok');
+ done();
+ });
+ });
+
+ test('can be re-aligned to the bottom', function(done) {
+ dropdown.verticalAlign = 'bottom';
+ runAfterOpen(dropdown, function() {
+ dropdownRect = dropdown.getBoundingClientRect();
+ assert.equal(dropdownRect.top, 0, 'top ok');
+ assert.equal(dropdownRect.left, 0, 'left ok');
+ assert.equal(dropdownRect.bottom, parentRect.bottom, 'bottom ok');
+ assert.equal(dropdownRect.right, parentRect.right, 'right ok');
+ done();
+ });
+ });
+
+ test('handles parent\'s stacking context', function(done) {
+ // This will create a new stacking context.
+ parent.style.transform = 'translateZ(0)';
+ runAfterOpen(dropdown, function() {
+ dropdownRect = dropdown.getBoundingClientRect();
+ assert.equal(dropdownRect.top, parentRect.top, 'top ok');
+ assert.equal(dropdownRect.left, 0, 'left ok');
+ assert.equal(dropdownRect.bottom, window.innerHeight, 'bottom ok');
+ assert.equal(dropdownRect.right, parentRect.right, 'right ok');
+ done();
+ });
+ });
+ });
+
+ suite('when align is left/top, with an offset', function() {
+ var dropdownRect;
+ var offsetDropdownRect;
+ setup(function() {
+ var parent = fixture('OffsetDropdownTopLeft');
+ dropdown = parent.querySelector('iron-dropdown');
+ });
+
+ test('can be offset towards the bottom right', function(done) {
+ runAfterOpen(dropdown, function() {
+ dropdownRect = dropdown.getBoundingClientRect();
+ dropdown.verticalOffset = 10;
+ dropdown.horizontalOffset = 10;
+ // Force refit instead of waiting for requestAnimationFrame.
+ dropdown.refit();
+ offsetDropdownRect = dropdown.getBoundingClientRect();
+ // verticalAlign is top, so a positive offset moves down.
+ assert.equal(dropdownRect.top + 10, offsetDropdownRect.top, 'top ok');
+ // horizontalAlign is left, so a positive offset moves to the right.
+ assert.equal(dropdownRect.left + 10, offsetDropdownRect.left, 'left ok');
+ done();
+ });
+ });
+
+ test('can be offset towards the top left', function(done) {
+ runAfterOpen(dropdown, function() {
+ dropdownRect = dropdown.getBoundingClientRect();
+ dropdown.verticalOffset = -10;
+ dropdown.horizontalOffset = -10;
+ // Force refit instead of waiting for requestAnimationFrame.
+ dropdown.refit();
+ offsetDropdownRect = dropdown.getBoundingClientRect();
+ // verticalAlign is top, so a negative offset moves up.
+ assert.equal(dropdownRect.top - 10, offsetDropdownRect.top, 'top ok');
+ // horizontalAlign is left, so a negative offset moves to the left.
+ assert.equal(dropdownRect.left - 10, offsetDropdownRect.left, 'left ok');
+ done();
+ });
+ });
+ });
+
+ suite('when align is right/bottom, with an offset', function() {
+ var dropdownRect;
+ var offsetDropdownRect;
+ setup(function() {
+ var parent = fixture('OffsetDropdownBottomRight');
+ dropdown = parent.querySelector('iron-dropdown');
+ });
+
+ test('can be offset towards the top left', function(done) {
+ runAfterOpen(dropdown, function() {
+ dropdownRect = dropdown.getBoundingClientRect();
+ dropdown.verticalOffset = 10;
+ dropdown.horizontalOffset = 10;
+ // Force refit instead of waiting for requestAnimationFrame.
+ dropdown.refit();
+ offsetDropdownRect = dropdown.getBoundingClientRect();
+ // verticalAlign is bottom, so a positive offset moves up.
+ assert.equal(dropdownRect.bottom - 10, offsetDropdownRect.bottom, 'bottom ok');
+ // horizontalAlign is right, so a positive offset moves to the left.
+ assert.equal(dropdownRect.right - 10, offsetDropdownRect.right, 'right ok');
+ done();
+ });
+ });
+
+ test('can be offset towards the bottom right', function(done) {
+ runAfterOpen(dropdown, function() {
+ dropdownRect = dropdown.getBoundingClientRect();
+ dropdown.verticalOffset = -10;
+ dropdown.horizontalOffset = -10;
+ // Force refit instead of waiting for requestAnimationFrame.
+ dropdown.refit();
+ offsetDropdownRect = dropdown.getBoundingClientRect();
+ // verticalAlign is bottom, so a negative offset moves down.
+ assert.equal(dropdownRect.bottom + 10, offsetDropdownRect.bottom, 'bottom ok');
+ // horizontalAlign is right, so a positive offset moves to the right.
+ assert.equal(dropdownRect.right + 10, offsetDropdownRect.right, 'right ok');
+ done();
+ });
+ });
+ });
+
+ suite('RTL', function() {
+ var dropdownRect;
+
+ test('with horizontalAlign=left', function(done) {
+ var parent = fixture('RTLDropdownLeft');
+ dropdown = parent.querySelector('iron-dropdown');
+ runAfterOpen(dropdown, function() {
+ // In RTL, if `horizontalAlign` is "left", that's the same as
+ // being right-aligned in LTR. So the dropdown should be in the top
+ // right corner.
+ dropdownRect = dropdown.getBoundingClientRect();
+ expect(dropdownRect.top).to.be.equal(0);
+ expect(dropdownRect.right).to.be.equal(100);
+ done();
+ });
+ });
+
+ test('with horizontalAlign=right', function(done) {
+ var parent = fixture('RTLDropdownRight');
+ dropdown = parent.querySelector('iron-dropdown');
+ runAfterOpen(dropdown, function() {
+ // In RTL, if `horizontalAlign` is "right", that's the same as
+ // being left-aligned in LTR. So the dropdown should be in the top
+ // left corner.
+ dropdownRect = dropdown.getBoundingClientRect();
+ expect(dropdownRect.top).to.be.equal(0);
+ expect(dropdownRect.left).to.be.equal(0);
+ done();
+ });
+ });
+ });
+
+ suite('sizing target', function() {
+ setup(function() {
+ dropdown = fixture('sizingTarget');
+ content = Polymer.dom(dropdown).querySelector('.dropdown-content');
+ });
+
+ test('sizingTarget is the content element by default', function(done) {
+ runAfterOpen(dropdown, function() {
+ expect(dropdown.sizingTarget).to.be.equal(content);
+ expect(content.style.maxHeight).to.be.not.empty;
+ expect(content.style.maxWidth).to.be.not.empty;
+ done();
+ });
+ });
+
+ test('sizingTarget can be set to a child element', function(done) {
+ var subcontent = Polymer.dom(dropdown).querySelector('.subcontent');
+ dropdown.sizingTarget = subcontent;
+
+ runAfterOpen(dropdown, function() {
+ expect(dropdown.sizingTarget).to.be.equal(subcontent);
+ expect(subcontent.style.maxHeight).to.be.not.empty;
+ expect(subcontent.style.maxWidth).to.be.not.empty;
+ done();
+ });
+ });
+ });
+ });
+ </script>
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-dropdown/test/x-scrollable-element.html b/catapult/third_party/polymer/components/iron-dropdown/test/x-scrollable-element.html
new file mode 100644
index 00000000..3bdb3cf9
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-dropdown/test/x-scrollable-element.html
@@ -0,0 +1,53 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+
+<dom-module id="x-scrollable-element">
+ <template>
+ <style>
+ :host {
+ display: block;
+ height: 100px;
+ border: 1px solid red;
+ overflow: auto;
+ }
+ #ChildOne, #ChildTwo {
+ height: 200px;
+ border: 1px solid blue;
+ overflow: auto;
+ }
+ #GrandchildOne, #GrandchildTwo {
+ height: 300px;
+ border: 1px solid green;
+ overflow: auto;
+ }
+ .scrollContent {
+ height: 400px;
+ background-color: yellow;
+ }
+ </style>
+ <div id="ChildOne">
+ <div id="GrandchildOne">
+ <div class="scrollContent"></div>
+ </div>
+ </div>
+ <div id="ChildTwo">
+ <div id="GrandchildTwo">
+ <div class="scrollContent"></div>
+ </div>
+ </div>
+ </template>
+ <script>
+ Polymer({
+ is: 'x-scrollable-element'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/.bower.json b/catapult/third_party/polymer/components/iron-fit-behavior/.bower.json
new file mode 100644
index 00000000..6078a5b7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/.bower.json
@@ -0,0 +1,41 @@
+{
+ "name": "iron-fit-behavior",
+ "version": "1.2.7",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Fits an element inside another element",
+ "private": true,
+ "main": "iron-fit-behavior.html",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-fit-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-fit-behavior",
+ "_release": "1.2.7",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.7",
+ "commit": "c4b8a39c910da28ca8846fe960c244ce2f64f7be"
+ },
+ "_source": "https://github.com/PolymerElements/iron-fit-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-fit-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-fit-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..0bd8a806
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-fit-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/.gitignore b/catapult/third_party/polymer/components/iron-fit-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-fit-behavior/.travis.yml
new file mode 100644
index 00000000..04f7693a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ L4lSnuQZDY+YcXYzBZSRKjJJ1rZf18Lc/8YDgQPfkMkAItrRHGR8vblBoKiPAmtvgNxztcpZxAXTiDy1vAeVv54QnX9b1JsuOs7rrQxB4BS04Dj7LdT6DDu1p/V09MJBN11lzLVxgpxljbumwGWE4gfpDl2+rjbBt7cRV5VkVnE=
+ - secure: >-
+ H7dHZ9FQvJszK2UMNZJiZmzOPET3muO/XvlkUc1x3KcUlV5/tD/404V05XfFMowH7DavHFYleZkb89deYjq9PHncO9c4bp4SHD7HKN4FaGyhzfpXjg66v3dZH/OcERjaas337uUE2jo/x1jCq4HJCz2bMVh+bvd4du1C/2OWarc=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-fit-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/README.md b/catapult/third_party/polymer/components/iron-fit-behavior/README.md
new file mode 100644
index 00000000..8dbc78e3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/README.md
@@ -0,0 +1,57 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-fit-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-fit-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-fit-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-fit-behavior)_
+
+
+##Polymer.IronFitBehavior
+
+`Polymer.IronFitBehavior` fits an element in another element using `max-height` and `max-width`, and
+optionally centers it in the window or another element.
+
+The element will only be sized and/or positioned if it has not already been sized and/or positioned
+by CSS.
+
+| CSS properties | Action |
+| --- | --- |
+| `position` set | Element is not centered horizontally or vertically |
+| `top` or `bottom` set | Element is not vertically centered |
+| `left` or `right` set | Element is not horizontally centered |
+| `max-height` set | Element respects `max-height` |
+| `max-width` set | Element respects `max-width` |
+
+`Polymer.IronFitBehavior` can position an element into another element using
+`verticalAlign` and `horizontalAlign`. This will override the element's css position.
+
+```html
+ <div class="container">
+ <iron-fit-impl vertical-align="top" horizontal-align="auto">
+ Positioned into the container
+ </iron-fit-impl>
+ </div>
+```
+
+Use `noOverlap` to position the element around another element without overlapping it.
+
+```html
+ <div class="container">
+ <iron-fit-impl no-overlap vertical-align="auto" horizontal-align="auto">
+ Positioned around the container
+ </iron-fit-impl>
+ </div>
+```
+
+
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/bower.json b/catapult/third_party/polymer/components/iron-fit-behavior/bower.json
new file mode 100644
index 00000000..96dd4785
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "iron-fit-behavior",
+ "version": "1.2.7",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Fits an element inside another element",
+ "private": true,
+ "main": "iron-fit-behavior.html",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-fit-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-fit-behavior/demo/index.html
new file mode 100644
index 00000000..2c716e06
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/demo/index.html
@@ -0,0 +1,166 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+
+ <title>iron-fit-behavior demo</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="simple-fit.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ .centered {
+ min-width: 500px;
+ }
+ demo-snippet {
+ --demo-snippet-code: {
+ max-height: 250px;
+ }
+ }
+ </style>
+
+</head>
+
+<body unresolved class="centered">
+ <h3>
+ An element with <code>IronFitBehavior</code> can be centered in
+ <code>fitInto</code> or positioned around a <code>positionTarget</code>
+ </h3>
+ <demo-snippet>
+ <template>
+ <style>
+ .target {
+ cursor: pointer;
+ text-align: center;
+ display: inline-block;
+ box-sizing: border-box;
+ border: 1px solid;
+ width: 100px;
+ padding: 20px 0;
+ margin: 5px;
+ }
+
+ #myFit {
+ z-index: 10;
+ padding: 20px;
+ overflow: auto;
+ min-width: 100px;
+ min-height: 100px;
+ }
+
+ button {
+ background-color: white;
+ border-radius: 5px;
+ border-width: 1px;
+ }
+
+ button.selected {
+ background-color: #b3e5fc;
+ }
+ </style>
+ <template is="dom-bind">
+ <template is="dom-repeat" items="[[containers]]">
+ <div class="target" on-tap="updatePositionTarget">Target</div>
+ </template>
+ <simple-fit id="myFit" auto-fit-on-attach>
+ <h2>Align</h2>
+ <p>
+ <button on-tap="updateAlign" vertical-align="top">top</button>
+ <button on-tap="updateAlign" vertical-align="bottom">bottom</button>
+ <button on-tap="updateAlign" vertical-align="auto">auto</button>
+ <button on-tap="updateAlign" vertical-align>null</button>
+ </p>
+ <p>
+ <button on-tap="updateAlign" horizontal-align="left">left</button>
+ <button on-tap="updateAlign" horizontal-align="right">right</button>
+ <button on-tap="updateAlign" horizontal-align="auto">auto</button>
+ <button on-tap="updateAlign" horizontal-align>null</button>
+ </p>
+ <button on-tap="toggleNoOverlap">no overlap</button>
+ <button on-tap="toggleDynamicAlign">dynamic align</button>
+ </simple-fit>
+ <script>
+ var defaultTarget = Polymer.dom(myFit).parentNode;
+ var template = document.querySelector('template[is="dom-bind"]');
+
+ template.containers = new Array(30);
+
+ template.updatePositionTarget = function(e) {
+ var target = Polymer.dom(e).rootTarget;
+ target = myFit.positionTarget === target ? defaultTarget : target;
+ myFit.positionTarget.style.backgroundColor = '';
+ target.style.backgroundColor = 'orange';
+ myFit.positionTarget = target;
+ template.refit();
+ };
+
+ template._raf = null;
+ template.refit = function() {
+ template._raf && window.cancelAnimationFrame(template._raf);
+ template._raf = window.requestAnimationFrame(function() {
+ template._raf = null;
+ myFit.refit();
+ });
+ };
+
+ template.updateAlign = function(e) {
+ var target = Polymer.dom(e).rootTarget;
+ if (target.hasAttribute('horizontal-align')) {
+ myFit.horizontalAlign = target.getAttribute('horizontal-align');
+ var children = target.parentNode.querySelectorAll('[horizontal-align]');
+ for (var i = 0; i < children.length; i++) {
+ toggleClass(children[i], 'selected', children[i] === target);
+ }
+ }
+ if (target.hasAttribute('vertical-align')) {
+ myFit.verticalAlign = target.getAttribute('vertical-align');
+ var children = target.parentNode.querySelectorAll('[vertical-align]');
+ for (var i = 0; i < children.length; i++) {
+ toggleClass(children[i], 'selected', children[i] === target);
+ }
+ }
+ template.refit();
+ };
+
+ template.toggleNoOverlap = function(e) {
+ myFit.noOverlap = !myFit.noOverlap;
+ toggleClass(Polymer.dom(e).rootTarget, 'selected', myFit.noOverlap);
+ template.refit();
+ };
+
+ template.toggleDynamicAlign = function(e) {
+ myFit.dynamicAlign = !myFit.dynamicAlign;
+ toggleClass(Polymer.dom(e).rootTarget, 'selected', myFit.dynamicAlign);
+ template.refit();
+ };
+
+ // Listen for resize and scroll on window.
+ window.addEventListener('resize', template.refit);
+ window.addEventListener('scroll', template.refit);
+
+ function toggleClass(element, cssClass, condition) {
+ element.classList[condition ? 'add' : 'remove'](cssClass);
+ }
+ </script>
+ </template>
+ </template>
+ </demo-snippet>
+
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/demo/simple-fit.html b/catapult/third_party/polymer/components/iron-fit-behavior/demo/simple-fit.html
new file mode 100644
index 00000000..2dc83fb7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/demo/simple-fit.html
@@ -0,0 +1,41 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-fit-behavior.html">
+<link rel="import" href="../../paper-styles/color.html">
+
+<dom-module id="simple-fit">
+ <template>
+ <style>
+ :host {
+ background-color: var(--paper-light-blue-500);
+ color: white;
+ text-align: center;
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'simple-fit',
+
+ behaviors: [
+ Polymer.IronFitBehavior
+ ]
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/index.html b/catapult/third_party/polymer/components/iron-fit-behavior/index.html
new file mode 100644
index 00000000..5ffa7d61
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-fit-behavior</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html b/catapult/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html
new file mode 100644
index 00000000..825b1360
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html
@@ -0,0 +1,611 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+/**
+`Polymer.IronFitBehavior` fits an element in another element using `max-height` and `max-width`, and
+optionally centers it in the window or another element.
+
+The element will only be sized and/or positioned if it has not already been sized and/or positioned
+by CSS.
+
+CSS properties | Action
+-----------------------------|-------------------------------------------
+`position` set | Element is not centered horizontally or vertically
+`top` or `bottom` set | Element is not vertically centered
+`left` or `right` set | Element is not horizontally centered
+`max-height` set | Element respects `max-height`
+`max-width` set | Element respects `max-width`
+
+`Polymer.IronFitBehavior` can position an element into another element using
+`verticalAlign` and `horizontalAlign`. This will override the element's css position.
+
+ <div class="container">
+ <iron-fit-impl vertical-align="top" horizontal-align="auto">
+ Positioned into the container
+ </iron-fit-impl>
+ </div>
+
+Use `noOverlap` to position the element around another element without overlapping it.
+
+ <div class="container">
+ <iron-fit-impl no-overlap vertical-align="auto" horizontal-align="auto">
+ Positioned around the container
+ </iron-fit-impl>
+ </div>
+
+Use `horizontalOffset, verticalOffset` to offset the element from its `positionTarget`;
+`Polymer.IronFitBehavior` will collapse these in order to keep the element
+within `fitInto` boundaries, while preserving the element's CSS margin values.
+
+ <div class="container">
+ <iron-fit-impl vertical-align="top" vertical-offset="20">
+ With vertical offset
+ </iron-fit-impl>
+ </div>
+
+
+@demo demo/index.html
+@polymerBehavior
+*/
+ Polymer.IronFitBehavior = {
+
+ properties: {
+
+ /**
+ * The element that will receive a `max-height`/`width`. By default it is the same as `this`,
+ * but it can be set to a child element. This is useful, for example, for implementing a
+ * scrolling region inside the element.
+ * @type {!Element}
+ */
+ sizingTarget: {
+ type: Object,
+ value: function() {
+ return this;
+ }
+ },
+
+ /**
+ * The element to fit `this` into.
+ */
+ fitInto: {
+ type: Object,
+ value: window
+ },
+
+ /**
+ * Will position the element around the positionTarget without overlapping it.
+ */
+ noOverlap: {
+ type: Boolean
+ },
+
+ /**
+ * The element that should be used to position the element. If not set, it will
+ * default to the parent node.
+ * @type {!Element}
+ */
+ positionTarget: {
+ type: Element
+ },
+
+ /**
+ * The orientation against which to align the element horizontally
+ * relative to the `positionTarget`. Possible values are "left", "right", "auto".
+ */
+ horizontalAlign: {
+ type: String
+ },
+
+ /**
+ * The orientation against which to align the element vertically
+ * relative to the `positionTarget`. Possible values are "top", "bottom", "auto".
+ */
+ verticalAlign: {
+ type: String
+ },
+
+ /**
+ * If true, it will use `horizontalAlign` and `verticalAlign` values as preferred alignment
+ * and if there's not enough space, it will pick the values which minimize the cropping.
+ */
+ dynamicAlign: {
+ type: Boolean
+ },
+
+ /**
+ * A pixel value that will be added to the position calculated for the
+ * given `horizontalAlign`, in the direction of alignment. You can think
+ * of it as increasing or decreasing the distance to the side of the
+ * screen given by `horizontalAlign`.
+ *
+ * If `horizontalAlign` is "left", this offset will increase or decrease
+ * the distance to the left side of the screen: a negative offset will
+ * move the dropdown to the left; a positive one, to the right.
+ *
+ * Conversely if `horizontalAlign` is "right", this offset will increase
+ * or decrease the distance to the right side of the screen: a negative
+ * offset will move the dropdown to the right; a positive one, to the left.
+ */
+ horizontalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
+ * A pixel value that will be added to the position calculated for the
+ * given `verticalAlign`, in the direction of alignment. You can think
+ * of it as increasing or decreasing the distance to the side of the
+ * screen given by `verticalAlign`.
+ *
+ * If `verticalAlign` is "top", this offset will increase or decrease
+ * the distance to the top side of the screen: a negative offset will
+ * move the dropdown upwards; a positive one, downwards.
+ *
+ * Conversely if `verticalAlign` is "bottom", this offset will increase
+ * or decrease the distance to the bottom side of the screen: a negative
+ * offset will move the dropdown downwards; a positive one, upwards.
+ */
+ verticalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
+ * Set to true to auto-fit on attach.
+ */
+ autoFitOnAttach: {
+ type: Boolean,
+ value: false
+ },
+
+ /** @type {?Object} */
+ _fitInfo: {
+ type: Object
+ }
+ },
+
+ get _fitWidth() {
+ var fitWidth;
+ if (this.fitInto === window) {
+ fitWidth = this.fitInto.innerWidth;
+ } else {
+ fitWidth = this.fitInto.getBoundingClientRect().width;
+ }
+ return fitWidth;
+ },
+
+ get _fitHeight() {
+ var fitHeight;
+ if (this.fitInto === window) {
+ fitHeight = this.fitInto.innerHeight;
+ } else {
+ fitHeight = this.fitInto.getBoundingClientRect().height;
+ }
+ return fitHeight;
+ },
+
+ get _fitLeft() {
+ var fitLeft;
+ if (this.fitInto === window) {
+ fitLeft = 0;
+ } else {
+ fitLeft = this.fitInto.getBoundingClientRect().left;
+ }
+ return fitLeft;
+ },
+
+ get _fitTop() {
+ var fitTop;
+ if (this.fitInto === window) {
+ fitTop = 0;
+ } else {
+ fitTop = this.fitInto.getBoundingClientRect().top;
+ }
+ return fitTop;
+ },
+
+ /**
+ * The element that should be used to position the element,
+ * if no position target is configured.
+ */
+ get _defaultPositionTarget() {
+ var parent = Polymer.dom(this).parentNode;
+
+ if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+ parent = parent.host;
+ }
+
+ return parent;
+ },
+
+ /**
+ * The horizontal align value, accounting for the RTL/LTR text direction.
+ */
+ get _localeHorizontalAlign() {
+ if (this._isRTL) {
+ // In RTL, "left" becomes "right".
+ if (this.horizontalAlign === 'right') {
+ return 'left';
+ }
+ if (this.horizontalAlign === 'left') {
+ return 'right';
+ }
+ }
+ return this.horizontalAlign;
+ },
+
+ attached: function() {
+ // Memoize this to avoid expensive calculations & relayouts.
+ // Make sure we do it only once
+ if (typeof this._isRTL === 'undefined') {
+ this._isRTL = window.getComputedStyle(this).direction == 'rtl';
+ }
+
+ this.positionTarget = this.positionTarget || this._defaultPositionTarget;
+ if (this.autoFitOnAttach) {
+ if (window.getComputedStyle(this).display === 'none') {
+ setTimeout(function() {
+ this.fit();
+ }.bind(this));
+ } else {
+ this.fit();
+ }
+ }
+ },
+
+ /**
+ * Positions and fits the element into the `fitInto` element.
+ */
+ fit: function() {
+ this.position();
+ this.constrain();
+ this.center();
+ },
+
+ /**
+ * Memoize information needed to position and size the target element.
+ * @suppress {deprecated}
+ */
+ _discoverInfo: function() {
+ if (this._fitInfo) {
+ return;
+ }
+ var target = window.getComputedStyle(this);
+ var sizer = window.getComputedStyle(this.sizingTarget);
+
+ this._fitInfo = {
+ inlineStyle: {
+ top: this.style.top || '',
+ left: this.style.left || '',
+ position: this.style.position || ''
+ },
+ sizerInlineStyle: {
+ maxWidth: this.sizingTarget.style.maxWidth || '',
+ maxHeight: this.sizingTarget.style.maxHeight || '',
+ boxSizing: this.sizingTarget.style.boxSizing || ''
+ },
+ positionedBy: {
+ vertically: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ?
+ 'bottom' : null),
+ horizontally: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ?
+ 'right' : null)
+ },
+ sizedBy: {
+ height: sizer.maxHeight !== 'none',
+ width: sizer.maxWidth !== 'none',
+ minWidth: parseInt(sizer.minWidth, 10) || 0,
+ minHeight: parseInt(sizer.minHeight, 10) || 0
+ },
+ margin: {
+ top: parseInt(target.marginTop, 10) || 0,
+ right: parseInt(target.marginRight, 10) || 0,
+ bottom: parseInt(target.marginBottom, 10) || 0,
+ left: parseInt(target.marginLeft, 10) || 0
+ }
+ };
+ },
+
+ /**
+ * Resets the target element's position and size constraints, and clear
+ * the memoized data.
+ */
+ resetFit: function() {
+ var info = this._fitInfo || {};
+ for (var property in info.sizerInlineStyle) {
+ this.sizingTarget.style[property] = info.sizerInlineStyle[property];
+ }
+ for (var property in info.inlineStyle) {
+ this.style[property] = info.inlineStyle[property];
+ }
+
+ this._fitInfo = null;
+ },
+
+ /**
+ * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
+ * the element or the `fitInto` element has been resized, or if any of the
+ * positioning properties (e.g. `horizontalAlign, verticalAlign`) is updated.
+ * It preserves the scroll position of the sizingTarget.
+ */
+ refit: function() {
+ var scrollLeft = this.sizingTarget.scrollLeft;
+ var scrollTop = this.sizingTarget.scrollTop;
+ this.resetFit();
+ this.fit();
+ this.sizingTarget.scrollLeft = scrollLeft;
+ this.sizingTarget.scrollTop = scrollTop;
+ },
+
+ /**
+ * Positions the element according to `horizontalAlign, verticalAlign`.
+ */
+ position: function() {
+ if (!this.horizontalAlign && !this.verticalAlign) {
+ // needs to be centered, and it is done after constrain.
+ return;
+ }
+ this._discoverInfo();
+
+ this.style.position = 'fixed';
+ // Need border-box for margin/padding.
+ this.sizingTarget.style.boxSizing = 'border-box';
+ // Set to 0, 0 in order to discover any offset caused by parent stacking contexts.
+ this.style.left = '0px';
+ this.style.top = '0px';
+
+ var rect = this.getBoundingClientRect();
+ var positionRect = this.__getNormalizedRect(this.positionTarget);
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+
+ var margin = this._fitInfo.margin;
+
+ // Consider the margin as part of the size for position calculations.
+ var size = {
+ width: rect.width + margin.left + margin.right,
+ height: rect.height + margin.top + margin.bottom
+ };
+
+ var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, positionRect, fitRect);
+
+ var left = position.left + margin.left;
+ var top = position.top + margin.top;
+
+ // We first limit right/bottom within fitInto respecting the margin,
+ // then use those values to limit top/left.
+ var right = Math.min(fitRect.right - margin.right, left + rect.width);
+ var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height);
+
+ // Keep left/top within fitInto respecting the margin.
+ left = Math.max(fitRect.left + margin.left,
+ Math.min(left, right - this._fitInfo.sizedBy.minWidth));
+ top = Math.max(fitRect.top + margin.top,
+ Math.min(top, bottom - this._fitInfo.sizedBy.minHeight));
+
+ // Use right/bottom to set maxWidth/maxHeight, and respect minWidth/minHeight.
+ this.sizingTarget.style.maxWidth = Math.max(right - left, this._fitInfo.sizedBy.minWidth) + 'px';
+ this.sizingTarget.style.maxHeight = Math.max(bottom - top, this._fitInfo.sizedBy.minHeight) + 'px';
+
+ // Remove the offset caused by any stacking context.
+ this.style.left = (left - rect.left) + 'px';
+ this.style.top = (top - rect.top) + 'px';
+ },
+
+ /**
+ * Constrains the size of the element to `fitInto` by setting `max-height`
+ * and/or `max-width`.
+ */
+ constrain: function() {
+ if (this.horizontalAlign || this.verticalAlign) {
+ return;
+ }
+ this._discoverInfo();
+
+ var info = this._fitInfo;
+ // position at (0px, 0px) if not already positioned, so we can measure the natural size.
+ if (!info.positionedBy.vertically) {
+ this.style.position = 'fixed';
+ this.style.top = '0px';
+ }
+ if (!info.positionedBy.horizontally) {
+ this.style.position = 'fixed';
+ this.style.left = '0px';
+ }
+
+ // need border-box for margin/padding
+ this.sizingTarget.style.boxSizing = 'border-box';
+ // constrain the width and height if not already set
+ var rect = this.getBoundingClientRect();
+ if (!info.sizedBy.height) {
+ this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height');
+ }
+ if (!info.sizedBy.width) {
+ this.__sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width');
+ }
+ },
+
+ /**
+ * @protected
+ * @deprecated
+ */
+ _sizeDimension: function(rect, positionedBy, start, end, extent) {
+ this.__sizeDimension(rect, positionedBy, start, end, extent);
+ },
+
+ /**
+ * @private
+ */
+ __sizeDimension: function(rect, positionedBy, start, end, extent) {
+ var info = this._fitInfo;
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+ var max = extent === 'Width' ? fitRect.width : fitRect.height;
+ var flip = (positionedBy === end);
+ var offset = flip ? max - rect[end] : rect[start];
+ var margin = info.margin[flip ? start : end];
+ var offsetExtent = 'offset' + extent;
+ var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent];
+ this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingOffset) + 'px';
+ },
+
+ /**
+ * Centers horizontally and vertically if not already positioned. This also sets
+ * `position:fixed`.
+ */
+ center: function() {
+ if (this.horizontalAlign || this.verticalAlign) {
+ return;
+ }
+ this._discoverInfo();
+
+ var positionedBy = this._fitInfo.positionedBy;
+ if (positionedBy.vertically && positionedBy.horizontally) {
+ // Already positioned.
+ return;
+ }
+ // Need position:fixed to center
+ this.style.position = 'fixed';
+ // Take into account the offset caused by parents that create stacking
+ // contexts (e.g. with transform: translate3d). Translate to 0,0 and
+ // measure the bounding rect.
+ if (!positionedBy.vertically) {
+ this.style.top = '0px';
+ }
+ if (!positionedBy.horizontally) {
+ this.style.left = '0px';
+ }
+ // It will take in consideration margins and transforms
+ var rect = this.getBoundingClientRect();
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+ if (!positionedBy.vertically) {
+ var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2;
+ this.style.top = top + 'px';
+ }
+ if (!positionedBy.horizontally) {
+ var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2;
+ this.style.left = left + 'px';
+ }
+ },
+
+ __getNormalizedRect: function(target) {
+ if (target === document.documentElement || target === window) {
+ return {
+ top: 0,
+ left: 0,
+ width: window.innerWidth,
+ height: window.innerHeight,
+ right: window.innerWidth,
+ bottom: window.innerHeight
+ };
+ }
+ return target.getBoundingClientRect();
+ },
+
+ __getCroppedArea: function(position, size, fitRect) {
+ var verticalCrop = Math.min(0, position.top) + Math.min(0, fitRect.bottom - (position.top + size.height));
+ var horizontalCrop = Math.min(0, position.left) + Math.min(0, fitRect.right - (position.left + size.width));
+ return Math.abs(verticalCrop) * size.width + Math.abs(horizontalCrop) * size.height;
+ },
+
+
+ __getPosition: function(hAlign, vAlign, size, positionRect, fitRect) {
+ // All the possible configurations.
+ // Ordered as top-left, top-right, bottom-left, bottom-right.
+ var positions = [{
+ verticalAlign: 'top',
+ horizontalAlign: 'left',
+ top: positionRect.top + this.verticalOffset,
+ left: positionRect.left + this.horizontalOffset
+ }, {
+ verticalAlign: 'top',
+ horizontalAlign: 'right',
+ top: positionRect.top + this.verticalOffset,
+ left: positionRect.right - size.width - this.horizontalOffset
+ }, {
+ verticalAlign: 'bottom',
+ horizontalAlign: 'left',
+ top: positionRect.bottom - size.height - this.verticalOffset,
+ left: positionRect.left + this.horizontalOffset
+ }, {
+ verticalAlign: 'bottom',
+ horizontalAlign: 'right',
+ top: positionRect.bottom - size.height - this.verticalOffset,
+ left: positionRect.right - size.width - this.horizontalOffset
+ }];
+
+ if (this.noOverlap) {
+ // Duplicate.
+ for (var i = 0, l = positions.length; i < l; i++) {
+ var copy = {};
+ for (var key in positions[i]) {
+ copy[key] = positions[i][key];
+ }
+ positions.push(copy);
+ }
+ // Horizontal overlap only.
+ positions[0].top = positions[1].top += positionRect.height;
+ positions[2].top = positions[3].top -= positionRect.height;
+ // Vertical overlap only.
+ positions[4].left = positions[6].left += positionRect.width;
+ positions[5].left = positions[7].left -= positionRect.width;
+ }
+
+ // Consider auto as null for coding convenience.
+ vAlign = vAlign === 'auto' ? null : vAlign;
+ hAlign = hAlign === 'auto' ? null : hAlign;
+
+ var position;
+ for (var i = 0; i < positions.length; i++) {
+ var pos = positions[i];
+
+ // If both vAlign and hAlign are defined, return exact match.
+ // For dynamicAlign and noOverlap we'll have more than one candidate, so
+ // we'll have to check the croppedArea to make the best choice.
+ if (!this.dynamicAlign && !this.noOverlap &&
+ pos.verticalAlign === vAlign && pos.horizontalAlign === hAlign) {
+ position = pos;
+ break;
+ }
+
+ // Align is ok if alignment preferences are respected. If no preferences,
+ // it is considered ok.
+ var alignOk = (!vAlign || pos.verticalAlign === vAlign) &&
+ (!hAlign || pos.horizontalAlign === hAlign);
+
+ // Filter out elements that don't match the alignment (if defined).
+ // With dynamicAlign, we need to consider all the positions to find the
+ // one that minimizes the cropped area.
+ if (!this.dynamicAlign && !alignOk) {
+ continue;
+ }
+
+ position = position || pos;
+ pos.croppedArea = this.__getCroppedArea(pos, size, fitRect);
+ var diff = pos.croppedArea - position.croppedArea;
+ // Check which crops less. If it crops equally, check if align is ok.
+ if (diff < 0 || (diff === 0 && alignOk)) {
+ position = pos;
+ }
+ // If not cropped and respects the align requirements, keep it.
+ // This allows to prefer positions overlapping horizontally over the
+ // ones overlapping vertically.
+ if (position.croppedArea === 0 && alignOk) {
+ break;
+ }
+ }
+
+ return position;
+ }
+
+ };
+</script>
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/test/index.html b/catapult/third_party/polymer/components/iron-fit-behavior/test/index.html
new file mode 100644
index 00000000..fd047ea3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/test/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>iron-fit-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'iron-fit-behavior.html',
+ 'iron-fit-behavior.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/test/iron-fit-behavior.html b/catapult/third_party/polymer/components/iron-fit-behavior/test/iron-fit-behavior.html
new file mode 100644
index 00000000..7dcc15db
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/test/iron-fit-behavior.html
@@ -0,0 +1,1029 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>iron-fit-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="test-fit.html">
+
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+
+ .absolute {
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+
+ .scrolling {
+ overflow: auto;
+ }
+
+ .sized-x {
+ width: 100px;
+ }
+
+ .sized-y {
+ height: 100px;
+ }
+
+ .positioned-left {
+ position: absolute;
+ left: 100px;
+ }
+
+ .positioned-right {
+ position: absolute;
+ right: 100px;
+ }
+
+ .positioned-top {
+ position: absolute;
+ top: 100px;
+ }
+
+ .positioned-bottom {
+ position: absolute;
+ bottom: 100px;
+ }
+
+ .with-max-width {
+ max-width: 500px;
+ }
+
+ .with-max-height {
+ max-height: 500px;
+ }
+
+ .with-margin {
+ margin: 20px;
+ }
+
+ .constrain {
+ position: fixed;
+ top: 20px;
+ left: 20px;
+ width: 150px;
+ height: 150px;
+ border: 1px solid black;
+ box-sizing: border-box;
+ }
+
+ .sizer {
+ width: 9999px;
+ height: 9999px;
+ }
+
+ </style>
+
+ </head>
+ <body>
+
+ <div class="constrain"></div>
+
+ <test-fixture id="basic">
+ <template>
+ <test-fit>
+ Basic
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="absolute">
+ <template>
+ <test-fit auto-fit-on-attach class="absolute">
+ Absolutely positioned
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="sized-xy">
+ <template>
+ <test-fit auto-fit-on-attach class="sized-x sized-y">
+ Sized (x/y), auto center/center
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="sized-x">
+ <template>
+ <test-fit auto-fit-on-attach class="sized-x">
+ Sized (x), auto center/center
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="positioned-xy">
+ <template>
+ <test-fit auto-fit-on-attach class="sized-x positioned-left positioned-top">
+ Sized (x/y), positioned/positioned
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="inline-positioned-xy">
+ <template>
+ <test-fit auto-fit-on-attach class="sized-x sized-y" style="position:absolute;left:100px;top:100px;">
+ Sized (x/y), positioned/positioned
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="sectioned">
+ <template>
+ <test-fit auto-fit-on-attach class="sized-x">
+ <div>
+ Sized (x), auto center/center with scrolling section
+ </div>
+ <div class="internal"></div>
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="constrain-target">
+ <template>
+ <div class="constrain">
+ <test-fit auto-fit-on-attach class="el sized-x sized-y">
+ <div>
+ Auto center/center to parent element
+ </div>
+ </test-fit>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="offscreen-container">
+ <template>
+ <div style="position: fixed; top: -1px; left: 0;">
+ <test-fit auto-fit-on-attach class="el sized-x">
+ <div>
+ Sized (x), auto center/center, container is offscreen
+ </div>
+ </test-fit>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="scrollable">
+ <template>
+ <test-fit auto-fit-on-attach class="scrolling">
+ scrollable
+ <div class="sizer"></div>
+ </test-fit>
+ </template>
+ </test-fixture>
+
+ <template id="ipsum">
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </template>
+
+ <script>
+
+ function makeScrolling(el) {
+ el.classList.add('scrolling');
+ var template = document.getElementById('ipsum');
+ for (var i = 0; i < 20; i++) {
+ el.appendChild(template.content.cloneNode(true));
+ }
+ }
+
+ function intersects(r1, r2) {
+ return !(r2.left >= r1.right || r2.right <= r1.left || r2.top >= r1.bottom || r2.bottom <= r1.top);
+ }
+
+ suite('basic', function() {
+
+ var el;
+ setup(function() {
+ el = fixture('basic');
+ });
+
+ test('position() works without autoFitOnAttach', function() {
+ el.verticalAlign = 'top';
+ el.horizontalAlign = 'left';
+ el.position();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 0, 'top ok');
+ assert.equal(rect.left, 0, 'left ok');
+ });
+
+ test('constrain() works without autoFitOnAttach', function() {
+ el.constrain();
+ var style = getComputedStyle(el);
+ assert.equal(style.maxWidth, window.innerWidth + 'px', 'maxWidth ok');
+ assert.equal(style.maxHeight, window.innerHeight + 'px', 'maxHeight ok');
+ });
+
+ test('center() works without autoFitOnAttach', function() {
+ el.center();
+ var rect = el.getBoundingClientRect();
+ assert.closeTo(rect.left - (window.innerWidth - rect.right), 0, 5, 'centered horizontally');
+ assert.closeTo(rect.top - (window.innerHeight - rect.bottom), 0, 5, 'centered vertically');
+ });
+
+ });
+
+ suite('manual positioning', function() {
+
+ test('css positioned element is not re-positioned', function() {
+ var el = fixture('positioned-xy');
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 100, 'top is unset');
+ assert.equal(rect.left, 100, 'left is unset');
+
+ });
+
+ test('inline positioned element is not re-positioned', function() {
+ var el = fixture('inline-positioned-xy');
+ var rect = el.getBoundingClientRect();
+ // need to measure document.body here because mocha sets a min-width on html,body, and
+ // the element is positioned wrt to that by css
+ var bodyRect = document.body.getBoundingClientRect();
+ assert.equal(rect.top, 100, 'top is unset');
+ assert.equal(rect.left, 100, 'left is unset');
+
+ el.refit();
+
+ rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 100, 'top is unset after refit');
+ assert.equal(rect.left, 100, 'left is unset after refit');
+
+ });
+
+ test('position property is preserved after', function() {
+ var el = fixture('absolute');
+ assert.equal(getComputedStyle(el).position, 'absolute', 'position:absolute is preserved');
+ });
+ });
+
+ suite('fit to window', function() {
+
+ test('sized element is centered in viewport', function() {
+ var el = fixture('sized-xy');
+ var rect = el.getBoundingClientRect();
+ assert.closeTo(rect.left - (window.innerWidth - rect.right), 0, 5, 'centered horizontally');
+ assert.closeTo(rect.top - (window.innerHeight - rect.bottom), 0, 5, 'centered vertically');
+ });
+
+ test('sized element with margin is centered in viewport', function() {
+ var el = fixture('sized-xy');
+ el.classList.add('with-margin');
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.closeTo(rect.left - (window.innerWidth - rect.right), 0, 5, 'centered horizontally');
+ assert.closeTo(rect.top - (window.innerHeight - rect.bottom), 0, 5, 'centered vertically');
+ });
+
+ test('sized element with transformed parent is centered in viewport', function() {
+ var constrain = fixture('constrain-target');
+ var el = Polymer.dom(constrain).querySelector('.el');
+ var rectBefore = el.getBoundingClientRect();
+ constrain.style.transform = 'translate3d(5px, 5px, 0)';
+ el.center();
+ var rectAfter = el.getBoundingClientRect();
+ assert.equal(rectBefore.top, rectAfter.top, 'top ok');
+ assert.equal(rectBefore.bottom, rectAfter.bottom, 'bottom ok');
+ assert.equal(rectBefore.left, rectAfter.left, 'left ok');
+ assert.equal(rectBefore.right, rectAfter.right, 'right ok');
+ });
+
+ test('scrolling element is centered in viewport', function() {
+ var el = fixture('sized-x');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.closeTo(rect.left - (window.innerWidth - rect.right), 0, 5, 'centered horizontally');
+ assert.closeTo(rect.top - (window.innerHeight - rect.bottom), 0, 5, 'centered vertically');
+ });
+
+ test('scrolling element is constrained to viewport height', function() {
+ var el = fixture('sized-x');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight, 'height is less than or equal to viewport height');
+ });
+
+ test('scrolling element with offscreen container is constrained to viewport height', function() {
+ var container = fixture('offscreen-container');
+ var el = Polymer.dom(container).querySelector('.el')
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight, 'height is less than or equal to viewport height');
+ });
+
+ test('scrolling element with max-height is centered in viewport', function() {
+ var el = fixture('sized-x');
+ el.classList.add('with-max-height');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.closeTo(rect.left - (window.innerWidth - rect.right), 0, 5, 'centered horizontally');
+ assert.closeTo(rect.top - (window.innerHeight - rect.bottom), 0, 5, 'centered vertically');
+ });
+
+ test('scrolling element with max-height respects max-height', function() {
+ var el = fixture('sized-x');
+ el.classList.add('with-max-height');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= 500, 'height is less than or equal to max-height');
+ });
+
+ test('css positioned, scrolling element is constrained to viewport height (top,left)', function() {
+ var el = fixture('positioned-xy');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight - 100, 'height is less than or equal to viewport height');
+ });
+
+ test('css positioned, scrolling element is constrained to viewport height (bottom, right)', function() {
+ var el = fixture('sized-x');
+ el.classList.add('positioned-bottom');
+ el.classList.add('positioned-right');
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight - 100, 'height is less than or equal to viewport height');
+ });
+
+ test('sized, scrolling element with margin is centered in viewport', function() {
+ var el = fixture('sized-x');
+ el.classList.add('with-margin');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.closeTo(rect.left - (window.innerWidth - rect.right), 0, 5, 'centered horizontally');
+ assert.closeTo(rect.top - (window.innerHeight - rect.bottom), 0, 5, 'centered vertically');
+ });
+
+ test('sized, scrolling element is constrained to viewport height', function() {
+ var el = fixture('sized-x');
+ el.classList.add('with-margin');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight - 20 * 2, 'height is less than or equal to viewport height');
+ });
+
+ test('css positioned, scrolling element with margin is constrained to viewport height (top, left)', function() {
+ var el = fixture('positioned-xy');
+ el.classList.add('with-margin');
+ makeScrolling(el);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight - 100 - 20 * 2, 'height is less than or equal to viewport height');
+ });
+
+ test('css positioned, scrolling element with margin is constrained to viewport height (bottom, right)', function() {
+ var el = fixture('sized-x');
+ el.classList.add('positioned-bottom');
+ el.classList.add('positioned-right');
+ el.classList.add('with-margin')
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight - 100 - 20 * 2, 'height is less than or equal to viewport height');
+ });
+
+ test('scrolling sizingTarget is constrained to viewport height', function() {
+ el = fixture('sectioned');
+ var internal = Polymer.dom(el).querySelector('.internal');
+ el.sizingTarget = internal;
+ makeScrolling(internal);
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isTrue(rect.height <= window.innerHeight, 'height is less than or equal to viewport height');
+ });
+
+ test('scrolling sizingTarget preserves scrolling position', function() {
+ el = fixture('scrollable');
+ el.scrollTop = 20;
+ el.scrollLeft = 20;
+ el.refit();
+ assert.equal(el.scrollTop, 20, 'scrollTop ok');
+ assert.equal(el.scrollLeft, 20, 'scrollLeft ok');
+ });
+
+ });
+
+ suite('fit to element', function() {
+
+ test('element fits in another element', function() {
+ var constrain = fixture('constrain-target');
+ var el = Polymer.dom(constrain).querySelector('.el')
+ makeScrolling(el);
+ el.fitInto = constrain;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ var crect = constrain.getBoundingClientRect();
+ assert.isTrue(rect.height <= crect.height, 'width is less than or equal to fitInto width');
+ assert.isTrue(rect.height <= crect.height, 'height is less than or equal to fitInto height');
+ });
+
+ test('element centers in another element', function() {
+ var constrain = fixture('constrain-target');
+ var el = Polymer.dom(constrain).querySelector('.el');
+ makeScrolling(el);
+ el.fitInto = constrain;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ var crect = constrain.getBoundingClientRect();
+ assert.closeTo(rect.left - crect.left - (crect.right - rect.right), 0, 5, 'centered horizontally in fitInto');
+ assert.closeTo(rect.top - crect.top - (crect.bottom - rect.bottom), 0, 5, 'centered vertically in fitInto');
+ });
+
+ test('element with max-width centers in another element', function() {
+ var constrain = document.querySelector('.constrain');
+ var el = fixture('sized-xy');
+ el.classList.add('with-max-width');
+ el.fitInto = constrain;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ var crect = constrain.getBoundingClientRect();
+ assert.closeTo(rect.left - crect.left - (crect.right - rect.right), 0, 5, 'centered horizontally in fitInto');
+ assert.closeTo(rect.top - crect.top - (crect.bottom - rect.bottom), 0, 5, 'centered vertically in fitInto');
+ });
+
+ test('positioned element fits in another element', function() {
+ var constrain = document.querySelector('.constrain');
+ // element's positionTarget is `body`, and fitInto is `constrain`.
+ var el = fixture('sized-xy');
+ el.verticalAlign = 'top';
+ el.horizontalAlign = 'left';
+ el.fitInto = constrain;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ var crect = constrain.getBoundingClientRect();
+ assert.equal(rect.top, crect.top, 'top ok');
+ assert.equal(rect.left, crect.left, 'left ok');
+ });
+
+ });
+
+ suite('horizontal/vertical align', function() {
+ var parent, parentRect;
+ var el, elRect;
+ var fitRect = {
+ left: 0,
+ top: 0,
+ right: window.innerWidth,
+ bottom: window.innerHeight,
+ width: window.innerWidth,
+ height: window.innerHeight
+ };
+
+ setup(function() {
+ parent = fixture('constrain-target');
+ parentRect = parent.getBoundingClientRect();
+ el = Polymer.dom(parent).querySelector('.el');
+ elRect = el.getBoundingClientRect();
+ });
+
+ test('intersects works', function() {
+ var base = {top: 0, bottom: 1, left:0, right: 1};
+ assert.isTrue(intersects(base, base), 'intersects itself');
+ assert.isFalse(intersects(base, {top:1, bottom: 2, left: 0, right: 1}), 'no intersect on edge');
+ assert.isFalse(intersects(base, {top:-2, bottom: -1, left: 0, right: 1}), 'no intersect on edge (negative values)');
+ assert.isFalse(intersects(base, {top:2, bottom: 3, left: 0, right: 1}), 'no intersect');
+ });
+
+ suite('when verticalAlign is top', function() {
+ test('element is aligned to the positionTarget top', function() {
+ el.verticalAlign = 'top';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, parentRect.top, 'top ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('element is aligned to the positionTarget top without overlapping it', function() {
+ // Allow enough space on the parent's bottom & right.
+ parent.style.width = '10px';
+ parent.style.height = '10px';
+ parentRect = parent.getBoundingClientRect();
+ el.verticalAlign = 'top';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('element margin is considered as offset', function() {
+ el.verticalAlign = 'top';
+ el.style.marginTop = '10px';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, parentRect.top + 10, 'top ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+
+ el.style.marginTop = '-10px';
+ el.refit();
+ rect = el.getBoundingClientRect();
+ assert.equal(rect.top, parentRect.top - 10, 'top ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('verticalOffset is applied', function() {
+ el.verticalAlign = 'top';
+ el.verticalOffset = 10;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, parentRect.top + 10, 'top ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('element is kept in viewport', function() {
+ el.verticalAlign = 'top';
+ // Make it go out of screen
+ el.verticalOffset = -1000;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 0, 'top in viewport');
+ assert.isTrue(rect.height < elRect.height, 'reduced size');
+ });
+
+ test('negative verticalOffset does not crop element', function() {
+ // Push to the bottom of the screen.
+ parent.style.top = (window.innerHeight - 50) +'px';
+ el.verticalAlign = 'top';
+ el.verticalOffset = -10;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, window.innerHeight - 60, 'top ok');
+ assert.equal(rect.bottom, window.innerHeight, 'bottom ok');
+ });
+
+ test('max-height is updated', function() {
+ parent.style.top = '-10px';
+ el.verticalAlign = 'top';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 0, 'top ok');
+ assert.isBelow(rect.height, elRect.height, 'height ok');
+ });
+
+ test('min-height is preserved: element is displayed even if partially', function() {
+ parent.style.top = '-10px';
+ el.verticalAlign = 'top';
+ el.style.minHeight = elRect.height + 'px';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 0, 'top ok');
+ assert.equal(rect.height, elRect.height, 'min-height ok');
+ assert.isTrue(intersects(rect, fitRect), 'partially visible');
+ });
+
+ test('dynamicAlign will prefer bottom align if it minimizes the cropping', function() {
+ parent.style.top = '-10px';
+ parentRect = parent.getBoundingClientRect();
+ el.verticalAlign = 'top';
+ el.dynamicAlign = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.bottom, parentRect.bottom, 'bottom ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+ });
+
+ suite('when verticalAlign is bottom', function() {
+ test('element is aligned to the positionTarget bottom', function() {
+ el.verticalAlign = 'bottom';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.bottom, parentRect.bottom, 'bottom ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('element is aligned to the positionTarget bottom without overlapping it', function() {
+ el.verticalAlign = 'bottom';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('element margin is considered as offset', function() {
+ el.verticalAlign = 'bottom';
+ el.style.marginBottom = '10px';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.bottom, parentRect.bottom - 10, 'bottom ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+
+ el.style.marginBottom = '-10px';
+ el.refit();
+ rect = el.getBoundingClientRect();
+ assert.equal(rect.bottom, parentRect.bottom + 10, 'bottom ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('verticalOffset is applied', function() {
+ el.verticalAlign = 'bottom';
+ el.verticalOffset = 10;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.bottom, parentRect.bottom - 10, 'bottom ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('element is kept in viewport', function() {
+ el.verticalAlign = 'bottom';
+ // Make it go out of screen
+ el.verticalOffset = 1000;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 0, 'top in viewport');
+ assert.isTrue(rect.height < elRect.height, 'reduced size');
+ });
+
+ test('element max-height is updated', function() {
+ parent.style.top = (100 - parentRect.height) + 'px';
+ el.verticalAlign = 'bottom';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.bottom, 100, 'bottom ok');
+ assert.equal(rect.height, 100, 'height ok');
+ });
+
+ test('min-height is preserved: element is displayed even if partially', function() {
+ parent.style.top = (elRect.height - 10 - parentRect.height) + 'px';
+ el.verticalAlign = 'bottom';
+ el.style.minHeight = elRect.height + 'px';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, 0, 'top ok');
+ assert.equal(rect.height, elRect.height, 'min-height ok');
+ assert.isTrue(intersects(rect, fitRect), 'partially visible');
+ });
+
+ test('dynamicAlign will prefer top align if it minimizes the cropping', function() {
+ parent.style.top = (window.innerHeight - elRect.height) + 'px';
+ parentRect = parent.getBoundingClientRect();
+ el.verticalAlign = 'bottom';
+ el.dynamicAlign = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, parentRect.top, 'top ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+ });
+
+ suite('when verticalAlign is auto', function() {
+ test('element is aligned to the positionTarget top', function() {
+ el.verticalAlign = 'auto';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.top, parentRect.top, 'auto aligned to top');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('element is aligned to the positionTarget top without overlapping it', function() {
+ // Allow enough space on the parent's bottom & right.
+ parent.style.width = '10px';
+ parent.style.height = '10px';
+ parentRect = parent.getBoundingClientRect();
+ el.verticalAlign = 'auto';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ });
+
+ test('bottom is preferred to top if it diminishes the cropped area', function() {
+ // This would cause a cropping of the element, so it should automatically
+ // align to the bottom to avoid it.
+ parent.style.top = '-10px';
+ parentRect = parent.getBoundingClientRect();
+ el.verticalAlign = 'auto';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.bottom, parentRect.bottom, 'auto aligned to bottom');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ test('bottom is preferred to top if it diminishes the cropped area, without overlapping positionTarget', function() {
+ // This would cause a cropping of the element, so it should automatically
+ // align to the bottom to avoid it.
+ parent.style.top = '-10px';
+ parentRect = parent.getBoundingClientRect();
+ el.verticalAlign = 'auto';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ });
+ });
+
+ suite('when horizontalAlign is left', function() {
+ test('element is aligned to the positionTarget left', function() {
+ el.horizontalAlign = 'left';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left, 'left ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('element is aligned to the positionTarget left without overlapping it', function() {
+ // Make space at the parent's right.
+ parent.style.width = '10px';
+ parentRect = parent.getBoundingClientRect();
+ el.horizontalAlign = 'left';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('element margin is considered as offset', function() {
+ el.horizontalAlign = 'left';
+ el.style.marginLeft = '10px';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left + 10, 'left ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+
+ el.style.marginLeft = '-10px';
+ el.refit();
+ rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left - 10, 'left ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('horizontalOffset is applied', function() {
+ el.horizontalAlign = 'left';
+ el.horizontalOffset = 10;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left + 10, 'left ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('element is kept in viewport', function() {
+ el.horizontalAlign = 'left';
+ // Make it go out of screen.
+ el.horizontalOffset = -1000;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, 0, 'left in viewport');
+ assert.isTrue(rect.width < elRect.width, 'reduced size');
+ });
+
+ test('negative horizontalOffset does not crop element', function() {
+ // Push to the bottom of the screen.
+ parent.style.left = (window.innerWidth - 50) +'px';
+ el.horizontalAlign = 'left';
+ el.horizontalOffset = -10;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, window.innerWidth - 60, 'left ok');
+ assert.equal(rect.right, window.innerWidth, 'right ok');
+ });
+
+ test('element max-width is updated', function() {
+ parent.style.left = '-10px';
+ el.horizontalAlign = 'left';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, 0, 'left ok');
+ assert.isBelow(rect.width, elRect.width, 'width ok');
+ });
+
+ test('min-width is preserved: element is displayed even if partially', function() {
+ parent.style.left = '-10px';
+ el.style.minWidth = elRect.width + 'px';
+ el.horizontalAlign = 'left';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, 0, 'left ok');
+ assert.equal(rect.width, elRect.width, 'min-width ok');
+ assert.isTrue(intersects(rect, fitRect), 'partially visible');
+ });
+
+ test('dynamicAlign will prefer right align if it minimizes the cropping', function() {
+ parent.style.left = '-10px';
+ parentRect = parent.getBoundingClientRect();
+ el.horizontalAlign = 'left';
+ el.dynamicAlign = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right, 'right ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ });
+
+ suite('when horizontalAlign is right', function() {
+ test('element is aligned to the positionTarget right', function() {
+ el.horizontalAlign = 'right';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right, 'right ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('element is aligned to the positionTarget right without overlapping it', function() {
+ // Make space at the parent's left.
+ parent.style.left = elRect.width + 'px';
+ parentRect = parent.getBoundingClientRect();
+ el.horizontalAlign = 'right';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('element margin is considered as offset', function() {
+ el.horizontalAlign = 'right';
+ el.style.marginRight = '10px';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right - 10, 'right ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+
+ el.style.marginRight = '-10px';
+ el.refit();
+ rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right + 10, 'right ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('horizontalOffset is applied', function() {
+ el.horizontalAlign = 'right';
+ el.horizontalOffset = 10;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right - 10, 'right ok');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('element is kept in viewport', function() {
+ el.horizontalAlign = 'right';
+ // Make it go out of screen.
+ el.horizontalOffset = 1000;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, 0, 'left in viewport');
+ assert.isTrue(rect.width < elRect.width, 'reduced width');
+ });
+
+ test('element max-width is updated', function() {
+ parent.style.left = (100 - parentRect.width) + 'px';
+ el.horizontalAlign = 'right';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, 100, 'right ok');
+ assert.equal(rect.width, 100, 'width ok');
+ });
+
+ test('min-width is preserved: element is displayed even if partially', function() {
+ parent.style.left = (elRect.width - 10 - parentRect.width) + 'px';
+ el.horizontalAlign = 'right';
+ el.style.minWidth = elRect.width + 'px';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, 0, 'left ok');
+ assert.equal(rect.width, elRect.width, 'min-width ok');
+ assert.isTrue(intersects(rect, fitRect), 'partially visible');
+ });
+
+ test('dynamicAlign will prefer left align if it minimizes the cropping', function() {
+ parent.style.left = (window.innerWidth - elRect.width) + 'px';
+ parentRect = parent.getBoundingClientRect();
+ el.horizontalAlign = 'right';
+ el.dynamicAlign = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left, 'left ok');
+ assert.equal(rect.height, elRect.height, 'no cropping');
+ });
+
+ });
+
+ suite('when horizontalAlign is auto', function() {
+ test('element is aligned to the positionTarget left', function() {
+ el.horizontalAlign = 'auto';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left, 'auto aligned to left');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('element is aligned to the positionTarget left without overlapping positionTarget', function() {
+ // Make space at the parent's left.
+ parent.style.left = elRect.width + 'px';
+ parentRect = parent.getBoundingClientRect();
+ el.horizontalAlign = 'auto';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ });
+
+ test('right is preferred to left if it diminishes the cropped area', function() {
+ // This would cause a cropping of the element, so it should automatically
+ // align to the right to avoid it.
+ parent.style.left = '-10px';
+ parentRect = parent.getBoundingClientRect();
+ el.horizontalAlign = 'auto';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right, 'auto aligned to right');
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ });
+
+ test('right is preferred to left if it diminishes the cropped area, without overlapping positionTarget', function() {
+ // Make space at the parent's right.
+ parent.style.width = '10px';
+ parentRect = parent.getBoundingClientRect();
+ el.horizontalAlign = 'auto';
+ el.noOverlap = true;
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.width, elRect.width, 'no cropping');
+ assert.isFalse(intersects(rect, parentRect), 'no overlap');
+ });
+ });
+
+ suite('prefer horizontal overlap to vertical overlap', function() {
+ setup(function() {
+ el.noOverlap = true;
+ el.dynamicAlign = true;
+ // Make space around the positionTarget.
+ parent.style.top = elRect.height + 'px';
+ parent.style.left = elRect.width + 'px';
+ parent.style.width = '10px';
+ parent.style.height = '10px';
+ parentRect = parent.getBoundingClientRect();
+ });
+
+ test('top-left aligns to target bottom-left', function() {
+ el.verticalAlign = 'top';
+ el.horizontalAlign = 'left';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left, 'left ok');
+ assert.equal(rect.top, parentRect.bottom, 'top ok');
+ });
+
+ test('top-right aligns to target bottom-right', function() {
+ el.verticalAlign = 'top';
+ el.horizontalAlign = 'right';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right, 'right ok');
+ assert.equal(rect.top, parentRect.bottom, 'top ok');
+ });
+
+ test('bottom-left aligns to target top-left', function() {
+ el.verticalAlign = 'bottom';
+ el.horizontalAlign = 'left';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.left, parentRect.left, 'left ok');
+ assert.equal(rect.bottom, parentRect.top, 'bottom ok');
+ });
+
+ test('bottom-right aligns to target top-right', function() {
+ el.verticalAlign = 'bottom';
+ el.horizontalAlign = 'right';
+ el.refit();
+ var rect = el.getBoundingClientRect();
+ assert.equal(rect.right, parentRect.right, 'right ok');
+ assert.equal(rect.bottom, parentRect.top, 'bottom ok');
+ });
+
+ });
+
+ });
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-fit-behavior/test/test-fit.html b/catapult/third_party/polymer/components/iron-fit-behavior/test/test-fit.html
new file mode 100644
index 00000000..80626087
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-fit-behavior/test/test-fit.html
@@ -0,0 +1,41 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-fit-behavior.html">
+
+<dom-module id="test-fit">
+ <template>
+ <style>
+ :host {
+ display: block;
+ background: black;
+ color: white;
+ padding: 8px;
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'test-fit',
+
+ behaviors: [
+ Polymer.IronFitBehavior
+ ]
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/.bower.json b/catapult/third_party/polymer/components/iron-flex-layout/.bower.json
new file mode 100644
index 00000000..07a77908
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/.bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "iron-flex-layout",
+ "version": "1.3.7",
+ "description": "Provide flexbox-based layouts",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "layout"
+ ],
+ "main": "iron-flex-layout.html",
+ "private": true,
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-flex-layout.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "pages": {
+ "Flexbox layout guide": "GUIDE.md"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/polymerelements/iron-flex-layout",
+ "_release": "1.3.7",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.3.7",
+ "commit": "2a9c084c933e87469824b69f3faadfcba12efbcd"
+ },
+ "_source": "https://github.com/polymerelements/iron-flex-layout.git",
+ "_target": "^1.0.0",
+ "_originalSource": "polymerelements/iron-flex-layout"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-flex-layout/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..b79eca40
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-flex-layout/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/.gitignore b/catapult/third_party/polymer/components/iron-flex-layout/.gitignore
new file mode 100644
index 00000000..1eb1fa5e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/.gitignore
@@ -0,0 +1,2 @@
+bower_components
+
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/.travis.yml b/catapult/third_party/polymer/components/iron-flex-layout/.travis.yml
new file mode 100644
index 00000000..6bd14cba
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ idlCNYtNQ7rLGCSl2NuZ++XZEwofpojkakXhL8m34pCX1ZwhGRprHsvNE7h7tz8SIV+mUjuUT8DaM7rlgZ+bLyw/Hq7G15DdmU5puUsCVibFFBBEhh82lxkdK/Rlfo+ytt0MFsbTJljEoqTSbnL0fSeC0cN/7wf6yEQs/+mTR0I=
+ - secure: >-
+ i/rAHa9rxszmbE+RbQ2b7ln6OYzrcoUssdPk36lNeJk/B1HjBS9cfg1PMwSAw+t6yvAN3I0fbOOl4dlGB1K5T4kAtmcAcC3fbptJL08IvK9K15pz/rMEzIBWLXj9oVJliwBZR2GFH+5zv6aFdEwQxh1leBnHQwmKgl24QX8XGO4=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-flex-layout/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/GUIDE.md b/catapult/third_party/polymer/components/iron-flex-layout/GUIDE.md
new file mode 100644
index 00000000..67a9e309
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/GUIDE.md
@@ -0,0 +1,1265 @@
+## Overview
+
+The `iron-flex-layout` component provides simple ways to use [CSS flexible box layout](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes), also known as _flexbox_. This component provides two different ways to use flexbox:
+
+* Layout classes. The layout class stylesheet provides a simple set of class-based flexbox rules. Layout classes
+ let you specify layout properties directly in markup.
+
+* Custom CSS mixins. The mixin stylesheet includes custom CSS mixins that can be applied
+ inside a CSS rule using the `@apply` function.
+
+Using the classes or CSS mixins is largely a matter of preference. The following sections discuss
+how to use the each of the stylesheets.
+
+> <b>Note:</b> Before using either of these stylesheets, it's helpful to be familiar with the basics
+of flexbox layout. Chris Coyier's [A Complete Guide to Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) is a
+good primer.</aside>
+
+### Using layout classes
+
+To use layout classes import the `iron-flex-layout-classes` file. You
+must do this in any element that uses any of the `iron-flex-layout` styles.
+```html
+<link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout-classes.html">
+```
+
+Then include the module(s) that you need:
+```html
+<!-- include classes in the main document -->
+<style is="custom-style" include="iron-flex iron-flex-alignment">
+```
+
+or:
+```html
+<!-- import classes in an element -->
+<style include="iron-flex iron-flex-alignment">
+```
+
+Then simply apply the classes to any element.
+```html
+<div class="layout horizontal wrap">
+```
+
+Many of the layout rules involve combinations of multiple classes (such as `layout horizontal wrap` above),
+and will need a combination of modules.
+The order in which the classes are specified doesn't matter, so `layout horizontal` and `horizontal layout`
+are equivalent.
+
+There are 5 modules available:
+- `iron-flex`. Basic flex layouts.
+- `iron-flex-reverse`. Reverse flexbox layouts.
+- `iron-flex-alignment`. Main axis, cross axis and self alignment.
+- `iron-flex-factors`. All the available flex factors.
+- `iron-positioning`. Generic, non-flexbox positioning helpers.
+
+**Example: using classes in the main document**
+```html
+<head>
+
+ ...
+
+ <link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout-classes.html">
+
+ ...
+
+ <!-- main document -- include the module you need in a custom-style element -->
+ <style is="custom-style" include="iron-flex"></style>
+
+</head>
+<body>
+
+ <div class="layout horizontal">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+ </div>
+
+</body>
+```
+**Example: using classes in a Polymer element**
+```html
+<link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout-classes.html">
+
+ ...
+
+<dom-module id="mixin-demo">
+
+ <!-- inside an element -- include the module you need in a standard style element -->
+ <style include="iron-flex"></style>
+
+ <template>
+ <div class="layout horizontal">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+ </div>
+ </template>
+
+ <script>
+ Polymer({ is: 'mixin-demo' });
+ </script>
+
+</dom-module>
+```
+
+It's important to note that unlike the previous layout class stylesheets
+(found in `/classes/iron-flex-layout.html`), the new version does not use the `/deep/`
+combinator: it does not work across local DOM boundaries,
+and the modules must be imported into each scope where they're used.
+
+### Using layout mixins
+
+Custom mixins can be applied inside a Polymer
+custom element's stylesheet, **or** inside a `custom-style` stylesheet to apply styles to the
+main document. (They cannot be applied in the main document without a `custom-style` stylesheet.)
+
+**Example: using mixins in the main document**
+```html
+<head>
+
+ ...
+
+ <link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout.html">
+
+ ...
+
+ <!-- main document -- apply mixins in a custom-style element -->
+ <style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-wrap);
+ }
+ </style>
+
+</head>
+<body>
+
+ <div class="container">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+ </div>
+
+</body>
+```
+**Example: using mixins in a Polymer element**
+```html
+<link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout.html">
+
+ ...
+
+<dom-module id="mixin-demo">
+
+ <!-- inside an element -- apply mixins in a standard style element -->
+ <style>
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-wrap);
+ }
+ </style>
+
+ <template>
+ <div class="container">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+ </div>
+ </template>
+
+ <script>
+ Polymer({ is: 'mixin-demo' });
+ </script>
+
+</dom-module>
+```
+
+In general the mixins require a little more code to use, but they can be preferable if you
+don't want to use the classes, or if you want to switch layouts based on a media query.
+
+Custom CSS properties and mixins are features provided by the Polymer library.
+See [Cross-scope styling](https://www.polymer-project.org/1.0/docs/devguide/styling.html#xscope-styling)
+in the Polymer developer guide.
+
+## Horizontal and vertical layout
+
+Create a flex container that lays out its children vertically or horizontally.
+
+Class | Mixin | Result
+:-|:-|:-
+<code>layout horizontal</code>| <code>--layout-horizontal</code> | Horizontal layout container.
+<code>layout vertical</code> | <code>--layout-vertical</code> | Vertical layout container.
+
+The classes listed here are included in the `iron-flex` module of the `iron-flex-layout-classes` file.
+
+**Example: classes**
+```html
+<div class="layout horizontal">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+</div>
+```
+
+**Example: mixins**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ }
+</style>
+
+<div class="container">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+</div>
+```
+
+### Flexible children
+
+Children of a flex container can use flex to control their own sizing.
+
+Class | Mixin | Result
+:-|:-|:-
+<code>flex</code>| <code>--layout-flex</code> | Expand the child to fill available space in the main axis.
+<code>flex-<var>ratio</var></code>| <code>--layout-flex-<var>ratio</var></code> | Assign a flex ratio of 1 to 12.
+<code>flex-none</code>| <code>--layout-flex-none</code> | Don't flex the child.
+<code>flex-auto</code>| <code>--layout-flex-auto</code> | Sets flex `flex-basis` to `auto` and `flex-grow` and `flex-shrink` to 1.
+
+The classes listed here are included in the `iron-flex` module of the `iron-flex-layout-classes` file.
+
+**Example: classes**
+```html
+<div class="horizontal layout">
+ <div>Alpha</div>
+ <div class="flex">Beta (flex)</div>
+ <div>Gamma</div>
+</div>
+```
+
+**Example: mixins**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ }
+ .flexchild {
+ @apply(--layout-flex);
+ }
+</style>
+
+<div class="container">
+<div>Alpha</div>
+ <div class="flexchild">Beta (flex)</div>
+ <div>Gamma</div>
+</div>
+```
+
+#### Flexible children in vertical layouts
+
+The same rules can be used for children in vertical layouts.
+
+**Example: classes**
+```html
+<div class="vertical layout" style="height:250px">
+ <div>Alpha</div>
+ <div class="flex">Beta (flex)</div>
+ <div>Gamma</div>
+</div>
+```
+
+**Example: mixins**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-vertical);
+ }
+ .flexchild {
+ @apply(--layout-flex);
+ }
+</style>
+
+<div class="container" style="height: 250px">
+ <div>One</div>
+ <div class="flexchild">Two</div>
+ <div>Three</div>
+</div>
+```
+
+> **Note**: for vertical layouts, the container needs to have a height for the
+children to flex correctly.
+
+#### Flex ratios
+
+Children elements can be told to take up more space by including a "flex ratio"
+from 1 to 12. This is equivalent to specifying the CSS `flex-grow` property.
+
+For example, the following examples make "Gamma" 2x larger than "Beta" and "Alpha" 3x larger, use
+`flex-2` and `flex-3`, respectively.
+
+The classes listed here are included in the `iron-flex-factors` module of the `iron-flex-layout-classes` file.
+
+**Example: classes**
+```html
+<div class="horizontal layout demo">
+ <div class="flex-3">Alpha</div>
+ <div class="flex">Beta</div>
+ <div class="flex-2">Gamma</div>
+</div>
+```
+
+**Example: mixins**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ }
+ .flexchild {
+ @apply(--layout-flex)
+ }
+ .flex2child {
+ @apply(--layout-flex-2);
+ }
+ .flex3child {
+ @apply(--layout-flex-3);
+ }
+</style>
+
+<div class="container">
+ <div class="flex3child">One</div>
+ <div class="flexchild">Two</div>
+ <div class="flex2child">Three</div>
+</div>
+```
+
+### Cross-axis alignment
+
+By default, children stretch to fit the cross-axis (e.g. _vertical_ stretching in a _horizontal_ layout).
+
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal layout" style="height: 154px">
+ <div>Stretch Fill</div>
+</div>
+```
+
+Center _across_ the main axis (e.g. _vertical_ centering elements in a _horizontal_ layout)
+by adding the `center` class or applying the `--layout-center` mixin.
+
+**Example: classes, cross-axis center**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal layout center" style="height: 154px">
+ <div>Center</div>
+</div>
+```
+
+**Example: mixins, cross-axis center**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ height: 154px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-center);
+ }
+</style>
+
+<div class="container" style="height: 154px">
+ <div>Center</div>
+</div>
+```
+
+You can also position at the top/bottom (or left/right in `vertical` layouts) using the `start` or `end`
+classes, or by applying the `--layout-start` or `--layout-end` mixins.
+
+
+**Example: classes, cross-axis start**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal layout start" style="height: 154px">
+ <div>start</div>
+</div>
+```
+
+**Example: mixins, cross-axis start**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-start);
+ }
+</style>
+
+<div class="container" style="height: 154px">
+ <div>start</div>
+</div>
+```
+
+**Example: classes, cross-axis end**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal layout end" style="height: 154px">
+ <div>end</div>
+</div>
+```
+
+**Example: mixins, cross-axis end**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-end);
+ }
+</style>
+
+<div class="container" style="height: 154px">
+ <div>end</div>
+</div>
+```
+
+### Justification
+
+Justifying aligns contents along the **main axis**. Justify the layout
+by specifying one of the following.
+
+
+Class | Mixin | Result
+:-|:-|:-
+`start-justified`| <code>--layout-start-justified</code> | Aligns contents at the start of the main axis.
+`center-justified` | <code>--layout-center-justified</code> | Centers contents along the main axis.
+`end-justified` | <code>--layout-end-justified</code> | Aligns contents to the end of the main axis.
+`justified` | <code>--layout-justified</code> | Aligns contents with equal spaces between children.
+`around-justified` | <code>--layout-around-justified</code> | Aligns contents with equal spaces arround children.
+
+The classes listed here are included in the `iron-flex-alignment` module of the `iron-flex-layout-classes` file.
+
+**Example: classes, start justified**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal start-justified layout">
+ <div>start-justified</div>
+</div>
+```
+
+**Example: mixins, center justified**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-center-justified);
+ }
+</style>
+
+<div class="container">
+ <div>center-justified</div>
+</div>
+```
+
+**Example: classes, end justified**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal end-justified layout">
+ <div>end-justified</div>
+</div>
+```
+
+**Example: mixins, equal space between elements**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-justified);
+ }
+</style>
+
+<div class="container">
+ <div>justified</div>
+ <div>justified</div>
+ <div>justified</div>
+</div>
+```
+
+**Example: classes, equal space around each element**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal around-justified layout">
+ <div>around-justified</div>
+ <div>around-justified</div>
+</div>
+```
+
+## Self alignment
+
+Alignment can also be set per-child (instead of using the layout container's rules).
+
+Class | Mixin | Result
+:-|:-|:-
+`self-start`| <code>--layout-self-start</code> | Aligns the child at the start of the cross-axis.
+`self-center` | <code>--layout-self-center</code> | Centers the child along the cross-axis.
+`self-end` | <code>--layout-self-end</code> | Aligns the child at the end of the cross-axis.
+`self-stretch` | <code>--layout-self-stretch</code> | Stretches the child to fit the cross-axis.
+
+**Example: classes**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal layout" style="height: 120px;">
+ <div class="flex self-start">Alpha</div>
+ <div class="flex self-center">Beta</div>
+ <div class="flex self-end">Gamma</div>
+ <div class="flex self-stretch">Delta</div>
+</div>
+```
+
+**Example: mixins**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal);
+ @apply(--layout-justified);
+ height: 120px;
+ }
+ .container div {
+ @apply(--layout-flex);
+ }
+ .child1 {
+ @apply(--layout-self-start);
+ }
+ .child2 {
+ @apply(--layout-self-center);
+ }
+ .child3 {
+ @apply(--layout-self-end);
+ }
+ .child4 {
+ @apply(--layout-self-stretch);
+ }
+</style>
+<div class="container">
+ <div class="child1">Alpha</div>
+ <div class="child2">Beta</div>
+ <div class="child3">Gamma</div>
+ <div class="child4">Delta</div>
+</div>
+```
+
+> <b>Note:</b> The <code>flex</code> class
+(and <code>--layout-flex</code> mixin) shown in these examples is
+added for the demo and not required for self-alignment.
+
+
+## Wrapping
+
+Wrapped layouts can be enabled with the `wrap` class or `--layout-wrap` mixin.
+
+**Example: classes**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="horizontal layout wrap" style="width: 220px">
+ <div>Alpha</div>
+ <div>Beta</div>
+ <div>Gamma</div>
+ <div>Delta</div>
+</div>
+```
+
+## Reversed layouts
+
+Layout direction can be mirrored using the following rules:
+
+Class | Mixin | Result
+:-|:-|:-
+<code>layout horizontal-reverse</code>| <code>--layout-horizontal-reverse</code> | Horizontal layout with children laid out in reverse order (last-to-first).
+<code>layout vertical-reverse</code> | <code>--layout-vertical-reverse</code> | Vertical layout with children laid out in reverse order.
+<code>layout wrap-reverse</code> | <code>--layout-wrap-reverse</code> | Wrap layout with wrapped rows placed in the reverse order (for example, in a vertical layout, the second row is placed above the first row, instead of below).
+
+The classes listed here are included in the `iron-flex-reverse` module of the `iron-flex-layout-classes` file.
+
+**Example: mixins**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment iron-flex-reverse"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<style is="custom-style">
+ .container {
+ @apply(--layout-horizontal-reverse);
+ }
+</style>
+
+<div class="container">
+ <div>Alpha</div>
+ <div>Beta</div>
+ <div>Gamma</div>
+ <div>Delta</div>
+</div>
+```
+
+## Full bleed &lt;body>
+
+It's common to want the entire `<body>` to fit to the viewport. By themselves, Polymer's layout features on
+`<body>` don't achieve the result. You can make `<body>` take up the entire viewport by adding the `fullbleed` class:
+
+```html
+<body class="fullbleed vertical layout">
+ <div class="flex">Fitting a fullbleed body.</div>
+</body>
+```
+
+This removes its margins and maximizes its height to the viewport. There is no equivalent mixin, but the same result can
+be achieved in CSS very simply:
+```css
+body {
+ margin: 0;
+ height: 100vh;
+}
+```
+
+This class is included in the `iron-positioning` module of the `iron-flex-layout-classes` file.
+
+Note that the `fullbleed` class **only works on the `<body>` tag.** This is the only rule in the
+stylesheet that is scoped to a particular tag.
+
+
+## General purpose rules
+
+Polymer also includes other general purpose rules for basic positioning:
+
+Class | Mixin | Result
+:-|:-|:-
+`block`| `--layout-block` | Assigns `display: block`
+`invisible` | `--layout-invisible` | Assigns `visibility: hidden`
+`relative` | `--layout-relative` | Assigns `position: relative`
+`fit` | `--layout-fit` | Sets `position: absolute` and sets `top:0;right:0;bottom:0;left:0;` (aka "trbl fitting").
+
+The classes listed here are included in the `iron-positioning` module of the `iron-flex-layout-classes` file.
+
+> <b>Note:</b>When using `fit` layout, the element must have an ancestor with fixed size and `position: relative` layout
+to fit inside of.
+
+
+**Example: classes**
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment iron-positioning"></style>
+ <style>
+ :host {
+ display: block;
+ background: #ccc;
+ }
+
+ .demo {
+ background-color: white;
+ margin: 12px;
+ padding: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="demo">Before <span>[A Span]</span> After</div>
+<div class="demo">Before <span class="block">[A Block Span]</span> After</div>
+<div class="demo">Before invisible span <span class="invisible">Not displayed</span> After invisible span</div>
+<div class="relative" style="height: 100px;">
+ <div class="fit" style="background-color: #000;color: white">Fit</div>
+</div>
+```
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/README.md b/catapult/third_party/polymer/components/iron-flex-layout/README.md
new file mode 100644
index 00000000..d5aac2fb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/README.md
@@ -0,0 +1,67 @@
+[![Build status](https://travis-ci.org/PolymerElements/iron-flex-layout.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-flex-layout)
+[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://beta.webcomponents.org/element/PolymerElements/iron-flex-layout)
+
+## &lt;iron-flex-layout&gt;
+
+The `<iron-flex-layout>` component provides simple ways to use
+[CSS flexible box layout](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes),
+also known as flexbox. This component provides two different ways to use flexbox:
+
+1. [Layout classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/iron-flex-layout-classes.html).
+The layout class stylesheet provides a simple set of class-based flexbox rules, that
+let you specify layout properties directly in markup. You must include this file
+in every element that needs to use them.
+
+Sample use:
+
+<!--
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <dom-module id="demo-element">
+ <template>
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .container, .layout {
+ background-color: #ccc;
+ padding: 4px;
+ }
+
+ .container div, .layout div {
+ background-color: white;
+ padding: 12px;
+ margin: 4px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+ <script>Polymer({is: "demo-element"});</script>
+ </dom-module>
+ <demo-element></demo-element>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div class="layout horizontal layout-start" style="height: 154px">
+ <div>cross axis start alignment</div>
+</div>
+```
+
+1. [Custom CSS mixins](https://github.com/PolymerElements/iron-flex-layout/blob/master/iron-flex-layout.html).
+The mixin stylesheet includes custom CSS mixins that can be applied inside a CSS rule using the `@apply` function.
+
+
+
+Please note that the old [/deep/ layout classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/classes)
+are deprecated, and should not be used. To continue using layout properties
+directly in markup, please switch to using the new `dom-module`-based
+[layout classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/iron-flex-layout-classes.html).
+Please note that the new version does not use `/deep/`, and therefore requires you
+to import the `dom-modules` in every element that needs to use them.
+
+A complete [guide](https://elements.polymer-project.org/guides/flex-layout) to `<iron-flex-layout>` is available.
+
+
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/bower.json b/catapult/third_party/polymer/components/iron-flex-layout/bower.json
new file mode 100644
index 00000000..7b6b1a2c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/bower.json
@@ -0,0 +1,34 @@
+{
+ "name": "iron-flex-layout",
+ "version": "1.3.7",
+ "description": "Provide flexbox-based layouts",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "layout"
+ ],
+ "main": "iron-flex-layout.html",
+ "private": true,
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-flex-layout.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "pages": {
+ "Flexbox layout guide": "GUIDE.md"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/classes/iron-flex-layout.html b/catapult/third_party/polymer/components/iron-flex-layout/classes/iron-flex-layout.html
new file mode 100644
index 00000000..5fa7ce65
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/classes/iron-flex-layout.html
@@ -0,0 +1,317 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="iron-shadow-flex-layout.html">
+
+<script>
+ console.warn('This file is deprecated. Please use `iron-flex-layout/iron-flex-layout-classes.html`, and one of the specific dom-modules instead');
+</script>
+
+<style>
+
+ /*******************************
+ Flex Layout
+ *******************************/
+
+ .layout.horizontal,
+ .layout.horizontal-reverse,
+ .layout.vertical,
+ .layout.vertical-reverse {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ }
+
+ .layout.inline {
+ display: -ms-inline-flexbox;
+ display: -webkit-inline-flex;
+ display: inline-flex;
+ }
+
+ .layout.horizontal {
+ -ms-flex-direction: row;
+ -webkit-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .layout.horizontal-reverse {
+ -ms-flex-direction: row-reverse;
+ -webkit-flex-direction: row-reverse;
+ flex-direction: row-reverse;
+ }
+
+ .layout.vertical {
+ -ms-flex-direction: column;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ }
+
+ .layout.vertical-reverse {
+ -ms-flex-direction: column-reverse;
+ -webkit-flex-direction: column-reverse;
+ flex-direction: column-reverse;
+ }
+
+ .layout.wrap {
+ -ms-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+ }
+
+ .layout.no-wrap {
+ -ms-flex-wrap: nowrap;
+ -webkit-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ .layout.wrap-reverse {
+ -ms-flex-wrap: wrap-reverse;
+ -webkit-flex-wrap: wrap-reverse;
+ flex-wrap: wrap-reverse;
+ }
+
+ .flex-auto {
+ -ms-flex: 1 1 auto;
+ -webkit-flex: 1 1 auto;
+ flex: 1 1 auto;
+ }
+
+ .flex-none {
+ -ms-flex: none;
+ -webkit-flex: none;
+ flex: none;
+ }
+
+ .flex,
+ .flex-1 {
+ -ms-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ }
+
+ .flex-2 {
+ -ms-flex: 2;
+ -webkit-flex: 2;
+ flex: 2;
+ }
+
+ .flex-3 {
+ -ms-flex: 3;
+ -webkit-flex: 3;
+ flex: 3;
+ }
+
+ .flex-4 {
+ -ms-flex: 4;
+ -webkit-flex: 4;
+ flex: 4;
+ }
+
+ .flex-5 {
+ -ms-flex: 5;
+ -webkit-flex: 5;
+ flex: 5;
+ }
+
+ .flex-6 {
+ -ms-flex: 6;
+ -webkit-flex: 6;
+ flex: 6;
+ }
+
+ .flex-7 {
+ -ms-flex: 7;
+ -webkit-flex: 7;
+ flex: 7;
+ }
+
+ .flex-8 {
+ -ms-flex: 8;
+ -webkit-flex: 8;
+ flex: 8;
+ }
+
+ .flex-9 {
+ -ms-flex: 9;
+ -webkit-flex: 9;
+ flex: 9;
+ }
+
+ .flex-10 {
+ -ms-flex: 10;
+ -webkit-flex: 10;
+ flex: 10;
+ }
+
+ .flex-11 {
+ -ms-flex: 11;
+ -webkit-flex: 11;
+ flex: 11;
+ }
+
+ .flex-12 {
+ -ms-flex: 12;
+ -webkit-flex: 12;
+ flex: 12;
+ }
+
+ /* alignment in cross axis */
+
+ .layout.start {
+ -ms-flex-align: start;
+ -webkit-align-items: flex-start;
+ align-items: flex-start;
+ }
+
+ .layout.center,
+ .layout.center-center {
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ .layout.end {
+ -ms-flex-align: end;
+ -webkit-align-items: flex-end;
+ align-items: flex-end;
+ }
+
+ /* alignment in main axis */
+
+ .layout.start-justified {
+ -ms-flex-pack: start;
+ -webkit-justify-content: flex-start;
+ justify-content: flex-start;
+ }
+
+ .layout.center-justified,
+ .layout.center-center {
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ }
+
+ .layout.end-justified {
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ }
+
+ .layout.around-justified {
+ -ms-flex-pack: around;
+ -webkit-justify-content: space-around;
+ justify-content: space-around;
+ }
+
+ .layout.justified {
+ -ms-flex-pack: justify;
+ -webkit-justify-content: space-between;
+ justify-content: space-between;
+ }
+
+ /* self alignment */
+
+ .self-start {
+ -ms-align-self: flex-start;
+ -webkit-align-self: flex-start;
+ align-self: flex-start;
+ }
+
+ .self-center {
+ -ms-align-self: center;
+ -webkit-align-self: center;
+ align-self: center;
+ }
+
+ .self-end {
+ -ms-align-self: flex-end;
+ -webkit-align-self: flex-end;
+ align-self: flex-end;
+ }
+
+ .self-stretch {
+ -ms-align-self: stretch;
+ -webkit-align-self: stretch;
+ align-self: stretch;
+ }
+
+ /*******************************
+ Other Layout
+ *******************************/
+
+ .block {
+ display: block;
+ }
+
+ /* IE 10 support for HTML5 hidden attr */
+ [hidden] {
+ display: none !important;
+ }
+
+ .invisible {
+ visibility: hidden !important;
+ }
+
+ .relative {
+ position: relative;
+ }
+
+ .fit {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ body.fullbleed {
+ margin: 0;
+ height: 100vh;
+ }
+
+ .scroll {
+ -webkit-overflow-scrolling: touch;
+ overflow: auto;
+ }
+
+ /* fixed position */
+
+ .fixed-bottom,
+ .fixed-left,
+ .fixed-right,
+ .fixed-top {
+ position: fixed;
+ }
+
+ .fixed-top {
+ top: 0;
+ left: 0;
+ right: 0;
+ }
+
+ .fixed-right {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ .fixed-bottom {
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ .fixed-left {
+ top: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/classes/iron-shadow-flex-layout.html b/catapult/third_party/polymer/components/iron-flex-layout/classes/iron-shadow-flex-layout.html
new file mode 100644
index 00000000..5dcde193
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/classes/iron-shadow-flex-layout.html
@@ -0,0 +1,313 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<script>
+ console.warn('This file is deprecated. Please use `iron-flex-layout/iron-flex-layout-classes.html`, and one of the specific dom-modules instead');
+</script>
+
+<style>
+
+ /*******************************
+ Flex Layout
+ *******************************/
+
+ html /deep/ .layout.horizontal,
+ html /deep/ .layout.horizontal-reverse,
+ html /deep/ .layout.vertical,
+ html /deep/ .layout.vertical-reverse {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ }
+
+ html /deep/ .layout.inline {
+ display: -ms-inline-flexbox;
+ display: -webkit-inline-flex;
+ display: inline-flex;
+ }
+
+ html /deep/ .layout.horizontal {
+ -ms-flex-direction: row;
+ -webkit-flex-direction: row;
+ flex-direction: row;
+ }
+
+ html /deep/ .layout.horizontal-reverse {
+ -ms-flex-direction: row-reverse;
+ -webkit-flex-direction: row-reverse;
+ flex-direction: row-reverse;
+ }
+
+ html /deep/ .layout.vertical {
+ -ms-flex-direction: column;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ }
+
+ html /deep/ .layout.vertical-reverse {
+ -ms-flex-direction: column-reverse;
+ -webkit-flex-direction: column-reverse;
+ flex-direction: column-reverse;
+ }
+
+ html /deep/ .layout.wrap {
+ -ms-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+ }
+
+ html /deep/ .layout-no-wrap {
+ -ms-flex-wrap: nowrap;
+ -webkit-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ html /deep/ .layout.wrap-reverse {
+ -ms-flex-wrap: wrap-reverse;
+ -webkit-flex-wrap: wrap-reverse;
+ flex-wrap: wrap-reverse;
+ }
+
+ html /deep/ .flex-auto {
+ -ms-flex: 1 1 auto;
+ -webkit-flex: 1 1 auto;
+ flex: 1 1 auto;
+ }
+
+ html /deep/ .flex-none {
+ -ms-flex: none;
+ -webkit-flex: none;
+ flex: none;
+ }
+
+ html /deep/ .flex,
+ html /deep/ .flex-1 {
+ -ms-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ }
+
+ html /deep/ .flex-2 {
+ -ms-flex: 2;
+ -webkit-flex: 2;
+ flex: 2;
+ }
+
+ html /deep/ .flex-3 {
+ -ms-flex: 3;
+ -webkit-flex: 3;
+ flex: 3;
+ }
+
+ html /deep/ .flex-4 {
+ -ms-flex: 4;
+ -webkit-flex: 4;
+ flex: 4;
+ }
+
+ html /deep/ .flex-5 {
+ -ms-flex: 5;
+ -webkit-flex: 5;
+ flex: 5;
+ }
+
+ html /deep/ .flex-6 {
+ -ms-flex: 6;
+ -webkit-flex: 6;
+ flex: 6;
+ }
+
+ html /deep/ .flex-7 {
+ -ms-flex: 7;
+ -webkit-flex: 7;
+ flex: 7;
+ }
+
+ html /deep/ .flex-8 {
+ -ms-flex: 8;
+ -webkit-flex: 8;
+ flex: 8;
+ }
+
+ html /deep/ .flex-9 {
+ -ms-flex: 9;
+ -webkit-flex: 9;
+ flex: 9;
+ }
+
+ html /deep/ .flex-10 {
+ -ms-flex: 10;
+ -webkit-flex: 10;
+ flex: 10;
+ }
+
+ html /deep/ .flex-11 {
+ -ms-flex: 11;
+ -webkit-flex: 11;
+ flex: 11;
+ }
+
+ html /deep/ .flex-12 {
+ -ms-flex: 12;
+ -webkit-flex: 12;
+ flex: 12;
+ }
+
+ /* alignment in cross axis */
+
+ html /deep/ .layout.start {
+ -ms-flex-align: start;
+ -webkit-align-items: flex-start;
+ align-items: flex-start;
+ }
+
+ html /deep/ .layout.center,
+ html /deep/ .layout.center-center {
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ html /deep/ .layout.end {
+ -ms-flex-align: end;
+ -webkit-align-items: flex-end;
+ align-items: flex-end;
+ }
+
+ /* alignment in main axis */
+
+ html /deep/ .layout.start-justified {
+ -ms-flex-pack: start;
+ -webkit-justify-content: flex-start;
+ justify-content: flex-start;
+ }
+
+ html /deep/ .layout.center-justified,
+ html /deep/ .layout.center-center {
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ }
+
+ html /deep/ .layout.end-justified {
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ }
+
+ html /deep/ .layout.around-justified {
+ -ms-flex-pack: around;
+ -webkit-justify-content: space-around;
+ justify-content: space-around;
+ }
+
+ html /deep/ .layout.justified {
+ -ms-flex-pack: justify;
+ -webkit-justify-content: space-between;
+ justify-content: space-between;
+ }
+
+ /* self alignment */
+
+ html /deep/ .self-start {
+ -ms-align-self: flex-start;
+ -webkit-align-self: flex-start;
+ align-self: flex-start;
+ }
+
+ html /deep/ .self-center {
+ -ms-align-self: center;
+ -webkit-align-self: center;
+ align-self: center;
+ }
+
+ html /deep/ .self-end {
+ -ms-align-self: flex-end;
+ -webkit-align-self: flex-end;
+ align-self: flex-end;
+ }
+
+ html /deep/ .self-stretch {
+ -ms-align-self: stretch;
+ -webkit-align-self: stretch;
+ align-self: stretch;
+ }
+
+ /*******************************
+ Other Layout
+ *******************************/
+
+ html /deep/ .block {
+ display: block;
+ }
+
+ /* IE 10 support for HTML5 hidden attr */
+ html /deep/ [hidden] {
+ display: none !important;
+ }
+
+ html /deep/ .invisible {
+ visibility: hidden !important;
+ }
+
+ html /deep/ .relative {
+ position: relative;
+ }
+
+ html /deep/ .fit {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ body.fullbleed {
+ margin: 0;
+ height: 100vh;
+ }
+
+ html /deep/ .scroll {
+ -webkit-overflow-scrolling: touch;
+ overflow: auto;
+ }
+
+ .fixed-bottom,
+ .fixed-left,
+ .fixed-right,
+ .fixed-top {
+ position: fixed;
+ }
+
+ html /deep/ .fixed-top {
+ top: 0;
+ left: 0;
+ right: 0;
+ }
+
+ html /deep/ .fixed-right {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ html /deep/ .fixed-bottom {
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ html /deep/ .fixed-left {
+ top: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/demo/index.html b/catapult/third_party/polymer/components/iron-flex-layout/demo/index.html
new file mode 100644
index 00000000..4bc2d410
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/demo/index.html
@@ -0,0 +1,396 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-flex-layout demo</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../iron-flex-layout.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ demo-snippet {
+ --demo-snippet-demo: {
+ padding: 0;
+ }
+ }
+ .container {
+ background-color: #ccc;
+ padding: 5px;
+ margin: 0;
+ }
+ .container > div {
+ padding: 15px;
+ margin: 5px;
+ background-color: white;
+ min-height: 20px;
+ }
+
+ .vertical-section-container {
+ max-width: 700px
+ }
+ </style>
+
+</head>
+<body unresolved class="fullbleed">
+ <div class="vertical-section-container centered">
+ <h4>Horizontal and vertical layout</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex {
+ @apply(--layout-horizontal);
+ }
+ </style>
+ <div class="container flex">
+ <div>one</div>
+ <div>two</div>
+ <div>three</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Flexible children</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-horizontal {
+ @apply(--layout-horizontal);
+ }
+ .flexchild {
+ @apply(--layout-flex);
+ }
+ </style>
+
+ <div class="container flex-horizontal">
+ <div>one</div>
+ <div class="flexchild">two (flex)</div>
+ <div>three</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Flexible children in vertical layouts</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-vertical {
+ @apply(--layout-vertical);
+ height: 220px;
+ }
+ .flexchild-vertical {
+ @apply(--layout-flex);
+ }
+ </style>
+
+ <div class="container flex-vertical">
+ <div>one</div>
+ <div class="flexchild-vertical">two (flex)</div>
+ <div>three</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Flex ratios</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-horizontal-with-ratios {
+ @apply(--layout-horizontal);
+ }
+ .flexchild {
+ @apply(--layout-flex);
+ }
+ .flex2child {
+ @apply(--layout-flex-2);
+ }
+ .flex3child {
+ @apply(--layout-flex-3);
+ }
+ </style>
+
+ <div class="container flex-horizontal-with-ratios">
+ <div class="flex3child">one</div>
+ <div class="flexchild">two</div>
+ <div class="flex2child">three</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Cross-axis stretch alignment (default)</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-stretch-align {
+ @apply(--layout-horizontal);
+ height: 120px;
+ }
+ </style>
+
+ <div class="container flex-stretch-align">
+ <div>stretch</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Cross-axis center alignment</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-center-align {
+ @apply(--layout-horizontal);
+ @apply(--layout-center);
+ height: 120px;
+ }
+ </style>
+
+ <div class="container flex-center-align">
+ <div>center</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Cross-axis start alignment</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-start-align {
+ @apply(--layout-horizontal);
+ @apply(--layout-start);
+ height: 120px;
+ }
+ </style>
+
+ <div class="container flex-start-align">
+ <div>start</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Cross-axis end alignment</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-end-align {
+ @apply(--layout-horizontal);
+ @apply(--layout-end);
+ height: 120px;
+ }
+ </style>
+
+ <div class="container flex-end-align">
+ <div>end</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Justification, start justified</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-start-justified {
+ @apply(--layout-horizontal);
+ @apply(--layout-start-justified);
+ }
+ </style>
+
+ <div class="container flex-start-justified">
+ <div>start-justified</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Justification, center justified</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-center-justified {
+ @apply(--layout-horizontal);
+ @apply(--layout-center-justified);
+ }
+ </style>
+
+ <div class="container flex-center-justified">
+ <div>center-justified</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Justification, end justified</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-end-justified {
+ @apply(--layout-horizontal);
+ @apply(--layout-end-justified);
+ }
+ </style>
+
+ <div class="container flex-end-justified">
+ <div>end-justified</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Justification, equal space between elements</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-equal-justified {
+ @apply(--layout-horizontal);
+ @apply(--layout-justified);
+ }
+ </style>
+
+ <div class="container flex-equal-justified">
+ <div>justified</div>
+ <div>justified</div>
+ <div>justified</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Justification, equal space around each element</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-equal-around-justified {
+ @apply(--layout-horizontal);
+ @apply(--layout-around-justified);
+ }
+ </style>
+
+ <div class="container flex-equal-around-justified">
+ <div>around-justified</div>
+ <div>around-justified</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Self alignment</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-self-align {
+ @apply(--layout-horizontal);
+ @apply(--layout-justified);
+ height: 120px;
+ }
+ .flex-self-align div {
+ @apply(--layout-flex);
+ }
+ .child1 {
+ @apply(--layout-self-start);
+ }
+ .child2 {
+ @apply(--layout-self-center);
+ }
+ .child3 {
+ @apply(--layout-self-end);
+ }
+ .child4 {
+ @apply(--layout-self-stretch);
+ }
+ </style>
+
+ <div class="container flex-self-align">
+ <div class="child1">one</div>
+ <div class="child2">two</div>
+ <div class="child3">three</div>
+ <div class="child4">four</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Wrapping</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-wrap {
+ @apply(--layout-horizontal);
+ @apply(--layout-wrap);
+ width: 200px;
+ }
+ </style>
+
+ <div class="container flex-wrap">
+ <div>one</div>
+ <div>two</div>
+ <div>three</div>
+ <div>four</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>Reversed layouts</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .flex-reversed {
+ @apply(--layout-horizontal-reverse);
+ }
+ </style>
+
+ <div class="container flex-reversed">
+ <div>one</div>
+ <div>two</div>
+ <div>three</div>
+ <div>four</div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h4>General purpose rules</h4>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .general {
+ width: 100%;
+ }
+ .general > div {
+ background-color: #ccc;
+ padding: 4px;
+ margin: 12px;
+ }
+ .block {
+ @apply(--layout-block);
+ }
+ .invisible {
+ @apply(--layout-invisible);
+ }
+ .relative {
+ @apply(--layout-relative);
+ }
+ .fit {
+ @apply(--layout-fit);
+ }
+ </style>
+
+ <div class="general">
+ <div>Before <span>[A Span]</span> After</div>
+ <div>Before <span class="block">[A Block Span]</span> After</div>
+ <div>Before invisible span <span class="invisible">Not displayed</span> After invisible span</div>
+ <div class="relative" style="height: 100px;">
+ <div class="fit" style="background-color: #000;color: white">Fit</div>
+ </div>
+ </div>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/index.html b/catapult/third_party/polymer/components/iron-flex-layout/index.html
new file mode 100644
index 00000000..7d3b0881
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-flex-layout</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout-classes.html b/catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout-classes.html
new file mode 100644
index 00000000..6bf47f8c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout-classes.html
@@ -0,0 +1,437 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<!--
+A set of layout classes that let you specify layout properties directly in markup.
+You must include this file in every element that needs to use them.
+
+Sample use:
+
+ <link rel="import" href="../iron-flex-layout/iron-flex-layout-classes.html">
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+
+ <div class="layout horizontal layout-start">
+ <div>cross axis start alignment</div>
+ </div>
+
+The following imports are available:
+ - iron-flex
+ - iron-flex-reverse
+ - iron-flex-alignment
+ - iron-flex-factors
+ - iron-positioning
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!-- Most common used flex styles-->
+<dom-module id="iron-flex">
+ <template>
+ <style>
+ .layout.horizontal,
+ .layout.vertical {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ }
+
+ .layout.inline {
+ display: -ms-inline-flexbox;
+ display: -webkit-inline-flex;
+ display: inline-flex;
+ }
+
+ .layout.horizontal {
+ -ms-flex-direction: row;
+ -webkit-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .layout.vertical {
+ -ms-flex-direction: column;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ }
+
+ .layout.wrap {
+ -ms-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+ }
+
+ .layout.no-wrap {
+ -ms-flex-wrap: nowrap;
+ -webkit-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ .layout.center,
+ .layout.center-center {
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ .layout.center-justified,
+ .layout.center-center {
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ }
+
+ .flex {
+ -ms-flex: 1 1 0.000000001px;
+ -webkit-flex: 1;
+ flex: 1;
+ -webkit-flex-basis: 0.000000001px;
+ flex-basis: 0.000000001px;
+ }
+
+ .flex-auto {
+ -ms-flex: 1 1 auto;
+ -webkit-flex: 1 1 auto;
+ flex: 1 1 auto;
+ }
+
+ .flex-none {
+ -ms-flex: none;
+ -webkit-flex: none;
+ flex: none;
+ }
+ </style>
+ </template>
+</dom-module>
+
+<!-- Basic flexbox reverse styles -->
+<dom-module id="iron-flex-reverse">
+ <template>
+ <style>
+ .layout.horizontal-reverse,
+ .layout.vertical-reverse {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ }
+
+ .layout.horizontal-reverse {
+ -ms-flex-direction: row-reverse;
+ -webkit-flex-direction: row-reverse;
+ flex-direction: row-reverse;
+ }
+
+ .layout.vertical-reverse {
+ -ms-flex-direction: column-reverse;
+ -webkit-flex-direction: column-reverse;
+ flex-direction: column-reverse;
+ }
+
+ .layout.wrap-reverse {
+ -ms-flex-wrap: wrap-reverse;
+ -webkit-flex-wrap: wrap-reverse;
+ flex-wrap: wrap-reverse;
+ }
+ </style>
+ </template>
+</dom-module>
+
+<!-- Flexbox alignment -->
+<dom-module id="iron-flex-alignment">
+ <template>
+ <style>
+ /**
+ * Alignment in cross axis.
+ */
+ .layout.start {
+ -ms-flex-align: start;
+ -webkit-align-items: flex-start;
+ align-items: flex-start;
+ }
+
+ .layout.center,
+ .layout.center-center {
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ .layout.end {
+ -ms-flex-align: end;
+ -webkit-align-items: flex-end;
+ align-items: flex-end;
+ }
+
+ .layout.baseline {
+ -ms-flex-align: baseline;
+ -webkit-align-items: baseline;
+ align-items: baseline;
+ }
+
+ /**
+ * Alignment in main axis.
+ */
+ .layout.start-justified {
+ -ms-flex-pack: start;
+ -webkit-justify-content: flex-start;
+ justify-content: flex-start;
+ }
+
+ .layout.center-justified,
+ .layout.center-center {
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ }
+
+ .layout.end-justified {
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ }
+
+ .layout.around-justified {
+ -ms-flex-pack: distribute;
+ -webkit-justify-content: space-around;
+ justify-content: space-around;
+ }
+
+ .layout.justified {
+ -ms-flex-pack: justify;
+ -webkit-justify-content: space-between;
+ justify-content: space-between;
+ }
+
+ /**
+ * Self alignment.
+ */
+ .self-start {
+ -ms-align-self: flex-start;
+ -webkit-align-self: flex-start;
+ align-self: flex-start;
+ }
+
+ .self-center {
+ -ms-align-self: center;
+ -webkit-align-self: center;
+ align-self: center;
+ }
+
+ .self-end {
+ -ms-align-self: flex-end;
+ -webkit-align-self: flex-end;
+ align-self: flex-end;
+ }
+
+ .self-stretch {
+ -ms-align-self: stretch;
+ -webkit-align-self: stretch;
+ align-self: stretch;
+ }
+
+ .self-baseline {
+ -ms-align-self: baseline;
+ -webkit-align-self: baseline;
+ align-self: baseline;
+ }
+
+ /**
+ * multi-line alignment in main axis.
+ */
+ .layout.start-aligned {
+ -ms-flex-line-pack: start; /* IE10 */
+ -ms-align-content: flex-start;
+ -webkit-align-content: flex-start;
+ align-content: flex-start;
+ }
+
+ .layout.end-aligned {
+ -ms-flex-line-pack: end; /* IE10 */
+ -ms-align-content: flex-end;
+ -webkit-align-content: flex-end;
+ align-content: flex-end;
+ }
+
+ .layout.center-aligned {
+ -ms-flex-line-pack: center; /* IE10 */
+ -ms-align-content: center;
+ -webkit-align-content: center;
+ align-content: center;
+ }
+
+ .layout.between-aligned {
+ -ms-flex-line-pack: justify; /* IE10 */
+ -ms-align-content: space-between;
+ -webkit-align-content: space-between;
+ align-content: space-between;
+ }
+
+ .layout.around-aligned {
+ -ms-flex-line-pack: distribute; /* IE10 */
+ -ms-align-content: space-around;
+ -webkit-align-content: space-around;
+ align-content: space-around;
+ }
+ </style>
+ </template>
+</dom-module>
+
+<dom-module id="iron-flex-factors">
+ <template>
+ <style>
+ .flex,
+ .flex-1 {
+ -ms-flex: 1 1 0.000000001px;
+ -webkit-flex: 1;
+ flex: 1;
+ -webkit-flex-basis: 0.000000001px;
+ flex-basis: 0.000000001px;
+ }
+
+ .flex-2 {
+ -ms-flex: 2;
+ -webkit-flex: 2;
+ flex: 2;
+ }
+
+ .flex-3 {
+ -ms-flex: 3;
+ -webkit-flex: 3;
+ flex: 3;
+ }
+
+ .flex-4 {
+ -ms-flex: 4;
+ -webkit-flex: 4;
+ flex: 4;
+ }
+
+ .flex-5 {
+ -ms-flex: 5;
+ -webkit-flex: 5;
+ flex: 5;
+ }
+
+ .flex-6 {
+ -ms-flex: 6;
+ -webkit-flex: 6;
+ flex: 6;
+ }
+
+ .flex-7 {
+ -ms-flex: 7;
+ -webkit-flex: 7;
+ flex: 7;
+ }
+
+ .flex-8 {
+ -ms-flex: 8;
+ -webkit-flex: 8;
+ flex: 8;
+ }
+
+ .flex-9 {
+ -ms-flex: 9;
+ -webkit-flex: 9;
+ flex: 9;
+ }
+
+ .flex-10 {
+ -ms-flex: 10;
+ -webkit-flex: 10;
+ flex: 10;
+ }
+
+ .flex-11 {
+ -ms-flex: 11;
+ -webkit-flex: 11;
+ flex: 11;
+ }
+
+ .flex-12 {
+ -ms-flex: 12;
+ -webkit-flex: 12;
+ flex: 12;
+ }
+ </style>
+ </template>
+</dom-module>
+
+<!-- Non-flexbox positioning helper styles -->
+<dom-module id="iron-positioning">
+ <template>
+ <style>
+ .block {
+ display: block;
+ }
+
+ /* IE 10 support for HTML5 hidden attr */
+ [hidden] {
+ display: none !important;
+ }
+
+ .invisible {
+ visibility: hidden !important;
+ }
+
+ .relative {
+ position: relative;
+ }
+
+ .fit {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ body.fullbleed {
+ margin: 0;
+ height: 100vh;
+ }
+
+ .scroll {
+ -webkit-overflow-scrolling: touch;
+ overflow: auto;
+ }
+
+ /* fixed position */
+ .fixed-bottom,
+ .fixed-left,
+ .fixed-right,
+ .fixed-top {
+ position: fixed;
+ }
+
+ .fixed-top {
+ top: 0;
+ left: 0;
+ right: 0;
+ }
+
+ .fixed-right {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ .fixed-bottom {
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ .fixed-left {
+ top: 0;
+ bottom: 0;
+ left: 0;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout.html b/catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout.html
new file mode 100644
index 00000000..a69f46a3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/iron-flex-layout.html
@@ -0,0 +1,418 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!--
+The `<iron-flex-layout>` component provides simple ways to use
+[CSS flexible box layout](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes),
+also known as flexbox. This component provides two different ways to use flexbox:
+
+1. [Layout classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/iron-flex-layout-classes.html).
+The layout class stylesheet provides a simple set of class-based flexbox rules, that
+let you specify layout properties directly in markup. You must include this file
+in every element that needs to use them.
+
+ Sample use:
+
+ ```
+ <custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <next-code-block></next-code-block>
+ </template>
+ </custom-element-demo>
+ ```
+
+ ```html
+ <link rel="import" href="iron-flex-layout-classes.html">
+ <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
+ <style>
+ .test { width: 100px; }
+ </style>
+ <div class="layout horizontal center-center">
+ <div class="test">horizontal layout center alignment</div>
+ </div>
+ ```
+
+2. [Custom CSS mixins](https://github.com/PolymerElements/iron-flex-layout/blob/master/iron-flex-layout.html).
+The mixin stylesheet includes custom CSS mixins that can be applied inside a CSS rule using the `@apply` function.
+
+Please note that the old [/deep/ layout classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/classes)
+are deprecated, and should not be used. To continue using layout properties
+directly in markup, please switch to using the new `dom-module`-based
+[layout classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/iron-flex-layout-classes.html).
+Please note that the new version does not use `/deep/`, and therefore requires you
+to import the `dom-modules` in every element that needs to use them.
+
+A complete [guide](https://elements.polymer-project.org/guides/flex-layout) to `<iron-flex-layout>` is available.
+
+@group Iron Elements
+@pseudoElement iron-flex-layout
+@demo demo/index.html
+-->
+
+<style>
+ /* IE 10 support for HTML5 hidden attr */
+ [hidden] {
+ display: none !important;
+ }
+</style>
+
+<style is="custom-style">
+ :root {
+
+ --layout: {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ };
+
+ --layout-inline: {
+ display: -ms-inline-flexbox;
+ display: -webkit-inline-flex;
+ display: inline-flex;
+ };
+
+ --layout-horizontal: {
+ @apply(--layout);
+
+ -ms-flex-direction: row;
+ -webkit-flex-direction: row;
+ flex-direction: row;
+ };
+
+ --layout-horizontal-reverse: {
+ @apply(--layout);
+
+ -ms-flex-direction: row-reverse;
+ -webkit-flex-direction: row-reverse;
+ flex-direction: row-reverse;
+ };
+
+ --layout-vertical: {
+ @apply(--layout);
+
+ -ms-flex-direction: column;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ };
+
+ --layout-vertical-reverse: {
+ @apply(--layout);
+
+ -ms-flex-direction: column-reverse;
+ -webkit-flex-direction: column-reverse;
+ flex-direction: column-reverse;
+ };
+
+ --layout-wrap: {
+ -ms-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+ };
+
+ --layout-no-wrap: {
+ -ms-flex-wrap: nowrap;
+ -webkit-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ };
+
+ --layout-wrap-reverse: {
+ -ms-flex-wrap: wrap-reverse;
+ -webkit-flex-wrap: wrap-reverse;
+ flex-wrap: wrap-reverse;
+ };
+
+ --layout-flex-auto: {
+ -ms-flex: 1 1 auto;
+ -webkit-flex: 1 1 auto;
+ flex: 1 1 auto;
+ };
+
+ --layout-flex-none: {
+ -ms-flex: none;
+ -webkit-flex: none;
+ flex: none;
+ };
+
+ --layout-flex: {
+ -ms-flex: 1 1 0.000000001px;
+ -webkit-flex: 1;
+ flex: 1;
+ -webkit-flex-basis: 0.000000001px;
+ flex-basis: 0.000000001px;
+ };
+
+ --layout-flex-2: {
+ -ms-flex: 2;
+ -webkit-flex: 2;
+ flex: 2;
+ };
+
+ --layout-flex-3: {
+ -ms-flex: 3;
+ -webkit-flex: 3;
+ flex: 3;
+ };
+
+ --layout-flex-4: {
+ -ms-flex: 4;
+ -webkit-flex: 4;
+ flex: 4;
+ };
+
+ --layout-flex-5: {
+ -ms-flex: 5;
+ -webkit-flex: 5;
+ flex: 5;
+ };
+
+ --layout-flex-6: {
+ -ms-flex: 6;
+ -webkit-flex: 6;
+ flex: 6;
+ };
+
+ --layout-flex-7: {
+ -ms-flex: 7;
+ -webkit-flex: 7;
+ flex: 7;
+ };
+
+ --layout-flex-8: {
+ -ms-flex: 8;
+ -webkit-flex: 8;
+ flex: 8;
+ };
+
+ --layout-flex-9: {
+ -ms-flex: 9;
+ -webkit-flex: 9;
+ flex: 9;
+ };
+
+ --layout-flex-10: {
+ -ms-flex: 10;
+ -webkit-flex: 10;
+ flex: 10;
+ };
+
+ --layout-flex-11: {
+ -ms-flex: 11;
+ -webkit-flex: 11;
+ flex: 11;
+ };
+
+ --layout-flex-12: {
+ -ms-flex: 12;
+ -webkit-flex: 12;
+ flex: 12;
+ };
+
+ /* alignment in cross axis */
+
+ --layout-start: {
+ -ms-flex-align: start;
+ -webkit-align-items: flex-start;
+ align-items: flex-start;
+ };
+
+ --layout-center: {
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ };
+
+ --layout-end: {
+ -ms-flex-align: end;
+ -webkit-align-items: flex-end;
+ align-items: flex-end;
+ };
+
+ --layout-baseline: {
+ -ms-flex-align: baseline;
+ -webkit-align-items: baseline;
+ align-items: baseline;
+ };
+
+ /* alignment in main axis */
+
+ --layout-start-justified: {
+ -ms-flex-pack: start;
+ -webkit-justify-content: flex-start;
+ justify-content: flex-start;
+ };
+
+ --layout-center-justified: {
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ };
+
+ --layout-end-justified: {
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ };
+
+ --layout-around-justified: {
+ -ms-flex-pack: distribute;
+ -webkit-justify-content: space-around;
+ justify-content: space-around;
+ };
+
+ --layout-justified: {
+ -ms-flex-pack: justify;
+ -webkit-justify-content: space-between;
+ justify-content: space-between;
+ };
+
+ --layout-center-center: {
+ @apply(--layout-center);
+ @apply(--layout-center-justified);
+ };
+
+ /* self alignment */
+
+ --layout-self-start: {
+ -ms-align-self: flex-start;
+ -webkit-align-self: flex-start;
+ align-self: flex-start;
+ };
+
+ --layout-self-center: {
+ -ms-align-self: center;
+ -webkit-align-self: center;
+ align-self: center;
+ };
+
+ --layout-self-end: {
+ -ms-align-self: flex-end;
+ -webkit-align-self: flex-end;
+ align-self: flex-end;
+ };
+
+ --layout-self-stretch: {
+ -ms-align-self: stretch;
+ -webkit-align-self: stretch;
+ align-self: stretch;
+ };
+
+ --layout-self-baseline: {
+ -ms-align-self: baseline;
+ -webkit-align-self: baseline;
+ align-self: baseline;
+ };
+
+ /* multi-line alignment in main axis */
+
+ --layout-start-aligned: {
+ -ms-flex-line-pack: start; /* IE10 */
+ -ms-align-content: flex-start;
+ -webkit-align-content: flex-start;
+ align-content: flex-start;
+ };
+
+ --layout-end-aligned: {
+ -ms-flex-line-pack: end; /* IE10 */
+ -ms-align-content: flex-end;
+ -webkit-align-content: flex-end;
+ align-content: flex-end;
+ };
+
+ --layout-center-aligned: {
+ -ms-flex-line-pack: center; /* IE10 */
+ -ms-align-content: center;
+ -webkit-align-content: center;
+ align-content: center;
+ };
+
+ --layout-between-aligned: {
+ -ms-flex-line-pack: justify; /* IE10 */
+ -ms-align-content: space-between;
+ -webkit-align-content: space-between;
+ align-content: space-between;
+ };
+
+ --layout-around-aligned: {
+ -ms-flex-line-pack: distribute; /* IE10 */
+ -ms-align-content: space-around;
+ -webkit-align-content: space-around;
+ align-content: space-around;
+ };
+
+ /*******************************
+ Other Layout
+ *******************************/
+
+ --layout-block: {
+ display: block;
+ };
+
+ --layout-invisible: {
+ visibility: hidden !important;
+ };
+
+ --layout-relative: {
+ position: relative;
+ };
+
+ --layout-fit: {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ };
+
+ --layout-scroll: {
+ -webkit-overflow-scrolling: touch;
+ overflow: auto;
+ };
+
+ --layout-fullbleed: {
+ margin: 0;
+ height: 100vh;
+ };
+
+ /* fixed position */
+
+ --layout-fixed-top: {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ };
+
+ --layout-fixed-right: {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ };
+
+ --layout-fixed-bottom: {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ };
+
+ --layout-fixed-left: {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ };
+
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/test/index.html b/catapult/third_party/polymer/components/iron-flex-layout/test/index.html
new file mode 100644
index 00000000..90e12a78
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/test/index.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+ <head>
+ <title>iron-flex-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'iron-flex-layout.html',
+ 'iron-flex-layout.html?dom=shadow',
+ 'iron-flex-layout-classes.html',
+ 'iron-flex-layout-classes.html?dom=shadow'
+ ]);
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout-classes.html b/catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout-classes.html
new file mode 100644
index 00000000..3173b308
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout-classes.html
@@ -0,0 +1,412 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+ <head>
+ <title>iron-flex-layout-classes tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../iron-flex-layout-classes.html">
+
+ <style is="custom-style" include="iron-flex iron-flex-reverse iron-flex-factors iron-flex-alignment iron-positioning">
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ .container {
+ width: 300px;
+ min-height: 50px;
+ background-color: #ccc;
+ }
+ .container > div {
+ width: 50px;
+ min-height: 50px; /* so that it can grow in vertical layouts. */
+ }
+ /* IE11 does not calculate flex proportions correctly in a vertical
+ * layout if the children just have a min-height. For those tests,
+ * use a fixed height instead. */
+ .container > div.fixed-height {
+ min-height: 0;
+ height: 50px;
+ }
+ .container.relative > div {
+ min-width: 50px;
+ min-height: 50px;
+ width: inherit;
+ }
+ .container.small { width: 120px; }
+ .container.tall { height: 300px; }
+
+ #c1 { background-color: #E91E63; }
+ #c2 { background-color: #03A9F4; }
+ #c3 { background-color: #CDDC39; }
+ #c4 { background-color: #03A9F4; }
+ #c5 { background-color: #E91E63; }
+ </style>
+ </head>
+ <body>
+ <test-fixture id="basic">
+ <template>
+ <div class="container layout">
+ <div id="c1"></div>
+ <div id="c2"></div>
+ <div id="c3"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="flex">
+ <template>
+ <div class="container layout">
+ <div id="c1" class="fixed-height"></div>
+ <div id="c2" class="fixed-height"></div>
+ <div id="c3" class="fixed-height"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="single-child">
+ <template>
+ <div class="container layout">
+ <div id="c1"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="positioning">
+ <template>
+ <div class="container layout relative">
+ <div id="c1"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="align-content">
+ <template>
+ <div class="container layout">
+ <div id="c1"></div>
+ <div id="c2"></div>
+ <div id="c3"></div>
+ <div id="c4"></div>
+ <div id="c5"></div>
+ </div>
+ </template>
+ </test-fixture>
+ <script>
+ function positionEquals(node, top, bottom, left, right) {
+ var rect = node.getBoundingClientRect();
+ return rect.top === top && rect.bottom === bottom &&
+ rect.left === left && rect.right === right;
+ }
+ suite('basic layout', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('basic');
+ });
+
+ test('layout-horizontal', function() {
+ container.classList.add('horizontal');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c1| |c2| |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 100, 150), "child 3 position ok");
+ });
+
+ test('layout-horizontal-reverse', function() {
+ container.classList.add('horizontal-reverse');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c3| |c2| |c1|
+ assert.isTrue(positionEquals(c1, 0, 50, 250, 300), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 200, 250), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 150, 200), "child 3 position ok");
+ });
+
+ test('layout-vertical', function() {
+ container.classList.add('vertical');
+ assert.isTrue(positionEquals(container, 0, 150, 0, 300), "container position ok");
+ // vertically, |c1| |c2| |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 100, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 100, 150, 0, 50), "child 3 position ok");
+ });
+
+ test('layout-vertical-reverse', function() {
+ container.classList.add('vertical-reverse');
+ assert.isTrue(positionEquals(container, 0, 150, 0, 300), "container position ok");
+ // vertically, |c3| |c2| |c1|
+ assert.isTrue(positionEquals(c1, 100, 150, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 100, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 0, 50), "child 3 position ok");
+ });
+
+ test('layout-wrap', function() {
+ container.classList.add('horizontal');
+ container.classList.add('wrap');
+ container.classList.add('small');
+ assert.isTrue(positionEquals(container, 0, 100, 0, 120), "container position ok");
+ // |c1| |c2|
+ // |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 50, 100, 0, 50), "child 3 position ok");
+ });
+
+ test('layout-wrap-reverse', function() {
+ container.classList.add('horizontal-reverse');
+ container.classList.add('wrap-reverse');
+ container.style.width = '100px';
+ assert.isTrue(positionEquals(container, 0, 100, 0, 100), "container position ok");
+ // |c3|
+ // |c2| |c1|
+ assert.isTrue(positionEquals(c1, 50, 100, 50, 100), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 100, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 50, 100), "child 3 position ok");
+ });
+ });
+
+ suite('flex', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('flex');
+ });
+
+ test('layout-flex child in a horizontal layout', function() {
+ container.classList.add('horizontal');
+ c2.classList.add('flex');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c1| | c2 | |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 250), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 250, 300), "child 3 position ok");
+ });
+
+ test('layout-flex child in a vertical layout', function() {
+ container.classList.add('vertical');
+ container.classList.add('tall');
+ c2.classList.add('flex');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ // vertically, |c1| | c2 | |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 250, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 250, 300, 0, 50), "child 3 position ok");
+ });
+
+ test('layout-flex, layout-flex-2, layout-flex-3 in a horizontal layout', function() {
+ container.classList.add('horizontal');
+ c1.classList.add('flex');
+ c2.classList.add('flex-2');
+ c3.classList.add('flex-3');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c1| | c2 | | c3 |
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 150), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 150, 300), "child 3 position ok");
+ });
+
+ test('layout-flex, layout-flex-2, layout-flex-3 in a vertical layout', function() {
+ container.classList.add('vertical');
+ container.classList.add('tall');
+ c1.classList.add('flex');
+ c2.classList.add('flex-2');
+ c3.classList.add('flex-3');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ // vertically, |c1| | c2 | | c3 |
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 150, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 150, 300, 0, 50), "child 3 position ok");
+ });
+ });
+
+ suite('alignment', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('single-child');
+ container.classList.add('horizontal');
+ });
+
+ test('stretch (default)', function() {
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 300, 0, 50), "child 1 position ok");
+ });
+
+ test('layout-center', function() {
+ container.classList.add('center');
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, center, center + 50, 0, 50), "child 1 position ok");
+ });
+
+ test('layout-start', function() {
+ container.classList.add('start');
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ });
+
+ test('layout-end', function() {
+ container.classList.add('end');
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 250, 300, 0, 50), "child 1 position ok");
+ });
+
+ test('layout-start-justified', function() {
+ container.classList.add('start-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ });
+
+ test('layout-end-justified', function() {
+ container.classList.add('end-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 250, 300), "child 1 position ok");
+ });
+
+ test('layout-center-justified', function() {
+ container.classList.add('center-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 0, 50, center, center + 50), "child 1 position ok");
+ });
+ });
+
+ suite('justification', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('flex');
+ container.classList.add('horizontal');
+ });
+
+ test('layout-justified', function() {
+ container.classList.add('justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, center, center + 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 250, 300), "child 3 position ok");
+ });
+
+ test('layout-around-justified', function() {
+ container.classList.add('around-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ var spacing = (300 - 50 * 3) / 6;
+ // Every child gets `spacing` on its left and right side.
+ assert.isTrue(positionEquals(c1, 0, 50, spacing, spacing + 50), "child 1 position ok");
+ var end = spacing + 50 + spacing;
+ assert.isTrue(positionEquals(c2, 0, 50, end + spacing, end + spacing + 50), "child 2 position ok");
+ end = end + spacing + 50 + spacing;
+ assert.isTrue(positionEquals(c3, 0, 50, end + spacing, end + spacing + 50), "child 3 position ok");
+ });
+ });
+
+ suite('align-content', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('align-content');
+ container.classList.add('small');
+ container.classList.add('tall');
+ container.classList.add('horizontal');
+ container.classList.add('flex');
+ container.classList.add('wrap');
+ });
+
+ test('default is stretch', function() {
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 100, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 100, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 100, 200, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, 100, 200, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 200, 300, 0, 50), "child 5 position ok");
+ });
+
+ test('layout-start-aligned', function() {
+ container.classList.add('start-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 50, 100, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, 50, 100, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 100, 150, 0, 50), "child 5 position ok");
+ });
+
+ test('layout-end-aligned', function() {
+ container.classList.add('end-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ assert.isTrue(positionEquals(c1, 150, 200, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 150, 200, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 200, 250, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, 200, 250, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 250, 300, 0, 50), "child 5 position ok");
+ });
+
+ test('layout-center-aligned', function() {
+ container.classList.add('center-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, center-50, center, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, center-50, center, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, center, center+50, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, center, center+50, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, center+50, center+100, 0, 50), "child 5 position ok");
+ });
+
+ test('layout-between-aligned', function() {
+ container.classList.add('between-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, center, center+50, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, center, center+50, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 250, 300, 0, 50), "child 5 position ok");
+ });
+
+ test('layout-around-aligned', function() {
+ container.classList.add('around-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 25, 75, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 25, 75, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, center, center+50, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, center, center+50, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 225, 275, 0, 50), "child 5 position ok");
+ });
+ });
+
+ suite('positioning', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('positioning');
+ container.classList.add('tall');
+ });
+
+ test('layout-fit', function() {
+ c1.classList.add('fit');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "child 1 position ok");
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout.html b/catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout.html
new file mode 100644
index 00000000..f2090878
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-flex-layout/test/iron-flex-layout.html
@@ -0,0 +1,434 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+ <head>
+ <title>iron-flex-layout tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../iron-flex-layout.html">
+
+ <style is="custom-style">
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ .container {
+ width: 300px;
+ min-height: 50px;
+ background-color: #ccc;
+ }
+ .container > div {
+ width: 50px;
+ min-height: 50px; /* so that it can grow in vertical layouts. */
+ }
+ /* IE11 does not calculate flex proportions correctly in a vertical
+ * layout if the children just have a min-height. For those tests,
+ * use a fixed height instead. */
+ .container > div.fixed-height {
+ min-height: 0;
+ height: 50px;
+ }
+ .relative { @apply(--layout-relative); }
+ .container.relative > div {
+ min-width: 50px;
+ min-height: 50px;
+ width: inherit;
+ }
+ .container.small { width: 120px; }
+ .container.tall { height: 300px; }
+
+ #c1 { background-color: #E91E63; }
+ #c2 { background-color: #03A9F4; }
+ #c3 { background-color: #CDDC39; }
+ #c4 { background-color: #03A9F4; }
+ #c5 { background-color: #E91E63; }
+
+ .horizontal { @apply(--layout-horizontal); }
+ .horizontal-reverse { @apply(--layout-horizontal-reverse); }
+ .vertical { @apply(--layout-vertical); }
+ .vertical-reverse { @apply(--layout-vertical-reverse); }
+ .wrap { @apply(--layout-wrap); }
+ .wrap-reverse { @apply(--layout-wrap-reverse); }
+ .flex { @apply(--layout-flex); }
+ .flex2 { @apply(--layout-flex-2); }
+ .flex3 { @apply(--layout-flex-3); }
+ .center { @apply(--layout-center); }
+ .start { @apply(--layout-start); }
+ .end { @apply(--layout-end); }
+ .start-justified { @apply(--layout-start-justified); }
+ .center-justified { @apply(--layout-center-justified); }
+ .end-justified { @apply(--layout-end-justified); }
+ .justified { @apply(--layout-justified); }
+ .around-justified { @apply(--layout-around-justified); }
+ .fit { @apply(--layout-fit); }
+ .start-aligned { @apply(--layout-start-aligned); }
+ .end-aligned { @apply(--layout-end-aligned); }
+ .center-aligned { @apply(--layout-center-aligned); }
+ .between-aligned { @apply(--layout-between-aligned); }
+ .around-aligned { @apply(--layout-around-aligned); }
+
+ </style>
+ </head>
+ <body>
+ <test-fixture id="basic">
+ <template>
+ <div class="container">
+ <div id="c1"></div>
+ <div id="c2"></div>
+ <div id="c3"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="flex">
+ <template>
+ <div class="container">
+ <div id="c1" class="fixed-height"></div>
+ <div id="c2" class="fixed-height"></div>
+ <div id="c3" class="fixed-height"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="single-child">
+ <template>
+ <div class="container">
+ <div id="c1"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="positioning">
+ <template>
+ <div class="container relative">
+ <div id="c1"></div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="align-content">
+ <template>
+ <div class="container small tall horizontal flex wrap">
+ <div id="c1"></div>
+ <div id="c2"></div>
+ <div id="c3"></div>
+ <div id="c4"></div>
+ <div id="c5"></div>
+ </div>
+ </template>
+ </test-fixture>
+ <script>
+ function positionEquals(node, top, bottom, left, right) {
+ var rect = node.getBoundingClientRect();
+ return rect.top === top && rect.bottom === bottom &&
+ rect.left === left && rect.right === right;
+ }
+ suite('basic layout', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('basic');
+ });
+
+ test('--layout-horizontal', function() {
+ container.classList.add('horizontal');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c1| |c2| |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 100, 150), "child 3 position ok");
+ });
+
+ test('--layout-horizontal-reverse', function() {
+ container.classList.add('horizontal-reverse');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c3| |c2| |c1|
+ assert.isTrue(positionEquals(c1, 0, 50, 250, 300), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 200, 250), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 150, 200), "child 3 position ok");
+ });
+
+ test('--layout-vertical', function() {
+ container.classList.add('vertical');
+ assert.isTrue(positionEquals(container, 0, 150, 0, 300), "container position ok");
+ // vertically, |c1| |c2| |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 100, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 100, 150, 0, 50), "child 3 position ok");
+ });
+
+ test('--layout-vertical-reverse', function() {
+ container.classList.add('vertical-reverse');
+ assert.isTrue(positionEquals(container, 0, 150, 0, 300), "container position ok");
+ // vertically, |c3| |c2| |c1|
+ assert.isTrue(positionEquals(c1, 100, 150, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 100, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 0, 50), "child 3 position ok");
+ });
+
+ test('--layout-wrap', function() {
+ container.classList.add('horizontal');
+ container.classList.add('wrap');
+ container.classList.add('small');
+ assert.isTrue(positionEquals(container, 0, 100, 0, 120), "container position ok");
+ // |c1| |c2|
+ // |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 50, 100, 0, 50), "child 3 position ok");
+ });
+
+ test('--layout-wrap-reverse', function() {
+ container.classList.add('horizontal');
+ container.classList.add('wrap-reverse');
+ container.classList.add('small');
+ assert.isTrue(positionEquals(container, 0, 100, 0, 120), "container position ok");
+ // |c3|
+ // |c1| |c2|
+ assert.isTrue(positionEquals(c1, 50, 100, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 100, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 0, 50), "child 3 position ok");
+ });
+ });
+
+ suite('flex', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('flex');
+ });
+
+ test('--layout-flex child in a horizontal layout', function() {
+ container.classList.add('horizontal');
+ c2.classList.add('flex');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c1| | c2 | |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 250), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 250, 300), "child 3 position ok");
+ });
+
+ test('--layout-flex child in a vertical layout', function() {
+ container.classList.add('vertical');
+ container.classList.add('tall');
+ c2.classList.add('flex');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ // vertically, |c1| | c2 | |c3|
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 250, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 250, 300, 0, 50), "child 3 position ok");
+ });
+
+ test('--layout-flex, --layout-flex-2, --layout-flex-3 in a horizontal layout', function() {
+ container.classList.add('horizontal');
+ c1.classList.add('flex');
+ c2.classList.add('flex2');
+ c3.classList.add('flex3');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ // |c1| | c2 | | c3 |
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 150), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 150, 300), "child 3 position ok");
+ });
+
+ test('--layout-flex, --layout-flex-2, --layout-flex-3 in a vertical layout', function() {
+ container.classList.add('vertical');
+ container.classList.add('tall');
+ c1.classList.add('flex');
+ c2.classList.add('flex2');
+ c3.classList.add('flex3');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ // vertically, |c1| | c2 | | c3 |
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 50, 150, 0, 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 150, 300, 0, 50), "child 3 position ok");
+ });
+ });
+
+ suite('alignment', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('single-child');
+ container.classList.add('horizontal');
+ });
+
+ test('stretch (default)', function() {
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 300, 0, 50), "child 1 position ok");
+ });
+
+ test('--layout-center', function() {
+ container.classList.add('center');
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, center, center + 50, 0, 50), "child 1 position ok");
+ });
+
+ test('--layout-start', function() {
+ container.classList.add('start');
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ });
+
+ test('--layout-end', function() {
+ container.classList.add('end');
+ container.classList.add('tall');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 250, 300, 0, 50), "child 1 position ok");
+ });
+
+ test('--layout-start-justified', function() {
+ container.classList.add('start-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ });
+
+ test('--layout-end-justified', function() {
+ container.classList.add('end-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 250, 300), "child 1 position ok");
+ });
+
+ test('--layout-center-justified', function() {
+ container.classList.add('center-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 0, 50, center, center + 50), "child 1 position ok");
+ });
+ });
+
+ suite('justification', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('flex');
+ container.classList.add('horizontal');
+ });
+
+ test('--layout-justified', function() {
+ container.classList.add('justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, center, center + 50), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 0, 50, 250, 300), "child 3 position ok");
+ });
+
+ test('--layout-around-justified', function() {
+ container.classList.add('around-justified');
+ assert.isTrue(positionEquals(container, 0, 50, 0, 300), "container position ok");
+ var spacing = (300 - 50 * 3) / 6;
+ // Every child gets `spacing` on its left and right side.
+ assert.isTrue(positionEquals(c1, 0, 50, spacing, spacing + 50), "child 1 position ok");
+ var end = spacing + 50 + spacing;
+ assert.isTrue(positionEquals(c2, 0, 50, end + spacing, end + spacing + 50), "child 2 position ok");
+ end = end + spacing + 50 + spacing;
+ assert.isTrue(positionEquals(c3, 0, 50, end + spacing, end + spacing + 50), "child 3 position ok");
+ });
+ });
+
+ suite('align-content', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('align-content');
+ });
+
+ test('default is stretch', function() {
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 100, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 100, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 100, 200, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, 100, 200, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 200, 300, 0, 50), "child 5 position ok");
+ });
+
+ test('--layout-start-aligned', function() {
+ container.classList.add('start-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 50, 100, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, 50, 100, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 100, 150, 0, 50), "child 5 position ok");
+ });
+
+ test('--layout-end-aligned', function() {
+ container.classList.add('end-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ assert.isTrue(positionEquals(c1, 150, 200, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 150, 200, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, 200, 250, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, 200, 250, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 250, 300, 0, 50), "child 5 position ok");
+ });
+
+ test('--layout-center-aligned', function() {
+ container.classList.add('center-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, center-50, center, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, center-50, center, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, center, center+50, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, center, center+50, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, center+50, center+100, 0, 50), "child 5 position ok");
+ });
+
+ test('--layout-between-aligned', function() {
+ container.classList.add('between-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 0, 50, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 0, 50, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, center, center+50, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, center, center+50, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 250, 300, 0, 50), "child 5 position ok");
+ });
+
+ test('--layout-around-aligned', function() {
+ container.classList.add('around-aligned');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 120), "container position ok");
+ var center = (300 - 50) / 2;
+ assert.isTrue(positionEquals(c1, 25, 75, 0, 50), "child 1 position ok");
+ assert.isTrue(positionEquals(c2, 25, 75, 50, 100), "child 2 position ok");
+ assert.isTrue(positionEquals(c3, center, center+50, 0, 50), "child 3 position ok");
+ assert.isTrue(positionEquals(c4, center, center+50, 50, 100), "child 4 position ok");
+ assert.isTrue(positionEquals(c5, 225, 275, 0, 50), "child 5 position ok");
+ });
+ });
+
+ suite('positioning', function() {
+ var container;
+
+ setup(function() {
+ container = fixture('positioning');
+ container.classList.add('tall');
+
+ });
+
+ test('--layout-fit', function() {
+ c1.classList.add('fit');
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "container position ok");
+ assert.isTrue(positionEquals(container, 0, 300, 0, 300), "child 1 position ok");
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/.bower.json b/catapult/third_party/polymer/components/iron-form-element-behavior/.bower.json
new file mode 100644
index 00000000..b31f1264
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/.bower.json
@@ -0,0 +1,41 @@
+{
+ "name": "iron-form-element-behavior",
+ "version": "1.0.7",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "private": true,
+ "main": "iron-form-element-behavior.html",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "description": "Enables a custom element to be included in an iron-form",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "form"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-form-element-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-form-element-behavior",
+ "_release": "1.0.7",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.7",
+ "commit": "1ff8e3da05ef1497eb11866121470f8c65035748"
+ },
+ "_source": "https://github.com/PolymerElements/iron-form-element-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-form-element-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-form-element-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..36493f30
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-form-element-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/.gitignore b/catapult/third_party/polymer/components/iron-form-element-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-form-element-behavior/.travis.yml
new file mode 100644
index 00000000..2201126c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ IJ2DKBWd4T446kn7yV/2tStL8tTZcmfguzymvE+U9p9JBJl6Qu+KwfzETjJPyiEhzwS8gqcrV8takloMto2dMLCyCc9RBzJt/AHlLC5vcZ0aJ06ArYkn98/lz4q9noDDpd1dqXbzC8ea4En/b+DCgf8iei7pFmoNjFo8+RGoSMN7GvmUIBEbZ0akQnZcvMNgbWrRQQ31Qkug492ybrmizBMlBoMSRVkMjKkuKQyOr/7CTEvmj62XuapM9VoE21qr6E0T1iXrBbT9ay5HPlitkfBXe+oV3tj3ONBp81+HhEiIkZua3xnGcs0YkT9qd/fipy/1hfPAj5bBco/IyYEjVYUuIrhdJEfzFlHBwNqlWtl5yuW9srHP5V4k7NiUWBESoQ2V4AH7ZDuxcsdhBT1N/2D0dksBP4UkfyBIYQaUOYSI0a4O6BpgTQRPh7yHZ88fG6B2/k59dlX1PuPFuiI7dECPMmaLVsUGH3PMkezHsD4BNdp89HzJVk0jTM8W5oP1/Lj8EEp1X48zHg7IZo+o0rOPtvjqrYtdhw9+BVujRKkLhXBDIh+bjQeX4WpeDPtAxAaOH7bAWxWa+w1h0DbOkHzB6cV3H8jzg/oJKwZyIgmrR9aVjeaW2IP3sYxbkGq2ZM/x9+mcRo+JV3TONStYRc6lEKpmOGQgUZbVMyU0S2E=
+ - secure: >-
+ KZmse/kaUHZKkjYYWssUihkjaWmhXFfJio6/TcyxlvzlRN+b+s8xkMx09F2mfCEzCuxbhMeYaOLYUP+x44mO/N5OKvRYqGKLBJASQ+LvsVe6ep972AfWtb2P2E6OK/b9vAdW0v52j1gTaj94KtUfjaWjxP78TTEG5slMMwadqBm5e7nW4vS+wV26sbNymnWB9NErLebJr0T+wwrXDyjXWkUTH8yPzlwFjPcfXZavQwFyM9E+k+HQVUOQ8b3oMxVnwc2aHRWLWRDu97k1S/hVk+c1j+qamOa16faJLTsG9lay7RRkeq/OQBvjdWtmsq95NkP27/uLlQrKKOY7nZZ331LlqYK/o1+i+nVjZqWAgMXun7OjTtZvU53dOFXPVroXNoIcAPhdVNxLNvlJLvTL1uoVybzjeDdiMrTpZc6FpSLRKOj9AGmnT4vy+A2MSxn3rwujvz2PlRvoXYxnN36QbONbF7Zr1Sv6i+53eN+/1xC78i7otJEsvJhKe6YAq9/7xyOBGhELhOYMIES2gy/Nbc+47pEWWgYQ/tvBO1RXuaRgKG2iwiWL83Phhgw3SAdYaAL5xk6lUTTsuCJZHmcDoD8bTDblBIjntWvtzT6Z1NPTiuUXszJXkLSwUjfNUeQtb4H9GnNWF0P4ze91RZippygyWfrDz7aQhf+NcbF/PE0=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-form-element-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/README.md b/catapult/third_party/polymer/components/iron-form-element-behavior/README.md
new file mode 100644
index 00000000..a6e1ca58
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/README.md
@@ -0,0 +1,25 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-form-element-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-form-element-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-form-element-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-form-element-behavior)_
+
+
+##Polymer.IronFormElementBehavior
+
+ Polymer.IronFormElementBehavior enables a custom element to be included
+ in an `iron-form`.
+
+
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/bower.json b/catapult/third_party/polymer/components/iron-form-element-behavior/bower.json
new file mode 100644
index 00000000..680c2a2c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "iron-form-element-behavior",
+ "version": "1.0.7",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "private": true,
+ "main": "iron-form-element-behavior.html",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "description": "Enables a custom element to be included in an iron-form",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "form"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-form-element-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-form-element-behavior/demo/index.html
new file mode 100644
index 00000000..e919d0bc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/demo/index.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>iron-form-element-behavior demo</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="simple-form.html">
+ <link rel="import" href="simple-element.html">
+ </head>
+ <style>
+ input {
+ margin-bottom: 24px;
+ width: 200px;
+ }
+ </style>
+ <body>
+
+ <div class="vertical-section-container centered">
+ <div class="vertical-section">
+ <form is="simple-form" id="form">
+ Element with iron-form-element-behavior <input is="simple-element" type="text" name="one" value="one">
+ <br>
+ Element without iron-form-element-behavior <input type="text" name="two" value="two">
+ <br>
+ Element with iron-form-element-behavior <input is="simple-element" type="text" name="three" value="three">
+ </form>
+
+ <h4>Elements tracked by the form: <button onclick="update()">Update</button> </h4>
+
+ <ul id="output">
+ </ul>
+ </div>
+ </div>
+
+ <script>
+ function update() {
+ var output = document.getElementById('output');
+ var elements = document.getElementById('form').formElements;
+ document.getElementById('output').innerHTML = '';
+ for (var i = 0; i < elements.length; i++) {
+ var li = document.createElement('li');
+ var text = document.createTextNode(elements[i].value);
+ li.appendChild(text);
+ output.appendChild(li);
+ }
+ }
+ </script>
+ </body>
+
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-element.html b/catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-element.html
new file mode 100644
index 00000000..45027692
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-element.html
@@ -0,0 +1,27 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-form-element-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'simple-element',
+
+ extends: 'input',
+
+ behaviors: [
+ Polymer.IronFormElementBehavior
+ ]
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-form.html b/catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-form.html
new file mode 100644
index 00000000..4dc5dc6b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/demo/simple-form.html
@@ -0,0 +1,53 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+
+<script>
+
+ Polymer({
+
+ is: 'simple-form',
+
+ extends: 'form',
+
+ properties: {
+ formElements: {
+ type: Object,
+ notify: true
+ }
+ },
+
+ listeners: {
+ 'iron-form-element-register': '_elementRegistered',
+ 'iron-form-element-unregister': '_elementUnregistered'
+ },
+
+ ready: function() {
+ this.formElements = [];
+ },
+
+ _elementRegistered: function(e) {
+ this.formElements.push(e.target);
+ e.target._parentForm = this;
+ },
+
+ _elementUnregistered: function(e) {
+ var target = e.detail.target;
+ if (target) {
+ var index = this.formElements.indexOf(target);
+ if (index > -1) {
+ this.formElements.splice(index, 1);
+ }
+ }
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/index.html b/catapult/third_party/polymer/components/iron-form-element-behavior/index.html
new file mode 100644
index 00000000..8d748c05
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-form-element-behavior</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/iron-form-element-behavior.html b/catapult/third_party/polymer/components/iron-form-element-behavior/iron-form-element-behavior.html
new file mode 100644
index 00000000..cd9c6b4a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/iron-form-element-behavior.html
@@ -0,0 +1,86 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+ /**
+ Polymer.IronFormElementBehavior enables a custom element to be included
+ in an `iron-form`.
+
+ @demo demo/index.html
+ @polymerBehavior
+ */
+ Polymer.IronFormElementBehavior = {
+
+ properties: {
+ /**
+ * Fired when the element is added to an `iron-form`.
+ *
+ * @event iron-form-element-register
+ */
+
+ /**
+ * Fired when the element is removed from an `iron-form`.
+ *
+ * @event iron-form-element-unregister
+ */
+
+ /**
+ * The name of this element.
+ */
+ name: {
+ type: String
+ },
+
+ /**
+ * The value for this element.
+ */
+ value: {
+ notify: true,
+ type: String
+ },
+
+ /**
+ * Set to true to mark the input as required. If used in a form, a
+ * custom element that uses this behavior should also use
+ * Polymer.IronValidatableBehavior and define a custom validation method.
+ * Otherwise, a `required` element will always be considered valid.
+ * It's also strongly recommended to provide a visual style for the element
+ * when its value is invalid.
+ */
+ required: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The form that the element is registered to.
+ */
+ _parentForm: {
+ type: Object
+ }
+ },
+
+ attached: function() {
+ // Note: the iron-form that this element belongs to will set this
+ // element's _parentForm property when handling this event.
+ this.fire('iron-form-element-register');
+ },
+
+ detached: function() {
+ if (this._parentForm) {
+ this._parentForm.fire('iron-form-element-unregister', {target: this});
+ }
+ }
+
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/test/basic.html b/catapult/third_party/polymer/components/iron-form-element-behavior/test/basic.html
new file mode 100644
index 00000000..5947a1e4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/test/basic.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-form-element-behavior</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../iron-form-element-behavior.html">
+ <link rel="import" href="simple-element.html">
+ <link rel="import" href="simple-form.html">
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <form is="simple-form"></form>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('basic', function() {
+ var form;
+
+ setup(function() {
+ form = fixture('basic');
+ });
+
+ test('elements fire an event when attached', function(done) {
+ var element = document.createElement('input', 'simple-element');
+
+ var handler = sinon.spy();
+ form.addEventListener('iron-form-element-register', handler);
+
+ form.appendChild(element);
+ Polymer.Base.async(function() {
+ expect(handler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ });
+
+ test('elements fire an event when detached', function(done) {
+ var element = document.createElement('input', 'simple-element');
+ form.appendChild(element);
+ element._parentForm = form;
+
+ var handler = sinon.spy();
+ form.addEventListener('iron-form-element-unregister', handler);
+
+ form.removeChild(element);
+ Polymer.Base.async(function() {
+ expect(handler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ });
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/test/index.html b/catapult/third_party/polymer/components/iron-form-element-behavior/test/index.html
new file mode 100644
index 00000000..3b2badac
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/test/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html'
+ ]);
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-element.html b/catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-element.html
new file mode 100644
index 00000000..b1aa3002
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-element.html
@@ -0,0 +1,23 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-form-element-behavior.html">
+
+<script>
+
+ Polymer({
+ is: 'simple-element',
+ extends: 'input',
+ behaviors: [
+ Polymer.IronFormElementBehavior
+ ]
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-form.html b/catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-form.html
new file mode 100644
index 00000000..7fa38d88
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form-element-behavior/test/simple-form.html
@@ -0,0 +1,19 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+
+<script>
+
+ Polymer({
+ is: 'simple-form',
+ extends: 'form'
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-form/.bower.json b/catapult/third_party/polymer/components/iron-form/.bower.json
new file mode 100644
index 00000000..d3ab78ce
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/.bower.json
@@ -0,0 +1,78 @@
+{
+ "name": "iron-form",
+ "version": "2.0.1",
+ "description": "Wrapper around native form that submits native and custom elements",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "form"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "main": "iron-form.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-form.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-form",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2",
+ "iron-ajax": "PolymerElements/iron-ajax#1 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#1 - 2",
+ "paper-button": "PolymerElements/paper-button#1 - 2",
+ "paper-checkbox": "PolymerElements/paper-checkbox#1 - 2",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#1 - 2",
+ "paper-input": "PolymerElements/paper-input#1 - 2",
+ "paper-item": "PolymerElements/paper-item#1 - 2",
+ "paper-listbox": "PolymerElements/paper-listbox#1 - 2",
+ "paper-spinner": "PolymerElements/paper-spinner#1 - 2",
+ "paper-styles": "PolymerElements/paper-styles#1 - 2",
+ "web-component-tester": "Polymer/web-component-tester#^6.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.9",
+ "iron-ajax": "PolymerElements/iron-ajax#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
+ "paper-input": "PolymerElements/paper-input#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
+ "paper-spinner": "PolymerElements/paper-spinner#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "Polymer/web-component-tester#^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "resolutions": {
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ },
+ "_release": "2.0.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v2.0.1",
+ "commit": "430bb2c9f82d08595dbe9479feb7ca1724582631"
+ },
+ "_source": "https://github.com/PolymerElements/iron-form.git",
+ "_target": "^2.0.0",
+ "_originalSource": "PolymerElements/iron-form"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-form/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-form/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..35461cd1
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-form/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-form/.gitignore b/catapult/third_party/polymer/components/iron-form/.gitignore
new file mode 100644
index 00000000..2be39e49
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/.gitignore
@@ -0,0 +1,2 @@
+bower_components*
+bower-*.json
diff --git a/catapult/third_party/polymer/components/iron-form/.travis.yml b/catapult/third_party/polymer/components/iron-form/.travis.yml
new file mode 100644
index 00000000..5975597f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/.travis.yml
@@ -0,0 +1,26 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g polymer-cli
+ - polymer install --variants
+env:
+ global:
+ - secure: >-
+ JeSfmBNQGyT7fu9NURSHqK9miBYrieoVdCnzOIq1QRDpWEiUGJKrcWKbabA/1MrlYfbQDFwXIDGrv0hcZzf0xbDblo5s/jUnep1vq/w/IRznEDnhJelpjG6c38vlBbPQOZI3L32eu8w5BBmlXhc4XXpUGkIy94O1Iz7Tn3zkeFI/fKhMyrmgDUOuehP0EHyoKxLCyWGLV+aTRtif6TK/wn6ULwM+GvLiS7nykrnA2WoDEECAuCoIsfRD9UFoLH4dNmOoiEHGeSx3rL6aE0TV9GVpPpBkL6qe8LFJLV4GJsekVyeOnKlekbzts8joIrR62P9m2BHZMQkizyx6Tj22vF6uXI8HAvocNIbijiJPSKFRA2Ir6ZAtBKPFMv73upROSvzqT+8UjW4DM9EDQiheCZ+Hkv0cQqtSmmiS3GxrTxW622lKdg2uVbD2qH2Ac2wJNGztukKtAyR2wSyjD7R/wE2MiQrT2YlGw95aMMqVylF1CtXoUJWGXUdfT6NZ7WCjbRj3fwABBY6BT+bJioIolHORdY6QbFUJ8GASqHqYlZShd23JtM5LJeDyJrJ4oEWNJ7q+jYl5xN7zwy/W0H86bn+6JX/Lxq71kvkohpzAHwSWAuAANLYf7cIUZ7GBo1lUzWaSJ1SaW981jH9s87NnyBP2thJAnyr78T6UXjPFZYk=
+ - secure: >-
+ THJhGmuNJqLVeRfN2qJL1FKH3TXNwovC0z8G1GU4ML3pYbRL8oFw7nrtnofwgI99axLNEoJFZnkWcmkKRWlDHYX+LwQCUtYr3eW/pncrWGVBElr9HUgw6q0tY0o6v1Oe0Oj3kqxWuFkA1Kv8V7bHvB9ABT7CimwXDAC+uwP4SDqEtlVz8LAg6G74ywJaKg+S1gL8EHTIAGjeMSfKw4UjFAi6QKebJBsPOu8qmGrBSSdaLoDKhQb+kJqcLncJ0PB2AKC8zTzpxsDSwZizGDGLcbuFR1pnqTqISKgfpTVycTHJrEQwYsS6bTR7X86i3lcJ4H5dtI/HHaAQTmeT3JpvTfz5f9dvAuJVqimQOzdFlKM7ehhNk3j9KtED/vnQW9+0AJhOT9+7trokW++jl219QS29FOPYTxSo/XvI6iLZfOr4X0l4VhNkgHeE6EyCUoGNwDRxUvn+KcsQMk6JdHLY+lCckZ+tRFQIUjMPqjn9tB/MjBqtXxDhF5ZjaOcLufzuywKBzlMT4OkAiOGK9qFENKpTVaqvmRPPM1bjkZuzfTBJTOzWDznf2V4r65/s6ZRpoqEy0ozb7NjUEjYOR+OuZfKYVwT5VmAykKLNW3eQJc1zJjvhjghSIxOWH9AvD18UeQtEcv2J5pVFIp+BFVR25i0BH+efOwWJ49hk0o0N+Dw=
+node_js: stable
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+ - xvfb-run polymer test
+ - >-
+ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then polymer test -s 'default';
+ fi
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-form/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-form/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-form/README.md b/catapult/third_party/polymer/components/iron-form/README.md
new file mode 100644
index 00000000..01357142
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/README.md
@@ -0,0 +1,52 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-form.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-form.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-form)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-form)_
+
+
+## &lt;iron-form&gt;
+`<iron-form>` is a wrapper around the HTML `<form>` element, that can
+validate and submit both custom and native HTML elements.
+
+It has two modes: if `allow-redirect` is true, then after the form submission you
+will be redirected to the server response. Otherwise, if it is false, it will
+use an `iron-ajax` element to submit the form contents to the server.
+
+ Example:
+
+```html
+ <iron-form>
+ <form method="get" action="/form/handler">
+ <input type="text" name="name" value="Batman">
+ <input type="checkbox" name="donuts" checked> I like donuts<br>
+ <paper-checkbox name="cheese" value="yes" checked></paper-checkbox>
+ </form>
+ </iron-form>
+```
+
+By default, a native `<button>` element (or `input type="submit"`) will submit this form. However, if you
+want to submit it from a custom element's click handler, you need to explicitly
+call the `iron-form`'s `submit` method.
+
+ Example:
+
+```html
+ <paper-button raised onclick="submitForm()">Submit</paper-button>
+
+ function submitForm() {
+ document.getElementById('iron-form').submit();
+ }
+```
diff --git a/catapult/third_party/polymer/components/iron-form/bower.json b/catapult/third_party/polymer/components/iron-form/bower.json
new file mode 100644
index 00000000..d4048a23
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/bower.json
@@ -0,0 +1,69 @@
+{
+ "name": "iron-form",
+ "version": "2.0.1",
+ "description": "Wrapper around native form that submits native and custom elements",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "form"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "main": "iron-form.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-form.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-form",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2",
+ "iron-ajax": "PolymerElements/iron-ajax#1 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#1 - 2",
+ "paper-button": "PolymerElements/paper-button#1 - 2",
+ "paper-checkbox": "PolymerElements/paper-checkbox#1 - 2",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#1 - 2",
+ "paper-input": "PolymerElements/paper-input#1 - 2",
+ "paper-item": "PolymerElements/paper-item#1 - 2",
+ "paper-listbox": "PolymerElements/paper-listbox#1 - 2",
+ "paper-spinner": "PolymerElements/paper-spinner#1 - 2",
+ "paper-styles": "PolymerElements/paper-styles#1 - 2",
+ "web-component-tester": "Polymer/web-component-tester#^6.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.9",
+ "iron-ajax": "PolymerElements/iron-ajax#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
+ "paper-input": "PolymerElements/paper-input#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
+ "paper-spinner": "PolymerElements/paper-spinner#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "Polymer/web-component-tester#^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "resolutions": {
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-form/demo/cats-only.html b/catapult/third_party/polymer/components/iron-form/demo/cats-only.html
new file mode 100644
index 00000000..116df8b8
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/demo/cats-only.html
@@ -0,0 +1,65 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-validatable-behavior/iron-validatable-behavior.html">
+
+<dom-module id="cats-only">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ }
+ input {
+ width: 250px;
+ }
+ :host([invalid]) input {
+ border-color: red;
+ }
+ </style>
+ <input value="{{value}}" id="input" placeholder="Type 'cats' to prove you like cats">
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+ is: 'cats-only',
+
+ properties: {
+ value: {
+ type: String
+ },
+
+ name: {
+ type: String
+ }
+ },
+
+ behaviors: [
+ Polymer.IronValidatableBehavior
+ ],
+
+ listeners: {
+ 'input': '_onInput'
+ },
+
+ _onInput: function() {
+ this.value = this.$.input.value;
+ },
+
+ // Overidden from Polymer.IronValidatableBehavior. Will set the `invalid`
+ // attribute automatically, which should be used for styling.
+ _getValidity: function() {
+ return this.value === 'cats';
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-form/demo/index.html b/catapult/third_party/polymer/components/iron-form/demo/index.html
new file mode 100644
index 00000000..2bafe2c5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/demo/index.html
@@ -0,0 +1,250 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-form demo</title>
+
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../paper-input/paper-input.html">
+ <link rel="import" href="../../paper-button/paper-button.html">
+ <link rel="import" href="../../paper-checkbox/paper-checkbox.html">
+ <link rel="import" href="../../paper-dropdown-menu/paper-dropdown-menu.html">
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../paper-listbox/paper-listbox.html">
+ <link rel="import" href="../../paper-spinner/paper-spinner.html">
+ <link rel="import" href="../../paper-styles/shadow.html">
+ <link rel="import" href="../iron-form.html">
+ <link rel="import" href="cats-only.html">
+
+ <custom-style>
+ <style is="custom-style" include="demo-pages-shared-styles">
+ input, paper-input, paper-checkbox {
+ margin-bottom: 8px;
+ }
+ iron-form {
+ @apply --shadow-elevation-2dp;
+ padding: 20px;
+ }
+ paper-button {
+ display: inline-block;
+ width: 45%;
+ text-align: center;
+ }
+ #form1 paper-button {
+ width: 30%;
+ }
+ paper-button:not([disabled]) {
+ background: #03a9f4;
+ color: white;
+ }
+ paper-spinner {
+ width: 14px;
+ height: 14px;
+ margin-right: 20px;
+ }
+ .output {
+ margin-top: 20px;
+ word-wrap: break-word;
+ font-family: monospace;
+ }
+ </style>
+ </custom-style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Iron-form works with native and custom elements, and can prevent automatic redirection</h3>
+ <demo-snippet>
+ <template>
+ <input type="checkbox" checked onchange="form1.allowRedirect = !this.checked;"> Prevent automatic form redirection<br><br>
+
+ <iron-form id="form1">
+ <form action="/foo" method="get">
+ <paper-input type="text" name="name" required label="Name" value="Batman"></paper-input>
+ <input name="foo" required>
+ <input type="checkbox" name="donuts" checked>I like donuts<br>
+ <paper-checkbox name="food" value="pizza" checked>pizza</paper-checkbox><br>
+ <paper-checkbox name="food" value="cheese" required>cheese</paper-checkbox>
+ <br>
+ <paper-dropdown-menu label="Cars" name="cars" required>
+ <paper-listbox class="dropdown-content" slot="dropdown-content">
+ <paper-item value="volvo">Volvo</paper-item>
+ <paper-item value="saab">Saab</paper-item>
+ <paper-item value="fiat">Fiat</paper-item>
+ <paper-item value="audi">Audi</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+ <br><br>
+ <paper-button raised onclick="form1.submit()">Submit</paper-button>
+ <paper-button raised onclick="form1.reset()">Reset</paper-button>
+ <paper-button raised onclick="form1.validate()">Validate</paper-button>
+ </form>
+ <br>
+ <div class="output"></div>
+ </iron-form>
+ <script>
+ form1.addEventListener('iron-form-submit', function(event) {
+ this.querySelector('.output').innerHTML = JSON.stringify(event.detail);
+ });
+ </script>
+ </template>
+ </demo-snippet>
+
+ <h3>You can submit a form in many different ways: by manually calling submit(),
+ using a native button, or by wrapping a paper-button in a native button:</h3>
+ <demo-snippet>
+ <template>
+ <iron-form id="form2">
+ <form action="/foo" method="get">
+ <paper-input label="Name" value="Batman" name="name"></paper-input>
+ <input type="checkbox" required> You must check this box.
+ <p>Using a native button to submit the form will display
+ the native browser validation UI for native elements: </p>
+ <button type="submit">Submit</button>
+ <button type="reset">Reset</button>
+
+ <p>Using a custom element to submit the form will not display
+ the native browser validation UI:</p>
+ <paper-button raised onclick="form2.submit()">Submit</paper-button>
+ <paper-button raised onclick="form2.reset()">Reset</paper-button>
+ </form>
+ <br>
+ <div class="output"></div>
+ </iron-form>
+ <script>
+ form2.addEventListener('iron-form-submit', function(event) {
+ this.querySelector('.output').innerHTML = JSON.stringify(event.detail);
+ });
+ </script>
+ </template>
+ </demo-snippet>
+
+ <h3>To customize the request sent to the server, you can listen to the `iron-form-presubmit` event</h3>
+ <demo-snippet>
+ <template>
+ <iron-form id="form3">
+ <form action="/foo" method="get">
+ <paper-input name="name" label="Name" value="Batman" required></paper-input>
+ <paper-button raised onclick="form3.submit()">Submit</paper-button>
+ <paper-button raised onclick="form3.reset()">Reset</paper-button>
+ </form>
+ <div class="output"></div>
+ </iron-form>
+ <script>
+ form3.addEventListener('iron-form-presubmit', function(event) {
+ this.request.params['sidekick'] = 'Robin';
+ });
+ form3.addEventListener('iron-form-submit', function(event) {
+ this.querySelector('.output').innerHTML = JSON.stringify(event.detail);
+ });
+ </script>
+ </template>
+ </demo-snippet>
+
+ <h3>Example of an <code>iron-form</code> reacting to state changes.</h3>
+ <p>This form will have the "Submit" button disabled until all fields
+ are valid, and then show a spinner during submission.</p>
+
+ <demo-snippet>
+ <template>
+ <iron-form id="form4">
+ <form action="/foo" method="get">
+ <paper-input name="name" label="Name" required auto-validate></paper-input>
+ <paper-checkbox name="read" required>You must check this box</paper-checkbox><br>
+
+ <paper-button raised onclick="_delayedSubmit(event)" disabled id="form4Submit">
+ <paper-spinner id="spinner" hidden></paper-spinner>Submit</paper-button>
+ <paper-button raised onclick="form4.reset()">Reset</paper-button>
+ </form>
+ <div class="output"></div>
+ </iron-form>
+ <script>
+ form4.addEventListener('iron-form-submit', function(event) {
+ this.querySelector('.output').innerHTML = JSON.stringify(event.detail);
+ spinner.active = false;
+ spinner.hidden = true;
+ form4Submit.disabled = false;
+ });
+ form4.addEventListener('change', function(event) {
+ // Validate the entire form to see if we should enable the `Submit` button.
+ form4Submit.disabled = !form4.validate();
+ });
+ function _delayedSubmit(event) {
+ spinner.active = true;
+ spinner.hidden = false;
+ form4Submit.disabled = true;
+ // Simulate a slow server response.
+ setTimeout(function() {
+ form4.submit()
+ }, 1000);
+ }
+ </script>
+ </template>
+ </demo-snippet>
+
+
+ <h3>Iron-form respects the novalidate form attribute</h3>
+ <demo-snippet>
+ <template>
+ <iron-form id="form5">
+ <form method="get" action="/foo" novalidate>
+ <paper-input name="name" label="Name" required></paper-input>
+ <cats-only name="cats"></cats-only>
+ <input name="animal" placeholder="animal" required value="meerkat"><br>
+ <paper-checkbox name="cheese" required>Cheese</paper-checkbox>
+ <br>
+ <paper-button raised onclick="form5.submit()">Submit</paper-button>
+ <paper-button raised onclick="form5.reset()">Reset</paper-button>
+ </form>
+ <div class="output"></div>
+ </iron-form>
+ <script>
+ form5.addEventListener('iron-form-submit', function(event) {
+ this.querySelector('.output').innerHTML = JSON.stringify(event.detail);
+ });
+ </script>
+ </template>
+ </demo-snippet>
+
+ <h3>Iron-form respects nested elements</h3>
+ <demo-snippet>
+ <template>
+ <iron-form id="form6">
+ <form method="get" action="/foo">
+ <p>
+ <input name="name" label="Name" required>
+ <cats-only name="cats"></cats-only>
+ <paper-checkbox name="cheese" required>Cheese</paper-checkbox>
+ </p>
+
+ <input type="submit" value="Submit">
+ <br>
+ <paper-button raised onclick="form6.submit()">Submit</paper-button>
+ <paper-button raised onclick="form6.reset()">Reset</paper-button>
+ </form>
+ <div class="output"></div>
+ </iron-form>
+ <script>
+ form6.addEventListener('iron-form-submit', function(event) {
+ this.querySelector('.output').innerHTML = JSON.stringify(event.detail);
+ });
+ </script>
+ </template>
+ </demo-snippet>
+ </div>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form/index.html b/catapult/third_party/polymer/components/iron-form/index.html
new file mode 100644
index 00000000..acd7140d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-form</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form/iron-form.html b/catapult/third_party/polymer/components/iron-form/iron-form.html
new file mode 100644
index 00000000..258ca997
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/iron-form.html
@@ -0,0 +1,462 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-ajax/iron-ajax.html">
+
+<!--
+`<iron-form>` is a wrapper around the HTML `<form>` element, that can
+validate and submit both custom and native HTML elements. Note that this
+is a breaking change from iron-form 1.0, which was a type extension.
+
+It has two modes: if `allow-redirect` is true, then after the form submission you
+will be redirected to the server response. Otherwise, if it is false, it will
+use an `iron-ajax` element to submit the form contents to the server.
+
+ Example:
+
+ <iron-form>
+ <form method="get" action="/form/handler">
+ <input type="text" name="name" value="Batman">
+ <input type="checkbox" name="donuts" checked> I like donuts<br>
+ <paper-checkbox name="cheese" value="yes" checked></paper-checkbox>
+ </form>
+ </iron-form>
+
+By default, a native `<button>` element will submit this form. However, if you
+want to submit it from a custom element's click handler, you need to explicitly
+call the `iron-form`'s `submit` method.
+
+ Example:
+
+ <paper-button raised onclick="submitForm()">Submit</paper-button>
+
+ function submitForm() {
+ document.getElementById('iron-form').submit();
+ }
+
+If you are not using the `allow-redirect` mode, then you also have the option of
+customizing the request sent to the server. To do so, you can listen to the `iron-form-presubmit`
+event, and modify the form's [`iron-ajax`](https://elements.polymer-project.org/elements/iron-ajax)
+object. However, If you want to not use `iron-ajax` at all, you can cancel the
+event and do your own custom submission:
+
+ Example of modifying the request, but still using the build-in form submission:
+
+ form.addEventListener('iron-form-presubmit', function() {
+ this.request.method = 'put';
+ this.request.params['extraParam'] = 'someValue';
+ });
+
+ Example of bypassing the build-in form submission:
+
+ form.addEventListener('iron-form-presubmit', function(event) {
+ event.preventDefault();
+ var firebase = new Firebase(form.getAttribute('action'));
+ firebase.set(form.serializeForm());
+ });
+
+Note that if you're dynamically creating this element, it's mandatory that you
+first create the contained `<form>` element and all its children, and only then
+attach it to the `<iron-form>`:
+
+ var wrapper = document.createElement('iron-form');
+ var form = document.createElement('form');
+ var input = document.createElement('input');
+ form.appendChild(input);
+ document.body.appendChild(wrapper);
+ wrapper.appendChild(form);
+
+@element iron-form
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="iron-form">
+ <template>
+ <style>
+ :host {
+ display: block;
+ }
+ </style>
+
+ <!-- This form is used to collect the elements that should be submitted -->
+ <slot></slot>
+
+ <!-- This form is used for submission -->
+ <form id="helper" action$="[[action]]" method$="[[method]]" enctype$="[[enctype]]"></form>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'iron-form',
+
+ properties: {
+ /*
+ * Set this to true if you don't want the form to be submitted through an
+ * ajax request, and you want the page to redirect to the action URL
+ * after the form has been submitted.
+ */
+ allowRedirect: {
+ type: Boolean,
+ value: false
+ },
+ /**
+ * HTTP request headers to send. See PolymerElements/iron-ajax for
+ * more details. Only works when `allowRedirect` is false.
+ */
+ headers: {
+ type: Object,
+ value: function() { return {}; }
+ },
+ /**
+ * Set the `withCredentials` flag on the request. See PolymerElements/iron-ajax for
+ * more details. Only works when `allowRedirect` is false.
+ */
+ withCredentials: {
+ type: Boolean,
+ value: false
+ },
+ },
+ /**
+ * Fired if the form cannot be submitted because it's invalid.
+ *
+ * @event iron-form-invalid
+ */
+
+ /**
+ * Fired after the form is submitted.
+ *
+ * @event iron-form-submit
+ */
+
+ /**
+ * Fired before the form is submitted.
+ *
+ * @event iron-form-presubmit
+ */
+
+ /**
+ * Fired after the form is submitted and a response is received. An
+ * IronRequestElement is included as the event.detail object.
+ *
+ * @event iron-form-response
+ */
+
+ /**
+ * Fired after the form is submitted and an error is received. An
+ * error message is included in event.detail.error and an
+ * IronRequestElement is included in event.detail.request.
+ *
+ * @event iron-form-error
+ */
+
+ attached: function() {
+ this._nodeObserver = Polymer.dom(this).observeNodes(
+ function(mutations) {
+ for (var i = 0; i < mutations.addedNodes.length; i++) {
+ if (mutations.addedNodes[i].tagName === 'FORM' && !this._alreadyCalledInit) {
+ this._alreadyCalledInit = true;
+ this._form = mutations.addedNodes[i];
+ this._init();
+ }
+ }
+ }.bind(this));
+ },
+
+ detached: function() {
+ if (this._nodeObserver) {
+ Polymer.dom(this).unobserveNodes(this._nodeObserver);
+ this._nodeObserver = null;
+ }
+ },
+
+ _init: function() {
+ this._form.addEventListener('submit', this.submit.bind(this));
+ this._form.addEventListener('reset', this.reset.bind(this));
+
+ // Save the initial values.
+ this._defaults = this._defaults || new WeakMap();
+ var nodes = this._getSubmittableElements();
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ if (!this._defaults.has(node)) {
+ this._defaults.set(node, {
+ checked: node.checked,
+ value: node.value,
+ });
+ }
+ }
+ },
+
+ /**
+ * Validates all the required elements (custom and native) in the form.
+ * @return {boolean} True if all the elements are valid.
+ */
+ validate: function() {
+ if (this._form.getAttribute('novalidate') === '')
+ return true;
+
+ // Start by making the form check the native elements it knows about.
+ var valid = this._form.checkValidity();
+ var elements = this._getValidatableElements();
+
+ // Go through all the elements, and validate the custom ones.
+ for (var el, i = 0; el = elements[i], i < elements.length; i++) {
+ // This is weird to appease the compiler. We assume the custom element
+ // has a validate() method, otherwise we can't check it.
+ var validatable = /** @type {{validate: (function() : boolean)}} */ (el);
+ if (validatable.validate) {
+ valid = !!validatable.validate() && valid;
+ }
+ }
+ return valid;
+ },
+
+ /**
+ * Submits the form.
+ */
+ submit: function(event) {
+ // We are not using this form for submission, so always cancel its event.
+ if (event) {
+ event.preventDefault();
+ }
+
+ // If you've called this before distribution happened, bail out.
+ if (!this._form) {
+ return;
+ }
+
+ if (!this.validate()) {
+ this.fire('iron-form-invalid');
+ return;
+ }
+
+ // Remove any existing children in the submission form (from a previous submit).
+ this.$.helper.textContent = '';
+
+ var json = this.serializeForm();
+
+ // If we want a redirect, submit the form natively.
+ if (this.allowRedirect) {
+ // If we're submitting the form natively, then create a hidden element for
+ // each of the values.
+ for (var element in json) {
+ this.$.helper.appendChild(this._createHiddenElement(element, json[element]));
+ }
+
+ // Copy the original form attributes.
+ this.$.helper.action = this._form.getAttribute('action');
+ this.$.helper.method = this._form.getAttribute('method') || 'GET';
+ this.$.helper.contentType = this._form.getAttribute('enctype') || 'application/x-www-form-urlencoded';
+
+ this.$.helper.submit();
+ this.fire('iron-form-submit');
+ } else {
+ this._makeAjaxRequest(json);
+ }
+ },
+
+ /**
+ * Resets the form to the default values.
+ */
+ reset: function(event) {
+ // We are not using this form for submission, so always cancel its event.
+ if (event)
+ event.preventDefault();
+
+ // If you've called this before distribution happened, bail out.
+ if (!this._form) {
+ return;
+ }
+
+ // Load the initial values.
+ var nodes = this._getSubmittableElements();
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ if (this._defaults.has(node)) {
+ var defaults = this._defaults.get(node);
+ node.value = defaults.value;
+ node.checked = defaults.checked;
+ }
+ }
+ },
+
+ /**
+ * Serializes the form as will be used in submission. Note that `serialize`
+ * is a Polymer reserved keyword, so calling `someIronForm`.serialize()`
+ * will give you unexpected results.
+ * @return {Object} An object containing name-value pairs for elements that
+ * would be submitted.
+ */
+ serializeForm: function() {
+ // Only elements that have a `name` and are not disabled are submittable.
+ var elements = this._getSubmittableElements();
+ var json = {};
+ for (var i = 0; i < elements.length; i++) {
+ var values = this._serializeElementValues(elements[i]);
+ for (var v = 0; v < values.length; v++) {
+ this._addSerializedElement(json, elements[i].name, values[v]);
+ }
+ }
+ return json;
+ },
+
+ _handleFormResponse: function (event) {
+ this.fire('iron-form-response', event.detail);
+ },
+
+ _handleFormError: function (event) {
+ this.fire('iron-form-error', event.detail);
+ },
+
+ _makeAjaxRequest: function(json) {
+ // Initialize the iron-ajax element if we haven't already.
+ if (!this.request) {
+ this.request = document.createElement('iron-ajax');
+ this.request.addEventListener('response', this._handleFormResponse.bind(this));
+ this.request.addEventListener('error', this._handleFormError.bind(this));
+ }
+
+ // Native forms can also index elements magically by their name (can't make
+ // this up if I tried) so we need to get the correct attributes, not the
+ // elements with those names.
+ this.request.url = this._form.getAttribute('action');
+ this.request.method = this._form.getAttribute('method') || 'GET';
+ this.request.contentType = this._form.getAttribute('enctype') || 'application/x-www-form-urlencoded';
+ this.request.withCredentials = this.withCredentials;
+ this.request.headers = this.headers;
+
+ if (this._form.method.toUpperCase() === 'POST') {
+ this.request.body = json;
+ } else {
+ this.request.params = json;
+ }
+
+ // Allow for a presubmit hook
+ var event = this.fire('iron-form-presubmit', {}, {cancelable: true});
+ if(!event.defaultPrevented) {
+ this.request.generateRequest();
+ this.fire('iron-form-submit', json);
+ }
+ },
+
+ _getValidatableElements: function() {
+ return this._findElements(this._form, true);
+ },
+
+ _getSubmittableElements: function() {
+ return this._findElements(this._form, false);
+ },
+
+ _findElements: function(parent, ignoreName) {
+ var nodes = Polymer.dom(parent).querySelectorAll('*');
+ var submittable = [];
+
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ // An element is submittable if it is not disabled, and if it has a
+ // 'name' attribute.
+ if(!node.disabled && (ignoreName || node.name)) {
+ submittable.push(node);
+ }
+ else {
+ // This element has a root which could contain more submittable elements.
+ if(node.root) {
+ Array.prototype.push.apply(submittable, this._findElements(node.root, ignoreName));
+ }
+ }
+ }
+ return submittable;
+ },
+
+ _serializeElementValues: function(element) {
+ // We will assume that every custom element that needs to be serialized
+ // has a `value` property, and it contains the correct value.
+ // The only weird one is an element that implements IronCheckedElementBehaviour,
+ // in which case like the native checkbox/radio button, it's only used
+ // when checked.
+ // For native elements, from https://www.w3.org/TR/html5/forms.html#the-form-element.
+ // Native submittable elements: button, input, keygen, object, select, textarea;
+ // 1. We will skip `keygen and `object` for this iteration, and deal with
+ // them if they're actually required.
+ // 2. <button> and <textarea> have a `value` property, so they behave like
+ // the custom elements.
+ // 3. <select> can have multiple options selected, in which case its
+ // `value` is incorrect, and we must use the values of each of its
+ // `selectedOptions`
+ // 4. <input> can have a whole bunch of behaviours, so it's handled separately.
+ // 5. Buttons are hard. The button that was clicked to submit the form
+ // is the one who's name/value gets sent to the server.
+ var tag = element.tagName.toLowerCase();
+ if (tag === 'button' || (tag === 'input' && (element.type === 'submit' || element.type === 'reset'))) {
+ return [];
+ }
+
+ if (tag === 'select') {
+ return this._serializeSelectValues(element);
+ } else if (tag === 'input') {
+ return this._serializeInputValues(element);
+ } else {
+ if (element['_hasIronCheckedElementBehavior'] && !element.checked)
+ return [];
+ return [element.value];
+ }
+ },
+
+ _serializeSelectValues: function(element) {
+ var values = [];
+
+ // A <select multiple> has an array of options, some of which can be selected.
+ for (var i = 0; i < element.options.length; i++) {
+ if (element.options[i].selected) {
+ values.push(element.options[i].value)
+ }
+ }
+ return values;
+ },
+
+ _serializeInputValues: function(element) {
+ // Most of the inputs use their 'value' attribute, with the exception
+ // of radio buttons, checkboxes and file.
+ var type = element.type.toLowerCase();
+
+ // Don't do anything for unchecked checkboxes/radio buttons.
+ // Don't do anything for file, since that requires a different request.
+ if (((type === 'checkbox' || type === 'radio') && !element.checked) ||
+ type === 'file') {
+ return [];
+ }
+ return [element.value];
+ },
+
+ _createHiddenElement: function(name, value) {
+ var input = document.createElement("input");
+ input.setAttribute("type", "hidden");
+ input.setAttribute("name", name);
+ input.setAttribute("value", value);
+ return input;
+ },
+
+ _addSerializedElement: function(json, name, value) {
+ // If the name doesn't exist, add it. Otherwise, serialize it to
+ // an array,
+ if (json[name] === undefined) {
+ json[name] = value;
+ } else {
+ if (!Array.isArray(json[name])) {
+ json[name] = [json[name]];
+ }
+ json[name].push(value);
+ }
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-form/test/basic.html b/catapult/third_party/polymer/components/iron-form/test/basic.html
new file mode 100644
index 00000000..8f649182
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/test/basic.html
@@ -0,0 +1,923 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-form</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../paper-checkbox/paper-checkbox.html">
+ <link rel="import" href="../../paper-input/paper-input.html">
+ <link rel="import" href="../../paper-button/paper-button.html">
+ <link rel="import" href="../iron-form.html">
+
+</head>
+<body>
+
+ <dom-module id="x-input-wrapper">
+ <template>
+ <input name="check_wrapped" value="foo"/>
+ </template>
+ </dom-module>
+
+ <test-fixture id="serialization">
+ <template>
+ <div>
+ <iron-form id="native-checkboxes">
+ <form action="/get" method="get">
+ <input type="checkbox" name="check1" checked> <!-- default value is "on" -->
+ <input type="checkbox" name="check1" value="1">
+ <input type="checkbox" name="check1" value="2" checked>
+ <input type="checkbox" name="check2" value="3" checked>
+ <input type="checkbox" name="check3" value="4">
+ </form>
+ </iron-form>
+ <iron-form id="native-radios">
+ <form action="/get" method="get">
+ <input type="radio" name="radio1" checked> <!-- default value is "on" -->
+ <input type="radio" name="radio1" value="1">
+ <input type="radio" name="radio2" value="2" checked>
+ <input type="radio" name="radio2" value="3" checked> <!-- it's a radio group -->
+ <input type="radio" name="radio3" value="4">
+ </form>
+ </iron-form>
+ <iron-form id="native-buttons">
+ <form action="/get" method="get">
+ <input type="reset" name="reset1" value="reset">
+ <input type="submit" name="submit1" value="submit">
+ <button name="button1" value="button">text</button>
+ </form>
+ </iron-form>
+ <iron-form id="native-selects">
+ <form action="/get" method="get">
+ <select name="select1" multiple>
+ <option value="1" selected>A</option>
+ <option value="2" selected>B</option>
+ <option value="3">C</option>
+ </select>
+ <select name="select2">
+ <option value="1" selected>A</option>
+ <option value="2">B</option>
+ </select>
+ </form>
+ </iron-form>
+ <iron-form id="native-inputs">
+ <form action="/get" method="get">
+ <input type="text" name="input1">
+ <input type="text" name="input1" value="foo">
+ <input type="text" name="input1" value="zag">
+ <input type="text" name="input2" value="bar">
+ <input type="password" name="pass1" value="pass">
+ <input type="number" name="number1" value="35">
+ <input name="empty" value="">
+ <input name="empty" value="">
+ </form>
+ </iron-form>
+ <iron-form id="native-inputs-empty">
+ <form action="/get" method="get">
+ <input type="text" name="input1">
+ </form>
+ </iron-form>
+ <iron-form id="custom-checkboxes">
+ <form action="/get" method="get">
+ <paper-checkbox name="check1" checked></paper-checkbox> <!-- default value is "on" -->
+ <paper-checkbox name="check1" value="1"></paper-checkbox>
+ <paper-checkbox name="check1" value="2" checked></paper-checkbox>
+ <paper-checkbox name="check2" value="3" checked></paper-checkbox>
+ <paper-checkbox name="check3" value="4"></paper-checkbox>
+ </form>
+ </iron-form>
+ <iron-form id="custom-inputs">
+ <form action="/get" method="get">
+ <paper-input name="input1" value=""></paper-input>
+ <paper-input name="input1" value="foo"></paper-input>
+ <paper-input name="input1" value="zag"></paper-input>
+ <paper-input name="input2" value="bar"></paper-input>
+ <paper-input type="password" name="pass1" value="pass"></paper-input>
+ <paper-input type="number" name="number1" value="35"></paper-input>
+ <paper-input name="empty" value=""></paper-input>
+ <paper-input name="empty" value=""></paper-input>
+ <x-input-wrapper></x-input-wrapper>
+ </form>
+ </iron-form>
+ <iron-form id="nested-elements">
+ <form action="/get" method="get">
+ <div>
+ <input type="text" name="input1" value="i1">
+ </div>
+ <div>
+ <paper-input name="paper-input1" value="p1"></paper-input>
+ </div>
+ <div>
+ <p>
+ <div>
+ <input type="text" name="input2" value="i2">
+ <paper-input name="paper-input2" value="p2"></paper-input>
+ </div>
+ </p>
+ </div>
+ </form>
+ </iron-form>
+ <iron-form id="duplicate-names">
+ <form action="/get" method="get">
+ <input name="input1" value="">
+ <input name="input1" value="foo">
+ <paper-input name="input1" value=""></paper-input>
+ <paper-input name="input1" value="bar"></paper-input>
+ <input name="empty" value="">
+ <input name="empty" value="">
+ <paper-input name="empty" value=""></paper-input>
+ <paper-input name="empty" value=""></paper-input>
+ </form>
+ </iron-form>
+
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="validation">
+ <template>
+ <div>
+ <iron-form id="native-required">
+ <form action="/get" method="get">
+ <input required>
+ </form>
+ </iron-form>
+
+ <iron-form id="native-invalid">
+ <form action="/get" method="get">
+ <input pattern="aa" value="b">
+ </form>
+ </iron-form>
+
+ <iron-form id="custom-required">
+ <form action="/get" method="get">
+ <paper-input required></paper-input>
+ </form>
+ </iron-form>
+
+ <iron-form id="custom-invalid">
+ <form action="/get" method="get">
+ <paper-input pattern="aa" value="b"></paper-input>
+ </form>
+ </iron-form>
+
+ <iron-form id="mixed-invalid">
+ <form action="/get" method="get">
+ <input pattern="aa" value="b">
+ <paper-input pattern="aa" value="b"></paper-input>
+ </form>
+ </iron-form>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="submission">
+ <template>
+ <iron-form>
+ <form action="/get" method="get">
+ <input type="checkbox" name="check1" checked>
+ <input type="submit">
+ <input type="button">
+ <paper-button></paper-button>
+ </form>
+ </iron-form>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="resetting">
+ <template>
+ <iron-form>
+ <form action="/get" method="get">
+ <input type="input" name="input1" id="input1" value="input1">
+ <input type="input" name="input2" id="input2">
+ <input type="checkbox" name="check1" id="check1" checked>
+ <input type="checkbox" name="check2" id="check2">
+ <input type="radio" name="radio1" id="radio1" checked>
+ <input type="radio" name="radio2" id="radio2">
+ <paper-checkbox name="papercheck1" id="papercheck1" checked></paper-checkbox>
+ <paper-checkbox name="papercheck2" id="papercheck2"></paper-checkbox>
+ <paper-input name="paper1" id="paper1" value="paper1"></paper-input>
+ <paper-input name="paper2" id="paper2" value=""></paper-input>
+ <input type="reset">
+ </form>
+ </iron-form>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="content-type">
+ <template>
+ <div>
+ <iron-form id="simple-form">
+ <form action="/valid/url" method="post">
+ <paper-input name="paper1" value="value1"></paper-input>
+ <paper-input name="paper2" value="value2"></paper-input>
+ </form>
+ </iron-form>
+ <iron-form id="json-form">
+ <form action="/valid/url" method="post" enctype="application/json">
+ <paper-input name="paper1" value="value1"></paper-input>
+ <paper-input name="paper2" value="value2"></paper-input>
+ </form>
+ </iron-form>
+ <iron-form id="plain-form">
+ <form action="/valid/url" method="post" enctype="text/plain">
+ <paper-input name="paper1" value="value1"></paper-input>
+ <paper-input name="paper2" value="value2"></paper-input>
+ </form>
+ </iron-form>
+ </div>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('serialization', function() {
+ var f;
+ var server;
+
+ suiteSetup(function () {
+ Polymer({is: 'x-input-wrapper'});
+ });
+
+ setup(function() {
+ f = fixture('serialization');
+
+ server = sinon.fakeServer.create();
+ server.respondWith(
+ 'GET',
+ /\/get.*/,
+ [
+ 200,
+ '{"Content-Type":"application/json"}',
+ '{"success":true}'
+ ]
+ );
+ });
+
+ teardown(function() {
+ server.restore();
+ });
+
+ test('serializes native checkboxes', function(done) {
+ var form = f.querySelector('#native-checkboxes');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?check1=on&check1=2&check2=3');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes native radio buttons', function(done) {
+ var form = f.querySelector('#native-radios');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?radio1=on&radio2=3');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes native buttons', function(done) {
+ var form = f.querySelector('#native-buttons');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes native selects', function(done) {
+ var form = f.querySelector('#native-selects');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?select1=1&select1=2&select2=1');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes native inputs', function(done) {
+ var form = f.querySelector('#native-inputs');
+
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?input1=&input1=foo&input1=zag&input2=bar&pass1=pass&number1=35&empty=&empty=');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes empty native inputs', function(done) {
+ var form = f.querySelector('#native-inputs-empty');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?input1=');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes custom checkboxes', function(done) {
+ var form = f.querySelector('#custom-checkboxes');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?check1=on&check1=2&check2=3');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes custom inputs', function(done) {
+ var form = f.querySelector('#custom-inputs');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?input1=&input1=foo&input1=zag&input2=bar&pass1=pass&number1=35&empty=&empty=&check_wrapped=foo');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes elements deeply nested in divs', function(done) {
+ var form = f.querySelector('#nested-elements');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?input1=i1&paper-input1=p1&input2=i2&paper-input2=p2');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('serializes elements with duplicate names', function(done) {
+ var form = f.querySelector('#duplicate-names');
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?input1=&input1=foo&input1=&input1=bar&empty=&empty=&empty=&empty=');
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+ });
+
+
+ suite('validation', function() {
+ var f;
+ var server;
+
+ setup(function() {
+ f = fixture('validation');
+
+ server = sinon.fakeServer.create();
+ server.respondWith(
+ 'GET',
+ /\/get.*/,
+ [
+ 200,
+ '{"Content-Type":"application/json"}',
+ '{"success":true}'
+ ]
+ );
+ });
+
+ teardown(function() {
+ server.restore();
+ });
+
+ test('fires iron-form-invalid if it can\'t submit', function(done) {
+ var form = f.querySelector('#mixed-invalid');
+ form.addEventListener('iron-form-invalid', function(event) {
+ expect(form.validate()).to.be.equal(false);
+ done();
+ });
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ expect(form.validate()).to.be.equal(false);
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('<input required> is validated and does not submit the form', function(done) {
+ var form = f.querySelector('#native-required');
+
+ var responses = 0;
+ form.addEventListener('iron-form-response', function(event) {
+ responses++;
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ expect(form.validate()).to.be.equal(false);
+ form.submit();
+ server.respond();
+ });
+
+ setTimeout(function() {
+ expect(responses).to.be.equal(0);
+ done();
+ }, 200);
+ });
+
+ test('invalid <input> but not required is validated and does not submit the form', function(done) {
+ var form = f.querySelector('#native-invalid');
+
+ var responses = 0;
+ form.addEventListener('iron-form-response', function(event) {
+ responses++;
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ expect(form.validate()).to.be.equal(false);
+ form.submit();
+ server.respond();
+ });
+
+ setTimeout(function() {
+ expect(responses).to.be.equal(0);
+ done();
+ }, 200);
+ });
+
+ test('<paper-input required> is validated and does not submit the form', function(done) {
+ var form = f.querySelector('#custom-required');
+
+ var responses = 0;
+ form.addEventListener('iron-form-response', function(event) {
+ responses++;
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.validate();
+ expect(form.validate()).to.be.equal(false);
+ form.submit();
+ server.respond();
+ });
+
+ setTimeout(function() {
+ expect(responses).to.be.equal(0);
+ done();
+ }, 200);
+ });
+
+ test('invalid <paper-input> but not required is validated and does not submit the form', function(done) {
+ var form = f.querySelector('#custom-invalid');
+
+ var responses = 0;
+ form.addEventListener('iron-form-response', function(event) {
+ responses++;
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ expect(form.validate()).to.be.equal(false);
+ form.submit();
+ server.respond();
+ });
+
+ setTimeout(function() {
+ expect(responses).to.be.equal(0);
+ done();
+ }, 200);
+ });
+ });
+
+ suite('submission', function() {
+ var form;
+ var server;
+
+ setup(function() {
+ form = fixture('submission');
+
+ server = sinon.fakeServer.create();
+ server.respondWith(
+ 'GET',
+ /\/get.*/,
+ [
+ 200,
+ '{"Content-Type":"application/json"}',
+ '{"success":true}'
+ ]
+ );
+ server.respondWith(
+ 'POST',
+ /\/post.*/,
+ [
+ 200,
+ '{"Content-Type":"application/json"}',
+ '{"success":true}'
+ ]
+ );
+ server.respondWith(
+ 'GET',
+ /\/error.*/,
+ [
+ 404,
+ '{"Content-Type":"application/text"}',
+ '{"success":false}'
+ ]
+ );
+ });
+
+ teardown(function() {
+ server.restore();
+ });
+
+ test('calling submit() on a form with method=get', function(done) {
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('calling submit() on a form with method=post', function(done) {
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form._form.setAttribute('method', 'POST');
+ form._form.setAttribute('action', '/post');
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('calling submit() on a form with method unset', function(done) {
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form._form.removeAttribute('method');
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('pressing an <input type=submit> submits the form', function(done) {
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form._form.querySelector('input[type=submit]').click();
+ server.respond();
+ });
+ });
+
+ test('pressing an <input type=button> with an event handler submits the form', function(done) {
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ var button = form._form.querySelector('input[type=button]');
+ button.addEventListener('click', function() {
+ form.submit();
+ });
+ button.click();
+
+ server.respond();
+ });
+ });
+
+ test('pressing a paper-button with an event handler submits the form', function(done) {
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ var button = form._form.querySelector('paper-button');
+ button.addEventListener('click', function() {
+ form.submit();
+ });
+ button.click();
+
+ server.respond();
+ });
+ });
+
+ test('can modify the request in the presubmit', function(done) {
+ var submitted = false;
+ var presubmitted = false;
+
+ form.addEventListener('iron-form-submit', function() {
+ submitted = true;
+ });
+ form.addEventListener('iron-form-presubmit', function() {
+ presubmitted = true;
+ this.request.params = {batman: true};
+ });
+
+ form.addEventListener('iron-form-response', function(event) {
+ expect(submitted).to.be.equal(true);
+ expect(presubmitted).to.be.equal(true);
+
+ // We have changed the json parameters
+ expect(event.detail.url).to.contain('batman=true');
+
+ var response = event.detail.response;
+ expect(response).to.be.ok;
+ expect(response).to.be.an('object');
+ expect(response.success).to.be.equal(true);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+
+ test('can do a custom submission in the presubmit', function(done) {
+ var presubmitted = false;
+ // Since we are not using the normal form submission, these events should
+ // never be called.
+ var formResponseHandler = sinon.spy();
+ form.addEventListener('iron-form-response', formResponseHandler);
+ var formSubmitHandler = sinon.spy();
+ form.addEventListener('iron-form-submit', formSubmitHandler);
+
+ form.addEventListener('iron-form-presubmit', function(event) {
+ presubmitted = true;
+ event.preventDefault();
+ // Your custom submission logic could go here (like using Firebase).
+ // In this case, fire a custom event as a an example.
+ this.fire('custom-form-submit');
+ });
+ form.addEventListener('custom-form-submit', function(event) {
+ expect(presubmitted).to.be.equal(true);
+ expect(formResponseHandler.callCount).to.be.equal(0);
+ expect(formSubmitHandler.callCount).to.be.equal(0);
+ done();
+ });
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ });
+ });
+
+ test('can relay errors', function(done) {
+ form.addEventListener('iron-form-error', function(event) {
+ var error = event.detail;
+ expect(error).to.be.ok;
+ expect(error).to.be.an('object');
+ expect(error.error).to.be.ok;
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form._form.setAttribute('action', '/error');
+ form.submit();
+ server.respond();
+ });
+ });
+ });
+
+ suite('resetting', function() {
+ test('can reset a form', function(done) {
+ var form = fixture('resetting');
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ var initial = form.serializeForm();
+ expect(JSON.stringify(initial)).to.be.equal('{"input1":"input1","input2":"","check1":"on","radio1":"on","papercheck1":"on","paper1":"paper1","paper2":""}');
+
+ // Modify all the values, flip all the inputs.
+ document.getElementById('input1').value = 'input1++';
+ document.getElementById('input2').value = 'input2++';
+ document.getElementById('check1').checked = false;
+ document.getElementById('check2').checked = true;
+ document.getElementById('radio1').checked = false;
+ document.getElementById('radio2').checked = true;
+ document.getElementById('papercheck1').checked = false;
+ document.getElementById('papercheck2').checked = true;
+ document.getElementById('paper1').value = 'paper1++';
+ document.getElementById('paper2').value = 'paper2++';
+
+ var updated = form.serializeForm();
+ expect(JSON.stringify(updated)).to.not.be.equal(initial);
+ form.reset();
+ var final = form.serializeForm();
+ expect(JSON.stringify(initial)).to.be.equal(JSON.stringify(final));
+ done();
+ });
+ });
+ });
+
+ suite('dynamically created', function() {
+ var server;
+
+ setup(function() {
+ server = sinon.fakeServer.create();
+ server.respondWith(
+ 'GET',
+ /\/get.*/,
+ [
+ 200,
+ '{"Content-Type":"application/json"}',
+ '{"success":true}'
+ ]
+ );
+ });
+
+ teardown(function() {
+ server.restore();
+ });
+
+ test('submits a form', function(done) {
+ var form = document.createElement('iron-form');
+ // Need to add to the document so observeNodes runs.
+ document.body.appendChild(form);
+
+ var nativeForm = document.createElement('form');
+ nativeForm.action = '/get';
+ nativeForm.method = 'get';
+
+ var input = document.createElement('input');
+ nativeForm.appendChild(input);
+ input.required = true;
+ input.name = 'foo';
+ Polymer.dom(form).appendChild(nativeForm);
+
+ form.addEventListener('iron-form-response', function(event) {
+ expect(event.detail.url).to.equal('/get?foo=bar');
+ expect(event.detail.response.success).to.be.equal(true);
+ document.body.removeChild(form);
+ done();
+ });
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ expect(form.validate()).to.be.equal(false);
+ input.value = 'bar';
+ expect(form.validate()).to.be.equal(true);
+ form.submit();
+ server.respond();
+ }, 1);
+ });
+ });
+
+ suite('content type', function() {
+ var server;
+ var f;
+
+ setup(function() {
+ server = sinon.fakeServer.create();
+ f = fixture('content-type');
+ });
+
+ teardown(function() {
+ server.restore();
+ });
+
+ test('submits a form with text/plain', function(done) {
+ var form = f.querySelector('#plain-form');
+
+ server.respondWith(
+ 'POST',
+ /\/valid\/url.*/,
+ function (request) {
+ expect(request.requestHeaders).to.deep.equal({
+ "content-type": "text/plain;charset=utf-8",
+ "accept": "application/json"});
+ expect(request.requestBody).to.deep.equal({
+ "paper1": "value1",
+ "paper2": "value2"});
+ done();
+ }
+ );
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+ test('submits a form with application/json', function(done) {
+ var form = f.querySelector('#json-form');
+
+ server.respondWith(
+ 'POST',
+ /\/valid\/url.*/,
+ function (request) {
+ expect(request.requestHeaders).to.deep.equal({
+ "content-type": "application/json;charset=utf-8",
+ "accept": "application/json"});
+ expect(request.requestBody).to.equal(
+ '{"paper1":"value1","paper2":"value2"}');
+ done();
+ }
+ );
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+ test('submits a form with application/x-www-form-urlencoded', function(done) {
+ var form = f.querySelector('#simple-form');
+
+ server.respondWith(
+ 'POST',
+ /\/valid\/url.*/,
+ function (request) {
+ expect(request.requestHeaders).to.deep.equal({
+ "content-type": "application/x-www-form-urlencoded;charset=utf-8",
+ "accept": "application/json"});
+ expect(request.requestBody).to.equal(
+ 'paper1=value1&paper2=value2');
+ done();
+ }
+ );
+
+ // Wait one tick for observeNodes.
+ Polymer.Base.async(function() {
+ form.submit();
+ server.respond();
+ });
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-form/test/index.html b/catapult/third_party/polymer/components/iron-form/test/index.html
new file mode 100644
index 00000000..d71cc47c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-form/test/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="utf-8">
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html?wc-shadydom=true&wc-ce=true',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-icon/.bower.json b/catapult/third_party/polymer/components/iron-icon/.bower.json
new file mode 100644
index 00000000..1fc1db97
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/.bower.json
@@ -0,0 +1,45 @@
+{
+ "name": "iron-icon",
+ "private": true,
+ "version": "1.0.13",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "An element that supports displaying an icon",
+ "main": "iron-icon.html",
+ "author": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "icon"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-icon.git"
+ },
+ "ignore": [],
+ "dependencies": {
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "iron-meta": "polymerelements/iron-meta#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
+ "iron-iconset": "polymerelements/iron-iconset#^1.0.0",
+ "iron-icons": "polymerelements/iron-icons#^1.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "homepage": "https://github.com/PolymerElements/iron-icon",
+ "_release": "1.0.13",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.13",
+ "commit": "4ea0cab59cb2bdfd537e72c9cfbd4af099acb5c9"
+ },
+ "_source": "https://github.com/PolymerElements/iron-icon.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-icon"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-icon/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-icon/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..61da83fb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-icon/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-icon/.gitignore b/catapult/third_party/polymer/components/iron-icon/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-icon/.travis.yml b/catapult/third_party/polymer/components/iron-icon/.travis.yml
new file mode 100644
index 00000000..87da386c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ TTp7q3OKEpaFqnqbYczhMd8iXTa1Ya0jOQVq1OhljpJogLWb78qvHLHgnxgMWkw+/KDyE5KHW1CXhYUQa7C9QF2Zn7uoN27+7+4q7HuK3pTuUtqdfstLVuLHQrfK6VqUT4XjSpeMzNX/HxuD3EMBH0bMBR4CIr76sLJOuIL/XF8=
+ - secure: >-
+ damHvQXygRYIJG/8Vmqh7U4zxoi5224JIZiZVQL6I5z//s5zqHq6AqwDyfOoc0zWndJCwE8NvOzKD/lmVYXIsPcY95kkZS45Dbye0krYWUzKnv42rDi/7olXcg647iAEDVhW3BRHmA+opzQtKUpAkXl97DtPVkszLL1ReyNyv3A=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-icon/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-icon/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-icon/README.md b/catapult/third_party/polymer/components/iron-icon/README.md
new file mode 100644
index 00000000..0649f738
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/README.md
@@ -0,0 +1,22 @@
+[![Build status](https://travis-ci.org/PolymerElements/iron-icon.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-icon)
+
+##&lt;iron-icon&gt;
+
+The `iron-icon` element displays an icon. By default an icon renders as a 24px square.
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-icons/iron-icons.html">
+ <link rel="import" href="iron-icon.html">
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<iron-icon icon="menu"></iron-icon>
+<iron-icon src="demo/location.png"></iron-icon>
+```
diff --git a/catapult/third_party/polymer/components/iron-icon/bower.json b/catapult/third_party/polymer/components/iron-icon/bower.json
new file mode 100644
index 00000000..3d1d4120
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "iron-icon",
+ "private": true,
+ "version": "1.0.13",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "An element that supports displaying an icon",
+ "main": "iron-icon.html",
+ "author": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "icon"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-icon.git"
+ },
+ "ignore": [],
+ "dependencies": {
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "iron-meta": "polymerelements/iron-meta#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
+ "iron-iconset": "polymerelements/iron-iconset#^1.0.0",
+ "iron-icons": "polymerelements/iron-icons#^1.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-icon/demo/async.html b/catapult/third_party/polymer/components/iron-icon/demo/async.html
new file mode 100644
index 00000000..eaf40d1b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/demo/async.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-icon demo</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-icon.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html" >
+
+ <style is="custom-style">
+ #loading_message {
+ color: #444;
+ margin-bottom: 16px;
+ }
+ .vertical-section h4 {
+ border-left: 3px solid var(--paper-grey-300);
+ padding-left: 10px;
+ }
+
+ .vertical-section h4:hover {
+ border-left-color: var(--google-blue-700);
+ }
+ </style>
+</head>
+<body>
+ <div class="vertical-section-container centered">
+ <h4>
+ This demo is for an &lt;iron-icon&gt; with an asynchronously loaded
+ iconset.
+ </h4>
+
+ <div id='loading_message'>Waiting to load iconset...</div>
+
+ <div class="vertical-section">
+ <!-- iron-icon using a iron-iconset as its icon source -->
+ <iron-iconset name="example" icons="location" src="location.png" size="24" width="24"></iron-iconset>
+
+ <h4>&lt;iron-icon icon="example:location"&gt;</h4>
+ <iron-icon icon="example:location"></iron-icon>
+
+ </div>
+ </div>
+ <script>
+ setTimeout(function() {
+ // Import the code that powers the iron-iconset asynchronously
+ var linkElem = document.createElement('link');
+ linkElem.setAttribute('rel', 'import');
+ linkElem.setAttribute('href', '../../iron-iconset/iron-iconset.html');
+ document.head.appendChild(linkElem);
+ document.querySelector('#loading_message').innerText = "Iconset Loaded.";
+ }, 1000);
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-icon/demo/index.html b/catapult/third_party/polymer/components/iron-icon/demo/index.html
new file mode 100644
index 00000000..999ed35b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/demo/index.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-icon demo</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-icon.html">
+ <link rel="import" href="../../iron-iconset/iron-iconset.html">
+ <link href="../../paper-styles/demo-pages.html" rel="import">
+
+ <style is="custom-style">
+ .vertical-section h4 {
+ border-left: 3px solid var(--paper-grey-300);
+ padding-left: 10px;
+ }
+
+ .vertical-section h4:hover {
+ border-left-color: var(--google-blue-700);
+ }
+ </style>
+</head>
+<body>
+ <div class="vertical-section-container centered">
+ <h4>This demo is for a single &lt;iron-icon&gt;. If you're looking for the
+ whole set of available icons, check out the <a href="/elements/iron-icons?view=demo:demo/index.html">&lt;iron-icons&gt; demo.</a></h5>
+
+ <div class="vertical-section">
+ <!-- iron-icon using a iron-iconset as its icon source -->
+ <iron-iconset name="example" icons="location" src="location.png" size="24" width="24"></iron-iconset>
+
+ <h4>&lt;iron-icon icon="example:location"&gt;</h4>
+ <iron-icon icon="example:location"></iron-icon>
+
+ <!-- iron-icon using an image url as its icon source -->
+ <h4>&lt;iron-icon src="location.png"&gt;</h4>
+ <iron-icon src="location.png"></iron-icon>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-icon/demo/location.png b/catapult/third_party/polymer/components/iron-icon/demo/location.png
new file mode 100644
index 00000000..9bb74236
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/demo/location.png
Binary files differ
diff --git a/catapult/third_party/polymer/components/iron-icon/hero.svg b/catapult/third_party/polymer/components/iron-icon/hero.svg
new file mode 100755
index 00000000..19f01c2f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/hero.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <circle cx="112" cy="61" r="8"/>
+ <path d="M129,78H95V44h34V78z M97,76h30V46H97V76z"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-icon/index.html b/catapult/third_party/polymer/components/iron-icon/index.html
new file mode 100644
index 00000000..487bb5c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-icon/iron-icon.html b/catapult/third_party/polymer/components/iron-icon/iron-icon.html
new file mode 100644
index 00000000..f2b0d9ad
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/iron-icon.html
@@ -0,0 +1,204 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-meta/iron-meta.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+
+<!--
+
+The `iron-icon` element displays an icon. By default an icon renders as a 24px square.
+
+Example using src:
+
+ <iron-icon src="star.png"></iron-icon>
+
+Example setting size to 32px x 32px:
+
+ <iron-icon class="big" src="big_star.png"></iron-icon>
+
+ <style is="custom-style">
+ .big {
+ --iron-icon-height: 32px;
+ --iron-icon-width: 32px;
+ }
+ </style>
+
+The iron elements include several sets of icons.
+To use the default set of icons, import `iron-icons.html` and use the `icon` attribute to specify an icon:
+
+ <link rel="import" href="/components/iron-icons/iron-icons.html">
+
+ <iron-icon icon="menu"></iron-icon>
+
+To use a different built-in set of icons, import the specific `iron-icons/<iconset>-icons.html`, and
+specify the icon as `<iconset>:<icon>`. For example, to use a communication icon, you would
+use:
+
+ <link rel="import" href="/components/iron-icons/communication-icons.html">
+
+ <iron-icon icon="communication:email"></iron-icon>
+
+You can also create custom icon sets of bitmap or SVG icons.
+
+Example of using an icon named `cherry` from a custom iconset with the ID `fruit`:
+
+ <iron-icon icon="fruit:cherry"></iron-icon>
+
+See [iron-iconset](iron-iconset) and [iron-iconset-svg](iron-iconset-svg) for more information about
+how to create a custom iconset.
+
+See the [iron-icons demo](iron-icons?view=demo:demo/index.html) to see the icons available
+in the various iconsets.
+
+To load a subset of icons from one of the default `iron-icons` sets, you can
+use the [poly-icon](https://poly-icon.appspot.com/) tool. It allows you
+to select individual icons, and creates an iconset from them that you can
+use directly in your elements.
+
+### Styling
+
+The following custom properties are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--iron-icon` | Mixin applied to the icon | {}
+`--iron-icon-width` | Width of the icon | `24px`
+`--iron-icon-height` | Height of the icon | `24px`
+`--iron-icon-fill-color` | Fill color of the svg icon | `currentcolor`
+`--iron-icon-stroke-color` | Stroke color of the svg icon | none
+
+@group Iron Elements
+@element iron-icon
+@demo demo/index.html
+@hero hero.svg
+@homepage polymer.github.io
+-->
+
+<dom-module id="iron-icon">
+ <template>
+ <style>
+ :host {
+ @apply(--layout-inline);
+ @apply(--layout-center-center);
+ position: relative;
+
+ vertical-align: middle;
+
+ fill: var(--iron-icon-fill-color, currentcolor);
+ stroke: var(--iron-icon-stroke-color, none);
+
+ width: var(--iron-icon-width, 24px);
+ height: var(--iron-icon-height, 24px);
+ @apply(--iron-icon);
+ }
+ </style>
+ </template>
+</dom-module>
+<script>
+Polymer({
+ is: 'iron-icon',
+
+ properties: {
+
+ /**
+ * The name of the icon to use. The name should be of the form:
+ * `iconset_name:icon_name`.
+ */
+ icon: {
+ type: String
+ },
+
+ /**
+ * The name of the theme to used, if one is specified by the
+ * iconset.
+ */
+ theme: {
+ type: String
+ },
+
+ /**
+ * If using iron-icon without an iconset, you can set the src to be
+ * the URL of an individual icon image file. Note that this will take
+ * precedence over a given icon attribute.
+ */
+ src: {
+ type: String
+ },
+
+ /**
+ * @type {!Polymer.IronMeta}
+ */
+ _meta: {
+ value: Polymer.Base.create('iron-meta', {type: 'iconset'})
+ }
+
+ },
+
+ observers: [
+ '_updateIcon(_meta, isAttached)',
+ '_updateIcon(theme, isAttached)',
+ '_srcChanged(src, isAttached)',
+ '_iconChanged(icon, isAttached)'
+ ],
+
+ _DEFAULT_ICONSET: 'icons',
+
+ _iconChanged: function(icon) {
+ var parts = (icon || '').split(':');
+ this._iconName = parts.pop();
+ this._iconsetName = parts.pop() || this._DEFAULT_ICONSET;
+ this._updateIcon();
+ },
+
+ _srcChanged: function(src) {
+ this._updateIcon();
+ },
+
+ _usesIconset: function() {
+ return this.icon || !this.src;
+ },
+
+ /** @suppress {visibility} */
+ _updateIcon: function() {
+ if (this._usesIconset()) {
+ if (this._img && this._img.parentNode) {
+ Polymer.dom(this.root).removeChild(this._img);
+ }
+ if (this._iconName === "") {
+ if (this._iconset) {
+ this._iconset.removeIcon(this);
+ }
+ } else if (this._iconsetName && this._meta) {
+ this._iconset = /** @type {?Polymer.Iconset} */ (
+ this._meta.byKey(this._iconsetName));
+ if (this._iconset) {
+ this._iconset.applyIcon(this, this._iconName, this.theme);
+ this.unlisten(window, 'iron-iconset-added', '_updateIcon');
+ } else {
+ this.listen(window, 'iron-iconset-added', '_updateIcon');
+ }
+ }
+ } else {
+ if (this._iconset) {
+ this._iconset.removeIcon(this);
+ }
+ if (!this._img) {
+ this._img = document.createElement('img');
+ this._img.style.width = '100%';
+ this._img.style.height = '100%';
+ this._img.draggable = false;
+ }
+ this._img.src = this.src;
+ Polymer.dom(this.root).appendChild(this._img);
+ }
+ }
+});
+</script>
diff --git a/catapult/third_party/polymer/components/iron-icon/test/index.html b/catapult/third_party/polymer/components/iron-icon/test/index.html
new file mode 100644
index 00000000..4414532e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/test/index.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+</head>
+<body>
+
+ <script>
+ WCT.loadSuites([
+ 'iron-icon.html',
+ 'iron-icon.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-icon/test/iron-icon.html b/catapult/third_party/polymer/components/iron-icon/test/iron-icon.html
new file mode 100644
index 00000000..4dd9a8d2
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icon/test/iron-icon.html
@@ -0,0 +1,246 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-icon</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../iron-icon.html">
+ <link rel="import" href="../../iron-iconset/iron-iconset.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="icon-holder.html">
+
+</head>
+<body>
+
+ <test-fixture id="TrivialIcon">
+ <template>
+ <iron-icon src="../demo/location.png"></iron-icon>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="IconFromIconset">
+ <template>
+ <iron-iconset name="example" icons="location blank" src="location.png" size="24" width="48"></iron-iconset>
+ <iron-icon icon="example:location"></iron-icon>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithoutAnIconSource">
+ <template>
+ <iron-icon icon=""></iron-icon>
+ <iron-icon></iron-icon>
+ <iron-icon src=""></iron-icon>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="UsingAsyncIconset">
+ <template>
+ <iron-icon icon="async:location"></iron-icon>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="AsyncIconset">
+ <template>
+ <iron-iconset name="async" icons="location blank" src="location.png" size="24" width="48"></iron-iconset>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="SrcIconSwitch">
+ <template>
+ <iron-iconset name="example" icons="location blank" src="location.png" size="24" width="48"></iron-iconset>
+ <iron-icon></iron-icon>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ParentForceUpdate">
+ <template>
+ <icon-holder>
+ <iron-icon></iron-icon>
+ </icon-holder>
+ </template>
+ </test-fixture>
+
+ <script>
+function iconElementFor (node) {
+ var nodes = Polymer.dom(node.root).childNodes;
+
+ for (var i = 0; i < nodes.length; ++i) {
+ if (nodes[i].nodeName.match(/DIV|IMG/)) {
+ return nodes[i];
+ }
+ }
+}
+
+function hasIcon (node) {
+ return /png/.test(node.style.backgroundImage);
+}
+
+suite('<iron-icon>', function() {
+ suite('basic behavior', function() {
+ var icon;
+
+ setup(function() {
+ icon = fixture('TrivialIcon');
+ });
+
+ test('can be assigned an icon with the src attribute', function() {
+ expect(iconElementFor(icon)).to.be.ok;
+ expect(iconElementFor(icon).src).to.match(/demo\/location\.png/);
+ });
+
+ test('can change its src dynamically', function() {
+ icon.src = 'foo.png';
+
+ expect(iconElementFor(icon).src).to.match(/foo\.png/);
+ });
+ });
+
+ suite('lifecycle', function() {
+ var icon;
+
+ setup(function() {
+ icon = document.createElement('iron-icon');
+ });
+
+ teardown(function() {
+ if (icon.parentNode != null) {
+ Polymer.dom(icon.parentNode).removeChild(icon);
+ }
+ });
+
+ test('does not create icon until attached', function() {
+ icon.src = 'location.png';
+
+ var children = Polymer.dom(icon.root).querySelectorAll('img,svg');
+
+ expect(children.length).to.be.eql(0);
+ Polymer.dom(document.body).appendChild(icon);
+ Polymer.dom.flush();
+ children = Polymer.dom(icon.root).querySelectorAll('img,svg');
+ expect(children.length).to.be.eql(1);
+ });
+ });
+
+ suite('when paired with an iconset', function() {
+ var icon;
+
+ setup(function() {
+ var elements = fixture('IconFromIconset');
+
+ icon = elements[1];
+ });
+
+ test('can be assigned an icon from the iconset', function() {
+ expect(hasIcon(icon)).to.be.ok;
+ });
+
+ test('can change its icon dynamically', function() {
+ var style = icon.style;
+
+ expect(style.backgroundPosition).to.match(/0(%|px) 0(%|px)/);
+
+ icon.icon = "example:blank";
+
+ expect(style.backgroundPosition).to.match(/-24px 0(%|px)/);
+ });
+ });
+
+ suite('when no icon source is provided', function() {
+ test('will politely wait for an icon source without throwing', function() {
+ document.createElement('iron-icon');
+ fixture('WithoutAnIconSource');
+ });
+ })
+
+ suite('when loading async', function() {
+ test('will get icon after iconset is added', function() {
+ var icon = fixture('UsingAsyncIconset');
+ expect(hasIcon(icon)).to.be.false;
+ return new Promise(function(resolve, reject) {
+ window.addEventListener('iron-iconset-added', function() {
+ if (hasIcon(icon)) {
+ resolve();
+ } else {
+ reject(new Error('icon didn\'t load after iconset loaded'));
+ }
+ });
+ fixture('AsyncIconset');
+ });
+ });
+ });
+
+ suite('when switching between src and icon properties', function() {
+ var icon;
+
+ setup(function() {
+ var elements = fixture('IconFromIconset');
+ icon = elements[1];
+ });
+
+ test('will display the icon if both icon and src are set', function() {
+ icon.src = '../demo/location.png';
+ icon.icon = 'example:location';
+ expect(hasIcon(icon)).to.be.true;
+ expect(iconElementFor(icon)).to.not.exist;
+
+ // Check if it works too it we change the affectation order
+ icon.icon = 'example:location';
+ icon.src = '../demo/location.png';
+ expect(hasIcon(icon)).to.be.true;
+ expect(iconElementFor(icon)).to.not.exist;
+ });
+
+ test('will display the icon when src is defined first and then reset', function() {
+ icon.src = '../demo/location.png';
+ icon.icon = null;
+ icon.src = null;
+ icon.icon = 'example:location';
+ expect(hasIcon(icon)).to.be.true;
+ expect(iconElementFor(icon)).to.not.exist;
+ });
+
+ test('will display the src when icon is defined first and then reset', function() {
+ icon.src = null;
+ icon.icon = 'example:location';
+ icon.src = '../demo/location.png';
+ icon.icon = null;
+ expect(hasIcon(icon)).to.be.false;
+ expect(iconElementFor(icon)).to.exist;
+ });
+
+ test('will display nothing if both properties are unset', function() {
+ icon.src = '../demo/location.png';
+ icon.icon = 'example:location';
+ icon.src = null;
+ icon.icon = null;
+ expect(hasIcon(icon)).to.be.false;
+ expect(iconElementFor(icon)).to.not.exist;
+ });
+ });
+ suite('ancestor direct updates', function() {
+ test('handle properties set before ready', function() {
+ var holder = fixture('ParentForceUpdate');
+ });
+ });
+});
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-icons/.bower.json b/catapult/third_party/polymer/components/iron-icons/.bower.json
new file mode 100644
index 00000000..3e7e2daf
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/.bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "iron-icons",
+ "version": "1.2.1",
+ "description": "A set of icons for use with iron-icon",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "icon"
+ ],
+ "main": "iron-icons.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-icons"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-icons",
+ "dependencies": {
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "polymerelements/iron-iconset-svg#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "polymerelements/iron-component-page#1.0.0",
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "iron-meta": "polymerelements/iron-meta#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "ignore": [
+ "util",
+ "update-icons.sh"
+ ],
+ "_release": "1.2.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.1",
+ "commit": "25b360efd0e751390e1916f012b8e769de0a1353"
+ },
+ "_source": "https://github.com/PolymerElements/iron-icons.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-icons"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-icons/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-icons/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..95ef5d37
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-icons/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-icons/.gitignore b/catapult/third_party/polymer/components/iron-icons/.gitignore
new file mode 100644
index 00000000..bb1944ea
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/.gitignore
@@ -0,0 +1,3 @@
+util/node_modules
+material-design-icons
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-icons/.travis.yml b/catapult/third_party/polymer/components/iron-icons/.travis.yml
new file mode 100644
index 00000000..14377dc0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ L3XJmmXJlYZYyvlKoZ25HpEC1FcTtWejNk2xRQKAH4cg8+oNMjE80OxkaIVHtZlatpLdIIYk79p+9OtGiskXZ1QsAjHtrxWE5YRSSz3nL/XyZqUu7tjiNtrih6PiEvocmwMCCdRGMSXwVl1YoUUYM0DWxHdykd0EMXYYkYe+yQo=
+ - secure: >-
+ PSLkHUoiTj6UxN+7KtZG2miLmeuDuGN4c+ksviIP4/4lh5x7xic7CIeMmf2HLd18MR8CwCHjfMrIrxYF/IusILn6fdQ8rdBw+XhEF7xP+8UqqgF6YjGHs/xPDYYiGtisEc2OOg+vzqXIMhmKTg2vVlhuNq16eggGsRDcsEhEong=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - true || xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then true || wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-icons/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-icons/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-icons/README.md b/catapult/third_party/polymer/components/iron-icons/README.md
new file mode 100644
index 00000000..05d4a3ac
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/README.md
@@ -0,0 +1,49 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-icons.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-icons.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-icons)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-icons)_
+
+
+##&lt;iron-icons&gt;
+
+`iron-icons` is a utility import that includes the definition for the `iron-icon` element, `iron-iconset-svg` element, as well as an import for the default icon set.
+
+The `iron-icons` directory also includes imports for additional icon sets that can be loaded into your project.
+
+Example loading icon set:
+
+```html
+<link rel="import" href="../iron-icons/maps-icons.html">
+```
+
+To use an icon from one of these sets, first prefix your `iron-icon` with the icon set name, followed by a colon, ":", and then the icon id.
+
+Example using the directions-bus icon from the maps icon set:
+
+```html
+<iron-icon icon="maps:directions-bus"></iron-icon>
+```
+
+To load a subset of icons from one of the default `iron-icons` sets, you can
+use the [poly-icon](https://poly-icon.appspot.com/) tool. It allows you
+to select individual icons, and creates an iconset from them that you can
+use directly in your elements.
+
+See [iron-icon](./iron-icon) for more information about working with icons.
+
+See [iron-iconset](./iron-iconset) and [iron-iconset-svg](./iron-iconset-svg) for more information about how to create a custom iconset.
+
+
diff --git a/catapult/third_party/polymer/components/iron-icons/av-icons.html b/catapult/third_party/polymer/components/iron-icons/av-icons.html
new file mode 100644
index 00000000..764d6229
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/av-icons.html
@@ -0,0 +1,96 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="av" size="24">
+<svg><defs>
+<g id="add-to-queue"><path d="M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12zm-5-7v2h-3v3h-2v-3H8v-2h3V7h2v3h3z"/></g>
+<g id="airplay"><path d="M6 22h12l-6-6zM21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V5h18v12h-4v2h4c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></g>
+<g id="album"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"/></g>
+<g id="art-track"><path d="M22 13h-8v-2h8v2zm0-6h-8v2h8V7zm-8 10h8v-2h-8v2zm-2-8v6c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V9c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2zm-1.5 6l-2.25-3-1.75 2.26-1.25-1.51L3.5 15h7z"/></g>
+<g id="av-timer"><path d="M11 17c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1zm0-14v4h2V5.08c3.39.49 6 3.39 6 6.92 0 3.87-3.13 7-7 7s-7-3.13-7-7c0-1.68.59-3.22 1.58-4.42L12 13l1.41-1.41-6.8-6.8v.02C4.42 6.45 3 9.05 3 12c0 4.97 4.02 9 9 9 4.97 0 9-4.03 9-9s-4.03-9-9-9h-1zm7 9c0-.55-.45-1-1-1s-1 .45-1 1 .45 1 1 1 1-.45 1-1zM6 12c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1z"/></g>
+<g id="branding-watermark"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16h-9v-6h9v6z"/></g>
+<g id="call-to-action"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3v-3h18v3z"/></g>
+<g id="closed-caption"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/></g>
+<g id="equalizer"><path d="M10 20h4V4h-4v16zm-6 0h4v-8H4v8zM16 9v11h4V9h-4z"/></g>
+<g id="explicit"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h4v2h-4v2h4v2H9V7h6v2z"/></g>
+<g id="fast-forward"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/></g>
+<g id="fast-rewind"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/></g>
+<g id="featured-play-list"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 8H3V9h9v2zm0-4H3V5h9v2z"/></g>
+<g id="featured-video"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 9H3V5h9v7z"/></g>
+<g id="fiber-dvr"><path d="M17.5 10.5h2v1h-2zm-13 0h2v3h-2zM21 3H3c-1.11 0-2 .89-2 2v14c0 1.1.89 2 2 2h18c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zM8 13.5c0 .85-.65 1.5-1.5 1.5H3V9h3.5c.85 0 1.5.65 1.5 1.5v3zm4.62 1.5h-1.5L9.37 9h1.5l1 3.43 1-3.43h1.5l-1.75 6zM21 11.5c0 .6-.4 1.15-.9 1.4L21 15h-1.5l-.85-2H17.5v2H16V9h3.5c.85 0 1.5.65 1.5 1.5v1z"/></g>
+<g id="fiber-manual-record"><circle cx="12" cy="12" r="8"/></g>
+<g id="fiber-new"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zM8.5 15H7.3l-2.55-3.5V15H3.5V9h1.25l2.5 3.5V9H8.5v6zm5-4.74H11v1.12h2.5v1.26H11v1.11h2.5V15h-4V9h4v1.26zm7 3.74c0 .55-.45 1-1 1h-4c-.55 0-1-.45-1-1V9h1.25v4.51h1.13V9.99h1.25v3.51h1.12V9h1.25v5z"/></g>
+<g id="fiber-pin"><path d="M5.5 10.5h2v1h-2zM20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zM9 11.5c0 .85-.65 1.5-1.5 1.5h-2v2H4V9h3.5c.85 0 1.5.65 1.5 1.5v1zm3.5 3.5H11V9h1.5v6zm7.5 0h-1.2l-2.55-3.5V15H15V9h1.25l2.5 3.5V9H20v6z"/></g>
+<g id="fiber-smart-record"><g><circle cx="9" cy="12" r="8"/><path d="M17 4.26v2.09c2.33.82 4 3.04 4 5.65s-1.67 4.83-4 5.65v2.09c3.45-.89 6-4.01 6-7.74s-2.55-6.85-6-7.74z"/></g></g>
+<g id="forward-10"><path d="M4 13c0 4.4 3.6 8 8 8s8-3.6 8-8h-2c0 3.3-2.7 6-6 6s-6-2.7-6-6 2.7-6 6-6v4l5-5-5-5v4c-4.4 0-8 3.6-8 8zm6.8 3H10v-3.3L9 13v-.7l1.8-.6h.1V16zm4.3-1.8c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1.3.2.5.3.2.3.3.6.1.5.1.8v.7zm-.8-.8v-.5s-.1-.2-.1-.3-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5z"/></g>
+<g id="forward-30"><path d="M9.6 13.5h.4c.2 0 .4-.1.5-.2s.2-.2.2-.4v-.2s-.1-.1-.1-.2-.1-.1-.2-.1h-.5s-.1.1-.2.1-.1.1-.1.2v.2h-1c0-.2 0-.3.1-.5s.2-.3.3-.4.3-.2.4-.2.4-.1.5-.1c.2 0 .4 0 .6.1s.3.1.5.2.2.2.3.4.1.3.1.5v.3s-.1.2-.1.3-.1.2-.2.2-.2.1-.3.2c.2.1.4.2.5.4s.2.4.2.6c0 .2 0 .4-.1.5s-.2.3-.3.4-.3.2-.5.2-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.4-.1-.6h.8v.2s.1.1.1.2.1.1.2.1h.5s.1-.1.2-.1.1-.1.1-.2v-.5s-.1-.1-.1-.2-.1-.1-.2-.1h-.6v-.7zm5.7.7c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1.3.2.5.3.2.3.3.6.1.5.1.8v.7zm-.9-.8v-.5s-.1-.2-.1-.3-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5zM4 13c0 4.4 3.6 8 8 8s8-3.6 8-8h-2c0 3.3-2.7 6-6 6s-6-2.7-6-6 2.7-6 6-6v4l5-5-5-5v4c-4.4 0-8 3.6-8 8z"/></g>
+<g id="forward-5"><path d="M4 13c0 4.4 3.6 8 8 8s8-3.6 8-8h-2c0 3.3-2.7 6-6 6s-6-2.7-6-6 2.7-6 6-6v4l5-5-5-5v4c-4.4 0-8 3.6-8 8zm6.7.9l.2-2.2h2.4v.7h-1.7l-.1.9s.1 0 .1-.1.1 0 .1-.1.1 0 .2 0h.2c.2 0 .4 0 .5.1s.3.2.4.3.2.3.3.5.1.4.1.6c0 .2 0 .4-.1.5s-.1.3-.3.5-.3.2-.5.3-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.3-.1-.5h.8c0 .2.1.3.2.4s.2.1.4.1c.1 0 .2 0 .3-.1l.2-.2s.1-.2.1-.3v-.6l-.1-.2-.2-.2s-.2-.1-.3-.1h-.2s-.1 0-.2.1-.1 0-.1.1-.1.1-.1.1h-.6z"/></g>
+<g id="games"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"/></g>
+<g id="hd"><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-8 12H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm2-6h4c.55 0 1 .45 1 1v4c0 .55-.45 1-1 1h-4V9zm1.5 4.5h2v-3h-2v3z"/></g>
+<g id="hearing"><path d="M17 20c-.29 0-.56-.06-.76-.15-.71-.37-1.21-.88-1.71-2.38-.51-1.56-1.47-2.29-2.39-3-.79-.61-1.61-1.24-2.32-2.53C9.29 10.98 9 9.93 9 9c0-2.8 2.2-5 5-5s5 2.2 5 5h2c0-3.93-3.07-7-7-7S7 5.07 7 9c0 1.26.38 2.65 1.07 3.9.91 1.65 1.98 2.48 2.85 3.15.81.62 1.39 1.07 1.71 2.05.6 1.82 1.37 2.84 2.73 3.55.51.23 1.07.35 1.64.35 2.21 0 4-1.79 4-4h-2c0 1.1-.9 2-2 2zM7.64 2.64L6.22 1.22C4.23 3.21 3 5.96 3 9s1.23 5.79 3.22 7.78l1.41-1.41C6.01 13.74 5 11.49 5 9s1.01-4.74 2.64-6.36zM11.5 9c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5-2.5 1.12-2.5 2.5z"/></g>
+<g id="high-quality"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 11H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/></g>
+<g id="library-add"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/></g>
+<g id="library-books"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/></g>
+<g id="library-music"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 5h-3v5.5c0 1.38-1.12 2.5-2.5 2.5S10 13.88 10 12.5s1.12-2.5 2.5-2.5c.57 0 1.08.19 1.5.51V5h4v2zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6z"/></g>
+<g id="loop"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></g>
+<g id="mic"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></g>
+<g id="mic-none"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1.2-9.1c0-.66.54-1.2 1.2-1.2.66 0 1.2.54 1.2 1.2l-.01 6.2c0 .66-.53 1.2-1.19 1.2-.66 0-1.2-.54-1.2-1.2V4.9zm6.5 6.1c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></g>
+<g id="mic-off"><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"/></g>
+<g id="movie"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"/></g>
+<g id="music-video"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM8 15c0-1.66 1.34-3 3-3 .35 0 .69.07 1 .18V6h5v2h-3v7.03c-.02 1.64-1.35 2.97-3 2.97-1.66 0-3-1.34-3-3z"/></g>
+<g id="new-releases"><path d="M23 12l-2.44-2.78.34-3.68-3.61-.82-1.89-3.18L12 3 8.6 1.54 6.71 4.72l-3.61.81.34 3.68L1 12l2.44 2.78-.34 3.69 3.61.82 1.89 3.18L12 21l3.4 1.46 1.89-3.18 3.61-.82-.34-3.68L23 12zm-10 5h-2v-2h2v2zm0-4h-2V7h2v6z"/></g>
+<g id="not-interested"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"/></g>
+<g id="note"><path d="M22 10l-6-6H4c-1.1 0-2 .9-2 2v12.01c0 1.1.9 1.99 2 1.99l16-.01c1.1 0 2-.89 2-1.99v-8zm-7-4.5l5.5 5.5H15V5.5z"/></g>
+<g id="pause"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></g>
+<g id="pause-circle-filled"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z"/></g>
+<g id="pause-circle-outline"><path d="M9 16h2V8H9v8zm3-14C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm1-4h2V8h-2v8z"/></g>
+<g id="play-arrow"><path d="M8 5v14l11-7z"/></g>
+<g id="play-circle-filled"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/></g>
+<g id="play-circle-outline"><path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="playlist-add"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"/></g>
+<g id="playlist-add-check"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zM2 16h8v-2H2v2zm19.5-4.5L23 13l-6.99 7-4.51-4.5L13 14l3.01 3 5.49-5.5z"/></g>
+<g id="playlist-play"><path d="M19 9H2v2h17V9zm0-4H2v2h17V5zM2 15h13v-2H2v2zm15-2v6l5-3-5-3z"/></g>
+<g id="queue"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/></g>
+<g id="queue-music"><path d="M15 6H3v2h12V6zm0 4H3v2h12v-2zM3 16h8v-2H3v2zM17 6v8.18c-.31-.11-.65-.18-1-.18-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3V8h3V6h-5z"/></g>
+<g id="queue-play-next"><path d="M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h2v-2H3V5h18v8h2V5c0-1.11-.9-2-2-2zm-8 7V7h-2v3H8v2h3v3h2v-3h3v-2h-3zm11 8l-4.5 4.5L18 21l3-3-3-3 1.5-1.5L24 18z"/></g>
+<g id="radio"><path d="M3.24 6.15C2.51 6.43 2 7.17 2 8v12c0 1.1.89 2 2 2h16c1.11 0 2-.9 2-2V8c0-1.11-.89-2-2-2H8.3l8.26-3.34L15.88 1 3.24 6.15zM7 20c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm13-8h-2v-2h-2v2H4V8h16v4z"/></g>
+<g id="recent-actors"><path d="M21 5v14h2V5h-2zm-4 14h2V5h-2v14zM14 5H2c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zM8 7.75c1.24 0 2.25 1.01 2.25 2.25S9.24 12.25 8 12.25 5.75 11.24 5.75 10 6.76 7.75 8 7.75zM12.5 17h-9v-.75c0-1.5 3-2.25 4.5-2.25s4.5.75 4.5 2.25V17z"/></g>
+<g id="remove-from-queue"><path d="M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12zm-5-7v2H8v-2h8z"/></g>
+<g id="repeat"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></g>
+<g id="repeat-one"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z"/></g>
+<g id="replay"><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></g>
+<g id="replay-10"><path d="M12 5V1L7 6l5 5V7c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8zm-1.1 11H10v-3.3L9 13v-.7l1.8-.6h.1V16zm4.3-1.8c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1c.2.1.3.2.5.3s.2.3.3.6.1.5.1.8v.7zm-.9-.8v-.5s-.1-.2-.1-.3-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5z"/></g>
+<g id="replay-30"><path d="M12 5V1L7 6l5 5V7c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8zm-2.4 8.5h.4c.2 0 .4-.1.5-.2s.2-.2.2-.4v-.2s-.1-.1-.1-.2-.1-.1-.2-.1h-.5s-.1.1-.2.1-.1.1-.1.2v.2h-1c0-.2 0-.3.1-.5s.2-.3.3-.4.3-.2.4-.2.4-.1.5-.1c.2 0 .4 0 .6.1s.3.1.5.2.2.2.3.4.1.3.1.5v.3s-.1.2-.1.3-.1.2-.2.2-.2.1-.3.2c.2.1.4.2.5.4s.2.4.2.6c0 .2 0 .4-.1.5s-.2.3-.3.4-.3.2-.5.2-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.4-.1-.6h.8v.2s.1.1.1.2.1.1.2.1h.5s.1-.1.2-.1.1-.1.1-.2v-.5s-.1-.1-.1-.2-.1-.1-.2-.1h-.6v-.7zm5.7.7c0 .3 0 .6-.1.8l-.3.6s-.3.3-.5.3-.4.1-.6.1-.4 0-.6-.1-.3-.2-.5-.3-.2-.3-.3-.6-.1-.5-.1-.8v-.7c0-.3 0-.6.1-.8l.3-.6s.3-.3.5-.3.4-.1.6-.1.4 0 .6.1.3.2.5.3.2.3.3.6.1.5.1.8v.7zm-.8-.8v-.5c0-.1-.1-.2-.1-.3s-.1-.1-.2-.2-.2-.1-.3-.1-.2 0-.3.1l-.2.2s-.1.2-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3-.1l.2-.2s.1-.2.1-.3v-1.5z"/></g>
+<g id="replay-5"><path d="M12 5V1L7 6l5 5V7c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8zm-1.3 8.9l.2-2.2h2.4v.7h-1.7l-.1.9s.1 0 .1-.1.1 0 .1-.1.1 0 .2 0h.2c.2 0 .4 0 .5.1s.3.2.4.3.2.3.3.5.1.4.1.6c0 .2 0 .4-.1.5s-.1.3-.3.5-.3.2-.4.3-.4.1-.6.1c-.2 0-.4 0-.5-.1s-.3-.1-.5-.2-.2-.2-.3-.4-.1-.3-.1-.5h.8c0 .2.1.3.2.4s.2.1.4.1c.1 0 .2 0 .3-.1l.2-.2s.1-.2.1-.3v-.6l-.1-.2-.2-.2s-.2-.1-.3-.1h-.2s-.1 0-.2.1-.1 0-.1.1-.1.1-.1.1h-.7z"/></g>
+<g id="shuffle"><path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/></g>
+<g id="skip-next"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></g>
+<g id="skip-previous"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></g>
+<g id="slow-motion-video"><path d="M13.05 9.79L10 7.5v9l3.05-2.29L16 12zm0 0L10 7.5v9l3.05-2.29L16 12zm0 0L10 7.5v9l3.05-2.29L16 12zM11 4.07V2.05c-2.01.2-3.84 1-5.32 2.21L7.1 5.69c1.11-.86 2.44-1.44 3.9-1.62zM5.69 7.1L4.26 5.68C3.05 7.16 2.25 8.99 2.05 11h2.02c.18-1.46.76-2.79 1.62-3.9zM4.07 13H2.05c.2 2.01 1 3.84 2.21 5.32l1.43-1.43c-.86-1.1-1.44-2.43-1.62-3.89zm1.61 6.74C7.16 20.95 9 21.75 11 21.95v-2.02c-1.46-.18-2.79-.76-3.9-1.62l-1.42 1.43zM22 12c0 5.16-3.92 9.42-8.95 9.95v-2.02C16.97 19.41 20 16.05 20 12s-3.03-7.41-6.95-7.93V2.05C18.08 2.58 22 6.84 22 12z"/></g>
+<g id="snooze"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm-3-9h3.63L9 15.2V17h6v-2h-3.63L15 10.8V9H9v2z"/></g>
+<g id="sort-by-alpha"><path d="M14.94 4.66h-4.72l2.36-2.36zm-4.69 14.71h4.66l-2.33 2.33zM6.1 6.27L1.6 17.73h1.84l.92-2.45h5.11l.92 2.45h1.84L7.74 6.27H6.1zm-1.13 7.37l1.94-5.18 1.94 5.18H4.97zm10.76 2.5h6.12v1.59h-8.53v-1.29l5.92-8.56h-5.88v-1.6h8.3v1.26l-5.93 8.6z"/></g>
+<g id="stop"><path d="M6 6h12v12H6z"/></g>
+<g id="subscriptions"><path d="M20 8H4V6h16v2zm-2-6H6v2h12V2zm4 10v8c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2v-8c0-1.1.9-2 2-2h16c1.1 0 2 .9 2 2zm-6 4l-6-3.27v6.53L16 16z"/></g>
+<g id="subtitles"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM4 12h4v2H4v-2zm10 6H4v-2h10v2zm6 0h-4v-2h4v2zm0-4H10v-2h10v2z"/></g>
+<g id="surround-sound"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM7.76 16.24l-1.41 1.41C4.78 16.1 4 14.05 4 12c0-2.05.78-4.1 2.34-5.66l1.41 1.41C6.59 8.93 6 10.46 6 12s.59 3.07 1.76 4.24zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm5.66 1.66l-1.41-1.41C17.41 15.07 18 13.54 18 12s-.59-3.07-1.76-4.24l1.41-1.41C19.22 7.9 20 9.95 20 12c0 2.05-.78 4.1-2.34 5.66zM12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
+<g id="video-call"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4zM14 13h-3v3H9v-3H6v-2h3V8h2v3h3v2z"/></g>
+<g id="video-label"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 13H3V5h18v11z"/></g>
+<g id="video-library"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 12.5v-9l6 4.5-6 4.5z"/></g>
+<g id="videocam"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></g>
+<g id="videocam-off"><path d="M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z"/></g>
+<g id="volume-down"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"/></g>
+<g id="volume-mute"><path d="M7 9v6h4l5 5V4l-5 5H7z"/></g>
+<g id="volume-off"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></g>
+<g id="volume-up"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></g>
+<g id="web"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z"/></g>
+<g id="web-asset"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm0 14H5V8h14v10z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/bower.json b/catapult/third_party/polymer/components/iron-icons/bower.json
new file mode 100644
index 00000000..97cb222f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/bower.json
@@ -0,0 +1,38 @@
+{
+ "name": "iron-icons",
+ "version": "1.2.1",
+ "description": "A set of icons for use with iron-icon",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "icon"
+ ],
+ "main": "iron-icons.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-icons"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-icons",
+ "dependencies": {
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "polymerelements/iron-iconset-svg#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "polymerelements/iron-component-page#1.0.0",
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "iron-meta": "polymerelements/iron-meta#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "ignore": [
+ "util",
+ "update-icons.sh"
+ ]
+}
diff --git a/catapult/third_party/polymer/components/iron-icons/communication-icons.html b/catapult/third_party/polymer/components/iron-icons/communication-icons.html
new file mode 100644
index 00000000..a9e1839a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/communication-icons.html
@@ -0,0 +1,66 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="communication" size="24">
+<svg><defs>
+<g id="business"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></g>
+<g id="call"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></g>
+<g id="call-end"><path d="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08c-.18-.17-.29-.42-.29-.7 0-.28.11-.53.29-.71C3.34 8.78 7.46 7 12 7s8.66 1.78 11.71 4.67c.18.18.29.43.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.11-.7-.28-.79-.74-1.69-1.36-2.67-1.85-.33-.16-.56-.5-.56-.9v-3.1C15.15 9.25 13.6 9 12 9z"/></g>
+<g id="call-made"><path d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z"/></g>
+<g id="call-merge"><path d="M17 20.41L18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z"/></g>
+<g id="call-missed"><path d="M19.59 7L12 14.59 6.41 9H11V7H3v8h2v-4.59l7 7 9-9z"/></g>
+<g id="call-missed-outgoing"><path d="M3 8.41l9 9 7-7V15h2V7h-8v2h4.59L12 14.59 4.41 7 3 8.41z"/></g>
+<g id="call-received"><path d="M20 5.41L18.59 4 7 15.59V9H5v10h10v-2H8.41z"/></g>
+<g id="call-split"><path d="M14 4l2.29 2.29-2.88 2.88 1.42 1.42 2.88-2.88L20 10V4zm-4 0H4v6l2.29-2.29 4.71 4.7V20h2v-8.41l-5.29-5.3z"/></g>
+<g id="chat"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 9h12v2H6V9zm8 5H6v-2h8v2zm4-6H6V6h12v2z"/></g>
+<g id="chat-bubble"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/></g>
+<g id="chat-bubble-outline"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></g>
+<g id="clear-all"><path d="M5 13h14v-2H5v2zm-2 4h14v-2H3v2zM7 7v2h14V7H7z"/></g>
+<g id="comment"><path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18zM18 14H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></g>
+<g id="contact-mail"><path d="M21 8V7l-3 2-3-2v1l3 2 3-2zm1-5H2C.9 3 0 3.9 0 5v14c0 1.1.9 2 2 2h20c1.1 0 1.99-.9 1.99-2L24 5c0-1.1-.9-2-2-2zM8 6c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H2v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1zm8-6h-8V6h8v6z"/></g>
+<g id="contact-phone"><path d="M22 3H2C.9 3 0 3.9 0 5v14c0 1.1.9 2 2 2h20c1.1 0 1.99-.9 1.99-2L24 5c0-1.1-.9-2-2-2zM8 6c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H2v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1zm3.85-4h1.64L21 16l-1.99 1.99c-1.31-.98-2.28-2.38-2.73-3.99-.18-.64-.28-1.31-.28-2s.1-1.36.28-2c.45-1.62 1.42-3.01 2.73-3.99L21 8l-1.51 2h-1.64c-.22.63-.35 1.3-.35 2s.13 1.37.35 2z"/></g>
+<g id="contacts"><path d="M20 0H4v2h16V0zM4 24h16v-2H4v2zM20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 2.75c1.24 0 2.25 1.01 2.25 2.25s-1.01 2.25-2.25 2.25S9.75 10.24 9.75 9 10.76 6.75 12 6.75zM17 17H7v-1.5c0-1.67 3.33-2.5 5-2.5s5 .83 5 2.5V17z"/></g>
+<g id="dialer-sip"><path d="M17 3h-1v5h1V3zm-2 2h-2V4h2V3h-3v3h2v1h-2v1h3V5zm3-2v5h1V6h2V3h-3zm2 2h-1V4h1v1zm0 10.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.01.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.27-.26.35-.65.24-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1z"/></g>
+<g id="dialpad"><path d="M12 19c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 1c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
+<g id="email"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></g>
+<g id="forum"><path d="M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z"/></g>
+<g id="import-contacts"><path d="M21 5c-1.11-.35-2.33-.5-3.5-.5-1.95 0-4.05.4-5.5 1.5-1.45-1.1-3.55-1.5-5.5-1.5S2.45 4.9 1 6v14.65c0 .25.25.5.5.5.1 0 .15-.05.25-.05C3.1 20.45 5.05 20 6.5 20c1.95 0 4.05.4 5.5 1.5 1.35-.85 3.8-1.5 5.5-1.5 1.65 0 3.35.3 4.75 1.05.1.05.15.05.25.05.25 0 .5-.25.5-.5V6c-.6-.45-1.25-.75-2-1zm0 13.5c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5V8c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5v11.5z"/></g>
+<g id="import-export"><path d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/></g>
+<g id="invert-colors-off"><path d="M20.65 20.87l-2.35-2.35-6.3-6.29-3.56-3.57-1.42-1.41L4.27 4.5 3 5.77l2.78 2.78c-2.55 3.14-2.36 7.76.56 10.69C7.9 20.8 9.95 21.58 12 21.58c1.79 0 3.57-.59 5.03-1.78l2.7 2.7L21 21.23l-.35-.36zM12 19.59c-1.6 0-3.11-.62-4.24-1.76C6.62 16.69 6 15.19 6 13.59c0-1.32.43-2.57 1.21-3.6L12 14.77v4.82zM12 5.1v4.58l7.25 7.26c1.37-2.96.84-6.57-1.6-9.01L12 2.27l-3.7 3.7 1.41 1.41L12 5.1z"/></g>
+<g id="live-help"><path d="M19 2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 16h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 11.9 13 12.5 13 14h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></g>
+<g id="location-off"><path d="M12 6.5c1.38 0 2.5 1.12 2.5 2.5 0 .74-.33 1.39-.83 1.85l3.63 3.63c.98-1.86 1.7-3.8 1.7-5.48 0-3.87-3.13-7-7-7-1.98 0-3.76.83-5.04 2.15l3.19 3.19c.46-.52 1.11-.84 1.85-.84zm4.37 9.6l-4.63-4.63-.11-.11L3.27 3 2 4.27l3.18 3.18C5.07 7.95 5 8.47 5 9c0 5.25 7 13 7 13s1.67-1.85 3.38-4.35L18.73 21 20 19.73l-3.63-3.63z"/></g>
+<g id="location-on"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></g>
+<g id="mail-outline"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/></g>
+<g id="message"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></g>
+<g id="no-sim"><path d="M18.99 5c0-1.1-.89-2-1.99-2h-7L7.66 5.34 19 16.68 18.99 5zM3.65 3.88L2.38 5.15 5 7.77V19c0 1.1.9 2 2 2h10.01c.35 0 .67-.1.96-.26l1.88 1.88 1.27-1.27L3.65 3.88z"/></g>
+<g id="phone"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></g>
+<g id="phonelink-erase"><path d="M13 8.2l-1-1-4 4-4-4-1 1 4 4-4 4 1 1 4-4 4 4 1-1-4-4 4-4zM19 1H9c-1.1 0-2 .9-2 2v3h2V4h10v16H9v-2H7v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2z"/></g>
+<g id="phonelink-lock"><path d="M19 1H9c-1.1 0-2 .9-2 2v3h2V4h10v16H9v-2H7v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm-8.2 10V9.5C10.8 8.1 9.4 7 8 7S5.2 8.1 5.2 9.5V11c-.6 0-1.2.6-1.2 1.2v3.5c0 .7.6 1.3 1.2 1.3h5.5c.7 0 1.3-.6 1.3-1.2v-3.5c0-.7-.6-1.3-1.2-1.3zm-1.3 0h-3V9.5c0-.8.7-1.3 1.5-1.3s1.5.5 1.5 1.3V11z"/></g>
+<g id="phonelink-ring"><path d="M20.1 7.7l-1 1c1.8 1.8 1.8 4.6 0 6.5l1 1c2.5-2.3 2.5-6.1 0-8.5zM18 9.8l-1 1c.5.7.5 1.6 0 2.3l1 1c1.2-1.2 1.2-3 0-4.3zM14 1H4c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 19H4V4h10v16z"/></g>
+<g id="phonelink-setup"><path d="M11.8 12.5v-1l1.1-.8c.1-.1.1-.2.1-.3l-1-1.7c-.1-.1-.2-.2-.3-.1l-1.3.4c-.3-.2-.6-.4-.9-.5l-.2-1.3c0-.1-.1-.2-.3-.2H7c-.1 0-.2.1-.3.2l-.2 1.3c-.3.1-.6.3-.9.5l-1.3-.5c-.1 0-.2 0-.3.1l-1 1.7c-.1.1 0 .2.1.3l1.1.8v1l-1.1.8c-.1.2-.1.3-.1.4l1 1.7c.1.1.2.2.3.1l1.4-.4c.3.2.6.4.9.5l.2 1.3c-.1.1.1.2.2.2h2c.1 0 .2-.1.3-.2l.2-1.3c.3-.1.6-.3.9-.5l1.3.5c.1 0 .2 0 .3-.1l1-1.7c.1-.1 0-.2-.1-.3l-1.1-.9zM8 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM19 1H9c-1.1 0-2 .9-2 2v3h2V4h10v16H9v-2H7v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2z"/></g>
+<g id="portable-wifi-off"><path d="M17.56 14.24c.28-.69.44-1.45.44-2.24 0-3.31-2.69-6-6-6-.79 0-1.55.16-2.24.44l1.62 1.62c.2-.03.41-.06.62-.06 2.21 0 4 1.79 4 4 0 .21-.02.42-.05.63l1.61 1.61zM12 4c4.42 0 8 3.58 8 8 0 1.35-.35 2.62-.95 3.74l1.47 1.47C21.46 15.69 22 13.91 22 12c0-5.52-4.48-10-10-10-1.91 0-3.69.55-5.21 1.47l1.46 1.46C9.37 4.34 10.65 4 12 4zM3.27 2.5L2 3.77l2.1 2.1C2.79 7.57 2 9.69 2 12c0 3.7 2.01 6.92 4.99 8.65l1-1.73C5.61 17.53 4 14.96 4 12c0-1.76.57-3.38 1.53-4.69l1.43 1.44C6.36 9.68 6 10.8 6 12c0 2.22 1.21 4.15 3 5.19l1-1.74c-1.19-.7-2-1.97-2-3.45 0-.65.17-1.25.44-1.79l1.58 1.58L10 12c0 1.1.9 2 2 2l.21-.02.01.01 7.51 7.51L21 20.23 4.27 3.5l-1-1z"/></g>
+<g id="present-to-all"><path d="M21 3H3c-1.11 0-2 .89-2 2v14c0 1.11.89 2 2 2h18c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 16.02H3V4.98h18v14.04zM10 12H8l4-4 4 4h-2v4h-4v-4z"/></g>
+<g id="ring-volume"><path d="M23.71 16.67C20.66 13.78 16.54 12 12 12 7.46 12 3.34 13.78.29 16.67c-.18.18-.29.43-.29.71 0 .28.11.53.29.71l2.48 2.48c.18.18.43.29.71.29.27 0 .52-.11.7-.28.79-.74 1.69-1.36 2.66-1.85.33-.16.56-.5.56-.9v-3.1c1.45-.48 3-.73 4.6-.73s3.15.25 4.6.72v3.1c0 .39.23.74.56.9.98.49 1.87 1.12 2.66 1.85.18.18.43.28.7.28.28 0 .53-.11.71-.29l2.48-2.48c.18-.18.29-.43.29-.71 0-.27-.11-.52-.29-.7zM21.16 6.26l-1.41-1.41-3.56 3.55 1.41 1.41s3.45-3.52 3.56-3.55zM13 2h-2v5h2V2zM6.4 9.81L7.81 8.4 4.26 4.84 2.84 6.26c.11.03 3.56 3.55 3.56 3.55z"/></g>
+<g id="rss-feed"><circle cx="6.18" cy="17.82" r="2.18"/><path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/></g>
+<g id="screen-share"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.11-.9-2-2-2H4c-1.11 0-2 .89-2 2v10c0 1.1.89 2 2 2H0v2h24v-2h-4zm-7-3.53v-2.19c-2.78 0-4.61.85-6 2.72.56-2.67 2.11-5.33 6-5.87V7l4 3.73-4 3.74z"/></g>
+<g id="speaker-phone"><path d="M7 7.07L8.43 8.5c.91-.91 2.18-1.48 3.57-1.48s2.66.57 3.57 1.48L17 7.07C15.72 5.79 13.95 5 12 5s-3.72.79-5 2.07zM12 1C8.98 1 6.24 2.23 4.25 4.21l1.41 1.41C7.28 4 9.53 3 12 3s4.72 1 6.34 2.62l1.41-1.41C17.76 2.23 15.02 1 12 1zm2.86 9.01L9.14 10C8.51 10 8 10.51 8 11.14v9.71c0 .63.51 1.14 1.14 1.14h5.71c.63 0 1.14-.51 1.14-1.14v-9.71c.01-.63-.5-1.13-1.13-1.13zM15 20H9v-8h6v8z"/></g>
+<g id="stay-current-landscape"><path d="M1.01 7L1 17c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2H3c-1.1 0-1.99.9-1.99 2zM19 7v10H5V7h14z"/></g>
+<g id="stay-current-portrait"><path d="M17 1.01L7 1c-1.1 0-1.99.9-1.99 2v18c0 1.1.89 2 1.99 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></g>
+<g id="stay-primary-landscape"><path d="M1.01 7L1 17c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2H3c-1.1 0-1.99.9-1.99 2zM19 7v10H5V7h14z"/></g>
+<g id="stay-primary-portrait"><path d="M17 1.01L7 1c-1.1 0-1.99.9-1.99 2v18c0 1.1.89 2 1.99 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></g>
+<g id="stop-screen-share"><path d="M21.22 18.02l2 2H24v-2h-2.78zm.77-2l.01-10c0-1.11-.9-2-2-2H7.22l5.23 5.23c.18-.04.36-.07.55-.1V7.02l4 3.73-1.58 1.47 5.54 5.54c.61-.33 1.03-.99 1.03-1.74zM2.39 1.73L1.11 3l1.54 1.54c-.4.36-.65.89-.65 1.48v10c0 1.1.89 2 2 2H0v2h18.13l2.71 2.71 1.27-1.27L2.39 1.73zM7 15.02c.31-1.48.92-2.95 2.07-4.06l1.59 1.59c-1.54.38-2.7 1.18-3.66 2.47z"/></g>
+<g id="swap-calls"><path d="M18 4l-4 4h3v7c0 1.1-.9 2-2 2s-2-.9-2-2V8c0-2.21-1.79-4-4-4S5 5.79 5 8v7H2l4 4 4-4H7V8c0-1.1.9-2 2-2s2 .9 2 2v7c0 2.21 1.79 4 4 4s4-1.79 4-4V8h3l-4-4z"/></g>
+<g id="textsms"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM9 11H7V9h2v2zm4 0h-2V9h2v2zm4 0h-2V9h2v2z"/></g>
+<g id="voicemail"><path d="M18.5 6C15.46 6 13 8.46 13 11.5c0 1.33.47 2.55 1.26 3.5H9.74c.79-.95 1.26-2.17 1.26-3.5C11 8.46 8.54 6 5.5 6S0 8.46 0 11.5 2.46 17 5.5 17h13c3.04 0 5.5-2.46 5.5-5.5S21.54 6 18.5 6zm-13 9C3.57 15 2 13.43 2 11.5S3.57 8 5.5 8 9 9.57 9 11.5 7.43 15 5.5 15zm13 0c-1.93 0-3.5-1.57-3.5-3.5S16.57 8 18.5 8 22 9.57 22 11.5 20.43 15 18.5 15z"/></g>
+<g id="vpn-key"><path d="M12.65 10C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H17v4h4v-4h2v-4H12.65zM7 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/demo/index.html b/catapult/third_party/polymer/components/iron-icons/demo/index.html
new file mode 100644
index 00000000..0dfbf365
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/demo/index.html
@@ -0,0 +1,116 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-icons demo</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../iron-flex-layout/iron-flex-layout.html">
+
+ <!-- load default iconset -->
+ <link rel="import" href="../iron-icons.html">
+
+ <!-- load the rest -->
+ <link rel="import" href="../av-icons.html">
+ <link rel="import" href="../communication-icons.html">
+ <link rel="import" href="../device-icons.html">
+ <link rel="import" href="../editor-icons.html">
+ <link rel="import" href="../hardware-icons.html">
+ <link rel="import" href="../image-icons.html">
+ <link rel="import" href="../maps-icons.html">
+ <link rel="import" href="../notification-icons.html">
+ <link rel="import" href="../social-icons.html">
+ <link rel="import" href="../places-icons.html">
+
+ <style is="custom-style">
+ h2 {
+ text-transform: capitalize;
+ }
+
+ iron-icon {
+ transition: all 0.2s;
+ -webkit-transition: all 0.2s;
+ }
+
+ iron-icon:hover {
+ fill: var(--google-yellow-700);
+ }
+
+ .set {
+ margin: auto;
+ padding: 1em 0;
+ border-bottom: 1px solid silver;
+ @apply(--layout-horizontal);
+ @apply(--layout-wrap);
+ }
+
+ .set:last-of-type {
+ border-bottom: none;
+ }
+
+ .set:nth-of-type(4n-3) {
+ color: var(--paper-grey-700);
+ }
+
+ .set:nth-of-type(4n-2) {
+ color: var(--paper-pink-500);
+ }
+
+ .set:nth-of-type(4n-1) {
+ color: var(--google-green-500);
+ }
+
+ .set:nth-of-type(4n) {
+ color: var( --google-blue-500);
+ }
+
+ .container {
+ min-width: 10em;
+ padding: 1em 0.5em;
+ text-align: center;
+ @apply(--layout-vertical);
+ @apply(--layout-center);
+ @apply(--layout-flex);
+ }
+
+ .container > div {
+ margin-top: 0.5em;
+ color: black;
+ font-size: 10px;
+ }
+ </style>
+</head>
+<body>
+ <template is="dom-bind">
+ <iron-meta type="iconset" list="{{iconsets}}"></iron-meta>
+
+ <template is="dom-repeat" items="{{iconsets}}">
+ <h2>{{item.name}}</h2>
+
+ <div class="set">
+ <template is="dom-repeat" items="{{getIconNames(item)}}">
+ <span class="container">
+ <iron-icon icon="{{item}}"></iron-icon>
+ <div>{{item}}</div>
+ </span>
+ </template>
+ </div>
+ </template>
+ </template>
+
+ <script>
+ document.querySelector('[is=dom-bind]').getIconNames = function(iconset) {
+ return iconset.getIconNames();
+ };
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-icons/device-icons.html b/catapult/third_party/polymer/components/iron-icons/device-icons.html
new file mode 100644
index 00000000..190f8962
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/device-icons.html
@@ -0,0 +1,95 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="device" size="24">
+<svg><defs>
+<g id="access-alarm"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12.5 8H11v6l4.75 2.85.75-1.23-4-2.37V8zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></g>
+<g id="access-alarms"><path d="M22 5.7l-4.6-3.9-1.3 1.5 4.6 3.9L22 5.7zM7.9 3.4L6.6 1.9 2 5.7l1.3 1.5 4.6-3.8zM12.5 8H11v6l4.7 2.9.8-1.2-4-2.4V8zM12 4c-5 0-9 4-9 9s4 9 9 9 9-4 9-9-4-9-9-9zm0 16c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/></g>
+<g id="access-time"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></g>
+<g id="add-alarm"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm1-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/></g>
+<g id="airplanemode-active"><path d="M10.18 9"/><path d="M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/></g>
+<g id="airplanemode-inactive"><path d="M13 9V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v3.68l7.83 7.83L21 16v-2l-8-5zM3 5.27l4.99 4.99L2 14v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-3.73L18.73 21 20 19.73 4.27 4 3 5.27z"/></g>
+<g id="battery-20"><path d="M7 17v3.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V17H7z"/><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V17h10V5.33z"/></g>
+<g id="battery-30"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V15h10V5.33z"/><path d="M7 15v5.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V15H7z"/></g>
+<g id="battery-50"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V13h10V5.33z"/><path d="M7 13v7.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V13H7z"/></g>
+<g id="battery-60"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V11h10V5.33z"/><path d="M7 11v9.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V11H7z"/></g>
+<g id="battery-80"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V9h10V5.33z"/><path d="M7 9v11.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V9H7z"/></g>
+<g id="battery-90"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h10V5.33z"/><path d="M7 8v12.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V8H7z"/></g>
+<g id="battery-alert"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zM13 18h-2v-2h2v2zm0-4h-2V9h2v5z"/></g>
+<g id="battery-charging-20"><path d="M11 20v-3H7v3.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V17h-4.4L11 20z"/><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V17h4v-2.5H9L13 7v5.5h2L12.6 17H17V5.33C17 4.6 16.4 4 15.67 4z"/></g>
+<g id="battery-charging-30"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v9.17h2L13 7v5.5h2l-1.07 2H17V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M11 20v-5.5H7v6.17C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V14.5h-3.07L11 20z"/></g>
+<g id="battery-charging-50"><path d="M14.47 13.5L11 20v-5.5H9l.53-1H7v7.17C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V13.5h-2.53z"/><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v8.17h2.53L13 7v5.5h2l-.53 1H17V5.33C17 4.6 16.4 4 15.67 4z"/></g>
+<g id="battery-charging-60"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V11h3.87L13 7v4h4V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M13 12.5h2L11 20v-5.5H9l1.87-3.5H7v9.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V11h-4v1.5z"/></g>
+<g id="battery-charging-80"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V9h4.93L13 7v2h4V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M13 12.5h2L11 20v-5.5H9L11.93 9H7v11.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V9h-4v3.5z"/></g>
+<g id="battery-charging-90"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h5.47L13 7v1h4V5.33C17 4.6 16.4 4 15.67 4z"/><path d="M13 12.5h2L11 20v-5.5H9L12.47 8H7v12.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V8h-4v4.5z"/></g>
+<g id="battery-charging-full"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zM11 20v-5.5H9L13 7v5.5h2L11 20z"/></g>
+<g id="battery-full"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z"/></g>
+<g id="battery-std"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z"/></g>
+<g id="battery-unknown"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zm-2.72 13.95h-1.9v-1.9h1.9v1.9zm1.35-5.26s-.38.42-.67.71c-.48.48-.83 1.15-.83 1.6h-1.6c0-.83.46-1.52.93-2l.93-.94c.27-.27.44-.65.44-1.06 0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5H9c0-1.66 1.34-3 3-3s3 1.34 3 3c0 .66-.27 1.26-.7 1.69z"/></g>
+<g id="bluetooth"><path d="M17.71 7.71L12 2h-1v7.59L6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 5.83l1.88 1.88L13 9.59V5.83zm1.88 10.46L13 18.17v-3.76l1.88 1.88z"/></g>
+<g id="bluetooth-connected"><path d="M7 12l-2-2-2 2 2 2 2-2zm10.71-4.29L12 2h-1v7.59L6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 5.83l1.88 1.88L13 9.59V5.83zm1.88 10.46L13 18.17v-3.76l1.88 1.88zM19 10l-2 2 2 2 2-2-2-2z"/></g>
+<g id="bluetooth-disabled"><path d="M13 5.83l1.88 1.88-1.6 1.6 1.41 1.41 3.02-3.02L12 2h-1v5.03l2 2v-3.2zM5.41 4L4 5.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l4.29-4.29 2.3 2.29L20 18.59 5.41 4zM13 18.17v-3.76l1.88 1.88L13 18.17z"/></g>
+<g id="bluetooth-searching"><path d="M14.24 12.01l2.32 2.32c.28-.72.44-1.51.44-2.33 0-.82-.16-1.59-.43-2.31l-2.33 2.32zm5.29-5.3l-1.26 1.26c.63 1.21.98 2.57.98 4.02s-.36 2.82-.98 4.02l1.2 1.2c.97-1.54 1.54-3.36 1.54-5.31-.01-1.89-.55-3.67-1.48-5.19zm-3.82 1L10 2H9v7.59L4.41 5 3 6.41 8.59 12 3 17.59 4.41 19 9 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM11 5.83l1.88 1.88L11 9.59V5.83zm1.88 10.46L11 18.17v-3.76l1.88 1.88z"/></g>
+<g id="brightness-auto"><path d="M10.85 12.65h2.3L12 9l-1.15 3.65zM20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM14.3 16l-.7-2h-3.2l-.7 2H7.8L11 7h2l3.2 9h-1.9z"/></g>
+<g id="brightness-high"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></g>
+<g id="brightness-low"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></g>
+<g id="brightness-medium"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18V6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"/></g>
+<g id="data-usage"><path d="M13 2.05v3.03c3.39.49 6 3.39 6 6.92 0 .9-.18 1.75-.48 2.54l2.6 1.53c.56-1.24.88-2.62.88-4.07 0-5.18-3.95-9.45-9-9.95zM12 19c-3.87 0-7-3.13-7-7 0-3.53 2.61-6.43 6-6.92V2.05c-5.06.5-9 4.76-9 9.95 0 5.52 4.47 10 9.99 10 3.31 0 6.24-1.61 8.06-4.09l-2.6-1.53C16.17 17.98 14.21 19 12 19z"/></g>
+<g id="developer-mode"><path d="M7 5h10v2h2V3c0-1.1-.9-1.99-2-1.99L7 1c-1.1 0-2 .9-2 2v4h2V5zm8.41 11.59L20 12l-4.59-4.59L14 8.83 17.17 12 14 15.17l1.41 1.42zM10 15.17L6.83 12 10 8.83 8.59 7.41 4 12l4.59 4.59L10 15.17zM17 19H7v-2H5v4c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2v-4h-2v2z"/></g>
+<g id="devices"><path d="M4 6h18V4H4c-1.1 0-2 .9-2 2v11H0v3h14v-3H4V6zm19 2h-6c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 9h-4v-7h4v7z"/></g>
+<g id="dvr"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12zm-2-9H8v2h11V8zm0 4H8v2h11v-2zM7 8H5v2h2V8zm0 4H5v2h2v-2z"/></g>
+<g id="gps-fixed"><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></g>
+<g id="gps-not-fixed"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></g>
+<g id="gps-off"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06c-1.13.12-2.19.46-3.16.97l1.5 1.5C10.16 5.19 11.06 5 12 5c3.87 0 7 3.13 7 7 0 .94-.19 1.84-.52 2.65l1.5 1.5c.5-.96.84-2.02.97-3.15H23v-2h-2.06zM3 4.27l2.04 2.04C3.97 7.62 3.25 9.23 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c1.77-.2 3.38-.91 4.69-1.98L19.73 21 21 19.73 4.27 3 3 4.27zm13.27 13.27C15.09 18.45 13.61 19 12 19c-3.87 0-7-3.13-7-7 0-1.61.55-3.09 1.46-4.27l9.81 9.81z"/></g>
+<g id="graphic-eq"><path d="M7 18h2V6H7v12zm4 4h2V2h-2v20zm-8-8h2v-4H3v4zm12 4h2V6h-2v12zm4-8v4h2v-4h-2z"/></g>
+<g id="location-disabled"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06c-1.13.12-2.19.46-3.16.97l1.5 1.5C10.16 5.19 11.06 5 12 5c3.87 0 7 3.13 7 7 0 .94-.19 1.84-.52 2.65l1.5 1.5c.5-.96.84-2.02.97-3.15H23v-2h-2.06zM3 4.27l2.04 2.04C3.97 7.62 3.25 9.23 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c1.77-.2 3.38-.91 4.69-1.98L19.73 21 21 19.73 4.27 3 3 4.27zm13.27 13.27C15.09 18.45 13.61 19 12 19c-3.87 0-7-3.13-7-7 0-1.61.55-3.09 1.46-4.27l9.81 9.81z"/></g>
+<g id="location-searching"><path d="M20.94 11c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></g>
+<g id="network-cell"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M17 7L2 22h15z"/></g>
+<g id="network-wifi"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M3.53 10.95l8.46 10.54.01.01.01-.01 8.46-10.54C20.04 10.62 16.81 8 12 8c-4.81 0-8.04 2.62-8.47 2.95z"/></g>
+<g id="nfc"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6.35-1 .98-1 1.72 0 1.1.9 2 2 2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z"/></g>
+<g id="screen-lock-landscape"><path d="M21 5H3c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-2 12H5V7h14v10zm-9-1h4c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1v-1c0-1.11-.9-2-2-2-1.11 0-2 .9-2 2v1c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1zm.8-6c0-.66.54-1.2 1.2-1.2.66 0 1.2.54 1.2 1.2v1h-2.4v-1z"/></g>
+<g id="screen-lock-portrait"><path d="M10 16h4c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1v-1c0-1.11-.9-2-2-2-1.11 0-2 .9-2 2v1c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1zm.8-6c0-.66.54-1.2 1.2-1.2.66 0 1.2.54 1.2 1.2v1h-2.4v-1zM17 1H7c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 18H7V5h10v14z"/></g>
+<g id="screen-lock-rotation"><path d="M23.25 12.77l-2.57-2.57-1.41 1.41 2.22 2.22-5.66 5.66L4.51 8.17l5.66-5.66 2.1 2.1 1.41-1.41L11.23.75c-.59-.59-1.54-.59-2.12 0L2.75 7.11c-.59.59-.59 1.54 0 2.12l12.02 12.02c.59.59 1.54.59 2.12 0l6.36-6.36c.59-.59.59-1.54 0-2.12zM8.47 20.48C5.2 18.94 2.86 15.76 2.5 12H1c.51 6.16 5.66 11 11.95 11l.66-.03-3.81-3.82-1.33 1.33zM16 9h5c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1v-.5C21 1.12 19.88 0 18.5 0S16 1.12 16 2.5V3c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1zm.8-6.5c0-.94.76-1.7 1.7-1.7s1.7.76 1.7 1.7V3h-3.4v-.5z"/></g>
+<g id="screen-rotation"><path d="M16.48 2.52c3.27 1.55 5.61 4.72 5.97 8.48h1.5C23.44 4.84 18.29 0 12 0l-.66.03 3.81 3.81 1.33-1.32zm-6.25-.77c-.59-.59-1.54-.59-2.12 0L1.75 8.11c-.59.59-.59 1.54 0 2.12l12.02 12.02c.59.59 1.54.59 2.12 0l6.36-6.36c.59-.59.59-1.54 0-2.12L10.23 1.75zm4.6 19.44L2.81 9.17l6.36-6.36 12.02 12.02-6.36 6.36zm-7.31.29C4.25 19.94 1.91 16.76 1.55 13H.05C.56 19.16 5.71 24 12 24l.66-.03-3.81-3.81-1.33 1.32z"/></g>
+<g id="sd-storage"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 6h-2V4h2v4zm3 0h-2V4h2v4zm3 0h-2V4h2v4z"/></g>
+<g id="settings-system-daydream"><path d="M9 16h6.5c1.38 0 2.5-1.12 2.5-2.5S16.88 11 15.5 11h-.05c-.24-1.69-1.69-3-3.45-3-1.4 0-2.6.83-3.16 2.02h-.16C7.17 10.18 6 11.45 6 13c0 1.66 1.34 3 3 3zM21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"/></g>
+<g id="signal-cellular-0-bar"><path fill-opacity=".3" d="M2 22h20V2z"/></g>
+<g id="signal-cellular-1-bar"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M12 12L2 22h10z"/></g>
+<g id="signal-cellular-2-bar"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M14 10L2 22h12z"/></g>
+<g id="signal-cellular-3-bar"><path fill-opacity=".3" d="M2 22h20V2z"/><path d="M17 7L2 22h15z"/></g>
+<g id="signal-cellular-4-bar"><path d="M2 22h20V2z"/></g>
+<g id="signal-cellular-connected-no-internet-0-bar"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M20 22h2v-2h-2v2zm0-12v8h2v-8h-2z"/></g>
+<g id="signal-cellular-connected-no-internet-1-bar"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M20 10v8h2v-8h-2zm-8 12V12L2 22h10zm8 0h2v-2h-2v2z"/></g>
+<g id="signal-cellular-connected-no-internet-2-bar"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M14 22V10L2 22h12zm6-12v8h2v-8h-2zm0 12h2v-2h-2v2z"/></g>
+<g id="signal-cellular-connected-no-internet-3-bar"><path fill-opacity=".3" d="M22 8V2L2 22h16V8z"/><path d="M17 22V7L2 22h15zm3-12v8h2v-8h-2zm0 12h2v-2h-2v2z"/></g>
+<g id="signal-cellular-connected-no-internet-4-bar"><path d="M20 18h2v-8h-2v8zm0 4h2v-2h-2v2zM2 22h16V8h4V2L2 22z"/></g>
+<g id="signal-cellular-no-sim"><path d="M18.99 5c0-1.1-.89-2-1.99-2h-7L7.66 5.34 19 16.68 18.99 5zM3.65 3.88L2.38 5.15 5 7.77V19c0 1.1.9 2 2 2h10.01c.35 0 .67-.1.96-.26l1.88 1.88 1.27-1.27L3.65 3.88z"/></g>
+<g id="signal-cellular-null"><path d="M20 6.83V20H6.83L20 6.83M22 2L2 22h20V2z"/></g>
+<g id="signal-cellular-off"><path d="M21 1l-8.59 8.59L21 18.18V1zM4.77 4.5L3.5 5.77l6.36 6.36L1 21h17.73l2 2L22 21.73 4.77 4.5z"/></g>
+<g id="signal-wifi-0-bar"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/></g>
+<g id="signal-wifi-1-bar"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M6.67 14.86L12 21.49v.01l.01-.01 5.33-6.63C17.06 14.65 15.03 13 12 13s-5.06 1.65-5.33 1.86z"/></g>
+<g id="signal-wifi-1-bar-lock"><path d="M23 16v-1.5c0-1.4-1.1-2.5-2.5-2.5S18 13.1 18 14.5V16c-.5 0-1 .5-1 1v4c0 .5.5 1 1 1h5c.5 0 1-.5 1-1v-4c0-.5-.5-1-1-1zm-1 0h-3v-1.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V16z"/><path d="M15.5 14.5c0-2.8 2.2-5 5-5 .4 0 .7 0 1 .1L23.6 7c-.4-.3-4.9-4-11.6-4C5.3 3 .8 6.7.4 7L12 21.5l3.5-4.3v-2.7z" opacity=".3"/><path d="M6.7 14.9l5.3 6.6 3.5-4.3v-2.6c0-.2 0-.5.1-.7-.9-.5-2.2-.9-3.6-.9-3 0-5.1 1.7-5.3 1.9z"/></g>
+<g id="signal-wifi-2-bar"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M4.79 12.52l7.2 8.98H12l.01-.01 7.2-8.98C18.85 12.24 16.1 10 12 10s-6.85 2.24-7.21 2.52z"/></g>
+<g id="signal-wifi-2-bar-lock"><path d="M23 16v-1.5c0-1.4-1.1-2.5-2.5-2.5S18 13.1 18 14.5V16c-.5 0-1 .5-1 1v4c0 .5.5 1 1 1h5c.5 0 1-.5 1-1v-4c0-.5-.5-1-1-1zm-1 0h-3v-1.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V16z"/><path d="M15.5 14.5c0-2.8 2.2-5 5-5 .4 0 .7 0 1 .1L23.6 7c-.4-.3-4.9-4-11.6-4C5.3 3 .8 6.7.4 7L12 21.5l3.5-4.3v-2.7z" opacity=".3"/><path d="M4.8 12.5l7.2 9 3.5-4.4v-2.6c0-1.3.5-2.5 1.4-3.4C15.6 10.5 14 10 12 10c-4.1 0-6.8 2.2-7.2 2.5z"/></g>
+<g id="signal-wifi-3-bar"><path fill-opacity=".3" d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/><path d="M3.53 10.95l8.46 10.54.01.01.01-.01 8.46-10.54C20.04 10.62 16.81 8 12 8c-4.81 0-8.04 2.62-8.47 2.95z"/></g>
+<g id="signal-wifi-3-bar-lock"><path opacity=".3" d="M12 3C5.3 3 .8 6.7.4 7l3.2 3.9L12 21.5l3.5-4.3v-2.6c0-2.2 1.4-4 3.3-4.7.3-.1.5-.2.8-.2.3-.1.6-.1.9-.1.4 0 .7 0 1 .1L23.6 7c-.4-.3-4.9-4-11.6-4z"/><path d="M23 16v-1.5c0-1.4-1.1-2.5-2.5-2.5S18 13.1 18 14.5V16c-.5 0-1 .5-1 1v4c0 .5.5 1 1 1h5c.5 0 1-.5 1-1v-4c0-.5-.5-1-1-1zm-1 0h-3v-1.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V16zm-10 5.5l3.5-4.3v-2.6c0-2.2 1.4-4 3.3-4.7C17.3 9 14.9 8 12 8c-4.8 0-8 2.6-8.5 2.9"/></g>
+<g id="signal-wifi-4-bar"><path d="M12.01 21.49L23.64 7c-.45-.34-4.93-4-11.64-4C5.28 3 .81 6.66.36 7l11.63 14.49.01.01.01-.01z"/></g>
+<g id="signal-wifi-4-bar-lock"><path d="M23 16v-1.5c0-1.4-1.1-2.5-2.5-2.5S18 13.1 18 14.5V16c-.5 0-1 .5-1 1v4c0 .5.5 1 1 1h5c.5 0 1-.5 1-1v-4c0-.5-.5-1-1-1zm-1 0h-3v-1.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V16zm-6.5-1.5c0-2.8 2.2-5 5-5 .4 0 .7 0 1 .1L23.6 7c-.4-.3-4.9-4-11.6-4C5.3 3 .8 6.7.4 7L12 21.5l3.5-4.4v-2.6z"/></g>
+<g id="signal-wifi-off"><path d="M23.64 7c-.45-.34-4.93-4-11.64-4-1.5 0-2.89.19-4.15.48L18.18 13.8 23.64 7zm-6.6 8.22L3.27 1.44 2 2.72l2.05 2.06C1.91 5.76.59 6.82.36 7l11.63 14.49.01.01.01-.01 3.9-4.86 3.32 3.32 1.27-1.27-3.46-3.46z"/></g>
+<g id="storage"><path d="M2 20h20v-4H2v4zm2-3h2v2H4v-2zM2 4v4h20V4H2zm4 3H4V5h2v2zm-4 7h20v-4H2v4zm2-3h2v2H4v-2z"/></g>
+<g id="usb"><path d="M15 7v4h1v2h-3V5h2l-3-4-3 4h2v8H8v-2.07c.7-.37 1.2-1.08 1.2-1.93 0-1.21-.99-2.2-2.2-2.2-1.21 0-2.2.99-2.2 2.2 0 .85.5 1.56 1.2 1.93V13c0 1.11.89 2 2 2h3v3.05c-.71.37-1.2 1.1-1.2 1.95 0 1.22.99 2.2 2.2 2.2 1.21 0 2.2-.98 2.2-2.2 0-.85-.49-1.58-1.2-1.95V15h3c1.11 0 2-.89 2-2v-2h1V7h-4z"/></g>
+<g id="wallpaper"><path d="M4 4h7V2H4c-1.1 0-2 .9-2 2v7h2V4zm6 9l-4 5h12l-3-4-2.03 2.71L10 13zm7-4.5c0-.83-.67-1.5-1.5-1.5S14 7.67 14 8.5s.67 1.5 1.5 1.5S17 9.33 17 8.5zM20 2h-7v2h7v7h2V4c0-1.1-.9-2-2-2zm0 18h-7v2h7c1.1 0 2-.9 2-2v-7h-2v7zM4 13H2v7c0 1.1.9 2 2 2h7v-2H4v-7z"/></g>
+<g id="widgets"><path d="M13 13v8h8v-8h-8zM3 21h8v-8H3v8zM3 3v8h8V3H3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66-5.66-5.65z"/></g>
+<g id="wifi-lock"><path d="M20.5 9.5c.28 0 .55.04.81.08L24 6c-3.34-2.51-7.5-4-12-4S3.34 3.49 0 6l12 16 3.5-4.67V14.5c0-2.76 2.24-5 5-5zM23 16v-1.5c0-1.38-1.12-2.5-2.5-2.5S18 13.12 18 14.5V16c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1v-4c0-.55-.45-1-1-1zm-1 0h-3v-1.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V16z"/></g>
+<g id="wifi-tethering"><path d="M12 11c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 2c0-3.31-2.69-6-6-6s-6 2.69-6 6c0 2.22 1.21 4.15 3 5.19l1-1.74c-1.19-.7-2-1.97-2-3.45 0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.48-.81 2.75-2 3.45l1 1.74c1.79-1.04 3-2.97 3-5.19zM12 3C6.48 3 2 7.48 2 13c0 3.7 2.01 6.92 4.99 8.65l1-1.73C5.61 18.53 4 15.96 4 13c0-4.42 3.58-8 8-8s8 3.58 8 8c0 2.96-1.61 5.53-4 6.92l1 1.73c2.99-1.73 5-4.95 5-8.65 0-5.52-4.48-10-10-10z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/editor-icons.html b/catapult/third_party/polymer/components/iron-icons/editor-icons.html
new file mode 100644
index 00000000..bc7084db
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/editor-icons.html
@@ -0,0 +1,84 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="editor" size="24">
+<svg><defs>
+<g id="attach-file"><path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/></g>
+<g id="attach-money"><path d="M11.8 10.9c-2.27-.59-3-1.2-3-2.15 0-1.09 1.01-1.85 2.7-1.85 1.78 0 2.44.85 2.5 2.1h2.21c-.07-1.72-1.12-3.3-3.21-3.81V3h-3v2.16c-1.94.42-3.5 1.68-3.5 3.61 0 2.31 1.91 3.46 4.7 4.13 2.5.6 3 1.48 3 2.41 0 .69-.49 1.79-2.7 1.79-2.06 0-2.87-.92-2.98-2.1h-2.2c.12 2.19 1.76 3.42 3.68 3.83V21h3v-2.15c1.95-.37 3.5-1.5 3.5-3.55 0-2.84-2.43-3.81-4.7-4.4z"/></g>
+<g id="border-all"><path d="M3 3v18h18V3H3zm8 16H5v-6h6v6zm0-8H5V5h6v6zm8 8h-6v-6h6v6zm0-8h-6V5h6v6z"/></g>
+<g id="border-bottom"><path d="M9 11H7v2h2v-2zm4 4h-2v2h2v-2zM9 3H7v2h2V3zm4 8h-2v2h2v-2zM5 3H3v2h2V3zm8 4h-2v2h2V7zm4 4h-2v2h2v-2zm-4-8h-2v2h2V3zm4 0h-2v2h2V3zm2 10h2v-2h-2v2zm0 4h2v-2h-2v2zM5 7H3v2h2V7zm14-4v2h2V3h-2zm0 6h2V7h-2v2zM5 11H3v2h2v-2zM3 21h18v-2H3v2zm2-6H3v2h2v-2z"/></g>
+<g id="border-clear"><path d="M7 5h2V3H7v2zm0 8h2v-2H7v2zm0 8h2v-2H7v2zm4-4h2v-2h-2v2zm0 4h2v-2h-2v2zm-8 0h2v-2H3v2zm0-4h2v-2H3v2zm0-4h2v-2H3v2zm0-4h2V7H3v2zm0-4h2V3H3v2zm8 8h2v-2h-2v2zm8 4h2v-2h-2v2zm0-4h2v-2h-2v2zm0 8h2v-2h-2v2zm0-12h2V7h-2v2zm-8 0h2V7h-2v2zm8-6v2h2V3h-2zm-8 2h2V3h-2v2zm4 16h2v-2h-2v2zm0-8h2v-2h-2v2zm0-8h2V3h-2v2z"/></g>
+<g id="border-color"><path d="M17.75 7L14 3.25l-10 10V17h3.75l10-10zm2.96-2.96c.39-.39.39-1.02 0-1.41L18.37.29c-.39-.39-1.02-.39-1.41 0L15 2.25 18.75 6l1.96-1.96z"/><path fill-opacity=".36" d="M0 20h24v4H0z"/></g>
+<g id="border-horizontal"><path d="M3 21h2v-2H3v2zM5 7H3v2h2V7zM3 17h2v-2H3v2zm4 4h2v-2H7v2zM5 3H3v2h2V3zm4 0H7v2h2V3zm8 0h-2v2h2V3zm-4 4h-2v2h2V7zm0-4h-2v2h2V3zm6 14h2v-2h-2v2zm-8 4h2v-2h-2v2zm-8-8h18v-2H3v2zM19 3v2h2V3h-2zm0 6h2V7h-2v2zm-8 8h2v-2h-2v2zm4 4h2v-2h-2v2zm4 0h2v-2h-2v2z"/></g>
+<g id="border-inner"><path d="M3 21h2v-2H3v2zm4 0h2v-2H7v2zM5 7H3v2h2V7zM3 17h2v-2H3v2zM9 3H7v2h2V3zM5 3H3v2h2V3zm12 0h-2v2h2V3zm2 6h2V7h-2v2zm0-6v2h2V3h-2zm-4 18h2v-2h-2v2zM13 3h-2v8H3v2h8v8h2v-8h8v-2h-8V3zm6 18h2v-2h-2v2zm0-4h2v-2h-2v2z"/></g>
+<g id="border-left"><path d="M11 21h2v-2h-2v2zm0-4h2v-2h-2v2zm0-12h2V3h-2v2zm0 4h2V7h-2v2zm0 4h2v-2h-2v2zm-4 8h2v-2H7v2zM7 5h2V3H7v2zm0 8h2v-2H7v2zm-4 8h2V3H3v18zM19 9h2V7h-2v2zm-4 12h2v-2h-2v2zm4-4h2v-2h-2v2zm0-14v2h2V3h-2zm0 10h2v-2h-2v2zm0 8h2v-2h-2v2zm-4-8h2v-2h-2v2zm0-8h2V3h-2v2z"/></g>
+<g id="border-outer"><path d="M13 7h-2v2h2V7zm0 4h-2v2h2v-2zm4 0h-2v2h2v-2zM3 3v18h18V3H3zm16 16H5V5h14v14zm-6-4h-2v2h2v-2zm-4-4H7v2h2v-2z"/></g>
+<g id="border-right"><path d="M7 21h2v-2H7v2zM3 5h2V3H3v2zm4 0h2V3H7v2zm0 8h2v-2H7v2zm-4 8h2v-2H3v2zm8 0h2v-2h-2v2zm-8-8h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm8 8h2v-2h-2v2zm4-4h2v-2h-2v2zm4-10v18h2V3h-2zm-4 18h2v-2h-2v2zm0-16h2V3h-2v2zm-4 8h2v-2h-2v2zm0-8h2V3h-2v2zm0 4h2V7h-2v2z"/></g>
+<g id="border-style"><path d="M15 21h2v-2h-2v2zm4 0h2v-2h-2v2zM7 21h2v-2H7v2zm4 0h2v-2h-2v2zm8-4h2v-2h-2v2zm0-4h2v-2h-2v2zM3 3v18h2V5h16V3H3zm16 6h2V7h-2v2z"/></g>
+<g id="border-top"><path d="M7 21h2v-2H7v2zm0-8h2v-2H7v2zm4 0h2v-2h-2v2zm0 8h2v-2h-2v2zm-8-4h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2v-2H3v2zm0-4h2V7H3v2zm8 8h2v-2h-2v2zm8-8h2V7h-2v2zm0 4h2v-2h-2v2zM3 3v2h18V3H3zm16 14h2v-2h-2v2zm-4 4h2v-2h-2v2zM11 9h2V7h-2v2zm8 12h2v-2h-2v2zm-4-8h2v-2h-2v2z"/></g>
+<g id="border-vertical"><path d="M3 9h2V7H3v2zm0-4h2V3H3v2zm4 16h2v-2H7v2zm0-8h2v-2H7v2zm-4 0h2v-2H3v2zm0 8h2v-2H3v2zm0-4h2v-2H3v2zM7 5h2V3H7v2zm12 12h2v-2h-2v2zm-8 4h2V3h-2v18zm8 0h2v-2h-2v2zm0-8h2v-2h-2v2zm0-10v2h2V3h-2zm0 6h2V7h-2v2zm-4-4h2V3h-2v2zm0 16h2v-2h-2v2zm0-8h2v-2h-2v2z"/></g>
+<g id="bubble-chart"><circle cx="7.2" cy="14.4" r="3.2"/><circle cx="14.8" cy="18" r="2"/><circle cx="15.2" cy="8.8" r="4.8"/></g>
+<g id="drag-handle"><path d="M20 9H4v2h16V9zM4 15h16v-2H4v2z"/></g>
+<g id="format-align-center"><path d="M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z"/></g>
+<g id="format-align-justify"><path d="M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zm0-6v2h18V3H3z"/></g>
+<g id="format-align-left"><path d="M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z"/></g>
+<g id="format-align-right"><path d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z"/></g>
+<g id="format-bold"><path d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z"/></g>
+<g id="format-clear"><path d="M3.27 5L2 6.27l6.97 6.97L6.5 19h3l1.57-3.66L16.73 21 18 19.73 3.55 5.27 3.27 5zM6 5v.18L8.82 8h2.4l-.72 1.68 2.1 2.1L14.21 8H20V5H6z"/></g>
+<g id="format-color-fill"><path d="M16.56 8.94L7.62 0 6.21 1.41l2.38 2.38-5.15 5.15c-.59.59-.59 1.54 0 2.12l5.5 5.5c.29.29.68.44 1.06.44s.77-.15 1.06-.44l5.5-5.5c.59-.58.59-1.53 0-2.12zM5.21 10L10 5.21 14.79 10H5.21zM19 11.5s-2 2.17-2 3.5c0 1.1.9 2 2 2s2-.9 2-2c0-1.33-2-3.5-2-3.5z"/><path fill-opacity=".36" d="M0 20h24v4H0z"/></g>
+<g id="format-color-reset"><path d="M18 14c0-4-6-10.8-6-10.8s-1.33 1.51-2.73 3.52l8.59 8.59c.09-.42.14-.86.14-1.31zm-.88 3.12L12.5 12.5 5.27 5.27 4 6.55l3.32 3.32C6.55 11.32 6 12.79 6 14c0 3.31 2.69 6 6 6 1.52 0 2.9-.57 3.96-1.5l2.63 2.63 1.27-1.27-2.74-2.74z"/></g>
+<g id="format-color-text"><path fill-opacity=".36" d="M0 20h24v4H0z"/><path d="M11 3L5.5 17h2.25l1.12-3h6.25l1.12 3h2.25L13 3h-2zm-1.38 9L12 5.67 14.38 12H9.62z"/></g>
+<g id="format-indent-decrease"><path d="M11 17h10v-2H11v2zm-8-5l4 4V8l-4 4zm0 9h18v-2H3v2zM3 3v2h18V3H3zm8 6h10V7H11v2zm0 4h10v-2H11v2z"/></g>
+<g id="format-indent-increase"><path d="M3 21h18v-2H3v2zM3 8v8l4-4-4-4zm8 9h10v-2H11v2zM3 3v2h18V3H3zm8 6h10V7H11v2zm0 4h10v-2H11v2z"/></g>
+<g id="format-italic"><path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z"/></g>
+<g id="format-line-spacing"><path d="M6 7h2.5L5 3.5 1.5 7H4v10H1.5L5 20.5 8.5 17H6V7zm4-2v2h12V5H10zm0 14h12v-2H10v2zm0-6h12v-2H10v2z"/></g>
+<g id="format-list-bulleted"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></g>
+<g id="format-list-numbered"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></g>
+<g id="format-paint"><path d="M18 4V3c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V6h1v4H9v11c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-9h8V4h-3z"/></g>
+<g id="format-quote"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></g>
+<g id="format-shapes"><path d="M23 7V1h-6v2H7V1H1v6h2v10H1v6h6v-2h10v2h6v-6h-2V7h2zM3 3h2v2H3V3zm2 18H3v-2h2v2zm12-2H7v-2H5V7h2V5h10v2h2v10h-2v2zm4 2h-2v-2h2v2zM19 5V3h2v2h-2zm-5.27 9h-3.49l-.73 2H7.89l3.4-9h1.4l3.41 9h-1.63l-.74-2zm-3.04-1.26h2.61L12 8.91l-1.31 3.83z"/></g>
+<g id="format-size"><path d="M9 4v3h5v12h3V7h5V4H9zm-6 8h3v7h3v-7h3V9H3v3z"/></g>
+<g id="format-strikethrough"><path d="M10 19h4v-3h-4v3zM5 4v3h5v3h4V7h5V4H5zM3 14h18v-2H3v2z"/></g>
+<g id="format-textdirection-l-to-r"><path d="M9 10v5h2V4h2v11h2V4h2V2H9C6.79 2 5 3.79 5 6s1.79 4 4 4zm12 8l-4-4v3H5v2h12v3l4-4z"/></g>
+<g id="format-textdirection-r-to-l"><path d="M10 10v5h2V4h2v11h2V4h2V2h-8C7.79 2 6 3.79 6 6s1.79 4 4 4zm-2 7v-3l-4 4 4 4v-3h12v-2H8z"/></g>
+<g id="format-underlined"><path d="M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z"/></g>
+<g id="functions"><path d="M18 4H6v2l6.5 6L6 18v2h12v-3h-7l5-5-5-5h7z"/></g>
+<g id="highlight"><path d="M6 14l3 3v5h6v-5l3-3V9H6zm5-12h2v3h-2zM3.5 5.875L4.914 4.46l2.12 2.122L5.62 7.997zm13.46.71l2.123-2.12 1.414 1.414L18.375 8z"/></g>
+<g id="insert-chart"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></g>
+<g id="insert-comment"><path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></g>
+<g id="insert-drive-file"><path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"/></g>
+<g id="insert-emoticon"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></g>
+<g id="insert-invitation"><path d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"/></g>
+<g id="insert-link"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></g>
+<g id="insert-photo"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></g>
+<g id="linear-scale"><path d="M19.5 9.5c-1.03 0-1.9.62-2.29 1.5h-2.92c-.39-.88-1.26-1.5-2.29-1.5s-1.9.62-2.29 1.5H6.79c-.39-.88-1.26-1.5-2.29-1.5C3.12 9.5 2 10.62 2 12s1.12 2.5 2.5 2.5c1.03 0 1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5s1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5 1.38 0 2.5-1.12 2.5-2.5s-1.12-2.5-2.5-2.5z"/></g>
+<g id="merge-type"><path d="M17 20.41L18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z"/></g>
+<g id="mode-comment"><path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18z"/></g>
+<g id="mode-edit"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></g>
+<g id="monetization-on"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"/></g>
+<g id="money-off"><path d="M12.5 6.9c1.78 0 2.44.85 2.5 2.1h2.21c-.07-1.72-1.12-3.3-3.21-3.81V3h-3v2.16c-.53.12-1.03.3-1.48.54l1.47 1.47c.41-.17.91-.27 1.51-.27zM5.33 4.06L4.06 5.33 7.5 8.77c0 2.08 1.56 3.21 3.91 3.91l3.51 3.51c-.34.48-1.05.91-2.42.91-2.06 0-2.87-.92-2.98-2.1h-2.2c.12 2.19 1.76 3.42 3.68 3.83V21h3v-2.15c.96-.18 1.82-.55 2.45-1.12l2.22 2.22 1.27-1.27L5.33 4.06z"/></g>
+<g id="multiline-chart"><path d="M22 6.92l-1.41-1.41-2.85 3.21C15.68 6.4 12.83 5 9.61 5 6.72 5 4.07 6.16 2 8l1.42 1.42C5.12 7.93 7.27 7 9.61 7c2.74 0 5.09 1.26 6.77 3.24l-2.88 3.24-4-4L2 16.99l1.5 1.5 6-6.01 4 4 4.05-4.55c.75 1.35 1.25 2.9 1.44 4.55H21c-.22-2.3-.95-4.39-2.04-6.14L22 6.92z"/></g>
+<g id="pie-chart"><path d="M11 2v20c-5.07-.5-9-4.79-9-10s3.93-9.5 9-10zm2.03 0v8.99H22c-.47-4.74-4.24-8.52-8.97-8.99zm0 11.01V22c4.74-.47 8.5-4.25 8.97-8.99h-8.97z"/></g>
+<g id="pie-chart-outlined"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm1 2.07c3.61.45 6.48 3.33 6.93 6.93H13V4.07zM4 12c0-4.06 3.07-7.44 7-7.93v15.87c-3.93-.5-7-3.88-7-7.94zm9 7.93V13h6.93c-.45 3.61-3.32 6.48-6.93 6.93z"/></g>
+<g id="publish"><path d="M5 4v2h14V4H5zm0 10h4v6h6v-6h4l-7-7-7 7z"/></g>
+<g id="short-text"><path d="M4 9h16v2H4zm0 4h10v2H4z"/></g>
+<g id="show-chart"><path d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z"/></g>
+<g id="space-bar"><path d="M18 9v4H6V9H4v6h16V9z"/></g>
+<g id="strikethrough-s"><path d="M7.24 8.75c-.26-.48-.39-1.03-.39-1.67 0-.61.13-1.16.4-1.67.26-.5.63-.93 1.11-1.29.48-.35 1.05-.63 1.7-.83.66-.19 1.39-.29 2.18-.29.81 0 1.54.11 2.21.34.66.22 1.23.54 1.69.94.47.4.83.88 1.08 1.43.25.55.38 1.15.38 1.81h-3.01c0-.31-.05-.59-.15-.85-.09-.27-.24-.49-.44-.68-.2-.19-.45-.33-.75-.44-.3-.1-.66-.16-1.06-.16-.39 0-.74.04-1.03.13-.29.09-.53.21-.72.36-.19.16-.34.34-.44.55-.1.21-.15.43-.15.66 0 .48.25.88.74 1.21.38.25.77.48 1.41.7H7.39c-.05-.08-.11-.17-.15-.25zM21 12v-2H3v2h9.62c.18.07.4.14.55.2.37.17.66.34.87.51.21.17.35.36.43.57.07.2.11.43.11.69 0 .23-.05.45-.14.66-.09.2-.23.38-.42.53-.19.15-.42.26-.71.35-.29.08-.63.13-1.01.13-.43 0-.83-.04-1.18-.13s-.66-.23-.91-.42c-.25-.19-.45-.44-.59-.75-.14-.31-.25-.76-.25-1.21H6.4c0 .55.08 1.13.24 1.58.16.45.37.85.65 1.21.28.35.6.66.98.92.37.26.78.48 1.22.65.44.17.9.3 1.38.39.48.08.96.13 1.44.13.8 0 1.53-.09 2.18-.28s1.21-.45 1.67-.79c.46-.34.82-.77 1.07-1.27s.38-1.07.38-1.71c0-.6-.1-1.14-.31-1.61-.05-.11-.11-.23-.17-.33H21z"/></g>
+<g id="text-fields"><path d="M2.5 4v3h5v12h3V7h5V4h-13zm19 5h-9v3h3v7h3v-7h3V9z"/></g>
+<g id="title"><path d="M5 4v3h5.5v12h3V7H19V4z"/></g>
+<g id="vertical-align-bottom"><path d="M16 13h-3V3h-2v10H8l4 4 4-4zM4 19v2h16v-2H4z"/></g>
+<g id="vertical-align-center"><path d="M8 19h3v4h2v-4h3l-4-4-4 4zm8-14h-3V1h-2v4H8l4 4 4-4zM4 11v2h16v-2H4z"/></g>
+<g id="vertical-align-top"><path d="M8 11h3v10h2V11h3l-4-4-4 4zM4 3v2h16V3H4z"/></g>
+<g id="wrap-text"><path d="M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3 3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/hardware-icons.html b/catapult/third_party/polymer/components/iron-icons/hardware-icons.html
new file mode 100644
index 00000000..3268e5ad
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/hardware-icons.html
@@ -0,0 +1,64 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="hardware" size="24">
+<svg><defs>
+<g id="cast"><path d="M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z"/></g>
+<g id="cast-connected"><path d="M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm18-7H5v1.63c3.96 1.28 7.09 4.41 8.37 8.37H19V7zM1 10v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></g>
+<g id="computer"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/></g>
+<g id="desktop-mac"><path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7l-2 3v1h8v-1l-2-3h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 12H3V4h18v10z"/></g>
+<g id="desktop-windows"><path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v2H8v2h8v-2h-2v-2h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H3V4h18v12z"/></g>
+<g id="developer-board"><path d="M22 9V7h-2V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2v-2h-2V9h2zm-4 10H4V5h14v14zM6 13h5v4H6zm6-6h4v3h-4zM6 7h5v5H6zm6 4h4v6h-4z"/></g>
+<g id="device-hub"><path d="M17 16l-4-4V8.82C14.16 8.4 15 7.3 15 6c0-1.66-1.34-3-3-3S9 4.34 9 6c0 1.3.84 2.4 2 2.82V12l-4 4H3v5h5v-3.05l4-4.2 4 4.2V21h5v-5h-4z"/></g>
+<g id="devices-other"><path d="M3 6h18V4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V6zm10 6H9v1.78c-.61.55-1 1.33-1 2.22s.39 1.67 1 2.22V20h4v-1.78c.61-.55 1-1.34 1-2.22s-.39-1.67-1-2.22V12zm-2 5.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM22 8h-6c-.5 0-1 .5-1 1v10c0 .5.5 1 1 1h6c.5 0 1-.5 1-1V9c0-.5-.5-1-1-1zm-1 10h-4v-8h4v8z"/></g>
+<g id="dock"><path d="M8 23h8v-2H8v2zm8-21.99L8 1c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM16 15H8V5h8v10z"/></g>
+<g id="gamepad"><path d="M15 7.5V2H9v5.5l3 3 3-3zM7.5 9H2v6h5.5l3-3-3-3zM9 16.5V22h6v-5.5l-3-3-3 3zM16.5 9l-3 3 3 3H22V9h-5.5z"/></g>
+<g id="headset"><path d="M12 1c-4.97 0-9 4.03-9 9v7c0 1.66 1.34 3 3 3h3v-8H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-4v8h3c1.66 0 3-1.34 3-3v-7c0-4.97-4.03-9-9-9z"/></g>
+<g id="headset-mic"><path d="M12 1c-4.97 0-9 4.03-9 9v7c0 1.66 1.34 3 3 3h3v-8H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-4v8h4v1h-7v2h6c1.66 0 3-1.34 3-3V10c0-4.97-4.03-9-9-9z"/></g>
+<g id="keyboard"><path d="M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z"/></g>
+<g id="keyboard-arrow-down"><path d="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z"/></g>
+<g id="keyboard-arrow-left"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/></g>
+<g id="keyboard-arrow-right"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/></g>
+<g id="keyboard-arrow-up"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></g>
+<g id="keyboard-backspace"><path d="M21 11H6.83l3.58-3.59L9 6l-6 6 6 6 1.41-1.41L6.83 13H21z"/></g>
+<g id="keyboard-capslock"><path d="M12 8.41L16.59 13 18 11.59l-6-6-6 6L7.41 13 12 8.41zM6 18h12v-2H6v2z"/></g>
+<g id="keyboard-hide"><path d="M20 3H4c-1.1 0-1.99.9-1.99 2L2 15c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 3h2v2h-2V6zm0 3h2v2h-2V9zM8 6h2v2H8V6zm0 3h2v2H8V9zm-1 2H5V9h2v2zm0-3H5V6h2v2zm9 7H8v-2h8v2zm0-4h-2V9h2v2zm0-3h-2V6h2v2zm3 3h-2V9h2v2zm0-3h-2V6h2v2zm-7 15l4-4H8l4 4z"/></g>
+<g id="keyboard-return"><path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/></g>
+<g id="keyboard-tab"><path d="M11.59 7.41L15.17 11H1v2h14.17l-3.59 3.59L13 18l6-6-6-6-1.41 1.41zM20 6v12h2V6h-2z"/></g>
+<g id="keyboard-voice"><path d="M12 15c1.66 0 2.99-1.34 2.99-3L15 6c0-1.66-1.34-3-3-3S9 4.34 9 6v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 15 6.7 12H5c0 3.42 2.72 6.23 6 6.72V22h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></g>
+<g id="laptop"><path d="M20 18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/></g>
+<g id="laptop-chromebook"><path d="M22 18V3H2v15H0v2h24v-2h-2zm-8 0h-4v-1h4v1zm6-3H4V5h16v10z"/></g>
+<g id="laptop-mac"><path d="M20 18c1.1 0 1.99-.9 1.99-2L22 5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2H0c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2h-4zM4 5h16v11H4V5zm8 14c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></g>
+<g id="laptop-windows"><path d="M20 18v-1c1.1 0 1.99-.9 1.99-2L22 5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2v1H0v2h24v-2h-4zM4 5h16v10H4V5z"/></g>
+<g id="memory"><path d="M15 9H9v6h6V9zm-2 4h-2v-2h2v2zm8-2V9h-2V7c0-1.1-.9-2-2-2h-2V3h-2v2h-2V3H9v2H7c-1.1 0-2 .9-2 2v2H3v2h2v2H3v2h2v2c0 1.1.9 2 2 2h2v2h2v-2h2v2h2v-2h2c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2zm-4 6H7V7h10v10z"/></g>
+<g id="mouse"><path d="M13 1.07V9h7c0-4.08-3.05-7.44-7-7.93zM4 15c0 4.42 3.58 8 8 8s8-3.58 8-8v-4H4v4zm7-13.93C7.05 1.56 4 4.92 4 9h7V1.07z"/></g>
+<g id="phone-android"><path d="M16 1H8C6.34 1 5 2.34 5 4v16c0 1.66 1.34 3 3 3h8c1.66 0 3-1.34 3-3V4c0-1.66-1.34-3-3-3zm-2 20h-4v-1h4v1zm3.25-3H6.75V4h10.5v14z"/></g>
+<g id="phone-iphone"><path d="M15.5 1h-8C6.12 1 5 2.12 5 3.5v17C5 21.88 6.12 23 7.5 23h8c1.38 0 2.5-1.12 2.5-2.5v-17C18 2.12 16.88 1 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z"/></g>
+<g id="phonelink"><path d="M4 6h18V4H4c-1.1 0-2 .9-2 2v11H0v3h14v-3H4V6zm19 2h-6c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 9h-4v-7h4v7z"/></g>
+<g id="phonelink-off"><path d="M22 6V4H6.82l2 2H22zM1.92 1.65L.65 2.92l1.82 1.82C2.18 5.08 2 5.52 2 6v11H0v3h17.73l2.35 2.35 1.27-1.27L3.89 3.62 1.92 1.65zM4 6.27L14.73 17H4V6.27zM23 8h-6c-.55 0-1 .45-1 1v4.18l2 2V10h4v7h-2.18l3 3H23c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1z"/></g>
+<g id="power-input"><path d="M2 9v2h19V9H2zm0 6h5v-2H2v2zm7 0h5v-2H9v2zm7 0h5v-2h-5v2z"/></g>
+<g id="router"><path d="M20.2 5.9l.8-.8C19.6 3.7 17.8 3 16 3s-3.6.7-5 2.1l.8.8C13 4.8 14.5 4.2 16 4.2s3 .6 4.2 1.7zm-.9.8c-.9-.9-2.1-1.4-3.3-1.4s-2.4.5-3.3 1.4l.8.8c.7-.7 1.6-1 2.5-1 .9 0 1.8.3 2.5 1l.8-.8zM19 13h-2V9h-2v4H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-4c0-1.1-.9-2-2-2zM8 18H6v-2h2v2zm3.5 0h-2v-2h2v2zm3.5 0h-2v-2h2v2z"/></g>
+<g id="scanner"><path d="M19.8 10.7L4.2 5l-.7 1.9L17.6 12H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-5.5c0-.8-.5-1.6-1.2-1.8zM7 17H5v-2h2v2zm12 0H9v-2h10v2z"/></g>
+<g id="security"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/></g>
+<g id="sim-card"><path d="M19.99 4c0-1.1-.89-2-1.99-2h-8L4 8v12c0 1.1.9 2 2 2h12.01c1.1 0 1.99-.9 1.99-2l-.01-16zM9 19H7v-2h2v2zm8 0h-2v-2h2v2zm-8-4H7v-4h2v4zm4 4h-2v-4h2v4zm0-6h-2v-2h2v2zm4 2h-2v-4h2v4z"/></g>
+<g id="smartphone"><path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></g>
+<g id="speaker"><path d="M17 2H7c-1.1 0-2 .9-2 2v16c0 1.1.9 1.99 2 1.99L17 22c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5 2c1.1 0 2 .9 2 2s-.9 2-2 2c-1.11 0-2-.9-2-2s.89-2 2-2zm0 16c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></g>
+<g id="speaker-group"><path d="M18.2 1H9.8C8.81 1 8 1.81 8 2.8v14.4c0 .99.81 1.79 1.8 1.79l8.4.01c.99 0 1.8-.81 1.8-1.8V2.8c0-.99-.81-1.8-1.8-1.8zM14 3c1.1 0 2 .89 2 2s-.9 2-2 2-2-.89-2-2 .9-2 2-2zm0 13.5c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/><circle cx="14" cy="12.5" r="2.5"/><path d="M6 5H4v16c0 1.1.89 2 2 2h10v-2H6V5z"/></g>
+<g id="tablet"><path d="M21 4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 1.99-.9 1.99-2L23 6c0-1.1-.9-2-2-2zm-2 14H5V6h14v12z"/></g>
+<g id="tablet-android"><path d="M18 0H6C4.34 0 3 1.34 3 3v18c0 1.66 1.34 3 3 3h12c1.66 0 3-1.34 3-3V3c0-1.66-1.34-3-3-3zm-4 22h-4v-1h4v1zm5.25-3H4.75V3h14.5v16z"/></g>
+<g id="tablet-mac"><path d="M18.5 0h-14C3.12 0 2 1.12 2 2.5v19C2 22.88 3.12 24 4.5 24h14c1.38 0 2.5-1.12 2.5-2.5v-19C21 1.12 19.88 0 18.5 0zm-7 23c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm7.5-4H4V3h15v16z"/></g>
+<g id="toys"><path d="M12 12c0-3 2.5-5.5 5.5-5.5S23 9 23 12H12zm0 0c0 3-2.5 5.5-5.5 5.5S1 15 1 12h11zm0 0c-3 0-5.5-2.5-5.5-5.5S9 1 12 1v11zm0 0c3 0 5.5 2.5 5.5 5.5S15 23 12 23V12z"/></g>
+<g id="tv"><path d="M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z"/></g>
+<g id="videogame-asset"><path d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-10 7H8v3H6v-3H3v-2h3V8h2v3h3v2zm4.5 2c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4-3c-.83 0-1.5-.67-1.5-1.5S18.67 9 19.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="watch"><path d="M20 12c0-2.54-1.19-4.81-3.04-6.27L16 0H8l-.95 5.73C5.19 7.19 4 9.45 4 12s1.19 4.81 3.05 6.27L8 24h8l.96-5.73C18.81 16.81 20 14.54 20 12zM6 12c0-3.31 2.69-6 6-6s6 2.69 6 6-2.69 6-6 6-6-2.69-6-6z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/hero.svg b/catapult/third_party/polymer/components/iron-icons/hero.svg
new file mode 100755
index 00000000..52949be5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/hero.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <circle cx="73" cy="24" r="4"/>
+ <path d="M82,33H64V15h18V33z M66,31h14V17H66V31z"/>
+ <circle cx="112.5" cy="24" r="4"/>
+ <circle cx="151" cy="24" r="4"/>
+ <path d="M121,33h-18V15h18V33z M105,31h14V17h-14V31z"/>
+ <path d="M160,33h-18V15h18V33z M144,31h14V17h-14V31z"/>
+ <circle cx="73" cy="62" r="4"/>
+ <path d="M82,71H64V53h18V71z M66,69h14V55H66V69z"/>
+ <circle cx="112.5" cy="62" r="4"/>
+ <path d="M121,71h-18V53h18V71z M105,69h14V55h-14V69z"/>
+ <circle cx="151" cy="62" r="4"/>
+ <path d="M160,71h-18V53h18V71z M144,69h14V55h-14V69z"/>
+ <circle cx="73" cy="102" r="4"/>
+ <path d="M82,111H64V93h18V111z M66,109h14V95H66V109z"/>
+ <circle cx="112.5" cy="102" r="4"/>
+ <path d="M121,111h-18V93h18V111z M105,109h14V95h-14V109z"/>
+ <circle cx="151" cy="102" r="4"/>
+ <path d="M160,111h-18V93h18V111z M144,109h14V95h-14V109z"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/image-icons.html b/catapult/third_party/polymer/components/iron-icons/image-icons.html
new file mode 100644
index 00000000..bb1a4da3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/image-icons.html
@@ -0,0 +1,171 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="image" size="24">
+<svg><defs>
+<g id="add-a-photo"><path d="M3 4V1h2v3h3v2H5v3H3V6H0V4h3zm3 6V7h3V4h7l1.83 2H21c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V10h3zm7 9c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-3.2-5c0 1.77 1.43 3.2 3.2 3.2s3.2-1.43 3.2-3.2-1.43-3.2-3.2-3.2-3.2 1.43-3.2 3.2z"/></g>
+<g id="add-to-photos"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/></g>
+<g id="adjust"><path d="M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3-8c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3z"/></g>
+<g id="assistant"><path d="M19 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5.12 10.88L12 17l-1.88-4.12L6 11l4.12-1.88L12 5l1.88 4.12L18 11l-4.12 1.88z"/></g>
+<g id="assistant-photo"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></g>
+<g id="audiotrack"><path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/></g>
+<g id="blur-circular"><path d="M10 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zM7 9.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm3 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-3-3c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm3-6c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-1.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm3 6c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-4c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm2-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-3.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"/></g>
+<g id="blur-linear"><path d="M5 17.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5zM9 13c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zM3 21h18v-2H3v2zM5 9.5c.83 0 1.5-.67 1.5-1.5S5.83 6.5 5 6.5 3.5 7.17 3.5 8 4.17 9.5 5 9.5zm0 4c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5zM9 17c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm8-.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM3 3v2h18V3H3zm14 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm0 4c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM13 9c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1z"/></g>
+<g id="blur-off"><path d="M14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-.2 4.48l.2.02c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5l.02.2c.09.67.61 1.19 1.28 1.28zM14 3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-4 0c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm11 7c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm8 8c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-4c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm-4 13.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM2.5 5.27l3.78 3.78L6 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1c0-.1-.03-.19-.06-.28l2.81 2.81c-.71.11-1.25.73-1.25 1.47 0 .83.67 1.5 1.5 1.5.74 0 1.36-.54 1.47-1.25l2.81 2.81c-.09-.03-.18-.06-.28-.06-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1c0-.1-.03-.19-.06-.28l3.78 3.78L20 20.23 3.77 4 2.5 5.27zM10 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm11-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zM3 9.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 11c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3-3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5z"/></g>
+<g id="blur-on"><path d="M6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3 .5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm15 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-11 10c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-17c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 5.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm8 .5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3 8.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-4-12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0 8.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm4-4.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-4c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"/></g>
+<g id="brightness-1"><circle cx="12" cy="12" r="10"/></g>
+<g id="brightness-2"><path d="M10 2c-1.82 0-3.53.5-5 1.35C7.99 5.08 10 8.3 10 12s-2.01 6.92-5 8.65C6.47 21.5 8.18 22 10 22c5.52 0 10-4.48 10-10S15.52 2 10 2z"/></g>
+<g id="brightness-3"><path d="M9 2c-1.05 0-2.05.16-3 .46 4.06 1.27 7 5.06 7 9.54 0 4.48-2.94 8.27-7 9.54.95.3 1.95.46 3 .46 5.52 0 10-4.48 10-10S14.52 2 9 2z"/></g>
+<g id="brightness-4"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"/></g>
+<g id="brightness-5"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></g>
+<g id="brightness-6"><path d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18V6c3.31 0 6 2.69 6 6s-2.69 6-6 6z"/></g>
+<g id="brightness-7"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></g>
+<g id="broken-image"><path d="M21 5v6.59l-3-3.01-4 4.01-4-4-4 4-3-3.01V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2zm-3 6.42l3 3.01V19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2v-6.58l3 2.99 4-4 4 4 4-3.99z"/></g>
+<g id="brush"><path d="M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2 2.21 0 4-1.79 4-4 0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34c-.39-.39-1.02-.39-1.41 0L9 12.25 11.75 15l8.96-8.96c.39-.39.39-1.02 0-1.41z"/></g>
+<g id="burst-mode"><path d="M1 5h2v14H1zm4 0h2v14H5zm17 0H10c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zM11 17l2.5-3.15L15.29 16l2.5-3.22L21 17H11z"/></g>
+<g id="camera"><path d="M9.4 10.5l4.77-8.26C13.47 2.09 12.75 2 12 2c-2.4 0-4.6.85-6.32 2.25l3.66 6.35.06-.1zM21.54 9c-.92-2.92-3.15-5.26-6-6.34L11.88 9h9.66zm.26 1h-7.49l.29.5 4.76 8.25C21 16.97 22 14.61 22 12c0-.69-.07-1.35-.2-2zM8.54 12l-3.9-6.75C3.01 7.03 2 9.39 2 12c0 .69.07 1.35.2 2h7.49l-1.15-2zm-6.08 3c.92 2.92 3.15 5.26 6 6.34L12.12 15H2.46zm11.27 0l-3.9 6.76c.7.15 1.42.24 2.17.24 2.4 0 4.6-.85 6.32-2.25l-3.66-6.35-.93 1.6z"/></g>
+<g id="camera-alt"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></g>
+<g id="camera-front"><path d="M10 20H5v2h5v2l3-3-3-3v2zm4 0v2h5v-2h-5zM12 8c1.1 0 2-.9 2-2s-.9-2-2-2-1.99.9-1.99 2S10.9 8 12 8zm5-8H7C5.9 0 5 .9 5 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zM7 2h10v10.5c0-1.67-3.33-2.5-5-2.5s-5 .83-5 2.5V2z"/></g>
+<g id="camera-rear"><path d="M10 20H5v2h5v2l3-3-3-3v2zm4 0v2h5v-2h-5zm3-20H7C5.9 0 5 .9 5 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V2c0-1.1-.9-2-2-2zm-5 6c-1.11 0-2-.9-2-2s.89-2 1.99-2 2 .9 2 2C14 5.1 13.1 6 12 6z"/></g>
+<g id="camera-roll"><path d="M14 5c0-1.1-.9-2-2-2h-1V2c0-.55-.45-1-1-1H6c-.55 0-1 .45-1 1v1H4c-1.1 0-2 .9-2 2v15c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2h8V5h-8zm-2 13h-2v-2h2v2zm0-9h-2V7h2v2zm4 9h-2v-2h2v2zm0-9h-2V7h2v2zm4 9h-2v-2h2v2zm0-9h-2V7h2v2z"/></g>
+<g id="center-focus-strong"><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm-7 7H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4z"/></g>
+<g id="center-focus-weak"><path d="M5 15H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></g>
+<g id="collections"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></g>
+<g id="collections-bookmark"><path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 10l-2.5-1.5L15 12V4h5v8z"/></g>
+<g id="color-lens"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="colorize"><path d="M20.71 5.63l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.92-1.92 3.12-3.12c.4-.4.4-1.03.01-1.42zM6.92 19L5 17.08l8.06-8.06 1.92 1.92L6.92 19z"/></g>
+<g id="compare"><path d="M10 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h5v2h2V1h-2v2zm0 15H5l5-6v6zm9-15h-5v2h5v13l-5-6v9h5c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></g>
+<g id="control-point"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="control-point-duplicate"><path d="M16 8h-2v3h-3v2h3v3h2v-3h3v-2h-3zM2 12c0-2.79 1.64-5.2 4.01-6.32V3.52C2.52 4.76 0 8.09 0 12s2.52 7.24 6.01 8.48v-2.16C3.64 17.2 2 14.79 2 12zm13-9c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z"/></g>
+<g id="crop"><path d="M17 15h2V7c0-1.1-.9-2-2-2H9v2h8v8zM7 17V1H5v4H1v2h4v10c0 1.1.9 2 2 2h10v4h2v-4h4v-2H7z"/></g>
+<g id="crop-16-9"><path d="M19 6H5c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H5V8h14v8z"/></g>
+<g id="crop-3-2"><path d="M19 4H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V6h14v12z"/></g>
+<g id="crop-5-4"><path d="M19 5H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 12H5V7h14v10z"/></g>
+<g id="crop-7-5"><path d="M19 7H5c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm0 8H5V9h14v6z"/></g>
+<g id="crop-din"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/></g>
+<g id="crop-free"><path d="M3 5v4h2V5h4V3H5c-1.1 0-2 .9-2 2zm2 10H3v4c0 1.1.9 2 2 2h4v-2H5v-4zm14 4h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zm0-16h-4v2h4v4h2V5c0-1.1-.9-2-2-2z"/></g>
+<g id="crop-landscape"><path d="M19 5H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 12H5V7h14v10z"/></g>
+<g id="crop-original"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zm-5.04-6.71l-2.75 3.54-1.96-2.36L6.5 17h11l-3.54-4.71z"/></g>
+<g id="crop-portrait"><path d="M17 3H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H7V5h10v14z"/></g>
+<g id="crop-rotate"><path d="M7.47 21.49C4.2 19.93 1.86 16.76 1.5 13H0c.51 6.16 5.66 11 11.95 11 .23 0 .44-.02.66-.03L8.8 20.15l-1.33 1.34zM12.05 0c-.23 0-.44.02-.66.04l3.81 3.81 1.33-1.33C19.8 4.07 22.14 7.24 22.5 11H24c-.51-6.16-5.66-11-11.95-11zM16 14h2V8c0-1.11-.9-2-2-2h-6v2h6v6zm-8 2V4H6v2H4v2h2v8c0 1.1.89 2 2 2h8v2h2v-2h2v-2H8z"/></g>
+<g id="crop-square"><path d="M18 4H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H6V6h12v12z"/></g>
+<g id="dehaze"><path d="M2 15.5v2h20v-2H2zm0-5v2h20v-2H2zm0-5v2h20v-2H2z"/></g>
+<g id="details"><path d="M3 4l9 16 9-16H3zm3.38 2h11.25L12 16 6.38 6z"/></g>
+<g id="edit"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></g>
+<g id="exposure"><path d="M15 17v2h2v-2h2v-2h-2v-2h-2v2h-2v2h2zm5-15H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM5 5h6v2H5V5zm15 15H4L20 4v16z"/></g>
+<g id="exposure-neg-1"><path d="M4 11v2h8v-2H4zm15 7h-2V7.38L14 8.4V6.7L18.7 5h.3v13z"/></g>
+<g id="exposure-neg-2"><path d="M15.05 16.29l2.86-3.07c.38-.39.72-.79 1.04-1.18.32-.39.59-.78.82-1.17.23-.39.41-.78.54-1.17s.19-.79.19-1.18c0-.53-.09-1.02-.27-1.46-.18-.44-.44-.81-.78-1.11-.34-.31-.77-.54-1.26-.71-.51-.16-1.08-.24-1.72-.24-.69 0-1.31.11-1.85.32-.54.21-1 .51-1.36.88-.37.37-.65.8-.84 1.3-.18.47-.27.97-.28 1.5h2.14c.01-.31.05-.6.13-.87.09-.29.23-.54.4-.75.18-.21.41-.37.68-.49.27-.12.6-.18.96-.18.31 0 .58.05.81.15.23.1.43.25.59.43.16.18.28.4.37.65.08.25.13.52.13.81 0 .22-.03.43-.08.65-.06.22-.15.45-.29.7-.14.25-.32.53-.56.83-.23.3-.52.65-.88 1.03l-4.17 4.55V18H21v-1.71h-5.95zM2 11v2h8v-2H2z"/></g>
+<g id="exposure-plus-1"><path d="M10 7H8v4H4v2h4v4h2v-4h4v-2h-4V7zm10 11h-2V7.38L15 8.4V6.7L19.7 5h.3v13z"/></g>
+<g id="exposure-plus-2"><path d="M16.05 16.29l2.86-3.07c.38-.39.72-.79 1.04-1.18.32-.39.59-.78.82-1.17.23-.39.41-.78.54-1.17.13-.39.19-.79.19-1.18 0-.53-.09-1.02-.27-1.46-.18-.44-.44-.81-.78-1.11-.34-.31-.77-.54-1.26-.71-.51-.16-1.08-.24-1.72-.24-.69 0-1.31.11-1.85.32-.54.21-1 .51-1.36.88-.37.37-.65.8-.84 1.3-.18.47-.27.97-.28 1.5h2.14c.01-.31.05-.6.13-.87.09-.29.23-.54.4-.75.18-.21.41-.37.68-.49.27-.12.6-.18.96-.18.31 0 .58.05.81.15.23.1.43.25.59.43.16.18.28.4.37.65.08.25.13.52.13.81 0 .22-.03.43-.08.65-.06.22-.15.45-.29.7-.14.25-.32.53-.56.83-.23.3-.52.65-.88 1.03l-4.17 4.55V18H22v-1.71h-5.95zM8 7H6v4H2v2h4v4h2v-4h4v-2H8V7z"/></g>
+<g id="exposure-zero"><path d="M16.14 12.5c0 1-.1 1.85-.3 2.55-.2.7-.48 1.27-.83 1.7-.36.44-.79.75-1.3.95-.51.2-1.07.3-1.7.3-.62 0-1.18-.1-1.69-.3-.51-.2-.95-.51-1.31-.95-.36-.44-.65-1.01-.85-1.7-.2-.7-.3-1.55-.3-2.55v-2.04c0-1 .1-1.85.3-2.55.2-.7.48-1.26.84-1.69.36-.43.8-.74 1.31-.93C10.81 5.1 11.38 5 12 5c.63 0 1.19.1 1.7.29.51.19.95.5 1.31.93.36.43.64.99.84 1.69.2.7.3 1.54.3 2.55v2.04zm-2.11-2.36c0-.64-.05-1.18-.13-1.62-.09-.44-.22-.79-.4-1.06-.17-.27-.39-.46-.64-.58-.25-.13-.54-.19-.86-.19-.32 0-.61.06-.86.18s-.47.31-.64.58c-.17.27-.31.62-.4 1.06s-.13.98-.13 1.62v2.67c0 .64.05 1.18.14 1.62.09.45.23.81.4 1.09s.39.48.64.61.54.19.87.19c.33 0 .62-.06.87-.19s.46-.33.63-.61c.17-.28.3-.64.39-1.09.09-.45.13-.99.13-1.62v-2.66z"/></g>
+<g id="filter"><path d="M15.96 10.29l-2.75 3.54-1.96-2.36L8.5 15h11l-3.54-4.71zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></g>
+<g id="filter-1"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 10h2V5h-4v2h2v8zm7-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></g>
+<g id="filter-2"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-4-4h-4v-2h2c1.1 0 2-.89 2-2V7c0-1.11-.9-2-2-2h-4v2h4v2h-2c-1.1 0-2 .89-2 2v4h6v-2z"/></g>
+<g id="filter-3"><path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7c0-1.11-.9-2-2-2h-4v2h4v2h-2v2h2v2h-4v2h4c1.1 0 2-.89 2-2z"/></g>
+<g id="filter-4"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm12 10h2V5h-2v4h-2V5h-2v6h4v4zm6-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></g>
+<g id="filter-5"><path d="M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-2c0-1.11-.9-2-2-2h-2V7h4V5h-6v6h4v2h-4v2h4c1.1 0 2-.89 2-2z"/></g>
+<g id="filter-6"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2h2c1.1 0 2-.89 2-2v-2c0-1.11-.9-2-2-2h-2V7h4V5h-4c-1.1 0-2 .89-2 2v6c0 1.11.9 2 2 2zm0-4h2v2h-2v-2z"/></g>
+<g id="filter-7"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2l4-8V5h-6v2h4l-4 8h2z"/></g>
+<g id="filter-8"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-8-2h2c1.1 0 2-.89 2-2v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7c0-1.11-.9-2-2-2h-2c-1.1 0-2 .89-2 2v1.5c0 .83.67 1.5 1.5 1.5-.83 0-1.5.67-1.5 1.5V13c0 1.11.9 2 2 2zm0-8h2v2h-2V7zm0 4h2v2h-2v-2z"/></g>
+<g id="filter-9"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM15 5h-2c-1.1 0-2 .89-2 2v2c0 1.11.9 2 2 2h2v2h-4v2h4c1.1 0 2-.89 2-2V7c0-1.11-.9-2-2-2zm0 4h-2V7h2v2z"/></g>
+<g id="filter-9-plus"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 7V8c0-1.11-.9-2-2-2h-1c-1.1 0-2 .89-2 2v1c0 1.11.9 2 2 2h1v1H9v2h3c1.1 0 2-.89 2-2zm-3-3V8h1v1h-1zm10-8H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 8h-2V7h-2v2h-2v2h2v2h2v-2h2v6H7V3h14v6z"/></g>
+<g id="filter-b-and-w"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16l-7-8v8H5l7-8V5h7v14z"/></g>
+<g id="filter-center-focus"><path d="M5 15H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4zM12 9c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></g>
+<g id="filter-drama"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.61 5.64 5.36 8.04 2.35 8.36 0 10.9 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4h2c0-2.76-1.86-5.08-4.4-5.78C8.61 6.88 10.2 6 12 6c3.03 0 5.5 2.47 5.5 5.5v.5H19c1.65 0 3 1.35 3 3s-1.35 3-3 3z"/></g>
+<g id="filter-frames"><path d="M20 4h-4l-4-4-4 4H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H4V6h4.52l3.52-3.5L15.52 6H20v14zM18 8H6v10h12"/></g>
+<g id="filter-hdr"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"/></g>
+<g id="filter-none"><path d="M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"/></g>
+<g id="filter-tilt-shift"><path d="M11 4.07V2.05c-2.01.2-3.84 1-5.32 2.21L7.1 5.69c1.11-.86 2.44-1.44 3.9-1.62zm7.32.19C16.84 3.05 15.01 2.25 13 2.05v2.02c1.46.18 2.79.76 3.9 1.62l1.42-1.43zM19.93 11h2.02c-.2-2.01-1-3.84-2.21-5.32L18.31 7.1c.86 1.11 1.44 2.44 1.62 3.9zM5.69 7.1L4.26 5.68C3.05 7.16 2.25 8.99 2.05 11h2.02c.18-1.46.76-2.79 1.62-3.9zM4.07 13H2.05c.2 2.01 1 3.84 2.21 5.32l1.43-1.43c-.86-1.1-1.44-2.43-1.62-3.89zM15 12c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3zm3.31 4.9l1.43 1.43c1.21-1.48 2.01-3.32 2.21-5.32h-2.02c-.18 1.45-.76 2.78-1.62 3.89zM13 19.93v2.02c2.01-.2 3.84-1 5.32-2.21l-1.43-1.43c-1.1.86-2.43 1.44-3.89 1.62zm-7.32-.19C7.16 20.95 9 21.75 11 21.95v-2.02c-1.46-.18-2.79-.76-3.9-1.62l-1.42 1.43z"/></g>
+<g id="filter-vintage"><path d="M18.7 12.4c-.28-.16-.57-.29-.86-.4.29-.11.58-.24.86-.4 1.92-1.11 2.99-3.12 3-5.19-1.79-1.03-4.07-1.11-6 0-.28.16-.54.35-.78.54.05-.31.08-.63.08-.95 0-2.22-1.21-4.15-3-5.19C10.21 1.85 9 3.78 9 6c0 .32.03.64.08.95-.24-.2-.5-.39-.78-.55-1.92-1.11-4.2-1.03-6 0 0 2.07 1.07 4.08 3 5.19.28.16.57.29.86.4-.29.11-.58.24-.86.4-1.92 1.11-2.99 3.12-3 5.19 1.79 1.03 4.07 1.11 6 0 .28-.16.54-.35.78-.54-.05.32-.08.64-.08.96 0 2.22 1.21 4.15 3 5.19 1.79-1.04 3-2.97 3-5.19 0-.32-.03-.64-.08-.95.24.2.5.38.78.54 1.92 1.11 4.2 1.03 6 0-.01-2.07-1.08-4.08-3-5.19zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/></g>
+<g id="flare"><path d="M7 11H1v2h6v-2zm2.17-3.24L7.05 5.64 5.64 7.05l2.12 2.12 1.41-1.41zM13 1h-2v6h2V1zm5.36 6.05l-1.41-1.41-2.12 2.12 1.41 1.41 2.12-2.12zM17 11v2h6v-2h-6zm-5-2c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm2.83 7.24l2.12 2.12 1.41-1.41-2.12-2.12-1.41 1.41zm-9.19.71l1.41 1.41 2.12-2.12-1.41-1.41-2.12 2.12zM11 23h2v-6h-2v6z"/></g>
+<g id="flash-auto"><path d="M3 2v12h3v9l7-12H9l4-9H3zm16 0h-2l-3.2 9h1.9l.7-2h3.2l.7 2h1.9L19 2zm-2.15 5.65L18 4l1.15 3.65h-2.3z"/></g>
+<g id="flash-off"><path d="M3.27 3L2 4.27l5 5V13h3v9l3.58-6.14L17.73 20 19 18.73 3.27 3zM17 10h-4l4-8H7v2.18l8.46 8.46L17 10z"/></g>
+<g id="flash-on"><path d="M7 2v11h3v9l7-12h-4l4-8z"/></g>
+<g id="flip"><path d="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8 20h2V1h-2v22zm8-6h2v-2h-2v2zM15 5h2V3h-2v2zm4 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z"/></g>
+<g id="gradient"><path d="M11 9h2v2h-2zm-2 2h2v2H9zm4 0h2v2h-2zm2-2h2v2h-2zM7 9h2v2H7zm12-6H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 18H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm2-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V5h14v6z"/></g>
+<g id="grain"><path d="M10 12c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-4 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4-4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
+<g id="grid-off"><path d="M8 4v1.45l2 2V4h4v4h-3.45l2 2H14v1.45l2 2V10h4v4h-3.45l2 2H20v1.45l2 2V4c0-1.1-.9-2-2-2H4.55l2 2H8zm8 0h4v4h-4V4zM1.27 1.27L0 2.55l2 2V20c0 1.1.9 2 2 2h15.46l2 2 1.27-1.27L1.27 1.27zM10 12.55L11.45 14H10v-1.45zm-6-6L5.45 8H4V6.55zM8 20H4v-4h4v4zm0-6H4v-4h3.45l.55.55V14zm6 6h-4v-4h3.45l.55.54V20zm2 0v-1.46L17.46 20H16z"/></g>
+<g id="grid-on"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"/></g>
+<g id="hdr-off"><path d="M17.5 15v-2h1.1l.9 2H21l-.9-2.1c.5-.2.9-.8.9-1.4v-1c0-.8-.7-1.5-1.5-1.5H16v4.9l1.1 1.1h.4zm0-4.5h2v1h-2v-1zm-4.5 0v.4l1.5 1.5v-1.9c0-.8-.7-1.5-1.5-1.5h-1.9l1.5 1.5h.4zm-3.5-1l-7-7-1.1 1L6.9 9h-.4v2h-2V9H3v6h1.5v-2.5h2V15H8v-4.9l1.5 1.5V15h3.4l7.6 7.6 1.1-1.1-12.1-12z"/></g>
+<g id="hdr-on"><path d="M21 11.5v-1c0-.8-.7-1.5-1.5-1.5H16v6h1.5v-2h1.1l.9 2H21l-.9-2.1c.5-.3.9-.8.9-1.4zm-1.5 0h-2v-1h2v1zm-13-.5h-2V9H3v6h1.5v-2.5h2V15H8V9H6.5v2zM13 9H9.5v6H13c.8 0 1.5-.7 1.5-1.5v-3c0-.8-.7-1.5-1.5-1.5zm0 4.5h-2v-3h2v3z"/></g>
+<g id="hdr-strong"><path d="M17 6c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zM5 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></g>
+<g id="hdr-weak"><path d="M5 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm12-2c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/></g>
+<g id="healing"><path d="M17.73 12.02l3.98-3.98c.39-.39.39-1.02 0-1.41l-4.34-4.34c-.39-.39-1.02-.39-1.41 0l-3.98 3.98L8 2.29C7.8 2.1 7.55 2 7.29 2c-.25 0-.51.1-.7.29L2.25 6.63c-.39.39-.39 1.02 0 1.41l3.98 3.98L2.25 16c-.39.39-.39 1.02 0 1.41l4.34 4.34c.39.39 1.02.39 1.41 0l3.98-3.98 3.98 3.98c.2.2.45.29.71.29.26 0 .51-.1.71-.29l4.34-4.34c.39-.39.39-1.02 0-1.41l-3.99-3.98zM12 9c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm-4.71 1.96L3.66 7.34l3.63-3.63 3.62 3.62-3.62 3.63zM10 13c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm2 2c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm2-4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2.66 9.34l-3.63-3.62 3.63-3.63 3.62 3.62-3.62 3.63z"/></g>
+<g id="image"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></g>
+<g id="image-aspect-ratio"><path d="M16 10h-2v2h2v-2zm0 4h-2v2h2v-2zm-8-4H6v2h2v-2zm4 0h-2v2h2v-2zm8-6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V6h16v12z"/></g>
+<g id="iso"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM5.5 7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5zM19 19H5L19 5v14zm-2-2v-1.5h-5V17h5z"/></g>
+<g id="landscape"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"/></g>
+<g id="leak-add"><path d="M6 3H3v3c1.66 0 3-1.34 3-3zm8 0h-2c0 4.97-4.03 9-9 9v2c6.08 0 11-4.93 11-11zm-4 0H8c0 2.76-2.24 5-5 5v2c3.87 0 7-3.13 7-7zm0 18h2c0-4.97 4.03-9 9-9v-2c-6.07 0-11 4.93-11 11zm8 0h3v-3c-1.66 0-3 1.34-3 3zm-4 0h2c0-2.76 2.24-5 5-5v-2c-3.87 0-7 3.13-7 7z"/></g>
+<g id="leak-remove"><path d="M10 3H8c0 .37-.04.72-.12 1.06l1.59 1.59C9.81 4.84 10 3.94 10 3zM3 4.27l2.84 2.84C5.03 7.67 4.06 8 3 8v2c1.61 0 3.09-.55 4.27-1.46L8.7 9.97C7.14 11.24 5.16 12 3 12v2c2.71 0 5.19-.99 7.11-2.62l2.5 2.5C10.99 15.81 10 18.29 10 21h2c0-2.16.76-4.14 2.03-5.69l1.43 1.43C14.55 17.91 14 19.39 14 21h2c0-1.06.33-2.03.89-2.84L19.73 21 21 19.73 4.27 3 3 4.27zM14 3h-2c0 1.5-.37 2.91-1.02 4.16l1.46 1.46C13.42 6.98 14 5.06 14 3zm5.94 13.12c.34-.08.69-.12 1.06-.12v-2c-.94 0-1.84.19-2.66.52l1.6 1.6zm-4.56-4.56l1.46 1.46C18.09 12.37 19.5 12 21 12v-2c-2.06 0-3.98.58-5.62 1.56z"/></g>
+<g id="lens"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/></g>
+<g id="linked-camera"><circle cx="12" cy="14" r="3.2"/><path d="M16 3.33c2.58 0 4.67 2.09 4.67 4.67H22c0-3.31-2.69-6-6-6v1.33M16 6c1.11 0 2 .89 2 2h1.33c0-1.84-1.49-3.33-3.33-3.33V6"/><path d="M17 9c0-1.11-.89-2-2-2V4H9L7.17 6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V9h-5zm-5 10c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></g>
+<g id="looks"><path d="M12 10c-3.86 0-7 3.14-7 7h2c0-2.76 2.24-5 5-5s5 2.24 5 5h2c0-3.86-3.14-7-7-7zm0-4C5.93 6 1 10.93 1 17h2c0-4.96 4.04-9 9-9s9 4.04 9 9h2c0-6.07-4.93-11-11-11z"/></g>
+<g id="looks-3"><path d="M19.01 3h-14c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 7.5c0 .83-.67 1.5-1.5 1.5.83 0 1.5.67 1.5 1.5V15c0 1.11-.9 2-2 2h-4v-2h4v-2h-2v-2h2V9h-4V7h4c1.1 0 2 .89 2 2v1.5z"/></g>
+<g id="looks-4"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 14h-2v-4H9V7h2v4h2V7h2v10z"/></g>
+<g id="looks-5"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h2c1.1 0 2 .89 2 2v2c0 1.11-.9 2-2 2H9v-2h4v-2H9V7h6v2z"/></g>
+<g id="looks-6"><path d="M11 15h2v-2h-2v2zm8-12H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h2c1.1 0 2 .89 2 2v2c0 1.11-.9 2-2 2h-2c-1.1 0-2-.89-2-2V9c0-1.11.9-2 2-2h4v2z"/></g>
+<g id="looks-one"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14h-2V9h-2V7h4v10z"/></g>
+<g id="looks-two"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 8c0 1.11-.9 2-2 2h-2v2h4v2H9v-4c0-1.11.9-2 2-2h2V9H9V7h4c1.1 0 2 .89 2 2v2z"/></g>
+<g id="loupe"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10h8c1.1 0 2-.9 2-2v-8c0-5.51-4.49-10-10-10zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="monochrome-photos"><path d="M20 5h-3.2L15 3H9L7.2 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 14h-8v-1c-2.8 0-5-2.2-5-5s2.2-5 5-5V7h8v12zm-3-6c0-2.8-2.2-5-5-5v1.8c1.8 0 3.2 1.4 3.2 3.2s-1.4 3.2-3.2 3.2V18c2.8 0 5-2.2 5-5zm-8.2 0c0 1.8 1.4 3.2 3.2 3.2V9.8c-1.8 0-3.2 1.4-3.2 3.2z"/></g>
+<g id="movie-creation"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"/></g>
+<g id="movie-filter"><path d="M18 4l2 3h-3l-2-3h-2l2 3h-3l-2-3H8l2 3H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4zm-6.75 11.25L10 18l-1.25-2.75L6 14l2.75-1.25L10 10l1.25 2.75L14 14l-2.75 1.25zm5.69-3.31L16 14l-.94-2.06L13 11l2.06-.94L16 8l.94 2.06L19 11l-2.06.94z"/></g>
+<g id="music-note"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/></g>
+<g id="nature"><path d="M13 16.12c3.47-.41 6.17-3.36 6.17-6.95 0-3.87-3.13-7-7-7s-7 3.13-7 7c0 3.47 2.52 6.34 5.83 6.89V20H5v2h14v-2h-6v-3.88z"/></g>
+<g id="nature-people"><path d="M22.17 9.17c0-3.87-3.13-7-7-7s-7 3.13-7 7c0 3.47 2.52 6.34 5.83 6.89V20H6v-3h1v-4c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v4h1v5h16v-2h-3v-3.88c3.47-.41 6.17-3.36 6.17-6.95zM4.5 11c.83 0 1.5-.67 1.5-1.5S5.33 8 4.5 8 3 8.67 3 9.5 3.67 11 4.5 11z"/></g>
+<g id="navigate-before"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></g>
+<g id="navigate-next"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></g>
+<g id="palette"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="panorama"><path d="M23 18V6c0-1.1-.9-2-2-2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zM8.5 12.5l2.5 3.01L14.5 11l4.5 6H5l3.5-4.5z"/></g>
+<g id="panorama-fish-eye"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="panorama-horizontal"><path d="M20 6.54v10.91c-2.6-.77-5.28-1.16-8-1.16-2.72 0-5.4.39-8 1.16V6.54c2.6.77 5.28 1.16 8 1.16 2.72.01 5.4-.38 8-1.16M21.43 4c-.1 0-.2.02-.31.06C18.18 5.16 15.09 5.7 12 5.7c-3.09 0-6.18-.55-9.12-1.64-.11-.04-.22-.06-.31-.06-.34 0-.57.23-.57.63v14.75c0 .39.23.62.57.62.1 0 .2-.02.31-.06 2.94-1.1 6.03-1.64 9.12-1.64 3.09 0 6.18.55 9.12 1.64.11.04.21.06.31.06.33 0 .57-.23.57-.63V4.63c0-.4-.24-.63-.57-.63z"/></g>
+<g id="panorama-vertical"><path d="M19.94 21.12c-1.1-2.94-1.64-6.03-1.64-9.12 0-3.09.55-6.18 1.64-9.12.04-.11.06-.22.06-.31 0-.34-.23-.57-.63-.57H4.63c-.4 0-.63.23-.63.57 0 .1.02.2.06.31C5.16 5.82 5.71 8.91 5.71 12c0 3.09-.55 6.18-1.64 9.12-.05.11-.07.22-.07.31 0 .33.23.57.63.57h14.75c.39 0 .63-.24.63-.57-.01-.1-.03-.2-.07-.31zM6.54 20c.77-2.6 1.16-5.28 1.16-8 0-2.72-.39-5.4-1.16-8h10.91c-.77 2.6-1.16 5.28-1.16 8 0 2.72.39 5.4 1.16 8H6.54z"/></g>
+<g id="panorama-wide-angle"><path d="M12 6c2.45 0 4.71.2 7.29.64.47 1.78.71 3.58.71 5.36 0 1.78-.24 3.58-.71 5.36-2.58.44-4.84.64-7.29.64s-4.71-.2-7.29-.64C4.24 15.58 4 13.78 4 12c0-1.78.24-3.58.71-5.36C7.29 6.2 9.55 6 12 6m0-2c-2.73 0-5.22.24-7.95.72l-.93.16-.25.9C2.29 7.85 2 9.93 2 12s.29 4.15.87 6.22l.25.89.93.16c2.73.49 5.22.73 7.95.73s5.22-.24 7.95-.72l.93-.16.25-.89c.58-2.08.87-4.16.87-6.23s-.29-4.15-.87-6.22l-.25-.89-.93-.16C17.22 4.24 14.73 4 12 4z"/></g>
+<g id="photo"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></g>
+<g id="photo-album"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4zm0 15l3-3.86 2.14 2.58 3-3.86L18 19H6z"/></g>
+<g id="photo-camera"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></g>
+<g id="photo-filter"><path d="M19.02 10v9H5V5h9V3H5.02c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-9h-2zM17 10l.94-2.06L20 7l-2.06-.94L17 4l-.94 2.06L14 7l2.06.94zm-3.75.75L12 8l-1.25 2.75L8 12l2.75 1.25L12 16l1.25-2.75L16 12z"/></g>
+<g id="photo-library"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></g>
+<g id="photo-size-select-actual"><path d="M21 3H3C2 3 1 4 1 5v14c0 1.1.9 2 2 2h18c1 0 2-1 2-2V5c0-1-1-2-2-2zM5 17l3.5-4.5 2.5 3.01L14.5 11l4.5 6H5z"/></g>
+<g id="photo-size-select-large"><path d="M21 15h2v2h-2v-2zm0-4h2v2h-2v-2zm2 8h-2v2c1 0 2-1 2-2zM13 3h2v2h-2V3zm8 4h2v2h-2V7zm0-4v2h2c0-1-1-2-2-2zM1 7h2v2H1V7zm16-4h2v2h-2V3zm0 16h2v2h-2v-2zM3 3C2 3 1 4 1 5h2V3zm6 0h2v2H9V3zM5 3h2v2H5V3zm-4 8v8c0 1.1.9 2 2 2h12V11H1zm2 8l2.5-3.21 1.79 2.15 2.5-3.22L13 19H3z"/></g>
+<g id="photo-size-select-small"><path d="M23 15h-2v2h2v-2zm0-4h-2v2h2v-2zm0 8h-2v2c1 0 2-1 2-2zM15 3h-2v2h2V3zm8 4h-2v2h2V7zm-2-4v2h2c0-1-1-2-2-2zM3 21h8v-6H1v4c0 1.1.9 2 2 2zM3 7H1v2h2V7zm12 12h-2v2h2v-2zm4-16h-2v2h2V3zm0 16h-2v2h2v-2zM3 3C2 3 1 4 1 5h2V3zm0 8H1v2h2v-2zm8-8H9v2h2V3zM7 3H5v2h2V3z"/></g>
+<g id="picture-as-pdf"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 7.5c0 .83-.67 1.5-1.5 1.5H9v2H7.5V7H10c.83 0 1.5.67 1.5 1.5v1zm5 2c0 .83-.67 1.5-1.5 1.5h-2.5V7H15c.83 0 1.5.67 1.5 1.5v3zm4-3H19v1h1.5V11H19v2h-1.5V7h3v1.5zM9 9.5h1v-1H9v1zM4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm10 5.5h1v-3h-1v3z"/></g>
+<g id="portrait"><path d="M12 12.25c1.24 0 2.25-1.01 2.25-2.25S13.24 7.75 12 7.75 9.75 8.76 9.75 10s1.01 2.25 2.25 2.25zm4.5 4c0-1.5-3-2.25-4.5-2.25s-4.5.75-4.5 2.25V17h9v-.75zM19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/></g>
+<g id="remove-red-eye"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></g>
+<g id="rotate-90-degrees-ccw"><path d="M7.34 6.41L.86 12.9l6.49 6.48 6.49-6.48-6.5-6.49zM3.69 12.9l3.66-3.66L11 12.9l-3.66 3.66-3.65-3.66zm15.67-6.26C17.61 4.88 15.3 4 13 4V.76L8.76 5 13 9.24V6c1.79 0 3.58.68 4.95 2.05 2.73 2.73 2.73 7.17 0 9.9C16.58 19.32 14.79 20 13 20c-.97 0-1.94-.21-2.84-.61l-1.49 1.49C10.02 21.62 11.51 22 13 22c2.3 0 4.61-.88 6.36-2.64 3.52-3.51 3.52-9.21 0-12.72z"/></g>
+<g id="rotate-left"><path d="M7.11 8.53L5.7 7.11C4.8 8.27 4.24 9.61 4.07 11h2.02c.14-.87.49-1.72 1.02-2.47zM6.09 13H4.07c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16.9 2.51 1.44 3.9 1.61V17.9c-.87-.15-1.71-.49-2.46-1.03L7.1 18.32zM13 4.07V1L8.45 5.55 13 10V6.09c2.84.48 5 2.94 5 5.91s-2.16 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93s-3.05-7.44-7-7.93z"/></g>
+<g id="rotate-right"><path d="M15.55 5.55L11 1v3.07C7.06 4.56 4 7.92 4 12s3.05 7.44 7 7.93v-2.02c-2.84-.48-5-2.94-5-5.91s2.16-5.43 5-5.91V10l4.55-4.45zM19.93 11c-.17-1.39-.72-2.73-1.62-3.89l-1.42 1.42c.54.75.88 1.6 1.02 2.47h2.02zM13 17.9v2.02c1.39-.17 2.74-.71 3.9-1.61l-1.44-1.44c-.75.54-1.59.89-2.46 1.03zm3.89-2.42l1.42 1.41c.9-1.16 1.45-2.5 1.62-3.89h-2.02c-.14.87-.48 1.72-1.02 2.48z"/></g>
+<g id="slideshow"><path d="M10 8v8l5-4-5-4zm9-5H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/></g>
+<g id="straighten"><path d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z"/></g>
+<g id="style"><path d="M2.53 19.65l1.34.56v-9.03l-2.43 5.86c-.41 1.02.08 2.19 1.09 2.61zm19.5-3.7L17.07 3.98c-.31-.75-1.04-1.21-1.81-1.23-.26 0-.53.04-.79.15L7.1 5.95c-.75.31-1.21 1.03-1.23 1.8-.01.27.04.54.15.8l4.96 11.97c.31.76 1.05 1.22 1.83 1.23.26 0 .52-.05.77-.15l7.36-3.05c1.02-.42 1.51-1.59 1.09-2.6zM7.88 8.75c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-2 11c0 1.1.9 2 2 2h1.45l-3.45-8.34v6.34z"/></g>
+<g id="switch-camera"><path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 11.5V13H9v2.5L5.5 12 9 8.5V11h6V8.5l3.5 3.5-3.5 3.5z"/></g>
+<g id="switch-video"><path d="M18 9.5V6c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h14c.55 0 1-.45 1-1v-3.5l4 4v-13l-4 4zm-5 6V13H7v2.5L3.5 12 7 8.5V11h6V8.5l3.5 3.5-3.5 3.5z"/></g>
+<g id="tag-faces"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></g>
+<g id="texture"><path d="M19.51 3.08L3.08 19.51c.09.34.27.65.51.9.25.24.56.42.9.51L20.93 4.49c-.19-.69-.73-1.23-1.42-1.41zM11.88 3L3 11.88v2.83L14.71 3h-2.83zM5 3c-1.1 0-2 .9-2 2v2l4-4H5zm14 18c.55 0 1.05-.22 1.41-.59.37-.36.59-.86.59-1.41v-2l-4 4h2zm-9.71 0h2.83L21 12.12V9.29L9.29 21z"/></g>
+<g id="timelapse"><path d="M16.24 7.76C15.07 6.59 13.54 6 12 6v6l-4.24 4.24c2.34 2.34 6.14 2.34 8.49 0 2.34-2.34 2.34-6.14-.01-8.48zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></g>
+<g id="timer"><path d="M15 1H9v2h6V1zm-4 13h2V8h-2v6zm8.03-6.61l1.42-1.42c-.43-.51-.9-.99-1.41-1.41l-1.42 1.42C16.07 4.74 14.12 4 12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9 9-4.03 9-9c0-2.12-.74-4.07-1.97-5.61zM12 20c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></g>
+<g id="timer-10"><path d="M0 7.72V9.4l3-1V18h2V6h-.25L0 7.72zm23.78 6.65c-.14-.28-.35-.53-.63-.74-.28-.21-.61-.39-1.01-.53s-.85-.27-1.35-.38c-.35-.07-.64-.15-.87-.23-.23-.08-.41-.16-.55-.25-.14-.09-.23-.19-.28-.3-.05-.11-.08-.24-.08-.39 0-.14.03-.28.09-.41.06-.13.15-.25.27-.34.12-.1.27-.18.45-.24s.4-.09.64-.09c.25 0 .47.04.66.11.19.07.35.17.48.29.13.12.22.26.29.42.06.16.1.32.1.49h1.95c0-.39-.08-.75-.24-1.09-.16-.34-.39-.63-.69-.88-.3-.25-.66-.44-1.09-.59C21.49 9.07 21 9 20.46 9c-.51 0-.98.07-1.39.21-.41.14-.77.33-1.06.57-.29.24-.51.52-.67.84-.16.32-.23.65-.23 1.01s.08.69.23.96c.15.28.36.52.64.73.27.21.6.38.98.53.38.14.81.26 1.27.36.39.08.71.17.95.26s.43.19.57.29c.13.1.22.22.27.34.05.12.07.25.07.39 0 .32-.13.57-.4.77-.27.2-.66.29-1.17.29-.22 0-.43-.02-.64-.08-.21-.05-.4-.13-.56-.24-.17-.11-.3-.26-.41-.44-.11-.18-.17-.41-.18-.67h-1.89c0 .36.08.71.24 1.05.16.34.39.65.7.93.31.27.69.49 1.15.66.46.17.98.25 1.58.25.53 0 1.01-.06 1.44-.19.43-.13.8-.31 1.11-.54.31-.23.54-.51.71-.83.17-.32.25-.67.25-1.06-.02-.4-.09-.74-.24-1.02zm-9.96-7.32c-.34-.4-.75-.7-1.23-.88-.47-.18-1.01-.27-1.59-.27-.58 0-1.11.09-1.59.27-.48.18-.89.47-1.23.88-.34.41-.6.93-.79 1.59-.18.65-.28 1.45-.28 2.39v1.92c0 .94.09 1.74.28 2.39.19.66.45 1.19.8 1.6.34.41.75.71 1.23.89.48.18 1.01.28 1.59.28.59 0 1.12-.09 1.59-.28.48-.18.88-.48 1.22-.89.34-.41.6-.94.78-1.6.18-.65.28-1.45.28-2.39v-1.92c0-.94-.09-1.74-.28-2.39-.18-.66-.44-1.19-.78-1.59zm-.92 6.17c0 .6-.04 1.11-.12 1.53-.08.42-.2.76-.36 1.02-.16.26-.36.45-.59.57-.23.12-.51.18-.82.18-.3 0-.58-.06-.82-.18s-.44-.31-.6-.57c-.16-.26-.29-.6-.38-1.02-.09-.42-.13-.93-.13-1.53v-2.5c0-.6.04-1.11.13-1.52.09-.41.21-.74.38-1 .16-.25.36-.43.6-.55.24-.11.51-.17.81-.17.31 0 .58.06.81.17.24.11.44.29.6.55.16.25.29.58.37.99.08.41.13.92.13 1.52v2.51z"/></g>
+<g id="timer-3"><path d="M11.61 12.97c-.16-.24-.36-.46-.62-.65-.25-.19-.56-.35-.93-.48.3-.14.57-.3.8-.5.23-.2.42-.41.57-.64.15-.23.27-.46.34-.71.08-.24.11-.49.11-.73 0-.55-.09-1.04-.28-1.46-.18-.42-.44-.77-.78-1.06-.33-.28-.73-.5-1.2-.64-.45-.13-.97-.2-1.53-.2-.55 0-1.06.08-1.52.24-.47.17-.87.4-1.2.69-.33.29-.6.63-.78 1.03-.2.39-.29.83-.29 1.29h1.98c0-.26.05-.49.14-.69.09-.2.22-.38.38-.52.17-.14.36-.25.58-.33.22-.08.46-.12.73-.12.61 0 1.06.16 1.36.47.3.31.44.75.44 1.32 0 .27-.04.52-.12.74-.08.22-.21.41-.38.57-.17.16-.38.28-.63.37-.25.09-.55.13-.89.13H6.72v1.57H7.9c.34 0 .64.04.91.11.27.08.5.19.69.35.19.16.34.36.44.61.1.24.16.54.16.87 0 .62-.18 1.09-.53 1.42-.35.33-.84.49-1.45.49-.29 0-.56-.04-.8-.13-.24-.08-.44-.2-.61-.36-.17-.16-.3-.34-.39-.56-.09-.22-.14-.46-.14-.72H4.19c0 .55.11 1.03.32 1.45.21.42.5.77.86 1.05s.77.49 1.24.63.96.21 1.48.21c.57 0 1.09-.08 1.58-.23.49-.15.91-.38 1.26-.68.36-.3.64-.66.84-1.1.2-.43.3-.93.3-1.48 0-.29-.04-.58-.11-.86-.08-.25-.19-.51-.35-.76zm9.26 1.4c-.14-.28-.35-.53-.63-.74-.28-.21-.61-.39-1.01-.53s-.85-.27-1.35-.38c-.35-.07-.64-.15-.87-.23-.23-.08-.41-.16-.55-.25-.14-.09-.23-.19-.28-.3-.05-.11-.08-.24-.08-.39s.03-.28.09-.41c.06-.13.15-.25.27-.34.12-.1.27-.18.45-.24s.4-.09.64-.09c.25 0 .47.04.66.11.19.07.35.17.48.29.13.12.22.26.29.42.06.16.1.32.1.49h1.95c0-.39-.08-.75-.24-1.09-.16-.34-.39-.63-.69-.88-.3-.25-.66-.44-1.09-.59-.43-.15-.92-.22-1.46-.22-.51 0-.98.07-1.39.21-.41.14-.77.33-1.06.57-.29.24-.51.52-.67.84-.16.32-.23.65-.23 1.01s.08.68.23.96c.15.28.37.52.64.73.27.21.6.38.98.53.38.14.81.26 1.27.36.39.08.71.17.95.26s.43.19.57.29c.13.1.22.22.27.34.05.12.07.25.07.39 0 .32-.13.57-.4.77-.27.2-.66.29-1.17.29-.22 0-.43-.02-.64-.08-.21-.05-.4-.13-.56-.24-.17-.11-.3-.26-.41-.44-.11-.18-.17-.41-.18-.67h-1.89c0 .36.08.71.24 1.05.16.34.39.65.7.93.31.27.69.49 1.15.66.46.17.98.25 1.58.25.53 0 1.01-.06 1.44-.19.43-.13.8-.31 1.11-.54.31-.23.54-.51.71-.83.17-.32.25-.67.25-1.06-.02-.4-.09-.74-.24-1.02z"/></g>
+<g id="timer-off"><path d="M19.04 4.55l-1.42 1.42C16.07 4.74 14.12 4 12 4c-1.83 0-3.53.55-4.95 1.48l1.46 1.46C9.53 6.35 10.73 6 12 6c3.87 0 7 3.13 7 7 0 1.27-.35 2.47-.94 3.49l1.45 1.45C20.45 16.53 21 14.83 21 13c0-2.12-.74-4.07-1.97-5.61l1.42-1.42-1.41-1.42zM15 1H9v2h6V1zm-4 8.44l2 2V8h-2v1.44zM3.02 4L1.75 5.27 4.5 8.03C3.55 9.45 3 11.16 3 13c0 4.97 4.02 9 9 9 1.84 0 3.55-.55 4.98-1.5l2.5 2.5 1.27-1.27-7.71-7.71L3.02 4zM12 20c-3.87 0-7-3.13-7-7 0-1.28.35-2.48.95-3.52l9.56 9.56c-1.03.61-2.23.96-3.51.96z"/></g>
+<g id="tonality"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93s3.05-7.44 7-7.93v15.86zm2-15.86c1.03.13 2 .45 2.87.93H13v-.93zM13 7h5.24c.25.31.48.65.68 1H13V7zm0 3h6.74c.08.33.15.66.19 1H13v-1zm0 9.93V19h2.87c-.87.48-1.84.8-2.87.93zM18.24 17H13v-1h5.92c-.2.35-.43.69-.68 1zm1.5-3H13v-1h6.93c-.04.34-.11.67-.19 1z"/></g>
+<g id="transform"><path d="M22 18v-2H8V4h2L7 1 4 4h2v2H2v2h4v8c0 1.1.9 2 2 2h8v2h-2l3 3 3-3h-2v-2h4zM10 8h6v6h2V8c0-1.1-.9-2-2-2h-6v2z"/></g>
+<g id="tune"><path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z"/></g>
+<g id="view-comfy"><path d="M3 9h4V5H3v4zm0 5h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zM8 9h4V5H8v4zm5-4v4h4V5h-4zm5 9h4v-4h-4v4zM3 19h4v-4H3v4zm5 0h4v-4H8v4zm5 0h4v-4h-4v4zm5 0h4v-4h-4v4zm0-14v4h4V5h-4z"/></g>
+<g id="view-compact"><path d="M3 19h6v-7H3v7zm7 0h12v-7H10v7zM3 5v6h19V5H3z"/></g>
+<g id="vignette"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-9 15c-4.42 0-8-2.69-8-6s3.58-6 8-6 8 2.69 8 6-3.58 6-8 6z"/></g>
+<g id="wb-auto"><path d="M6.85 12.65h2.3L8 9l-1.15 3.65zM22 7l-1.2 6.29L19.3 7h-1.6l-1.49 6.29L15 7h-.76C12.77 5.17 10.53 4 8 4c-4.42 0-8 3.58-8 8s3.58 8 8 8c3.13 0 5.84-1.81 7.15-4.43l.1.43H17l1.5-6.1L20 16h1.75l2.05-9H22zm-11.7 9l-.7-2H6.4l-.7 2H3.8L7 7h2l3.2 9h-1.9z"/></g>
+<g id="wb-cloudy"><path d="M19.36 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.64-4.96z"/></g>
+<g id="wb-incandescent"><path d="M3.55 18.54l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8zM11 22.45h2V19.5h-2v2.95zM4 10.5H1v2h3v-2zm11-4.19V1.5H9v4.81C7.21 7.35 6 9.28 6 11.5c0 3.31 2.69 6 6 6s6-2.69 6-6c0-2.22-1.21-4.15-3-5.19zm5 4.19v2h3v-2h-3zm-2.76 7.66l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4z"/></g>
+<g id="wb-iridescent"><path d="M5 14.5h14v-6H5v6zM11 .55V3.5h2V.55h-2zm8.04 2.5l-1.79 1.79 1.41 1.41 1.8-1.79-1.42-1.41zM13 22.45V19.5h-2v2.95h2zm7.45-3.91l-1.8-1.79-1.41 1.41 1.79 1.8 1.42-1.42zM3.55 4.46l1.79 1.79 1.41-1.41-1.79-1.79-1.41 1.41zm1.41 15.49l1.79-1.8-1.41-1.41-1.79 1.79 1.41 1.42z"/></g>
+<g id="wb-sunny"><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/index.html b/catapult/third_party/polymer/components/iron-icons/index.html
new file mode 100644
index 00000000..fa5b89bb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-icons/iron-icons.html b/catapult/third_party/polymer/components/iron-icons/iron-icons.html
new file mode 100644
index 00000000..739131cc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/iron-icons.html
@@ -0,0 +1,353 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<!--
+
+`iron-icons` is a utility import that includes the definition for the `iron-icon` element, `iron-iconset-svg` element, as well as an import for the default icon set.
+
+The `iron-icons` directory also includes imports for additional icon sets that can be loaded into your project.
+
+Example loading icon set:
+
+ <link rel="import" href="../iron-icons/maps-icons.html">
+
+To use an icon from one of these sets, first prefix your `iron-icon` with the icon set name, followed by a colon, ":", and then the icon id.
+
+Example using the directions-bus icon from the maps icon set:
+
+ <iron-icon icon="maps:directions-bus"></iron-icon>
+
+ To load a subset of icons from one of the default `iron-icons` sets, you can
+ use the [poly-icon](https://poly-icon.appspot.com/) tool. It allows you
+ to select individual icons, and creates an iconset from them that you can
+ use directly in your elements.
+
+See [iron-icon](#iron-icon) for more information about working with icons.
+
+See [iron-iconset](#iron-iconset) and [iron-iconset-svg](#iron-iconset-svg) for more information about how to create a custom iconset.
+
+@group Iron Elements
+@pseudoElement iron-icons
+@demo demo/index.html
+-->
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="icons" size="24">
+<svg><defs>
+<g id="3d-rotation"><path d="M7.52 21.48C4.25 19.94 1.91 16.76 1.55 13H.05C.56 19.16 5.71 24 12 24l.66-.03-3.81-3.81-1.33 1.32zm.89-6.52c-.19 0-.37-.03-.52-.08-.16-.06-.29-.13-.4-.24-.11-.1-.2-.22-.26-.37-.06-.14-.09-.3-.09-.47h-1.3c0 .36.07.68.21.95.14.27.33.5.56.69.24.18.51.32.82.41.3.1.62.15.96.15.37 0 .72-.05 1.03-.15.32-.1.6-.25.83-.44s.42-.43.55-.72c.13-.29.2-.61.2-.97 0-.19-.02-.38-.07-.56-.05-.18-.12-.35-.23-.51-.1-.16-.24-.3-.4-.43-.17-.13-.37-.23-.61-.31.2-.09.37-.2.52-.33.15-.13.27-.27.37-.42.1-.15.17-.3.22-.46.05-.16.07-.32.07-.48 0-.36-.06-.68-.18-.96-.12-.28-.29-.51-.51-.69-.2-.19-.47-.33-.77-.43C9.1 8.05 8.76 8 8.39 8c-.36 0-.69.05-1 .16-.3.11-.57.26-.79.45-.21.19-.38.41-.51.67-.12.26-.18.54-.18.85h1.3c0-.17.03-.32.09-.45s.14-.25.25-.34c.11-.09.23-.17.38-.22.15-.05.3-.08.48-.08.4 0 .7.1.89.31.19.2.29.49.29.86 0 .18-.03.34-.08.49-.05.15-.14.27-.25.37-.11.1-.25.18-.41.24-.16.06-.36.09-.58.09H7.5v1.03h.77c.22 0 .42.02.6.07s.33.13.45.23c.12.11.22.24.29.4.07.16.1.35.1.57 0 .41-.12.72-.35.93-.23.23-.55.33-.95.33zm8.55-5.92c-.32-.33-.7-.59-1.14-.77-.43-.18-.92-.27-1.46-.27H12v8h2.3c.55 0 1.06-.09 1.51-.27.45-.18.84-.43 1.16-.76.32-.33.57-.73.74-1.19.17-.47.26-.99.26-1.57v-.4c0-.58-.09-1.1-.26-1.57-.18-.47-.43-.87-.75-1.2zm-.39 3.16c0 .42-.05.79-.14 1.13-.1.33-.24.62-.43.85-.19.23-.43.41-.71.53-.29.12-.62.18-.99.18h-.91V9.12h.97c.72 0 1.27.23 1.64.69.38.46.57 1.12.57 1.99v.4zM12 0l-.66.03 3.81 3.81 1.33-1.33c3.27 1.55 5.61 4.72 5.96 8.48h1.5C23.44 4.84 18.29 0 12 0z"/></g>
+<g id="accessibility"><path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z"/></g>
+<g id="accessible"><circle cx="12" cy="4" r="2"/><path d="M19 13v-2c-1.54.02-3.09-.75-4.07-1.83l-1.29-1.43c-.17-.19-.38-.34-.61-.45-.01 0-.01-.01-.02-.01H13c-.35-.2-.75-.3-1.19-.26C10.76 7.11 10 8.04 10 9.09V15c0 1.1.9 2 2 2h5v5h2v-5.5c0-1.1-.9-2-2-2h-3v-3.45c1.29 1.07 3.25 1.94 5 1.95zm-6.17 5c-.41 1.16-1.52 2-2.83 2-1.66 0-3-1.34-3-3 0-1.31.84-2.41 2-2.83V12.1c-2.28.46-4 2.48-4 4.9 0 2.76 2.24 5 5 5 2.42 0 4.44-1.72 4.9-4h-2.07z"/></g>
+<g id="account-balance"><path d="M4 10v7h3v-7H4zm6 0v7h3v-7h-3zM2 22h19v-3H2v3zm14-12v7h3v-7h-3zm-4.5-9L2 6v2h19V6l-9.5-5z"/></g>
+<g id="account-balance-wallet"><path d="M21 18v1c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2V5c0-1.1.89-2 2-2h14c1.1 0 2 .9 2 2v1h-9c-1.11 0-2 .9-2 2v8c0 1.1.89 2 2 2h9zm-9-2h10V8H12v8zm4-2.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="account-box"><path d="M3 5v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2H5c-1.11 0-2 .9-2 2zm12 4c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3zm-9 8c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6v-1z"/></g>
+<g id="account-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/></g>
+<g id="add"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></g>
+<g id="add-alert"><path d="M10.01 21.01c0 1.1.89 1.99 1.99 1.99s1.99-.89 1.99-1.99h-3.98zm8.87-4.19V11c0-3.25-2.25-5.97-5.29-6.69v-.72C13.59 2.71 12.88 2 12 2s-1.59.71-1.59 1.59v.72C7.37 5.03 5.12 7.75 5.12 11v5.82L3 18.94V20h18v-1.06l-2.12-2.12zM16 13.01h-3v3h-2v-3H8V11h3V8h2v3h3v2.01z"/></g>
+<g id="add-box"><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></g>
+<g id="add-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></g>
+<g id="add-circle-outline"><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="add-shopping-cart"><path d="M11 9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4 9c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zm10 0c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2zm-9.83-3.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.86-7.01L19.42 4h-.01l-1.1 2-2.76 5H8.53l-.13-.27L6.16 6l-.95-2-.94-2H1v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.13 0-.25-.11-.25-.25z"/></g>
+<g id="alarm"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12.5 8H11v6l4.75 2.85.75-1.23-4-2.37V8zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></g>
+<g id="alarm-add"><path d="M7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm1-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/></g>
+<g id="alarm-off"><path d="M12 6c3.87 0 7 3.13 7 7 0 .84-.16 1.65-.43 2.4l1.52 1.52c.58-1.19.91-2.51.91-3.92 0-4.97-4.03-9-9-9-1.41 0-2.73.33-3.92.91L9.6 6.43C10.35 6.16 11.16 6 12 6zm10-.28l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM2.92 2.29L1.65 3.57 2.98 4.9l-1.11.93 1.42 1.42 1.11-.94.8.8C3.83 8.69 3 10.75 3 13c0 4.97 4.02 9 9 9 2.25 0 4.31-.83 5.89-2.2l2.2 2.2 1.27-1.27L3.89 3.27l-.97-.98zm13.55 16.1C15.26 19.39 13.7 20 12 20c-3.87 0-7-3.13-7-7 0-1.7.61-3.26 1.61-4.47l9.86 9.86zM8.02 3.28L6.6 1.86l-.86.71 1.42 1.42.86-.71z"/></g>
+<g id="alarm-on"><path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm-1.46-5.47L8.41 12.4l-1.06 1.06 3.18 3.18 6-6-1.06-1.06-4.93 4.95z"/></g>
+<g id="all-out"><path d="M16.21 4.16l4 4v-4zm4 12l-4 4h4zm-12 4l-4-4v4zm-4-12l4-4h-4zm12.95-.95c-2.73-2.73-7.17-2.73-9.9 0s-2.73 7.17 0 9.9 7.17 2.73 9.9 0 2.73-7.16 0-9.9zm-1.1 8.8c-2.13 2.13-5.57 2.13-7.7 0s-2.13-5.57 0-7.7 5.57-2.13 7.7 0 2.13 5.57 0 7.7z"/></g>
+<g id="android"><path d="M6 18c0 .55.45 1 1 1h1v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h2v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h1c.55 0 1-.45 1-1V8H6v10zM3.5 8C2.67 8 2 8.67 2 9.5v7c0 .83.67 1.5 1.5 1.5S5 17.33 5 16.5v-7C5 8.67 4.33 8 3.5 8zm17 0c-.83 0-1.5.67-1.5 1.5v7c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-7c0-.83-.67-1.5-1.5-1.5zm-4.97-5.84l1.3-1.3c.2-.2.2-.51 0-.71-.2-.2-.51-.2-.71 0l-1.48 1.48C13.85 1.23 12.95 1 12 1c-.96 0-1.86.23-2.66.63L7.85.15c-.2-.2-.51-.2-.71 0-.2.2-.2.51 0 .71l1.31 1.31C6.97 3.26 6 5.01 6 7h12c0-1.99-.97-3.75-2.47-4.84zM10 5H9V4h1v1zm5 0h-1V4h1v1z"/></g>
+<g id="announcement"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 9h-2V5h2v6zm0 4h-2v-2h2v2z"/></g>
+<g id="apps"><path d="M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z"/></g>
+<g id="archive"><path d="M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zM12 17.5L6.5 12H10v-2h4v2h3.5L12 17.5zM5.12 5l.81-1h12l.94 1H5.12z"/></g>
+<g id="arrow-back"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></g>
+<g id="arrow-downward"><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></g>
+<g id="arrow-drop-down"><path d="M7 10l5 5 5-5z"/></g>
+<g id="arrow-drop-down-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 12l-4-4h8l-4 4z"/></g>
+<g id="arrow-drop-up"><path d="M7 14l5-5 5 5z"/></g>
+<g id="arrow-forward"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/></g>
+<g id="arrow-upward"><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></g>
+<g id="aspect-ratio"><path d="M19 12h-2v3h-3v2h5v-5zM7 9h3V7H5v5h2V9zm14-6H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"/></g>
+<g id="assessment"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></g>
+<g id="assignment"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/></g>
+<g id="assignment-ind"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm0 4c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H6v-1.4c0-2 4-3.1 6-3.1s6 1.1 6 3.1V19z"/></g>
+<g id="assignment-late"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-6 15h-2v-2h2v2zm0-4h-2V8h2v6zm-1-9c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></g>
+<g id="assignment-return"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm4 12h-4v3l-5-5 5-5v3h4v4z"/></g>
+<g id="assignment-returned"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm0 15l-5-5h3V9h4v4h3l-5 5z"/></g>
+<g id="assignment-turned-in"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm-2 14l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z"/></g>
+<g id="attachment"><path d="M2 12.5C2 9.46 4.46 7 7.5 7H18c2.21 0 4 1.79 4 4s-1.79 4-4 4H9.5C8.12 15 7 13.88 7 12.5S8.12 10 9.5 10H17v2H9.41c-.55 0-.55 1 0 1H18c1.1 0 2-.9 2-2s-.9-2-2-2H7.5C5.57 9 4 10.57 4 12.5S5.57 16 7.5 16H17v2H7.5C4.46 18 2 15.54 2 12.5z"/></g>
+<g id="autorenew"><path d="M12 6v3l4-4-4-4v3c-4.42 0-8 3.58-8 8 0 1.57.46 3.03 1.24 4.26L6.7 14.8c-.45-.83-.7-1.79-.7-2.8 0-3.31 2.69-6 6-6zm6.76 1.74L17.3 9.2c.44.84.7 1.79.7 2.8 0 3.31-2.69 6-6 6v-3l-4 4 4 4v-3c4.42 0 8-3.58 8-8 0-1.57-.46-3.03-1.24-4.26z"/></g>
+<g id="backspace"><path d="M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z"/></g>
+<g id="backup"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></g>
+<g id="block"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"/></g>
+<g id="book"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4z"/></g>
+<g id="bookmark"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></g>
+<g id="bookmark-border"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"/></g>
+<g id="bug-report"><path d="M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"/></g>
+<g id="build"><path d="M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z"/></g>
+<g id="cached"><path d="M19 8l-4 4h3c0 3.31-2.69 6-6 6-1.01 0-1.97-.25-2.8-.7l-1.46 1.46C8.97 19.54 10.43 20 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46C15.03 4.46 13.57 4 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z"/></g>
+<g id="camera-enhance"><path d="M9 3L7.17 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2h-3.17L15 3H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-1l1.25-2.75L16 13l-2.75-1.25L12 9l-1.25 2.75L8 13l2.75 1.25z"/></g>
+<g id="cancel"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/></g>
+<g id="card-giftcard"><path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/></g>
+<g id="card-membership"><path d="M20 2H4c-1.11 0-2 .89-2 2v11c0 1.11.89 2 2 2h4v5l4-2 4 2v-5h4c1.11 0 2-.89 2-2V4c0-1.11-.89-2-2-2zm0 13H4v-2h16v2zm0-5H4V4h16v6z"/></g>
+<g id="card-travel"><path d="M20 6h-3V4c0-1.11-.89-2-2-2H9c-1.11 0-2 .89-2 2v2H4c-1.11 0-2 .89-2 2v11c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zM9 4h6v2H9V4zm11 15H4v-2h16v2zm0-5H4V8h3v2h2V8h6v2h2V8h3v6z"/></g>
+<g id="change-history"><path d="M12 7.77L18.39 18H5.61L12 7.77M12 4L2 20h20L12 4z"/></g>
+<g id="check"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></g>
+<g id="check-box"><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></g>
+<g id="check-box-outline-blank"><path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></g>
+<g id="check-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></g>
+<g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></g>
+<g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></g>
+<g id="chrome-reader-mode"><path d="M13 12h7v1.5h-7zm0-2.5h7V11h-7zm0 5h7V16h-7zM21 4H3c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 15h-9V6h9v13z"/></g>
+<g id="class"><path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4z"/></g>
+<g id="clear"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></g>
+<g id="close"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></g>
+<g id="cloud"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/></g>
+<g id="cloud-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.5 14H8c-1.66 0-3-1.34-3-3s1.34-3 3-3l.14.01C8.58 8.28 10.13 7 12 7c2.21 0 4 1.79 4 4h.5c1.38 0 2.5 1.12 2.5 2.5S17.88 16 16.5 16z"/></g>
+<g id="cloud-done"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM10 17l-3.5-3.5 1.41-1.41L10 14.17 15.18 9l1.41 1.41L10 17z"/></g>
+<g id="cloud-download"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"/></g>
+<g id="cloud-off"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4c-1.48 0-2.85.43-4.01 1.17l1.46 1.46C10.21 6.23 11.08 6 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3 0 1.13-.64 2.11-1.56 2.62l1.45 1.45C23.16 18.16 24 16.68 24 15c0-2.64-2.05-4.78-4.65-4.96zM3 5.27l2.75 2.74C2.56 8.15 0 10.77 0 14c0 3.31 2.69 6 6 6h11.73l2 2L21 20.73 4.27 4 3 5.27zM7.73 10l8 8H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h1.73z"/></g>
+<g id="cloud-queue"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h.71C7.37 7.69 9.48 6 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3s-1.34 3-3 3z"/></g>
+<g id="cloud-upload"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></g>
+<g id="code"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></g>
+<g id="compare-arrows"><path d="M9.01 14H2v2h7.01v3L13 15l-3.99-4v3zm5.98-1v-3H22V8h-7.01V5L11 9l3.99 4z"/></g>
+<g id="content-copy"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></g>
+<g id="content-cut"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"/></g>
+<g id="content-paste"><path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"/></g>
+<g id="copyright"><path d="M10.08 10.86c.05-.33.16-.62.3-.87s.34-.46.59-.62c.24-.15.54-.22.91-.23.23.01.44.05.63.13.2.09.38.21.52.36s.25.33.34.53.13.42.14.64h1.79c-.02-.47-.11-.9-.28-1.29s-.4-.73-.7-1.01-.66-.5-1.08-.66-.88-.23-1.39-.23c-.65 0-1.22.11-1.7.34s-.88.53-1.2.92-.56.84-.71 1.36S8 11.29 8 11.87v.27c0 .58.08 1.12.23 1.64s.39.97.71 1.35.72.69 1.2.91 1.05.34 1.7.34c.47 0 .91-.08 1.32-.23s.77-.36 1.08-.63.56-.58.74-.94.29-.74.3-1.15h-1.79c-.01.21-.06.4-.15.58s-.21.33-.36.46-.32.23-.52.3c-.19.07-.39.09-.6.1-.36-.01-.66-.08-.89-.23-.25-.16-.45-.37-.59-.62s-.25-.55-.3-.88-.08-.67-.08-1v-.27c0-.35.03-.68.08-1.01zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="create"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></g>
+<g id="create-new-folder"><path d="M20 6h-8l-2-2H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-1 8h-3v3h-2v-3h-3v-2h3V9h2v3h3v2z"/></g>
+<g id="credit-card"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/></g>
+<g id="dashboard"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/></g>
+<g id="date-range"><path d="M9 11H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11z"/></g>
+<g id="delete"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></g>
+<g id="delete-forever"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"/></g>
+<g id="delete-sweep"><path d="M15 16h4v2h-4zm0-8h7v2h-7zm0 4h6v2h-6zM3 18c0 1.1.9 2 2 2h6c1.1 0 2-.9 2-2V8H3v10zM14 5h-3l-1-1H6L5 5H2v2h12z"/></g>
+<g id="description"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></g>
+<g id="dns"><path d="M20 13H4c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h16c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1zM7 19c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM20 3H4c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h16c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1zM7 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></g>
+<g id="done"><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></g>
+<g id="done-all"><path d="M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z"/></g>
+<g id="donut-large"><path d="M11 5.08V2c-5 .5-9 4.81-9 10s4 9.5 9 10v-3.08c-3-.48-6-3.4-6-6.92s3-6.44 6-6.92zM18.97 11H22c-.47-5-4-8.53-9-9v3.08C16 5.51 18.54 8 18.97 11zM13 18.92V22c5-.47 8.53-4 9-9h-3.03c-.43 3-2.97 5.49-5.97 5.92z"/></g>
+<g id="donut-small"><path d="M11 9.16V2c-5 .5-9 4.79-9 10s4 9.5 9 10v-7.16c-1-.41-2-1.52-2-2.84s1-2.43 2-2.84zM14.86 11H22c-.48-4.75-4-8.53-9-9v7.16c1 .3 1.52.98 1.86 1.84zM13 14.84V22c5-.47 8.52-4.25 9-9h-7.14c-.34.86-.86 1.54-1.86 1.84z"/></g>
+<g id="drafts"><path d="M21.99 8c0-.72-.37-1.35-.94-1.7L12 1 2.95 6.3C2.38 6.65 2 7.28 2 8v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2l-.01-10zM12 13L3.74 7.84 12 3l8.26 4.84L12 13z"/></g>
+<g id="eject"><path d="M5 17h14v2H5zm7-12L5.33 15h13.34z"/></g>
+<g id="error"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></g>
+<g id="error-outline"><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></g>
+<g id="euro-symbol"><path d="M15 18.5c-2.51 0-4.68-1.42-5.76-3.5H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24C10.32 6.92 12.5 5.5 15 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3C19.41 3.87 17.3 3 15 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06c-.04.33-.06.66-.06 1 0 .34.02.67.06 1H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z"/></g>
+<g id="event"><path d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"/></g>
+<g id="event-seat"><path d="M4 18v3h3v-3h10v3h3v-6H4zm15-8h3v3h-3zM2 10h3v3H2zm15 3H7V5c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v8z"/></g>
+<g id="exit-to-app"><path d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></g>
+<g id="expand-less"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"/></g>
+<g id="expand-more"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/></g>
+<g id="explore"><path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z"/></g>
+<g id="extension"><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/></g>
+<g id="face"><path d="M9 11.75c-.69 0-1.25.56-1.25 1.25s.56 1.25 1.25 1.25 1.25-.56 1.25-1.25-.56-1.25-1.25-1.25zm6 0c-.69 0-1.25.56-1.25 1.25s.56 1.25 1.25 1.25 1.25-.56 1.25-1.25-.56-1.25-1.25-1.25zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8 0-.29.02-.58.05-.86 2.36-1.05 4.23-2.98 5.21-5.37C11.07 8.33 14.05 10 17.42 10c.78 0 1.53-.09 2.25-.26.21.71.33 1.47.33 2.26 0 4.41-3.59 8-8 8z"/></g>
+<g id="favorite"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></g>
+<g id="favorite-border"><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></g>
+<g id="feedback"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z"/></g>
+<g id="file-download"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></g>
+<g id="file-upload"><path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/></g>
+<g id="filter-list"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></g>
+<g id="find-in-page"><path d="M20 19.59V8l-6-6H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c.45 0 .85-.15 1.19-.4l-4.43-4.43c-.8.52-1.74.83-2.76.83-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L20 19.59zM9 13c0 1.66 1.34 3 3 3s3-1.34 3-3-1.34-3-3-3-3 1.34-3 3z"/></g>
+<g id="find-replace"><path d="M11 6c1.38 0 2.63.56 3.54 1.46L12 10h6V4l-2.05 2.05C14.68 4.78 12.93 4 11 4c-3.53 0-6.43 2.61-6.92 6H6.1c.46-2.28 2.48-4 4.9-4zm5.64 9.14c.66-.9 1.12-1.97 1.28-3.14H15.9c-.46 2.28-2.48 4-4.9 4-1.38 0-2.63-.56-3.54-1.46L10 12H4v6l2.05-2.05C7.32 17.22 9.07 18 11 18c1.55 0 2.98-.51 4.14-1.36L20 21.49 21.49 20l-4.85-4.86z"/></g>
+<g id="fingerprint"><path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"/></g>
+<g id="first-page"><path d="M18.41 16.59L13.82 12l4.59-4.59L17 6l-6 6 6 6zM6 6h2v12H6z"/></g>
+<g id="flag"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></g>
+<g id="flight-land"><path d="M2.5 19h19v2h-19zm7.18-5.73l4.35 1.16 5.31 1.42c.8.21 1.62-.26 1.84-1.06.21-.8-.26-1.62-1.06-1.84l-5.31-1.42-2.76-9.02L10.12 2v8.28L5.15 8.95l-.93-2.32-1.45-.39v5.17l1.6.43 5.31 1.43z"/></g>
+<g id="flight-takeoff"><path d="M2.5 19h19v2h-19zm19.57-9.36c-.21-.8-1.04-1.28-1.84-1.06L14.92 10l-6.9-6.43-1.93.51 4.14 7.17-4.97 1.33-1.97-1.54-1.45.39 1.82 3.16.77 1.33 1.6-.43 5.31-1.42 4.35-1.16L21 11.49c.81-.23 1.28-1.05 1.07-1.85z"/></g>
+<g id="flip-to-back"><path d="M9 7H7v2h2V7zm0 4H7v2h2v-2zm0-8c-1.11 0-2 .9-2 2h2V3zm4 12h-2v2h2v-2zm6-12v2h2c0-1.1-.9-2-2-2zm-6 0h-2v2h2V3zM9 17v-2H7c0 1.1.89 2 2 2zm10-4h2v-2h-2v2zm0-4h2V7h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zM5 7H3v12c0 1.1.89 2 2 2h12v-2H5V7zm10-2h2V3h-2v2zm0 12h2v-2h-2v2z"/></g>
+<g id="flip-to-front"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.89 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9c-1.11 0-2 .9-2 2v10c0 1.1.89 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z"/></g>
+<g id="folder"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></g>
+<g id="folder-open"><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"/></g>
+<g id="folder-shared"><path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z"/></g>
+<g id="font-download"><path d="M9.93 13.5h4.14L12 7.98zM20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-4.05 16.5l-1.14-3H9.17l-1.12 3H5.96l5.11-13h1.86l5.11 13h-2.09z"/></g>
+<g id="forward"><path d="M12 8V4l8 8-8 8v-4H4V8z"/></g>
+<g id="fullscreen"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></g>
+<g id="fullscreen-exit"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></g>
+<g id="g-translate"><path d="M20 5h-9.12L10 2H4c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h7l1 3h8c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zM7.17 14.59c-2.25 0-4.09-1.83-4.09-4.09s1.83-4.09 4.09-4.09c1.04 0 1.99.37 2.74 1.07l.07.06-1.23 1.18-.06-.05c-.29-.27-.78-.59-1.52-.59-1.31 0-2.38 1.09-2.38 2.42s1.07 2.42 2.38 2.42c1.37 0 1.96-.87 2.12-1.46H7.08V9.91h3.95l.01.07c.04.21.05.4.05.61 0 2.35-1.61 4-3.92 4zm6.03-1.71c.33.6.74 1.18 1.19 1.7l-.54.53-.65-2.23zm.77-.76h-.99l-.31-1.04h3.99s-.34 1.31-1.56 2.74c-.52-.62-.89-1.23-1.13-1.7zM21 20c0 .55-.45 1-1 1h-7l2-2-.81-2.77.92-.92L17.79 18l.73-.73-2.71-2.68c.9-1.03 1.6-2.25 1.92-3.51H19v-1.04h-3.64V9h-1.04v1.04h-1.96L11.18 6H20c.55 0 1 .45 1 1v13z"/></g>
+<g id="gavel"><path d="M1 21h12v2H1zM5.245 8.07l2.83-2.827 14.14 14.142-2.828 2.828zM12.317 1l5.657 5.656-2.83 2.83-5.654-5.66zM3.825 9.485l5.657 5.657-2.828 2.828-5.657-5.657z"/></g>
+<g id="gesture"><path d="M4.59 6.89c.7-.71 1.4-1.35 1.71-1.22.5.2 0 1.03-.3 1.52-.25.42-2.86 3.89-2.86 6.31 0 1.28.48 2.34 1.34 2.98.75.56 1.74.73 2.64.46 1.07-.31 1.95-1.4 3.06-2.77 1.21-1.49 2.83-3.44 4.08-3.44 1.63 0 1.65 1.01 1.76 1.79-3.78.64-5.38 3.67-5.38 5.37 0 1.7 1.44 3.09 3.21 3.09 1.63 0 4.29-1.33 4.69-6.1H21v-2.5h-2.47c-.15-1.65-1.09-4.2-4.03-4.2-2.25 0-4.18 1.91-4.94 2.84-.58.73-2.06 2.48-2.29 2.72-.25.3-.68.84-1.11.84-.45 0-.72-.83-.36-1.92.35-1.09 1.4-2.86 1.85-3.52.78-1.14 1.3-1.92 1.3-3.28C8.95 3.69 7.31 3 6.44 3 5.12 3 3.97 4 3.72 4.25c-.36.36-.66.66-.88.93l1.75 1.71zm9.29 11.66c-.31 0-.74-.26-.74-.72 0-.6.73-2.2 2.87-2.76-.3 2.69-1.43 3.48-2.13 3.48z"/></g>
+<g id="get-app"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></g>
+<g id="gif"><path d="M11.5 9H13v6h-1.5zM9 9H6c-.6 0-1 .5-1 1v4c0 .5.4 1 1 1h3c.6 0 1-.5 1-1v-2H8.5v1.5h-2v-3H10V10c0-.5-.4-1-1-1zm10 1.5V9h-4.5v6H16v-2h2v-1.5h-2v-1z"/></g>
+<g id="grade"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></g>
+<g id="group-work"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM8 17.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5zM9.5 8c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5S9.5 9.38 9.5 8zm6.5 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></g>
+<g id="help"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></g>
+<g id="help-outline"><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></g>
+<g id="highlight-off"><path d="M14.59 8L12 10.59 9.41 8 8 9.41 10.59 12 8 14.59 9.41 16 12 13.41 14.59 16 16 14.59 13.41 12 16 9.41 14.59 8zM12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="history"><path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/></g>
+<g id="home"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></g>
+<g id="hourglass-empty"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6zm10 14.5V20H8v-3.5l4-4 4 4zm-4-5l-4-4V4h8v3.5l-4 4z"/></g>
+<g id="hourglass-full"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6z"/></g>
+<g id="http"><path d="M4.5 11h-2V9H1v6h1.5v-2.5h2V15H6V9H4.5v2zm2.5-.5h1.5V15H10v-4.5h1.5V9H7v1.5zm5.5 0H14V15h1.5v-4.5H17V9h-4.5v1.5zm9-1.5H18v6h1.5v-2h2c.8 0 1.5-.7 1.5-1.5v-1c0-.8-.7-1.5-1.5-1.5zm0 2.5h-2v-1h2v1z"/></g>
+<g id="https"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></g>
+<g id="important-devices"><path d="M23 11.01L18 11c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h5c.55 0 1-.45 1-1v-9c0-.55-.45-.99-1-.99zM23 20h-5v-7h5v7zM20 2H2C.89 2 0 2.89 0 4v12c0 1.1.89 2 2 2h7v2H7v2h8v-2h-2v-2h2v-2H2V4h18v5h2V4c0-1.11-.9-2-2-2zm-8.03 7L11 6l-.97 3H7l2.47 1.76-.94 2.91 2.47-1.8 2.47 1.8-.94-2.91L15 9h-3.03z"/></g>
+<g id="inbox"><path d="M19 3H4.99c-1.11 0-1.98.89-1.98 2L3 19c0 1.1.88 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.11-.9-2-2-2zm0 12h-4c0 1.66-1.35 3-3 3s-3-1.34-3-3H4.99V5H19v10z"/></g>
+<g id="indeterminate-check-box"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z"/></g>
+<g id="info"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></g>
+<g id="info-outline"><path d="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"/></g>
+<g id="input"><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3z"/></g>
+<g id="invert-colors"><path d="M17.66 7.93L12 2.27 6.34 7.93c-3.12 3.12-3.12 8.19 0 11.31C7.9 20.8 9.95 21.58 12 21.58c2.05 0 4.1-.78 5.66-2.34 3.12-3.12 3.12-8.19 0-11.31zM12 19.59c-1.6 0-3.11-.62-4.24-1.76C6.62 16.69 6 15.19 6 13.59s.62-3.11 1.76-4.24L12 5.1v14.49z"/></g>
+<g id="label"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></g>
+<g id="label-outline"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></g>
+<g id="language"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/></g>
+<g id="last-page"><path d="M5.59 7.41L10.18 12l-4.59 4.59L7 18l6-6-6-6zM16 6h2v12h-2z"/></g>
+<g id="launch"><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></g>
+<g id="lightbulb-outline"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/></g>
+<g id="line-style"><path d="M3 16h5v-2H3v2zm6.5 0h5v-2h-5v2zm6.5 0h5v-2h-5v2zM3 20h2v-2H3v2zm4 0h2v-2H7v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zM3 12h8v-2H3v2zm10 0h8v-2h-8v2zM3 4v4h18V4H3z"/></g>
+<g id="line-weight"><path d="M3 17h18v-2H3v2zm0 3h18v-1H3v1zm0-7h18v-3H3v3zm0-9v4h18V4H3z"/></g>
+<g id="link"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></g>
+<g id="list"><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></g>
+<g id="lock"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></g>
+<g id="lock-open"><path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"/></g>
+<g id="lock-outline"><path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H8.9V6zM18 20H6V10h12v10z"/></g>
+<g id="low-priority"><path d="M14 5h8v2h-8zm0 5.5h8v2h-8zm0 5.5h8v2h-8zM2 11.5C2 15.08 4.92 18 8.5 18H9v2l3-3-3-3v2h-.5C6.02 16 4 13.98 4 11.5S6.02 7 8.5 7H12V5H8.5C4.92 5 2 7.92 2 11.5z"/></g>
+<g id="loyalty"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7zm11.77 8.27L13 19.54l-4.27-4.27C8.28 14.81 8 14.19 8 13.5c0-1.38 1.12-2.5 2.5-2.5.69 0 1.32.28 1.77.74l.73.72.73-.73c.45-.45 1.08-.73 1.77-.73 1.38 0 2.5 1.12 2.5 2.5 0 .69-.28 1.32-.73 1.77z"/></g>
+<g id="mail"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></g>
+<g id="markunread"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></g>
+<g id="markunread-mailbox"><path d="M20 6H10v6H8V4h6V0H6v6H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2z"/></g>
+<g id="menu"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></g>
+<g id="more-horiz"><path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
+<g id="more-vert"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
+<g id="motorcycle"><path d="M19.44 9.03L15.41 5H11v2h3.59l2 2H5c-2.8 0-5 2.2-5 5s2.2 5 5 5c2.46 0 4.45-1.69 4.9-4h1.65l2.77-2.77c-.21.54-.32 1.14-.32 1.77 0 2.8 2.2 5 5 5s5-2.2 5-5c0-2.65-1.97-4.77-4.56-4.97zM7.82 15C7.4 16.15 6.28 17 5 17c-1.63 0-3-1.37-3-3s1.37-3 3-3c1.28 0 2.4.85 2.82 2H5v2h2.82zM19 17c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z"/></g>
+<g id="move-to-inbox"><path d="M19 3H4.99c-1.11 0-1.98.9-1.98 2L3 19c0 1.1.88 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12h-4c0 1.66-1.35 3-3 3s-3-1.34-3-3H4.99V5H19v10zm-3-5h-2V7h-4v3H8l4 4 4-4z"/></g>
+<g id="next-week"><path d="M20 7h-4V5c0-.55-.22-1.05-.59-1.41C15.05 3.22 14.55 3 14 3h-4c-1.1 0-2 .9-2 2v2H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zM10 5h4v2h-4V5zm1 13.5l-1-1 3-3-3-3 1-1 4 4-4 4z"/></g>
+<g id="note-add"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 14h-3v3h-2v-3H8v-2h3v-3h2v3h3v2zm-3-7V3.5L18.5 9H13z"/></g>
+<g id="offline-pin"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm5 16H7v-2h10v2zm-6.7-4L7 10.7l1.4-1.4 1.9 1.9 5.3-5.3L17 7.3 10.3 14z"/></g>
+<g id="opacity"><path d="M17.66 8L12 2.35 6.34 8C4.78 9.56 4 11.64 4 13.64s.78 4.11 2.34 5.67 3.61 2.35 5.66 2.35 4.1-.79 5.66-2.35S20 15.64 20 13.64 19.22 9.56 17.66 8zM6 14c.01-2 .62-3.27 1.76-4.4L12 5.27l4.24 4.38C17.38 10.77 17.99 12 18 14H6z"/></g>
+<g id="open-in-browser"><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"/></g>
+<g id="open-in-new"><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></g>
+<g id="open-with"><path d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"/></g>
+<g id="pageview"><path d="M11.5 9C10.12 9 9 10.12 9 11.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5S12.88 9 11.5 9zM20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-3.21 14.21l-2.91-2.91c-.69.44-1.51.7-2.39.7C9.01 16 7 13.99 7 11.5S9.01 7 11.5 7 16 9.01 16 11.5c0 .88-.26 1.69-.7 2.39l2.91 2.9-1.42 1.42z"/></g>
+<g id="pan-tool"><path d="M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z"/></g>
+<g id="payment"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/></g>
+<g id="perm-camera-mic"><path d="M20 5h-3.17L15 3H9L7.17 5H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v-2.09c-2.83-.48-5-2.94-5-5.91h2c0 2.21 1.79 4 4 4s4-1.79 4-4h2c0 2.97-2.17 5.43-5 5.91V21h7c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-6 8c0 1.1-.9 2-2 2s-2-.9-2-2V9c0-1.1.9-2 2-2s2 .9 2 2v4z"/></g>
+<g id="perm-contact-calendar"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm6 12H6v-1c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1z"/></g>
+<g id="perm-data-setting"><path d="M18.99 11.5c.34 0 .67.03 1 .07L20 0 0 20h11.56c-.04-.33-.07-.66-.07-1 0-4.14 3.36-7.5 7.5-7.5zm3.71 7.99c.02-.16.04-.32.04-.49 0-.17-.01-.33-.04-.49l1.06-.83c.09-.08.12-.21.06-.32l-1-1.73c-.06-.11-.19-.15-.31-.11l-1.24.5c-.26-.2-.54-.37-.85-.49l-.19-1.32c-.01-.12-.12-.21-.24-.21h-2c-.12 0-.23.09-.25.21l-.19 1.32c-.3.13-.59.29-.85.49l-1.24-.5c-.11-.04-.24 0-.31.11l-1 1.73c-.06.11-.04.24.06.32l1.06.83c-.02.16-.03.32-.03.49 0 .17.01.33.03.49l-1.06.83c-.09.08-.12.21-.06.32l1 1.73c.06.11.19.15.31.11l1.24-.5c.26.2.54.37.85.49l.19 1.32c.02.12.12.21.25.21h2c.12 0 .23-.09.25-.21l.19-1.32c.3-.13.59-.29.84-.49l1.25.5c.11.04.24 0 .31-.11l1-1.73c.06-.11.03-.24-.06-.32l-1.07-.83zm-3.71 1.01c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="perm-device-information"><path d="M13 7h-2v2h2V7zm0 4h-2v6h2v-6zm4-9.99L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></g>
+<g id="perm-identity"><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></g>
+<g id="perm-media"><path d="M2 6H0v5h.01L0 20c0 1.1.9 2 2 2h18v-2H2V6zm20-2h-8l-2-2H6c-1.1 0-1.99.9-1.99 2L4 16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM7 15l4.5-6 3.5 4.51 2.5-3.01L21 15H7z"/></g>
+<g id="perm-phone-msg"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.58l2.2-2.21c.28-.27.36-.66.25-1.01C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM12 3v10l3-3h6V3h-9z"/></g>
+<g id="perm-scan-wifi"><path d="M12 3C6.95 3 3.15 4.85 0 7.23L12 22 24 7.25C20.85 4.87 17.05 3 12 3zm1 13h-2v-6h2v6zm-2-8V6h2v2h-2z"/></g>
+<g id="pets"><circle cx="4.5" cy="9.5" r="2.5"/><circle cx="9" cy="5.5" r="2.5"/><circle cx="15" cy="5.5" r="2.5"/><circle cx="19.5" cy="9.5" r="2.5"/><path d="M17.34 14.86c-.87-1.02-1.6-1.89-2.48-2.91-.46-.54-1.05-1.08-1.75-1.32-.11-.04-.22-.07-.33-.09-.25-.04-.52-.04-.78-.04s-.53 0-.79.05c-.11.02-.22.05-.33.09-.7.24-1.28.78-1.75 1.32-.87 1.02-1.6 1.89-2.48 2.91-1.31 1.31-2.92 2.76-2.62 4.79.29 1.02 1.02 2.03 2.33 2.32.73.15 3.06-.44 5.54-.44h.18c2.48 0 4.81.58 5.54.44 1.31-.29 2.04-1.31 2.33-2.32.31-2.04-1.3-3.49-2.61-4.8z"/></g>
+<g id="picture-in-picture"><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/></g>
+<g id="picture-in-picture-alt"><path d="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z"/></g>
+<g id="play-for-work"><path d="M11 5v5.59H7.5l4.5 4.5 4.5-4.5H13V5h-2zm-5 9c0 3.31 2.69 6 6 6s6-2.69 6-6h-2c0 2.21-1.79 4-4 4s-4-1.79-4-4H6z"/></g>
+<g id="polymer"><path d="M19 4h-4L7.11 16.63 4.5 12 9 4H5L.5 12 5 20h4l7.89-12.63L19.5 12 15 20h4l4.5-8z"/></g>
+<g id="power-settings-new"><path d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z"/></g>
+<g id="pregnant-woman"><path d="M9 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm7 9c-.01-1.34-.83-2.51-2-3 0-1.66-1.34-3-3-3s-3 1.34-3 3v7h2v5h3v-5h3v-4z"/></g>
+<g id="print"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></g>
+<g id="query-builder"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></g>
+<g id="question-answer"><path d="M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z"/></g>
+<g id="radio-button-checked"><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></g>
+<g id="radio-button-unchecked"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></g>
+<g id="receipt"><path d="M18 17H6v-2h12v2zm0-4H6v-2h12v2zm0-4H6V7h12v2zM3 22l1.5-1.5L6 22l1.5-1.5L9 22l1.5-1.5L12 22l1.5-1.5L15 22l1.5-1.5L18 22l1.5-1.5L21 22V2l-1.5 1.5L18 2l-1.5 1.5L15 2l-1.5 1.5L12 2l-1.5 1.5L9 2 7.5 3.5 6 2 4.5 3.5 3 2v20z"/></g>
+<g id="record-voice-over"><circle cx="9" cy="9" r="4"/><path d="M9 15c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4zm7.76-9.64l-1.68 1.69c.84 1.18.84 2.71 0 3.89l1.68 1.69c2.02-2.02 2.02-5.07 0-7.27zM20.07 2l-1.63 1.63c2.77 3.02 2.77 7.56 0 10.74L20.07 16c3.9-3.89 3.91-9.95 0-14z"/></g>
+<g id="redeem"><path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/></g>
+<g id="redo"><path d="M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z"/></g>
+<g id="refresh"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></g>
+<g id="remove"><path d="M19 13H5v-2h14v2z"/></g>
+<g id="remove-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/></g>
+<g id="remove-circle-outline"><path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></g>
+<g id="remove-shopping-cart"><path d="M22.73 22.73L2.77 2.77 2 2l-.73-.73L0 2.54l4.39 4.39 2.21 4.66-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h7.46l1.38 1.38c-.5.36-.83.95-.83 1.62 0 1.1.89 2 1.99 2 .67 0 1.26-.33 1.62-.84L21.46 24l1.27-1.27zM7.42 15c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h2.36l2 2H7.42zm8.13-2c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H6.54l9.01 9zM7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2z"/></g>
+<g id="reorder"><path d="M3 15h18v-2H3v2zm0 4h18v-2H3v2zm0-8h18V9H3v2zm0-6v2h18V5H3z"/></g>
+<g id="reply"><path d="M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"/></g>
+<g id="reply-all"><path d="M7 8V5l-7 7 7 7v-3l-4-4 4-4zm6 1V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"/></g>
+<g id="report"><path d="M15.73 3H8.27L3 8.27v7.46L8.27 21h7.46L21 15.73V8.27L15.73 3zM12 17.3c-.72 0-1.3-.58-1.3-1.3 0-.72.58-1.3 1.3-1.3.72 0 1.3.58 1.3 1.3 0 .72-.58 1.3-1.3 1.3zm1-4.3h-2V7h2v6z"/></g>
+<g id="report-problem"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></g>
+<g id="restore"><path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"/></g>
+<g id="restore-page"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm-2 16c-2.05 0-3.81-1.24-4.58-3h1.71c.63.9 1.68 1.5 2.87 1.5 1.93 0 3.5-1.57 3.5-3.5S13.93 9.5 12 9.5c-1.35 0-2.52.78-3.1 1.9l1.6 1.6h-4V9l1.3 1.3C8.69 8.92 10.23 8 12 8c2.76 0 5 2.24 5 5s-2.24 5-5 5z"/></g>
+<g id="room"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></g>
+<g id="rounded-corner"><path d="M19 19h2v2h-2v-2zm0-2h2v-2h-2v2zM3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm0-4h2V3H3v2zm4 0h2V3H7v2zm8 16h2v-2h-2v2zm-4 0h2v-2h-2v2zm4 0h2v-2h-2v2zm-8 0h2v-2H7v2zm-4 0h2v-2H3v2zM21 8c0-2.76-2.24-5-5-5h-5v2h5c1.65 0 3 1.35 3 3v5h2V8z"/></g>
+<g id="rowing"><path d="M8.5 14.5L4 19l1.5 1.5L9 17h2l-2.5-2.5zM15 1c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 20.01L18 24l-2.99-3.01V19.5l-7.1-7.09c-.31.05-.61.07-.91.07v-2.16c1.66.03 3.61-.87 4.67-2.04l1.4-1.55c.19-.21.43-.38.69-.5.29-.14.62-.23.96-.23h.03C15.99 6.01 17 7.02 17 8.26v5.75c0 .84-.35 1.61-.92 2.16l-3.58-3.58v-2.27c-.63.52-1.43 1.02-2.29 1.39L16.5 18H18l3 3.01z"/></g>
+<g id="save"><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></g>
+<g id="schedule"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></g>
+<g id="search"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></g>
+<g id="select-all"><path d="M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2zM7 17h10V7H7v10zm2-8h6v6H9V9z"/></g>
+<g id="send"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></g>
+<g id="settings"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></g>
+<g id="settings-applications"><path d="M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm7-7H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-1.75 9c0 .23-.02.46-.05.68l1.48 1.16c.13.11.17.3.08.45l-1.4 2.42c-.09.15-.27.21-.43.15l-1.74-.7c-.36.28-.76.51-1.18.69l-.26 1.85c-.03.17-.18.3-.35.3h-2.8c-.17 0-.32-.13-.35-.29l-.26-1.85c-.43-.18-.82-.41-1.18-.69l-1.74.7c-.16.06-.34 0-.43-.15l-1.4-2.42c-.09-.15-.05-.34.08-.45l1.48-1.16c-.03-.23-.05-.46-.05-.69 0-.23.02-.46.05-.68l-1.48-1.16c-.13-.11-.17-.3-.08-.45l1.4-2.42c.09-.15.27-.21.43-.15l1.74.7c.36-.28.76-.51 1.18-.69l.26-1.85c.03-.17.18-.3.35-.3h2.8c.17 0 .32.13.35.29l.26 1.85c.43.18.82.41 1.18.69l1.74-.7c.16-.06.34 0 .43.15l1.4 2.42c.09.15.05.34-.08.45l-1.48 1.16c.03.23.05.46.05.69z"/></g>
+<g id="settings-backup-restore"><path d="M14 12c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-9c-4.97 0-9 4.03-9 9H0l4 4 4-4H5c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.51 0-2.91-.49-4.06-1.3l-1.42 1.44C8.04 20.3 9.94 21 12 21c4.97 0 9-4.03 9-9s-4.03-9-9-9z"/></g>
+<g id="settings-bluetooth"><path d="M11 24h2v-2h-2v2zm-4 0h2v-2H7v2zm8 0h2v-2h-2v2zm2.71-18.29L12 0h-1v7.59L6.41 3 5 4.41 10.59 10 5 15.59 6.41 17 11 12.41V20h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 3.83l1.88 1.88L13 7.59V3.83zm1.88 10.46L13 16.17v-3.76l1.88 1.88z"/></g>
+<g id="settings-brightness"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02zM8 16h2.5l1.5 1.5 1.5-1.5H16v-2.5l1.5-1.5-1.5-1.5V8h-2.5L12 6.5 10.5 8H8v2.5L6.5 12 8 13.5V16zm4-7c1.66 0 3 1.34 3 3s-1.34 3-3 3V9z"/></g>
+<g id="settings-cell"><path d="M7 24h2v-2H7v2zm4 0h2v-2h-2v2zm4 0h2v-2h-2v2zM16 .01L8 0C6.9 0 6 .9 6 2v16c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V2c0-1.1-.9-1.99-2-1.99zM16 16H8V4h8v12z"/></g>
+<g id="settings-ethernet"><path d="M7.77 6.76L6.23 5.48.82 12l5.41 6.52 1.54-1.28L3.42 12l4.35-5.24zM7 13h2v-2H7v2zm10-2h-2v2h2v-2zm-6 2h2v-2h-2v2zm6.77-7.52l-1.54 1.28L20.58 12l-4.35 5.24 1.54 1.28L23.18 12l-5.41-6.52z"/></g>
+<g id="settings-input-antenna"><path d="M12 5c-3.87 0-7 3.13-7 7h2c0-2.76 2.24-5 5-5s5 2.24 5 5h2c0-3.87-3.13-7-7-7zm1 9.29c.88-.39 1.5-1.26 1.5-2.29 0-1.38-1.12-2.5-2.5-2.5S9.5 10.62 9.5 12c0 1.02.62 1.9 1.5 2.29v3.3L7.59 21 9 22.41l3-3 3 3L16.41 21 13 17.59v-3.3zM12 1C5.93 1 1 5.93 1 12h2c0-4.97 4.03-9 9-9s9 4.03 9 9h2c0-6.07-4.93-11-11-11z"/></g>
+<g id="settings-input-component"><path d="M5 2c0-.55-.45-1-1-1s-1 .45-1 1v4H1v6h6V6H5V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2H9v2zm-8 0c0 1.3.84 2.4 2 2.82V23h2v-4.18C6.16 18.4 7 17.3 7 16v-2H1v2zM21 6V2c0-.55-.45-1-1-1s-1 .45-1 1v4h-2v6h6V6h-2zm-8-4c0-.55-.45-1-1-1s-1 .45-1 1v4H9v6h6V6h-2V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2h-6v2z"/></g>
+<g id="settings-input-composite"><path d="M5 2c0-.55-.45-1-1-1s-1 .45-1 1v4H1v6h6V6H5V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2H9v2zm-8 0c0 1.3.84 2.4 2 2.82V23h2v-4.18C6.16 18.4 7 17.3 7 16v-2H1v2zM21 6V2c0-.55-.45-1-1-1s-1 .45-1 1v4h-2v6h6V6h-2zm-8-4c0-.55-.45-1-1-1s-1 .45-1 1v4H9v6h6V6h-2V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2h-6v2z"/></g>
+<g id="settings-input-hdmi"><path d="M18 7V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v3H5v6l3 6v3h8v-3l3-6V7h-1zM8 4h8v3h-2V5h-1v2h-2V5h-1v2H8V4z"/></g>
+<g id="settings-input-svideo"><path d="M8 11.5c0-.83-.67-1.5-1.5-1.5S5 10.67 5 11.5 5.67 13 6.5 13 8 12.33 8 11.5zm7-5c0-.83-.67-1.5-1.5-1.5h-3C9.67 5 9 5.67 9 6.5S9.67 8 10.5 8h3c.83 0 1.5-.67 1.5-1.5zM8.5 15c-.83 0-1.5.67-1.5 1.5S7.67 18 8.5 18s1.5-.67 1.5-1.5S9.33 15 8.5 15zM12 1C5.93 1 1 5.93 1 12s4.93 11 11 11 11-4.93 11-11S18.07 1 12 1zm0 20c-4.96 0-9-4.04-9-9s4.04-9 9-9 9 4.04 9 9-4.04 9-9 9zm5.5-11c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm-2 5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"/></g>
+<g id="settings-overscan"><path d="M12.01 5.5L10 8h4l-1.99-2.5zM18 10v4l2.5-1.99L18 10zM6 10l-2.5 2.01L6 14v-4zm8 6h-4l2.01 2.5L14 16zm7-13H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16.01H3V4.99h18v14.02z"/></g>
+<g id="settings-phone"><path d="M13 9h-2v2h2V9zm4 0h-2v2h2V9zm3 6.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.58l2.2-2.21c.28-.27.36-.66.25-1.01C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 9v2h2V9h-2z"/></g>
+<g id="settings-power"><path d="M7 24h2v-2H7v2zm4 0h2v-2h-2v2zm2-22h-2v10h2V2zm3.56 2.44l-1.45 1.45C16.84 6.94 18 8.83 18 11c0 3.31-2.69 6-6 6s-6-2.69-6-6c0-2.17 1.16-4.06 2.88-5.12L7.44 4.44C5.36 5.88 4 8.28 4 11c0 4.42 3.58 8 8 8s8-3.58 8-8c0-2.72-1.36-5.12-3.44-6.56zM15 24h2v-2h-2v2z"/></g>
+<g id="settings-remote"><path d="M15 9H9c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V10c0-.55-.45-1-1-1zm-3 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM7.05 6.05l1.41 1.41C9.37 6.56 10.62 6 12 6s2.63.56 3.54 1.46l1.41-1.41C15.68 4.78 13.93 4 12 4s-3.68.78-4.95 2.05zM12 0C8.96 0 6.21 1.23 4.22 3.22l1.41 1.41C7.26 3.01 9.51 2 12 2s4.74 1.01 6.36 2.64l1.41-1.41C17.79 1.23 15.04 0 12 0z"/></g>
+<g id="settings-voice"><path d="M7 24h2v-2H7v2zm5-11c1.66 0 2.99-1.34 2.99-3L15 4c0-1.66-1.34-3-3-3S9 2.34 9 4v6c0 1.66 1.34 3 3 3zm-1 11h2v-2h-2v2zm4 0h2v-2h-2v2zm4-14h-1.7c0 3-2.54 5.1-5.3 5.1S6.7 13 6.7 10H5c0 3.41 2.72 6.23 6 6.72V20h2v-3.28c3.28-.49 6-3.31 6-6.72z"/></g>
+<g id="shop"><path d="M16 6V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H2v13c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6h-6zm-6-2h4v2h-4V4zM9 18V9l7.5 4L9 18z"/></g>
+<g id="shop-two"><path d="M3 9H1v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2H3V9zm15-4V3c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H5v11c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2V5h-5zm-6-2h4v2h-4V3zm0 12V8l5.5 3-5.5 4z"/></g>
+<g id="shopping-basket"><path d="M17.21 9l-4.38-6.56c-.19-.28-.51-.42-.83-.42-.32 0-.64.14-.83.43L6.79 9H2c-.55 0-1 .45-1 1 0 .09.01.18.04.27l2.54 9.27c.23.84 1 1.46 1.92 1.46h13c.92 0 1.69-.62 1.93-1.46l2.54-9.27L23 10c0-.55-.45-1-1-1h-4.79zM9 9l3-4.4L15 9H9zm3 8c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></g>
+<g id="shopping-cart"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/></g>
+<g id="sort"><path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/></g>
+<g id="speaker-notes"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 14H6v-2h2v2zm0-3H6V9h2v2zm0-3H6V6h2v2zm7 6h-5v-2h5v2zm3-3h-8V9h8v2zm0-3h-8V6h8v2z"/></g>
+<g id="speaker-notes-off"><path d="M10.54 11l-.54-.54L7.54 8 6 6.46 2.38 2.84 1.27 1.73 0 3l2.01 2.01L2 22l4-4h9l5.73 5.73L22 22.46 17.54 18l-7-7zM8 14H6v-2h2v2zm-2-3V9l2 2H6zm14-9H4.08L10 7.92V6h8v2h-7.92l1 1H18v2h-4.92l6.99 6.99C21.14 17.95 22 17.08 22 16V4c0-1.1-.9-2-2-2z"/></g>
+<g id="spellcheck"><path d="M12.45 16h2.09L9.43 3H7.57L2.46 16h2.09l1.12-3h5.64l1.14 3zm-6.02-5L8.5 5.48 10.57 11H6.43zm15.16.59l-8.09 8.09L9.83 16l-1.41 1.41 5.09 5.09L23 13l-1.41-1.41z"/></g>
+<g id="star"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></g>
+<g id="star-border"><path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/></g>
+<g id="star-half"><path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4V6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/></g>
+<g id="stars"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm4.24 16L12 15.45 7.77 18l1.12-4.81-3.73-3.23 4.92-.42L12 5l1.92 4.53 4.92.42-3.73 3.23L16.23 18z"/></g>
+<g id="store"><path d="M20 4H4v2h16V4zm1 10v-2l-1-5H4l-1 5v2h1v6h10v-6h4v6h2v-6h1zm-9 4H6v-4h6v4z"/></g>
+<g id="subdirectory-arrow-left"><path d="M11 9l1.42 1.42L8.83 14H18V4h2v12H8.83l3.59 3.58L11 21l-6-6 6-6z"/></g>
+<g id="subdirectory-arrow-right"><path d="M19 15l-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z"/></g>
+<g id="subject"><path d="M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z"/></g>
+<g id="supervisor-account"><path d="M16.5 12c1.38 0 2.49-1.12 2.49-2.5S17.88 7 16.5 7C15.12 7 14 8.12 14 9.5s1.12 2.5 2.5 2.5zM9 11c1.66 0 2.99-1.34 2.99-3S10.66 5 9 5C7.34 5 6 6.34 6 8s1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"/></g>
+<g id="swap-horiz"><path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z"/></g>
+<g id="swap-vert"><path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/></g>
+<g id="swap-vertical-circle"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM6.5 9L10 5.5 13.5 9H11v4H9V9H6.5zm11 6L14 18.5 10.5 15H13v-4h2v4h2.5z"/></g>
+<g id="system-update-alt"><path d="M12 16.5l4-4h-3v-9h-2v9H8l4 4zm9-13h-6v1.99h6v14.03H3V5.49h6V3.5H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-14c0-1.1-.9-2-2-2z"/></g>
+<g id="tab"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h10v4h8v10z"/></g>
+<g id="tab-unselected"><path d="M1 9h2V7H1v2zm0 4h2v-2H1v2zm0-8h2V3c-1.1 0-2 .9-2 2zm8 16h2v-2H9v2zm-8-4h2v-2H1v2zm2 4v-2H1c0 1.1.9 2 2 2zM21 3h-8v6h10V5c0-1.1-.9-2-2-2zm0 14h2v-2h-2v2zM9 5h2V3H9v2zM5 21h2v-2H5v2zM5 5h2V3H5v2zm16 16c1.1 0 2-.9 2-2h-2v2zm0-8h2v-2h-2v2zm-8 8h2v-2h-2v2zm4 0h2v-2h-2v2z"/></g>
+<g id="text-format"><path d="M5 17v2h14v-2H5zm4.5-4.2h5l.9 2.2h2.1L12.75 4h-1.5L6.5 15h2.1l.9-2.2zM12 5.98L13.87 11h-3.74L12 5.98z"/></g>
+<g id="theaters"><path d="M18 3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8 17H6v-2h2v2zm0-4H6v-2h2v2zm0-4H6V7h2v2zm10 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V7h2v2z"/></g>
+<g id="thumb-down"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v1.91l.01.01L1 14c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></g>
+<g id="thumb-up"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-1.91l-.01-.01L23 10z"/></g>
+<g id="thumbs-up-down"><path d="M12 6c0-.55-.45-1-1-1H5.82l.66-3.18.02-.23c0-.31-.13-.59-.33-.8L5.38 0 .44 4.94C.17 5.21 0 5.59 0 6v6.5c0 .83.67 1.5 1.5 1.5h6.75c.62 0 1.15-.38 1.38-.91l2.26-5.29c.07-.17.11-.36.11-.55V6zm10.5 4h-6.75c-.62 0-1.15.38-1.38.91l-2.26 5.29c-.07.17-.11.36-.11.55V18c0 .55.45 1 1 1h5.18l-.66 3.18-.02.24c0 .31.13.59.33.8l.79.78 4.94-4.94c.27-.27.44-.65.44-1.06v-6.5c0-.83-.67-1.5-1.5-1.5z"/></g>
+<g id="timeline"><path d="M23 8c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2z"/></g>
+<g id="toc"><path d="M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z"/></g>
+<g id="today"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"/></g>
+<g id="toll"><path d="M15 4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zM3 12c0-2.61 1.67-4.83 4-5.65V4.26C3.55 5.15 1 8.27 1 12s2.55 6.85 6 7.74v-2.09c-2.33-.82-4-3.04-4-5.65z"/></g>
+<g id="touch-app"><path d="M9 11.24V7.5C9 6.12 10.12 5 11.5 5S14 6.12 14 7.5v3.74c1.21-.81 2-2.18 2-3.74C16 5.01 13.99 3 11.5 3S7 5.01 7 7.5c0 1.56.79 2.93 2 3.74zm9.84 4.63l-4.54-2.26c-.17-.07-.35-.11-.54-.11H13v-6c0-.83-.67-1.5-1.5-1.5S10 6.67 10 7.5v10.74l-3.43-.72c-.08-.01-.15-.03-.24-.03-.31 0-.59.13-.79.33l-.79.8 4.94 4.94c.27.27.65.44 1.06.44h6.79c.75 0 1.33-.55 1.44-1.28l.75-5.27c.01-.07.02-.14.02-.2 0-.62-.38-1.16-.91-1.38z"/></g>
+<g id="track-changes"><path d="M19.07 4.93l-1.41 1.41C19.1 7.79 20 9.79 20 12c0 4.42-3.58 8-8 8s-8-3.58-8-8c0-4.08 3.05-7.44 7-7.93v2.02C8.16 6.57 6 9.03 6 12c0 3.31 2.69 6 6 6s6-2.69 6-6c0-1.66-.67-3.16-1.76-4.24l-1.41 1.41C15.55 9.9 16 10.9 16 12c0 2.21-1.79 4-4 4s-4-1.79-4-4c0-1.86 1.28-3.41 3-3.86v2.14c-.6.35-1 .98-1 1.72 0 1.1.9 2 2 2s2-.9 2-2c0-.74-.4-1.38-1-1.72V2h-1C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10c0-2.76-1.12-5.26-2.93-7.07z"/></g>
+<g id="translate"><path d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/></g>
+<g id="trending-down"><path d="M16 18l2.29-2.29-4.88-4.88-4 4L2 7.41 3.41 6l6 6 4-4 6.3 6.29L22 12v6z"/></g>
+<g id="trending-flat"><path d="M22 12l-4-4v3H3v2h15v3z"/></g>
+<g id="trending-up"><path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z"/></g>
+<g id="turned-in"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></g>
+<g id="turned-in-not"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"/></g>
+<g id="unarchive"><path d="M20.55 5.22l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.15.55L3.46 5.22C3.17 5.57 3 6.01 3 6.5V19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.49-.17-.93-.45-1.28zM12 9.5l5.5 5.5H14v2h-4v-2H6.5L12 9.5zM5.12 5l.82-1h12l.93 1H5.12z"/></g>
+<g id="undo"><path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"/></g>
+<g id="unfold-less"><path d="M7.41 18.59L8.83 20 12 16.83 15.17 20l1.41-1.41L12 14l-4.59 4.59zm9.18-13.18L15.17 4 12 7.17 8.83 4 7.41 5.41 12 10l4.59-4.59z"/></g>
+<g id="unfold-more"><path d="M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z"/></g>
+<g id="update"><path d="M21 10.12h-6.78l2.74-2.82c-2.73-2.7-7.15-2.8-9.88-.1-2.73 2.71-2.73 7.08 0 9.79 2.73 2.71 7.15 2.71 9.88 0C18.32 15.65 19 14.08 19 12.1h2c0 1.98-.88 4.55-2.64 6.29-3.51 3.48-9.21 3.48-12.72 0-3.5-3.47-3.53-9.11-.02-12.58 3.51-3.47 9.14-3.47 12.65 0L21 3v7.12zM12.5 8v4.25l3.5 2.08-.72 1.21L11 13V8h1.5z"/></g>
+<g id="verified-user"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z"/></g>
+<g id="view-agenda"><path d="M20 13H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1zm0-10H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1z"/></g>
+<g id="view-array"><path d="M4 18h3V5H4v13zM18 5v13h3V5h-3zM8 18h9V5H8v13z"/></g>
+<g id="view-carousel"><path d="M7 19h10V4H7v15zm-5-2h4V6H2v11zM18 6v11h4V6h-4z"/></g>
+<g id="view-column"><path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"/></g>
+<g id="view-day"><path d="M2 21h19v-3H2v3zM20 8H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zM2 3v3h19V3H2z"/></g>
+<g id="view-headline"><path d="M4 15h16v-2H4v2zm0 4h16v-2H4v2zm0-8h16V9H4v2zm0-6v2h16V5H4z"/></g>
+<g id="view-list"><path d="M4 14h4v-4H4v4zm0 5h4v-4H4v4zM4 9h4V5H4v4zm5 5h12v-4H9v4zm0 5h12v-4H9v4zM9 5v4h12V5H9z"/></g>
+<g id="view-module"><path d="M4 11h5V5H4v6zm0 7h5v-6H4v6zm6 0h5v-6h-5v6zm6 0h5v-6h-5v6zm-6-7h5V5h-5v6zm6-6v6h5V5h-5z"/></g>
+<g id="view-quilt"><path d="M10 18h5v-6h-5v6zm-6 0h5V5H4v13zm12 0h5v-6h-5v6zM10 5v6h11V5H10z"/></g>
+<g id="view-stream"><path d="M4 18h17v-6H4v6zM4 5v6h17V5H4z"/></g>
+<g id="view-week"><path d="M6 5H3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zm14 0h-3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1zm-7 0h-3c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V6c0-.55-.45-1-1-1z"/></g>
+<g id="visibility"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></g>
+<g id="visibility-off"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></g>
+<g id="warning"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></g>
+<g id="watch-later"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z"/></g>
+<g id="weekend"><path d="M21 10c-1.1 0-2 .9-2 2v3H5v-3c0-1.1-.9-2-2-2s-2 .9-2 2v5c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-5c0-1.1-.9-2-2-2zm-3-5H6c-1.1 0-2 .9-2 2v2.15c1.16.41 2 1.51 2 2.82V14h12v-2.03c0-1.3.84-2.4 2-2.82V7c0-1.1-.9-2-2-2z"/></g>
+<g id="work"><path d="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z"/></g>
+<g id="youtube-searched-for"><path d="M17.01 14h-.8l-.27-.27c.98-1.14 1.57-2.61 1.57-4.23 0-3.59-2.91-6.5-6.5-6.5s-6.5 3-6.5 6.5H2l3.84 4 4.16-4H6.51C6.51 7 8.53 5 11.01 5s4.5 2.01 4.5 4.5c0 2.48-2.02 4.5-4.5 4.5-.65 0-1.26-.14-1.82-.38L7.71 15.1c.97.57 2.09.9 3.3.9 1.61 0 3.08-.59 4.22-1.57l.27.27v.79l5.01 4.99L22 19l-4.99-5z"/></g>
+<g id="zoom-in"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zm2.5-4h-2v2H9v-2H7V9h2V7h1v2h2v1z"/></g>
+<g id="zoom-out"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/maps-icons.html b/catapult/third_party/polymer/components/iron-icons/maps-icons.html
new file mode 100644
index 00000000..d6ad092f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/maps-icons.html
@@ -0,0 +1,84 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="maps" size="24">
+<svg><defs>
+<g id="add-location"><path d="M12 2C8.14 2 5 5.14 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.86-3.14-7-7-7zm4 8h-3v3h-2v-3H8V8h3V5h2v3h3v2z"/></g>
+<g id="beenhere"><path d="M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z"/></g>
+<g id="directions"><path d="M21.71 11.29l-9-9c-.39-.39-1.02-.39-1.41 0l-9 9c-.39.39-.39 1.02 0 1.41l9 9c.39.39 1.02.39 1.41 0l9-9c.39-.38.39-1.01 0-1.41zM14 14.5V12h-4v3H8v-4c0-.55.45-1 1-1h5V7.5l3.5 3.5-3.5 3.5z"/></g>
+<g id="directions-bike"><path d="M15.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM5 12c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 8.5c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5zm5.8-10l2.4-2.4.8.8c1.3 1.3 3 2.1 5.1 2.1V9c-1.5 0-2.7-.6-3.6-1.5l-1.9-1.9c-.5-.4-1-.6-1.6-.6s-1.1.2-1.4.6L7.8 8.4c-.4.4-.6.9-.6 1.4 0 .6.2 1.1.6 1.4L11 14v5h2v-6.2l-2.2-2.3zM19 12c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 8.5c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5z"/></g>
+<g id="directions-boat"><path d="M20 21c-1.39 0-2.78-.47-4-1.32-2.44 1.71-5.56 1.71-8 0C6.78 20.53 5.39 21 4 21H2v2h2c1.38 0 2.74-.35 4-.99 2.52 1.29 5.48 1.29 8 0 1.26.65 2.62.99 4 .99h2v-2h-2zM3.95 19H4c1.6 0 3.02-.88 4-2 .98 1.12 2.4 2 4 2s3.02-.88 4-2c.98 1.12 2.4 2 4 2h.05l1.89-6.68c.08-.26.06-.54-.06-.78s-.34-.42-.6-.5L20 10.62V6c0-1.1-.9-2-2-2h-3V1H9v3H6c-1.1 0-2 .9-2 2v4.62l-1.29.42c-.26.08-.48.26-.6.5s-.15.52-.06.78L3.95 19zM6 6h12v3.97L12 8 6 9.97V6z"/></g>
+<g id="directions-bus"><path d="M4 16c0 .88.39 1.67 1 2.22V20c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h8v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1.78c.61-.55 1-1.34 1-2.22V6c0-3.5-3.58-4-8-4s-8 .5-8 4v10zm3.5 1c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm9 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6H6V6h12v5z"/></g>
+<g id="directions-car"><path d="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z"/></g>
+<g id="directions-railway"><path d="M4 15.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h12v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V5c0-3.5-3.58-4-8-4s-8 .5-8 4v10.5zm8 1.5c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm6-7H6V5h12v5z"/></g>
+<g id="directions-run"><path d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z"/></g>
+<g id="directions-subway"><path d="M12 2c-4.42 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h12v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4zM7.5 17c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm3.5-6H6V6h5v5zm5.5 6c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6h-5V6h5v5z"/></g>
+<g id="directions-transit"><path d="M12 2c-4.42 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h12v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4zM7.5 17c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm3.5-6H6V6h5v5zm5.5 6c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6h-5V6h5v5z"/></g>
+<g id="directions-walk"><path d="M13.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM9.8 8.9L7 23h2.1l1.8-8 2.1 2v6h2v-7.5l-2.1-2 .6-3C14.8 12 16.8 13 19 13v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1L6 8.3V13h2V9.6l1.8-.7"/></g>
+<g id="edit-location"><path d="M12 2C8.14 2 5 5.14 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.86-3.14-7-7-7zm-1.56 10H9v-1.44l3.35-3.34 1.43 1.43L10.44 12zm4.45-4.45l-.7.7-1.44-1.44.7-.7c.15-.15.39-.15.54 0l.9.9c.15.15.15.39 0 .54z"/></g>
+<g id="ev-station"><path d="M19.77 7.23l.01-.01-3.72-3.72L15 4.56l2.11 2.11c-.94.36-1.61 1.26-1.61 2.33 0 1.38 1.12 2.5 2.5 2.5.36 0 .69-.08 1-.21v7.21c0 .55-.45 1-1 1s-1-.45-1-1V14c0-1.1-.9-2-2-2h-1V5c0-1.1-.9-2-2-2H6c-1.1 0-2 .9-2 2v16h10v-7.5h1.5v5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V9c0-.69-.28-1.32-.73-1.77zM18 10c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zM8 18v-4.5H6L10 6v5h2l-4 7z"/></g>
+<g id="flight"><path d="M10.18 9"/><path d="M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/></g>
+<g id="hotel"><path d="M7 13c1.66 0 3-1.34 3-3S8.66 7 7 7s-3 1.34-3 3 1.34 3 3 3zm12-6h-8v7H3V5H1v15h2v-3h18v3h2v-9c0-2.21-1.79-4-4-4z"/></g>
+<g id="layers"><path d="M11.99 18.54l-7.37-5.73L3 14.07l9 7 9-7-1.63-1.27-7.38 5.74zM12 16l7.36-5.73L21 9l-9-7-9 7 1.63 1.27L12 16z"/></g>
+<g id="layers-clear"><path d="M19.81 14.99l1.19-.92-1.43-1.43-1.19.92 1.43 1.43zm-.45-4.72L21 9l-9-7-2.91 2.27 7.87 7.88 2.4-1.88zM3.27 1L2 2.27l4.22 4.22L3 9l1.63 1.27L12 16l2.1-1.63 1.43 1.43L12 18.54l-7.37-5.73L3 14.07l9 7 4.95-3.85L20.73 21 22 19.73 3.27 1z"/></g>
+<g id="local-activity"><path d="M20 12c0-1.1.9-2 2-2V6c0-1.1-.9-2-2-2H4c-1.1 0-1.99.9-1.99 2v4c1.1 0 1.99.9 1.99 2s-.89 2-2 2v4c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-4c-1.1 0-2-.9-2-2zm-4.42 4.8L12 14.5l-3.58 2.3 1.08-4.12-3.29-2.69 4.24-.25L12 5.8l1.54 3.95 4.24.25-3.29 2.69 1.09 4.11z"/></g>
+<g id="local-airport"><path d="M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/></g>
+<g id="local-atm"><path d="M11 17h2v-1h1c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1h-3v-1h4V8h-2V7h-2v1h-1c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1h3v1H9v2h2v1zm9-13H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4V6h16v12z"/></g>
+<g id="local-bar"><path d="M21 5V3H3v2l8 9v5H6v2h12v-2h-5v-5l8-9zM7.43 7L5.66 5h12.69l-1.78 2H7.43z"/></g>
+<g id="local-cafe"><path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM2 21h18v-2H2v2z"/></g>
+<g id="local-car-wash"><path d="M17 5c.83 0 1.5-.67 1.5-1.5 0-1-1.5-2.7-1.5-2.7s-1.5 1.7-1.5 2.7c0 .83.67 1.5 1.5 1.5zm-5 0c.83 0 1.5-.67 1.5-1.5 0-1-1.5-2.7-1.5-2.7s-1.5 1.7-1.5 2.7c0 .83.67 1.5 1.5 1.5zM7 5c.83 0 1.5-.67 1.5-1.5C8.5 2.5 7 .8 7 .8S5.5 2.5 5.5 3.5C5.5 4.33 6.17 5 7 5zm11.92 3.01C18.72 7.42 18.16 7 17.5 7h-11c-.66 0-1.21.42-1.42 1.01L3 14v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 18c-.83 0-1.5-.67-1.5-1.5S5.67 15 6.5 15s1.5.67 1.5 1.5S7.33 18 6.5 18zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 13l1.5-4.5h11L19 13H5z"/></g>
+<g id="local-convenience-store"><path d="M19 7V4H5v3H2v13h8v-4h4v4h8V7h-3zm-8 3H9v1h2v1H8V9h2V8H8V7h3v3zm5 2h-1v-2h-2V7h1v2h1V7h1v5z"/></g>
+<g id="local-dining"><path d="M8.1 13.34l2.83-2.83L3.91 3.5c-1.56 1.56-1.56 4.09 0 5.66l4.19 4.18zm6.78-1.81c1.53.71 3.68.21 5.27-1.38 1.91-1.91 2.28-4.65.81-6.12-1.46-1.46-4.2-1.1-6.12.81-1.59 1.59-2.09 3.74-1.38 5.27L3.7 19.87l1.41 1.41L12 14.41l6.88 6.88 1.41-1.41L13.41 13l1.47-1.47z"/></g>
+<g id="local-drink"><path d="M3 2l2.01 18.23C5.13 21.23 5.97 22 7 22h10c1.03 0 1.87-.77 1.99-1.77L21 2H3zm9 17c-1.66 0-3-1.34-3-3 0-2 3-5.4 3-5.4s3 3.4 3 5.4c0 1.66-1.34 3-3 3zm6.33-11H5.67l-.44-4h13.53l-.43 4z"/></g>
+<g id="local-florist"><path d="M12 22c4.97 0 9-4.03 9-9-4.97 0-9 4.03-9 9zM5.6 10.25c0 1.38 1.12 2.5 2.5 2.5.53 0 1.01-.16 1.42-.44l-.02.19c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5l-.02-.19c.4.28.89.44 1.42.44 1.38 0 2.5-1.12 2.5-2.5 0-1-.59-1.85-1.43-2.25.84-.4 1.43-1.25 1.43-2.25 0-1.38-1.12-2.5-2.5-2.5-.53 0-1.01.16-1.42.44l.02-.19C14.5 2.12 13.38 1 12 1S9.5 2.12 9.5 3.5l.02.19c-.4-.28-.89-.44-1.42-.44-1.38 0-2.5 1.12-2.5 2.5 0 1 .59 1.85 1.43 2.25-.84.4-1.43 1.25-1.43 2.25zM12 5.5c1.38 0 2.5 1.12 2.5 2.5s-1.12 2.5-2.5 2.5S9.5 9.38 9.5 8s1.12-2.5 2.5-2.5zM3 13c0 4.97 4.03 9 9 9 0-4.97-4.03-9-9-9z"/></g>
+<g id="local-gas-station"><path d="M19.77 7.23l.01-.01-3.72-3.72L15 4.56l2.11 2.11c-.94.36-1.61 1.26-1.61 2.33 0 1.38 1.12 2.5 2.5 2.5.36 0 .69-.08 1-.21v7.21c0 .55-.45 1-1 1s-1-.45-1-1V14c0-1.1-.9-2-2-2h-1V5c0-1.1-.9-2-2-2H6c-1.1 0-2 .9-2 2v16h10v-7.5h1.5v5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V9c0-.69-.28-1.32-.73-1.77zM12 10H6V5h6v5zm6 0c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></g>
+<g id="local-grocery-store"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/></g>
+<g id="local-hospital"><path d="M19 3H5c-1.1 0-1.99.9-1.99 2L3 19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 11h-4v4h-4v-4H6v-4h4V6h4v4h4v4z"/></g>
+<g id="local-hotel"><path d="M7 13c1.66 0 3-1.34 3-3S8.66 7 7 7s-3 1.34-3 3 1.34 3 3 3zm12-6h-8v7H3V5H1v15h2v-3h18v3h2v-9c0-2.21-1.79-4-4-4z"/></g>
+<g id="local-laundry-service"><path d="M9.17 16.83c1.56 1.56 4.1 1.56 5.66 0 1.56-1.56 1.56-4.1 0-5.66l-5.66 5.66zM18 2.01L6 2c-1.11 0-2 .89-2 2v16c0 1.11.89 2 2 2h12c1.11 0 2-.89 2-2V4c0-1.11-.89-1.99-2-1.99zM10 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM7 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm5 16c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></g>
+<g id="local-library"><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></g>
+<g id="local-mall"><path d="M19 6h-2c0-2.76-2.24-5-5-5S7 3.24 7 6H5c-1.1 0-1.99.9-1.99 2L3 20c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-7-3c1.66 0 3 1.34 3 3H9c0-1.66 1.34-3 3-3zm0 10c-2.76 0-5-2.24-5-5h2c0 1.66 1.34 3 3 3s3-1.34 3-3h2c0 2.76-2.24 5-5 5z"/></g>
+<g id="local-movies"><path d="M18 3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8 17H6v-2h2v2zm0-4H6v-2h2v2zm0-4H6V7h2v2zm10 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V7h2v2z"/></g>
+<g id="local-offer"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/></g>
+<g id="local-parking"><path d="M13 3H6v18h4v-6h3c3.31 0 6-2.69 6-6s-2.69-6-6-6zm.2 8H10V7h3.2c1.1 0 2 .9 2 2s-.9 2-2 2z"/></g>
+<g id="local-pharmacy"><path d="M21 5h-2.64l1.14-3.14L17.15 1l-1.46 4H3v2l2 6-2 6v2h18v-2l-2-6 2-6V5zm-5 9h-3v3h-2v-3H8v-2h3V9h2v3h3v2z"/></g>
+<g id="local-phone"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></g>
+<g id="local-pizza"><path d="M12 2C8.43 2 5.23 3.54 3.01 6L12 22l8.99-16C18.78 3.55 15.57 2 12 2zM7 7c0-1.1.9-2 2-2s2 .9 2 2-.9 2-2 2-2-.9-2-2zm5 8c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></g>
+<g id="local-play"><path d="M20 12c0-1.1.9-2 2-2V6c0-1.1-.9-2-2-2H4c-1.1 0-1.99.9-1.99 2v4c1.1 0 1.99.9 1.99 2s-.89 2-2 2v4c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-4c-1.1 0-2-.9-2-2zm-4.42 4.8L12 14.5l-3.58 2.3 1.08-4.12-3.29-2.69 4.24-.25L12 5.8l1.54 3.95 4.24.25-3.29 2.69 1.09 4.11z"/></g>
+<g id="local-post-office"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></g>
+<g id="local-printshop"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></g>
+<g id="local-see"><circle cx="12" cy="12" r="3.2"/><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/></g>
+<g id="local-shipping"><path d="M20 8h-3V4H3c-1.1 0-2 .9-2 2v11h2c0 1.66 1.34 3 3 3s3-1.34 3-3h6c0 1.66 1.34 3 3 3s3-1.34 3-3h2v-5l-3-4zM6 18.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm13.5-9l1.96 2.5H17V9.5h2.5zm-1.5 9c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="local-taxi"><path d="M18.92 6.01C18.72 5.42 18.16 5 17.5 5H15V3H9v2H6.5c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z"/></g>
+<g id="map"><path d="M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z"/></g>
+<g id="my-location"><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></g>
+<g id="navigation"><path d="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z"/></g>
+<g id="near-me"><path d="M21 3L3 10.53v.98l6.84 2.65L12.48 21h.98L21 3z"/></g>
+<g id="person-pin"><path d="M19 2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 3.3c1.49 0 2.7 1.21 2.7 2.7 0 1.49-1.21 2.7-2.7 2.7-1.49 0-2.7-1.21-2.7-2.7 0-1.49 1.21-2.7 2.7-2.7zM18 16H6v-.9c0-2 4-3.1 6-3.1s6 1.1 6 3.1v.9z"/></g>
+<g id="person-pin-circle"><path d="M12 2C8.14 2 5 5.14 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.86-3.14-7-7-7zm0 2c1.1 0 2 .9 2 2 0 1.11-.9 2-2 2s-2-.89-2-2c0-1.1.9-2 2-2zm0 10c-1.67 0-3.14-.85-4-2.15.02-1.32 2.67-2.05 4-2.05s3.98.73 4 2.05c-.86 1.3-2.33 2.15-4 2.15z"/></g>
+<g id="pin-drop"><path d="M18 8c0-3.31-2.69-6-6-6S6 4.69 6 8c0 4.5 6 11 6 11s6-6.5 6-11zm-8 0c0-1.1.9-2 2-2s2 .9 2 2-.89 2-2 2c-1.1 0-2-.9-2-2zM5 20v2h14v-2H5z"/></g>
+<g id="place"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></g>
+<g id="rate-review"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 14v-2.47l6.88-6.88c.2-.2.51-.2.71 0l1.77 1.77c.2.2.2.51 0 .71L8.47 14H6zm12 0h-7.5l2-2H18v2z"/></g>
+<g id="restaurant"><path d="M11 9H9V2H7v7H5V2H3v7c0 2.12 1.66 3.84 3.75 3.97V22h2.5v-9.03C11.34 12.84 13 11.12 13 9V2h-2v7zm5-3v8h2.5v8H21V2c-2.76 0-5 2.24-5 4z"/></g>
+<g id="restaurant-menu"><path d="M8.1 13.34l2.83-2.83L3.91 3.5c-1.56 1.56-1.56 4.09 0 5.66l4.19 4.18zm6.78-1.81c1.53.71 3.68.21 5.27-1.38 1.91-1.91 2.28-4.65.81-6.12-1.46-1.46-4.2-1.1-6.12.81-1.59 1.59-2.09 3.74-1.38 5.27L3.7 19.87l1.41 1.41L12 14.41l6.88 6.88 1.41-1.41L13.41 13l1.47-1.47z"/></g>
+<g id="satellite"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM5 4.99h3C8 6.65 6.66 8 5 8V4.99zM5 12v-2c2.76 0 5-2.25 5-5.01h2C12 8.86 8.87 12 5 12zm0 6l3.5-4.5 2.5 3.01L14.5 12l4.5 6H5z"/></g>
+<g id="store-mall-directory"><path d="M20 4H4v2h16V4zm1 10v-2l-1-5H4l-1 5v2h1v6h10v-6h4v6h2v-6h1zm-9 4H6v-4h6v4z"/></g>
+<g id="streetview"><path d="M12.56 14.33c-.34.27-.56.7-.56 1.17V21h7c1.1 0 2-.9 2-2v-5.98c-.94-.33-1.95-.52-3-.52-2.03 0-3.93.7-5.44 1.83z"/><circle cx="18" cy="6" r="5"/><path d="M11.5 6c0-1.08.27-2.1.74-3H5c-1.1 0-2 .9-2 2v14c0 .55.23 1.05.59 1.41l9.82-9.82C12.23 9.42 11.5 7.8 11.5 6z"/></g>
+<g id="subway"><circle cx="15.5" cy="16" r="1"/><circle cx="8.5" cy="16" r="1"/><path d="M7.01 9h10v5h-10zM17.8 2.8C16 2.09 13.86 2 12 2c-1.86 0-4 .09-5.8.8C3.53 3.84 2 6.05 2 8.86V22h20V8.86c0-2.81-1.53-5.02-4.2-6.06zm.2 13.08c0 1.45-1.18 2.62-2.63 2.62l1.13 1.12V20H15l-1.5-1.5h-2.83L9.17 20H7.5v-.38l1.12-1.12C7.18 18.5 6 17.32 6 15.88V9c0-2.63 3-3 6-3 3.32 0 6 .38 6 3v6.88z"/></g>
+<g id="terrain"><path d="M14 6l-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22L14 6z"/></g>
+<g id="traffic"><path d="M20 10h-3V8.86c1.72-.45 3-2 3-3.86h-3V4c0-.55-.45-1-1-1H8c-.55 0-1 .45-1 1v1H4c0 1.86 1.28 3.41 3 3.86V10H4c0 1.86 1.28 3.41 3 3.86V15H4c0 1.86 1.28 3.41 3 3.86V20c0 .55.45 1 1 1h8c.55 0 1-.45 1-1v-1.14c1.72-.45 3-2 3-3.86h-3v-1.14c1.72-.45 3-2 3-3.86zm-8 9c-1.11 0-2-.9-2-2s.89-2 2-2c1.1 0 2 .9 2 2s-.89 2-2 2zm0-5c-1.11 0-2-.9-2-2s.89-2 2-2c1.1 0 2 .9 2 2s-.89 2-2 2zm0-5c-1.11 0-2-.9-2-2 0-1.11.89-2 2-2 1.1 0 2 .89 2 2 0 1.1-.89 2-2 2z"/></g>
+<g id="train"><path d="M12 2c-4 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h2.23l2-2H14l2 2h2v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4zM7.5 17c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm3.5-7H6V6h5v4zm2 0V6h5v4h-5zm3.5 7c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="tram"><path d="M19 16.94V8.5c0-2.79-2.61-3.4-6.01-3.49l.76-1.51H17V2H7v1.5h4.75l-.76 1.52C7.86 5.11 5 5.73 5 8.5v8.44c0 1.45 1.19 2.66 2.59 2.97L6 21.5v.5h2.23l2-2H14l2 2h2v-.5L16.5 20h-.08c1.69 0 2.58-1.37 2.58-3.06zm-7 1.56c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm5-4.5H7V9h10v5z"/></g>
+<g id="transfer-within-a-station"><path d="M16.49 15.5v-1.75L14 16.25l2.49 2.5V17H22v-1.5zm3.02 4.25H14v1.5h5.51V23L22 20.5 19.51 18zM9.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM5.75 8.9L3 23h2.1l1.75-8L9 17v6h2v-7.55L8.95 13.4l.6-3C10.85 12 12.8 13 15 13v-2c-1.85 0-3.45-1-4.35-2.45l-.95-1.6C9.35 6.35 8.7 6 8 6c-.25 0-.5.05-.75.15L2 8.3V13h2V9.65l1.75-.75"/></g>
+<g id="zoom-out-map"><path d="M15 3l2.3 2.3-2.89 2.87 1.42 1.42L18.7 6.7 21 9V3zM3 9l2.3-2.3 2.87 2.89 1.42-1.42L6.7 5.3 9 3H3zm6 12l-2.3-2.3 2.89-2.87-1.42-1.42L5.3 17.3 3 15v6zm12-6l-2.3 2.3-2.87-2.89-1.42 1.42 2.89 2.87L15 21h6z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/notification-icons.html b/catapult/third_party/polymer/components/iron-icons/notification-icons.html
new file mode 100644
index 00000000..272b95b0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/notification-icons.html
@@ -0,0 +1,70 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="notification" size="24">
+<svg><defs>
+<g id="adb"><path d="M5 16c0 3.87 3.13 7 7 7s7-3.13 7-7v-4H5v4zM16.12 4.37l2.1-2.1-.82-.83-2.3 2.31C14.16 3.28 13.12 3 12 3s-2.16.28-3.09.75L6.6 1.44l-.82.83 2.1 2.1C6.14 5.64 5 7.68 5 10v1h14v-1c0-2.32-1.14-4.36-2.88-5.63zM9 9c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm6 0c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></g>
+<g id="airline-seat-flat"><path d="M22 11v2H9V7h9c2.21 0 4 1.79 4 4zM2 14v2h6v2h8v-2h6v-2H2zm5.14-1.9c1.16-1.19 1.14-3.08-.04-4.24-1.19-1.16-3.08-1.14-4.24.04-1.16 1.19-1.14 3.08.04 4.24 1.19 1.16 3.08 1.14 4.24-.04z"/></g>
+<g id="airline-seat-flat-angled"><path d="M22.25 14.29l-.69 1.89L9.2 11.71l2.08-5.66 8.56 3.09c2.1.76 3.18 3.06 2.41 5.15zM1.5 12.14L8 14.48V19h8v-1.63L20.52 19l.69-1.89-19.02-6.86-.69 1.89zm5.8-1.94c1.49-.72 2.12-2.51 1.41-4C7.99 4.71 6.2 4.08 4.7 4.8c-1.49.71-2.12 2.5-1.4 4 .71 1.49 2.5 2.12 4 1.4z"/></g>
+<g id="airline-seat-individual-suite"><path d="M7 13c1.65 0 3-1.35 3-3S8.65 7 7 7s-3 1.35-3 3 1.35 3 3 3zm12-6h-8v7H3V7H1v10h22v-6c0-2.21-1.79-4-4-4z"/></g>
+<g id="airline-seat-legroom-extra"><path d="M4 12V3H2v9c0 2.76 2.24 5 5 5h6v-2H7c-1.66 0-3-1.34-3-3zm18.83 5.24c-.38-.72-1.29-.97-2.03-.63l-1.09.5-3.41-6.98c-.34-.68-1.03-1.12-1.79-1.12L11 9V3H5v8c0 1.66 1.34 3 3 3h7l3.41 7 3.72-1.7c.77-.36 1.1-1.3.7-2.06z"/></g>
+<g id="airline-seat-legroom-normal"><path d="M5 12V3H3v9c0 2.76 2.24 5 5 5h6v-2H8c-1.66 0-3-1.34-3-3zm15.5 6H19v-7c0-1.1-.9-2-2-2h-5V3H6v8c0 1.65 1.35 3 3 3h7v7h4.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5z"/></g>
+<g id="airline-seat-legroom-reduced"><path d="M19.97 19.2c.18.96-.55 1.8-1.47 1.8H14v-3l1-4H9c-1.65 0-3-1.35-3-3V3h6v6h5c1.1 0 2 .9 2 2l-2 7h1.44c.73 0 1.39.49 1.53 1.2zM5 12V3H3v9c0 2.76 2.24 5 5 5h4v-2H8c-1.66 0-3-1.34-3-3z"/></g>
+<g id="airline-seat-recline-extra"><path d="M5.35 5.64c-.9-.64-1.12-1.88-.49-2.79.63-.9 1.88-1.12 2.79-.49.9.64 1.12 1.88.49 2.79-.64.9-1.88 1.12-2.79.49zM16 19H8.93c-1.48 0-2.74-1.08-2.96-2.54L4 7H2l1.99 9.76C4.37 19.2 6.47 21 8.94 21H16v-2zm.23-4h-4.88l-1.03-4.1c1.58.89 3.28 1.54 5.15 1.22V9.99c-1.63.31-3.44-.27-4.69-1.25L9.14 7.47c-.23-.18-.49-.3-.76-.38-.32-.09-.66-.12-.99-.06h-.02c-1.23.22-2.05 1.39-1.84 2.61l1.35 5.92C7.16 16.98 8.39 18 9.83 18h6.85l3.82 3 1.5-1.5-5.77-4.5z"/></g>
+<g id="airline-seat-recline-normal"><path d="M7.59 5.41c-.78-.78-.78-2.05 0-2.83.78-.78 2.05-.78 2.83 0 .78.78.78 2.05 0 2.83-.79.79-2.05.79-2.83 0zM6 16V7H4v9c0 2.76 2.24 5 5 5h6v-2H9c-1.66 0-3-1.34-3-3zm14 4.07L14.93 15H11.5v-3.68c1.4 1.15 3.6 2.16 5.5 2.16v-2.16c-1.66.02-3.61-.87-4.67-2.04l-1.4-1.55c-.19-.21-.43-.38-.69-.5-.29-.14-.62-.23-.96-.23h-.03C8.01 7 7 8.01 7 9.25V15c0 1.66 1.34 3 3 3h5.07l3.5 3.5L20 20.07z"/></g>
+<g id="bluetooth-audio"><path d="M14.24 12.01l2.32 2.32c.28-.72.44-1.51.44-2.33 0-.82-.16-1.59-.43-2.31l-2.33 2.32zm5.29-5.3l-1.26 1.26c.63 1.21.98 2.57.98 4.02s-.36 2.82-.98 4.02l1.2 1.2c.97-1.54 1.54-3.36 1.54-5.31-.01-1.89-.55-3.67-1.48-5.19zm-3.82 1L10 2H9v7.59L4.41 5 3 6.41 8.59 12 3 17.59 4.41 19 9 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM11 5.83l1.88 1.88L11 9.59V5.83zm1.88 10.46L11 18.17v-3.76l1.88 1.88z"/></g>
+<g id="confirmation-number"><path d="M22 10V6c0-1.11-.9-2-2-2H4c-1.1 0-1.99.89-1.99 2v4c1.1 0 1.99.9 1.99 2s-.89 2-2 2v4c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-4c-1.1 0-2-.9-2-2s.9-2 2-2zm-9 7.5h-2v-2h2v2zm0-4.5h-2v-2h2v2zm0-4.5h-2v-2h2v2z"/></g>
+<g id="disc-full"><path d="M20 16h2v-2h-2v2zm0-9v5h2V7h-2zM10 4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm0 10c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></g>
+<g id="do-not-disturb"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z"/></g>
+<g id="do-not-disturb-alt"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zM4 12c0-4.4 3.6-8 8-8 1.8 0 3.5.6 4.9 1.7L5.7 16.9C4.6 15.5 4 13.8 4 12zm8 8c-1.8 0-3.5-.6-4.9-1.7L18.3 7.1C19.4 8.5 20 10.2 20 12c0 4.4-3.6 8-8 8z"/></g>
+<g id="do-not-disturb-off"><path d="M17 11v2h-1.46l4.68 4.68C21.34 16.07 22 14.11 22 12c0-5.52-4.48-10-10-10-2.11 0-4.07.66-5.68 1.78L13.54 11H17zM2.27 2.27L1 3.54l2.78 2.78C2.66 7.93 2 9.89 2 12c0 5.52 4.48 10 10 10 2.11 0 4.07-.66 5.68-1.78L20.46 23l1.27-1.27L11 11 2.27 2.27zM7 13v-2h1.46l2 2H7z"/></g>
+<g id="do-not-disturb-on"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/></g>
+<g id="drive-eta"><path d="M18.92 5.01C18.72 4.42 18.16 4 17.5 4h-11c-.66 0-1.21.42-1.42 1.01L3 11v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 15c-.83 0-1.5-.67-1.5-1.5S5.67 12 6.5 12s1.5.67 1.5 1.5S7.33 15 6.5 15zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 10l1.5-4.5h11L19 10H5z"/></g>
+<g id="enhanced-encryption"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H8.9V6zM16 16h-3v3h-2v-3H8v-2h3v-3h2v3h3v2z"/></g>
+<g id="event-available"><path d="M16.53 11.06L15.47 10l-4.88 4.88-2.12-2.12-1.06 1.06L10.59 17l5.94-5.94zM19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z"/></g>
+<g id="event-busy"><path d="M9.31 17l2.44-2.44L14.19 17l1.06-1.06-2.44-2.44 2.44-2.44L14.19 10l-2.44 2.44L9.31 10l-1.06 1.06 2.44 2.44-2.44 2.44L9.31 17zM19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z"/></g>
+<g id="event-note"><path d="M17 10H7v2h10v-2zm2-7h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zm-5-5H7v2h7v-2z"/></g>
+<g id="folder-special"><path d="M20 6h-8l-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-2.06 11L15 15.28 12.06 17l.78-3.33-2.59-2.24 3.41-.29L15 8l1.34 3.14 3.41.29-2.59 2.24.78 3.33z"/></g>
+<g id="live-tv"><path d="M21 6h-7.59l3.29-3.29L16 2l-4 4-4-4-.71.71L10.59 6H3c-1.1 0-2 .89-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.11-.9-2-2-2zm0 14H3V8h18v12zM9 10v8l7-4z"/></g>
+<g id="mms"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM5 14l3.5-4.5 2.5 3.01L14.5 8l4.5 6H5z"/></g>
+<g id="more"><path d="M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.97.89 1.66.89H22c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 13.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm5 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm5 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></g>
+<g id="network-check"><path d="M15.9 5c-.17 0-.32.09-.41.23l-.07.15-5.18 11.65c-.16.29-.26.61-.26.96 0 1.11.9 2.01 2.01 2.01.96 0 1.77-.68 1.96-1.59l.01-.03L16.4 5.5c0-.28-.22-.5-.5-.5zM1 9l2 2c2.88-2.88 6.79-4.08 10.53-3.62l1.19-2.68C9.89 3.84 4.74 5.27 1 9zm20 2l2-2c-1.64-1.64-3.55-2.82-5.59-3.57l-.53 2.82c1.5.62 2.9 1.53 4.12 2.75zm-4 4l2-2c-.8-.8-1.7-1.42-2.66-1.89l-.55 2.92c.42.27.83.59 1.21.97zM5 13l2 2c1.13-1.13 2.56-1.79 4.03-2l1.28-2.88c-2.63-.08-5.3.87-7.31 2.88z"/></g>
+<g id="network-locked"><path d="M19.5 10c.17 0 .33.03.5.05V1L1 20h13v-3c0-.89.39-1.68 1-2.23v-.27c0-2.48 2.02-4.5 4.5-4.5zm2.5 6v-1.5c0-1.38-1.12-2.5-2.5-2.5S17 13.12 17 14.5V16c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1v-4c0-.55-.45-1-1-1zm-1 0h-3v-1.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V16z"/></g>
+<g id="no-encryption"><path d="M21 21.78L4.22 5 3 6.22l2.04 2.04C4.42 8.6 4 9.25 4 10v10c0 1.1.9 2 2 2h12c.23 0 .45-.05.66-.12L19.78 23 21 21.78zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H9.66L20 18.34V10c0-1.1-.9-2-2-2h-1V6c0-2.76-2.24-5-5-5-2.56 0-4.64 1.93-4.94 4.4L8.9 7.24V6z"/></g>
+<g id="ondemand-video"><path d="M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12zm-5-6l-7 4V7z"/></g>
+<g id="personal-video"><path d="M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12z"/></g>
+<g id="phone-bluetooth-speaker"><path d="M14.71 9.5L17 7.21V11h.5l2.85-2.85L18.21 6l2.15-2.15L17.5 1H17v3.79L14.71 2.5l-.71.71L16.79 6 14 8.79l.71.71zM18 2.91l.94.94-.94.94V2.91zm0 4.3l.94.94-.94.94V7.21zm2 8.29c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.28-.26.36-.65.25-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1z"/></g>
+<g id="phone-forwarded"><path d="M18 11l5-5-5-5v3h-4v4h4v3zm2 4.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.28-.26.36-.65.25-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1z"/></g>
+<g id="phone-in-talk"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.28-.26.36-.65.25-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 12h2c0-4.97-4.03-9-9-9v2c3.87 0 7 3.13 7 7zm-4 0h2c0-2.76-2.24-5-5-5v2c1.66 0 3 1.34 3 3z"/></g>
+<g id="phone-locked"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.28-.26.36-.65.25-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM20 4v-.5C20 2.12 18.88 1 17.5 1S15 2.12 15 3.5V4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zm-.8 0h-3.4v-.5c0-.94.76-1.7 1.7-1.7s1.7.76 1.7 1.7V4z"/></g>
+<g id="phone-missed"><path d="M6.5 5.5L12 11l7-7-1-1-6 6-4.5-4.5H11V3H5v6h1.5V5.5zm17.21 11.17C20.66 13.78 16.54 12 12 12 7.46 12 3.34 13.78.29 16.67c-.18.18-.29.43-.29.71s.11.53.29.71l2.48 2.48c.18.18.43.29.71.29.27 0 .52-.11.7-.28.79-.74 1.69-1.36 2.66-1.85.33-.16.56-.5.56-.9v-3.1c1.45-.48 3-.73 4.6-.73 1.6 0 3.15.25 4.6.72v3.1c0 .39.23.74.56.9.98.49 1.87 1.12 2.67 1.85.18.18.43.28.7.28.28 0 .53-.11.71-.29l2.48-2.48c.18-.18.29-.43.29-.71s-.12-.52-.3-.7z"/></g>
+<g id="phone-paused"><path d="M17 3h-2v7h2V3zm3 12.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.28-.26.36-.65.25-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 3v7h2V3h-2z"/></g>
+<g id="power"><path d="M16.01 7L16 3h-2v4h-4V3H8v4h-.01C7 6.99 6 7.99 6 8.99v5.49L9.5 18v3h5v-3l3.5-3.51v-5.5c0-1-1-2-1.99-1.99z"/></g>
+<g id="priority-high"><circle cx="12" cy="19" r="2"/><path d="M10 3h4v12h-4z"/></g>
+<g id="rv-hookup"><path d="M20 17v-6c0-1.1-.9-2-2-2H7V7l-3 3 3 3v-2h4v3H4v3c0 1.1.9 2 2 2h2c0 1.66 1.34 3 3 3s3-1.34 3-3h8v-2h-2zm-9 3c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm7-6h-4v-3h4v3zM17 2v2H9v2h8v2l3-3z"/></g>
+<g id="sd-card"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 6h-2V4h2v4zm3 0h-2V4h2v4zm3 0h-2V4h2v4z"/></g>
+<g id="sim-card-alert"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5 15h-2v-2h2v2zm0-4h-2V8h2v5z"/></g>
+<g id="sms"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM9 11H7V9h2v2zm4 0h-2V9h2v2zm4 0h-2V9h2v2z"/></g>
+<g id="sms-failed"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z"/></g>
+<g id="sync"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></g>
+<g id="sync-disabled"><path d="M10 6.35V4.26c-.8.21-1.55.54-2.23.96l1.46 1.46c.25-.12.5-.24.77-.33zm-7.14-.94l2.36 2.36C4.45 8.99 4 10.44 4 12c0 2.21.91 4.2 2.36 5.64L4 20h6v-6l-2.24 2.24C6.68 15.15 6 13.66 6 12c0-1 .25-1.94.68-2.77l8.08 8.08c-.25.13-.5.25-.77.34v2.09c.8-.21 1.55-.54 2.23-.96l2.36 2.36 1.27-1.27L4.14 4.14 2.86 5.41zM20 4h-6v6l2.24-2.24C17.32 8.85 18 10.34 18 12c0 1-.25 1.94-.68 2.77l1.46 1.46C19.55 15.01 20 13.56 20 12c0-2.21-.91-4.2-2.36-5.64L20 4z"/></g>
+<g id="sync-problem"><path d="M3 12c0 2.21.91 4.2 2.36 5.64L3 20h6v-6l-2.24 2.24C5.68 15.15 5 13.66 5 12c0-2.61 1.67-4.83 4-5.65V4.26C5.55 5.15 3 8.27 3 12zm8 5h2v-2h-2v2zM21 4h-6v6l2.24-2.24C18.32 8.85 19 10.34 19 12c0 2.61-1.67 4.83-4 5.65v2.09c3.45-.89 6-4.01 6-7.74 0-2.21-.91-4.2-2.36-5.64L21 4zm-10 9h2V7h-2v6z"/></g>
+<g id="system-update"><path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14zm-1-6h-3V8h-2v5H8l4 4 4-4z"/></g>
+<g id="tap-and-play"><path d="M2 16v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0 4v3h3c0-1.66-1.34-3-3-3zm0-8v2c4.97 0 9 4.03 9 9h2c0-6.08-4.92-11-11-11zM17 1.01L7 1c-1.1 0-2 .9-2 2v7.37c.69.16 1.36.37 2 .64V5h10v13h-3.03c.52 1.25.84 2.59.95 4H17c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99z"/></g>
+<g id="time-to-leave"><path d="M18.92 5.01C18.72 4.42 18.16 4 17.5 4h-11c-.66 0-1.21.42-1.42 1.01L3 11v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 15c-.83 0-1.5-.67-1.5-1.5S5.67 12 6.5 12s1.5.67 1.5 1.5S7.33 15 6.5 15zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 10l1.5-4.5h11L19 10H5z"/></g>
+<g id="vibration"><path d="M0 15h2V9H0v6zm3 2h2V7H3v10zm19-8v6h2V9h-2zm-3 8h2V7h-2v10zM16.5 3h-9C6.67 3 6 3.67 6 4.5v15c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-15c0-.83-.67-1.5-1.5-1.5zM16 19H8V5h8v14z"/></g>
+<g id="voice-chat"><path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12l-4-3.2V14H6V6h8v3.2L18 6v8z"/></g>
+<g id="vpn-lock"><path d="M22 4v-.5C22 2.12 20.88 1 19.5 1S17 2.12 17 3.5V4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h5c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zm-.8 0h-3.4v-.5c0-.94.76-1.7 1.7-1.7s1.7.76 1.7 1.7V4zm-2.28 8c.04.33.08.66.08 1 0 2.08-.8 3.97-2.1 5.39-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H7v-2h2c.55 0 1-.45 1-1V8h2c1.1 0 2-.9 2-2V3.46c-.95-.3-1.95-.46-3-.46C5.48 3 1 7.48 1 13s4.48 10 10 10 10-4.48 10-10c0-.34-.02-.67-.05-1h-2.03zM10 20.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L8 16v1c0 1.1.9 2 2 2v1.93z"/></g>
+<g id="wc"><path d="M5.5 22v-7.5H4V9c0-1.1.9-2 2-2h3c1.1 0 2 .9 2 2v5.5H9.5V22h-4zM18 22v-6h3l-2.54-7.63C18.18 7.55 17.42 7 16.56 7h-.12c-.86 0-1.63.55-1.9 1.37L12 16h3v6h3zM7.5 6c1.11 0 2-.89 2-2s-.89-2-2-2-2 .89-2 2 .89 2 2 2zm9 0c1.11 0 2-.89 2-2s-.89-2-2-2-2 .89-2 2 .89 2 2 2z"/></g>
+<g id="wifi"><path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/places-icons.html b/catapult/third_party/polymer/components/iron-icons/places-icons.html
new file mode 100644
index 00000000..fa4827bf
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/places-icons.html
@@ -0,0 +1,35 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="places" size="24">
+<svg><defs>
+<g id="ac-unit"><path d="M22 11h-4.17l3.24-3.24-1.41-1.42L15 11h-2V9l4.66-4.66-1.42-1.41L13 6.17V2h-2v4.17L7.76 2.93 6.34 4.34 11 9v2H9L4.34 6.34 2.93 7.76 6.17 11H2v2h4.17l-3.24 3.24 1.41 1.42L9 13h2v2l-4.66 4.66 1.42 1.41L11 17.83V22h2v-4.17l3.24 3.24 1.42-1.41L13 15v-2h2l4.66 4.66 1.41-1.42L17.83 13H22z"/></g>
+<g id="airport-shuttle"><path d="M17 5H3c-1.1 0-2 .89-2 2v9h2c0 1.65 1.34 3 3 3s3-1.35 3-3h5.5c0 1.65 1.34 3 3 3s3-1.35 3-3H23v-5l-6-6zM3 11V7h4v4H3zm3 6.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm7-6.5H9V7h4v4zm4.5 6.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM15 11V7h1l4 4h-5z"/></g>
+<g id="all-inclusive"><path d="M18.6 6.62c-1.44 0-2.8.56-3.77 1.53L12 10.66 10.48 12h.01L7.8 14.39c-.64.64-1.49.99-2.4.99-1.87 0-3.39-1.51-3.39-3.38S3.53 8.62 5.4 8.62c.91 0 1.76.35 2.44 1.03l1.13 1 1.51-1.34L9.22 8.2C8.2 7.18 6.84 6.62 5.4 6.62 2.42 6.62 0 9.04 0 12s2.42 5.38 5.4 5.38c1.44 0 2.8-.56 3.77-1.53l2.83-2.5.01.01L13.52 12h-.01l2.69-2.39c.64-.64 1.49-.99 2.4-.99 1.87 0 3.39 1.51 3.39 3.38s-1.52 3.38-3.39 3.38c-.9 0-1.76-.35-2.44-1.03l-1.14-1.01-1.51 1.34 1.27 1.12c1.02 1.01 2.37 1.57 3.82 1.57 2.98 0 5.4-2.41 5.4-5.38s-2.42-5.37-5.4-5.37z"/></g>
+<g id="beach-access"><path d="M13.127 14.56l1.43-1.43 6.44 6.443L19.57 21zm4.293-5.73l2.86-2.86c-3.95-3.95-10.35-3.96-14.3-.02 3.93-1.3 8.31-.25 11.44 2.88zM5.95 5.98c-3.94 3.95-3.93 10.35.02 14.3l2.86-2.86C5.7 14.29 4.65 9.91 5.95 5.98zm.02-.02l-.01.01c-.38 3.01 1.17 6.88 4.3 10.02l5.73-5.73c-3.13-3.13-7.01-4.68-10.02-4.3z"/></g>
+<g id="business-center"><path d="M10 16v-1H3.01L3 19c0 1.11.89 2 2 2h14c1.11 0 2-.89 2-2v-4h-7v1h-4zm10-9h-4.01V5l-2-2h-4l-2 2v2H4c-1.1 0-2 .9-2 2v3c0 1.11.89 2 2 2h6v-2h4v2h6c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm-6 0h-4V5h4v2z"/></g>
+<g id="casino"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM7.5 18c-.83 0-1.5-.67-1.5-1.5S6.67 15 7.5 15s1.5.67 1.5 1.5S8.33 18 7.5 18zm0-9C6.67 9 6 8.33 6 7.5S6.67 6 7.5 6 9 6.67 9 7.5 8.33 9 7.5 9zm4.5 4.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5 4.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm0-9c-.83 0-1.5-.67-1.5-1.5S15.67 6 16.5 6s1.5.67 1.5 1.5S17.33 9 16.5 9z"/></g>
+<g id="child-care"><circle cx="14.5" cy="10.5" r="1.25"/><circle cx="9.5" cy="10.5" r="1.25"/><path d="M22.94 12.66c.04-.21.06-.43.06-.66s-.02-.45-.06-.66c-.25-1.51-1.36-2.74-2.81-3.17-.53-1.12-1.28-2.1-2.19-2.91C16.36 3.85 14.28 3 12 3s-4.36.85-5.94 2.26c-.92.81-1.67 1.8-2.19 2.91-1.45.43-2.56 1.65-2.81 3.17-.04.21-.06.43-.06.66s.02.45.06.66c.25 1.51 1.36 2.74 2.81 3.17.52 1.11 1.27 2.09 2.17 2.89C7.62 20.14 9.71 21 12 21s4.38-.86 5.97-2.28c.9-.8 1.65-1.79 2.17-2.89 1.44-.43 2.55-1.65 2.8-3.17zM19 14c-.1 0-.19-.02-.29-.03-.2.67-.49 1.29-.86 1.86C16.6 17.74 14.45 19 12 19s-4.6-1.26-5.85-3.17c-.37-.57-.66-1.19-.86-1.86-.1.01-.19.03-.29.03-1.1 0-2-.9-2-2s.9-2 2-2c.1 0 .19.02.29.03.2-.67.49-1.29.86-1.86C7.4 6.26 9.55 5 12 5s4.6 1.26 5.85 3.17c.37.57.66 1.19.86 1.86.1-.01.19-.03.29-.03 1.1 0 2 .9 2 2s-.9 2-2 2zM7.5 14c.76 1.77 2.49 3 4.5 3s3.74-1.23 4.5-3h-9z"/></g>
+<g id="child-friendly"><path d="M13 2v8h8c0-4.42-3.58-8-8-8zm6.32 13.89C20.37 14.54 21 12.84 21 11H6.44l-.95-2H2v2h2.22s1.89 4.07 2.12 4.42c-1.1.59-1.84 1.75-1.84 3.08C4.5 20.43 6.07 22 8 22c1.76 0 3.22-1.3 3.46-3h2.08c.24 1.7 1.7 3 3.46 3 1.93 0 3.5-1.57 3.5-3.5 0-1.04-.46-1.97-1.18-2.61zM8 20c-.83 0-1.5-.67-1.5-1.5S7.17 17 8 17s1.5.67 1.5 1.5S8.83 20 8 20zm9 0c-.83 0-1.5-.67-1.5-1.5S16.17 17 17 17s1.5.67 1.5 1.5S17.83 20 17 20z"/></g>
+<g id="fitness-center"><path d="M20.57 14.86L22 13.43 20.57 12 17 15.57 8.43 7 12 3.43 10.57 2 9.14 3.43 7.71 2 5.57 4.14 4.14 2.71 2.71 4.14l1.43 1.43L2 7.71l1.43 1.43L2 10.57 3.43 12 7 8.43 15.57 17 12 20.57 13.43 22l1.43-1.43L16.29 22l2.14-2.14 1.43 1.43 1.43-1.43-1.43-1.43L22 16.29z"/></g>
+<g id="free-breakfast"><path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/></g>
+<g id="golf-course"><circle cx="19.5" cy="19.5" r="1.5"/><path d="M17 5.92L9 2v18H7v-1.73c-1.79.35-3 .99-3 1.73 0 1.1 2.69 2 6 2s6-.9 6-2c0-.99-2.16-1.81-5-1.97V8.98l6-3.06z"/></g>
+<g id="hot-tub"><circle cx="7" cy="6" r="2"/><path d="M11.15 12c-.31-.22-.59-.46-.82-.72l-1.4-1.55c-.19-.21-.43-.38-.69-.5-.29-.14-.62-.23-.96-.23h-.03C6.01 9 5 10.01 5 11.25V12H2v8c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-8H11.15zM7 20H5v-6h2v6zm4 0H9v-6h2v6zm4 0h-2v-6h2v6zm4 0h-2v-6h2v6zm-.35-14.14l-.07-.07c-.57-.62-.82-1.41-.67-2.2L18 3h-1.89l-.06.43c-.2 1.36.27 2.71 1.3 3.72l.07.06c.57.62.82 1.41.67 2.2l-.11.59h1.91l.06-.43c.21-1.36-.27-2.71-1.3-3.71zm-4 0l-.07-.07c-.57-.62-.82-1.41-.67-2.2L14 3h-1.89l-.06.43c-.2 1.36.27 2.71 1.3 3.72l.07.06c.57.62.82 1.41.67 2.2l-.11.59h1.91l.06-.43c.21-1.36-.27-2.71-1.3-3.71z"/></g>
+<g id="kitchen"><path d="M18 2.01L6 2c-1.1 0-2 .89-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.11-.9-1.99-2-1.99zM18 20H6v-9.02h12V20zm0-11H6V4h12v5zM8 5h2v3H8zm0 7h2v5H8z"/></g>
+<g id="pool"><path d="M22 21c-1.11 0-1.73-.37-2.18-.64-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.46.27-1.07.64-2.18.64s-1.73-.37-2.18-.64c-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.46.27-1.08.64-2.19.64-1.11 0-1.73-.37-2.18-.64-.37-.23-.6-.36-1.15-.36s-.78.13-1.15.36c-.46.27-1.08.64-2.19.64v-2c.56 0 .78-.13 1.15-.36.46-.27 1.08-.64 2.19-.64s1.73.37 2.18.64c.37.23.59.36 1.15.36.56 0 .78-.13 1.15-.36.46-.27 1.08-.64 2.19-.64 1.11 0 1.73.37 2.18.64.37.22.6.36 1.15.36s.78-.13 1.15-.36c.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.23.59.36 1.15.36v2zm0-4.5c-1.11 0-1.73-.37-2.18-.64-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.45.27-1.07.64-2.18.64s-1.73-.37-2.18-.64c-.37-.22-.6-.36-1.15-.36-.56 0-.78.13-1.15.36-.45.27-1.07.64-2.18.64s-1.73-.37-2.18-.64c-.37-.22-.6-.36-1.15-.36s-.78.13-1.15.36c-.47.27-1.09.64-2.2.64v-2c.56 0 .78-.13 1.15-.36.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.22.6.36 1.15.36.56 0 .78-.13 1.15-.36.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.22.6.36 1.15.36s.78-.13 1.15-.36c.45-.27 1.07-.64 2.18-.64s1.73.37 2.18.64c.37.22.6.36 1.15.36v2zM8.67 12c.56 0 .78-.13 1.15-.36.46-.27 1.08-.64 2.19-.64 1.11 0 1.73.37 2.18.64.37.22.6.36 1.15.36s.78-.13 1.15-.36c.12-.07.26-.15.41-.23L10.48 5C8.93 3.45 7.5 2.99 5 3v2.5c1.82-.01 2.89.39 4 1.5l1 1-3.25 3.25c.31.12.56.27.77.39.37.23.59.36 1.15.36z"/><circle cx="16.5" cy="5.5" r="2.5"/></g>
+<g id="room-service"><path d="M2 17h20v2H2zm11.84-9.21c.1-.24.16-.51.16-.79 0-1.1-.9-2-2-2s-2 .9-2 2c0 .28.06.55.16.79C6.25 8.6 3.27 11.93 3 16h18c-.27-4.07-3.25-7.4-7.16-8.21z"/></g>
+<g id="rv-hookup"><path d="M20 17v-6c0-1.1-.9-2-2-2H7V7l-3 3 3 3v-2h4v3H4v3c0 1.1.9 2 2 2h2c0 1.66 1.34 3 3 3s3-1.34 3-3h8v-2h-2zm-9 3c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm7-6h-4v-3h4v3zM17 2v2H9v2h8v2l3-3z"/></g>
+<g id="smoke-free"><path d="M2 6l6.99 7H2v3h9.99l7 7 1.26-1.25-17-17zm18.5 7H22v3h-1.5zM18 13h1.5v3H18zm.85-8.12c.62-.61 1-1.45 1-2.38h-1.5c0 1.02-.83 1.85-1.85 1.85v1.5c2.24 0 4 1.83 4 4.07V12H22V9.92c0-2.23-1.28-4.15-3.15-5.04zM14.5 8.7h1.53c1.05 0 1.97.74 1.97 2.05V12h1.5v-1.59c0-1.8-1.6-3.16-3.47-3.16H14.5c-1.02 0-1.85-.98-1.85-2s.83-1.75 1.85-1.75V2c-1.85 0-3.35 1.5-3.35 3.35s1.5 3.35 3.35 3.35zm2.5 7.23V13h-2.93z"/></g>
+<g id="smoking-rooms"><path d="M2 16h15v3H2zm18.5 0H22v3h-1.5zM18 16h1.5v3H18zm.85-8.27c.62-.61 1-1.45 1-2.38C19.85 3.5 18.35 2 16.5 2v1.5c1.02 0 1.85.83 1.85 1.85S17.52 7.2 16.5 7.2v1.5c2.24 0 4 1.83 4 4.07V15H22v-2.24c0-2.22-1.28-4.14-3.15-5.03zm-2.82 2.47H14.5c-1.02 0-1.85-.98-1.85-2s.83-1.75 1.85-1.75v-1.5c-1.85 0-3.35 1.5-3.35 3.35s1.5 3.35 3.35 3.35h1.53c1.05 0 1.97.74 1.97 2.05V15h1.5v-1.64c0-1.81-1.6-3.16-3.47-3.16z"/></g>
+<g id="spa"><path d="M8.55 12c-1.07-.71-2.25-1.27-3.53-1.61 1.28.34 2.46.9 3.53 1.61zm10.43-1.61c-1.29.34-2.49.91-3.57 1.64 1.08-.73 2.28-1.3 3.57-1.64z"/><path d="M15.49 9.63c-.18-2.79-1.31-5.51-3.43-7.63-2.14 2.14-3.32 4.86-3.55 7.63 1.28.68 2.46 1.56 3.49 2.63 1.03-1.06 2.21-1.94 3.49-2.63zm-6.5 2.65c-.14-.1-.3-.19-.45-.29.15.11.31.19.45.29zm6.42-.25c-.13.09-.27.16-.4.26.13-.1.27-.17.4-.26zM12 15.45C9.85 12.17 6.18 10 2 10c0 5.32 3.36 9.82 8.03 11.49.63.23 1.29.4 1.97.51.68-.12 1.33-.29 1.97-.51C18.64 19.82 22 15.32 22 10c-4.18 0-7.85 2.17-10 5.45z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/social-icons.html b/catapult/third_party/polymer/components/iron-icons/social-icons.html
new file mode 100644
index 00000000..a0424735
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/social-icons.html
@@ -0,0 +1,46 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+<iron-iconset-svg name="social" size="24">
+<svg><defs>
+<g id="cake"><path d="M12 6c1.11 0 2-.9 2-2 0-.38-.1-.73-.29-1.03L12 0l-1.71 2.97c-.19.3-.29.65-.29 1.03 0 1.1.9 2 2 2zm4.6 9.99l-1.07-1.07-1.08 1.07c-1.3 1.3-3.58 1.31-4.89 0l-1.07-1.07-1.09 1.07C6.75 16.64 5.88 17 4.96 17c-.73 0-1.4-.23-1.96-.61V21c0 .55.45 1 1 1h16c.55 0 1-.45 1-1v-4.61c-.56.38-1.23.61-1.96.61-.92 0-1.79-.36-2.44-1.01zM18 9h-5V7h-2v2H6c-1.66 0-3 1.34-3 3v1.54c0 1.08.88 1.96 1.96 1.96.52 0 1.02-.2 1.38-.57l2.14-2.13 2.13 2.13c.74.74 2.03.74 2.77 0l2.14-2.13 2.13 2.13c.37.37.86.57 1.38.57 1.08 0 1.96-.88 1.96-1.96V12C21 10.34 19.66 9 18 9z"/></g>
+<g id="domain"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></g>
+<g id="group"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></g>
+<g id="group-add"><path d="M8 10H5V7H3v3H0v2h3v3h2v-3h3v-2zm10 1c1.66 0 2.99-1.34 2.99-3S19.66 5 18 5c-.32 0-.63.05-.91.14.57.81.9 1.79.9 2.86s-.34 2.04-.9 2.86c.28.09.59.14.91.14zm-5 0c1.66 0 2.99-1.34 2.99-3S14.66 5 13 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm6.62 2.16c.83.73 1.38 1.66 1.38 2.84v2h3v-2c0-1.54-2.37-2.49-4.38-2.84zM13 13c-2 0-6 1-6 3v2h12v-2c0-2-4-3-6-3z"/></g>
+<g id="location-city"><path d="M15 11V5l-3-3-3 3v2H3v14h18V11h-6zm-8 8H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm6 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm6 12h-2v-2h2v2zm0-4h-2v-2h2v2z"/></g>
+<g id="mood"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></g>
+<g id="mood-bad"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 3c-2.33 0-4.31 1.46-5.11 3.5h10.22c-.8-2.04-2.78-3.5-5.11-3.5z"/></g>
+<g id="notifications"><path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/></g>
+<g id="notifications-active"><path d="M7.58 4.08L6.15 2.65C3.75 4.48 2.17 7.3 2.03 10.5h2c.15-2.65 1.51-4.97 3.55-6.42zm12.39 6.42h2c-.15-3.2-1.73-6.02-4.12-7.85l-1.42 1.43c2.02 1.45 3.39 3.77 3.54 6.42zM18 11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2v-5zm-6 11c.14 0 .27-.01.4-.04.65-.14 1.18-.58 1.44-1.18.1-.24.15-.5.15-.78h-4c.01 1.1.9 2 2.01 2z"/></g>
+<g id="notifications-none"><path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2zm-2 1H8v-6c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6z"/></g>
+<g id="notifications-off"><path d="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z"/></g>
+<g id="notifications-paused"><path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.93 6 11v5l-2 2v1h16v-1l-2-2zm-3.5-6.2l-2.8 3.4h2.8V15h-5v-1.8l2.8-3.4H9.5V8h5v1.8z"/></g>
+<g id="pages"><path d="M3 5v6h5L7 7l4 1V3H5c-1.1 0-2 .9-2 2zm5 8H3v6c0 1.1.9 2 2 2h6v-5l-4 1 1-4zm9 4l-4-1v5h6c1.1 0 2-.9 2-2v-6h-5l1 4zm2-14h-6v5l4-1-1 4h5V5c0-1.1-.9-2-2-2z"/></g>
+<g id="party-mode"><path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 3c1.63 0 3.06.79 3.98 2H12c-1.66 0-3 1.34-3 3 0 .35.07.69.18 1H7.1c-.06-.32-.1-.66-.1-1 0-2.76 2.24-5 5-5zm0 10c-1.63 0-3.06-.79-3.98-2H12c1.66 0 3-1.34 3-3 0-.35-.07-.69-.18-1h2.08c.07.32.1.66.1 1 0 2.76-2.24 5-5 5z"/></g>
+<g id="people"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></g>
+<g id="people-outline"><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"/></g>
+<g id="person"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></g>
+<g id="person-add"><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></g>
+<g id="person-outline"><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></g>
+<g id="plus-one"><path d="M10 8H8v4H4v2h4v4h2v-4h4v-2h-4zm4.5-1.92V7.9l2.5-.5V18h2V5z"/></g>
+<g id="poll"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></g>
+<g id="public"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></g>
+<g id="school"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></g>
+<g id="sentiment-dissatisfied"><circle cx="15.5" cy="9.5" r="1.5"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-6c-2.33 0-4.32 1.45-5.12 3.5h1.67c.69-1.19 1.97-2 3.45-2s2.75.81 3.45 2h1.67c-.8-2.05-2.79-3.5-5.12-3.5z"/></g>
+<g id="sentiment-neutral"><path d="M9 14h6v1.5H9z"/><circle cx="15.5" cy="9.5" r="1.5"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></g>
+<g id="sentiment-satisfied"><circle cx="15.5" cy="9.5" r="1.5"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-4c-1.48 0-2.75-.81-3.45-2H6.88c.8 2.05 2.79 3.5 5.12 3.5s4.32-1.45 5.12-3.5h-1.67c-.7 1.19-1.97 2-3.45 2z"/></g>
+<g id="sentiment-very-dissatisfied"><path d="M11.99 2C6.47 2 2 6.47 2 12s4.47 10 9.99 10S22 17.53 22 12 17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm4.18-12.24l-1.06 1.06-1.06-1.06L13 8.82l1.06 1.06L13 10.94 14.06 12l1.06-1.06L16.18 12l1.06-1.06-1.06-1.06 1.06-1.06zM7.82 12l1.06-1.06L9.94 12 11 10.94 9.94 9.88 11 8.82 9.94 7.76 8.88 8.82 7.82 7.76 6.76 8.82l1.06 1.06-1.06 1.06zM12 14c-2.33 0-4.31 1.46-5.11 3.5h10.22c-.8-2.04-2.78-3.5-5.11-3.5z"/></g>
+<g id="sentiment-very-satisfied"><path d="M11.99 2C6.47 2 2 6.47 2 12s4.47 10 9.99 10S22 17.53 22 12 17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm1-10.06L14.06 11l1.06-1.06L16.18 11l1.06-1.06-2.12-2.12zm-4.12 0L9.94 11 11 9.94 8.88 7.82 6.76 9.94 7.82 11zM12 17.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></g>
+<g id="share"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></g>
+<g id="whatshot"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-icons/test/index.html b/catapult/third_party/polymer/components/iron-icons/test/index.html
new file mode 100644
index 00000000..d1a1d048
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/test/index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html><!--
+@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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+</head>
+<body>
+
+ <script>
+ WCT.loadSuites([
+ 'iron-icons.html',
+ 'iron-icons.html?dom=shadow'
+ ]);
+ </script>
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-icons/test/iron-icons.html b/catapult/third_party/polymer/components/iron-icons/test/iron-icons.html
new file mode 100644
index 00000000..41ef21a2
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-icons/test/iron-icons.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<!--
+@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
+-->
+
+<html>
+<head>
+
+ <title>iron-icons</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../av-icons.html">
+ <link rel="import" href="../communication-icons.html">
+ <link rel="import" href="../device-icons.html">
+ <link rel="import" href="../editor-icons.html">
+ <link rel="import" href="../hardware-icons.html">
+ <link rel="import" href="../image-icons.html">
+ <link rel="import" href="../iron-icons.html">
+ <link rel="import" href="../maps-icons.html">
+ <link rel="import" href="../notification-icons.html">
+ <link rel="import" href="../places-icons.html">
+ <link rel="import" href="../social-icons.html">
+
+</head>
+<body>
+
+ <script>
+
+suite('<iron-icons>', function() {
+ suite('basic behavior', function() {
+ var meta;
+ var iconsetNames = [
+ 'av', 'communication', 'device', 'editor', 'hardware', 'icons', 'image',
+ 'maps', 'notification', 'places', 'social'
+ ];
+
+ setup(function() {
+ meta = Polymer.Base.create('iron-meta', {type: 'iconset'});
+ });
+
+ test('all uniquely named', function() {
+ var allIcons = {};
+ for (var i = 0; i < iconsetNames.length; i++) {
+ var iconset = meta.byKey(iconsetNames[i]);
+ expect(iconset).to.be.ok;
+
+ var iconNames = iconset.getIconNames();
+ expect(iconNames).to.not.be.empty;
+
+ for (var j = 0; j < iconNames.length; j++) {
+ var parts = iconNames[j].split(':');
+ expect(parts).to.have.length(2);
+ expect(parts[0]).to.equal(iconsetNames[i]);
+ expect(parts[1]).to.have.length.at.least(1);
+ expect(allIcons.hasOwnProperty(parts[1])).to.be.false;
+
+ allIcons[parts[1]] = true;
+ }
+ }
+
+ // Sanity check.
+ expect(Object.getOwnPropertyNames(allIcons))
+ .to.have.length.of.at.least(iconsetNames.length);
+ });
+ });
+});
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/.bower.json b/catapult/third_party/polymer/components/iron-iconset-svg/.bower.json
new file mode 100644
index 00000000..9c1f255d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/.bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "iron-iconset-svg",
+ "description": "Manages a set of svg icons",
+ "version": "1.1.2",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "icon"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-iconset-svg.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0",
+ "iron-meta": "PolymerElements/iron-meta#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "main": "iron-iconset-svg.html",
+ "ignore": [],
+ "homepage": "https://github.com/polymerelements/iron-iconset-svg",
+ "_release": "1.1.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.2",
+ "commit": "9647bdd1d15ad63b6cbe44cd128e43192bfe94c8"
+ },
+ "_source": "https://github.com/polymerelements/iron-iconset-svg.git",
+ "_target": "^1.0.0",
+ "_originalSource": "polymerelements/iron-iconset-svg"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-iconset-svg/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..9f7691f5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-iconset-svg/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/.gitignore b/catapult/third_party/polymer/components/iron-iconset-svg/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/.travis.yml b/catapult/third_party/polymer/components/iron-iconset-svg/.travis.yml
new file mode 100644
index 00000000..8fd260dd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ FVt0rYGSWqJW3i57y9CQHuDaa97UDwZuGc5ts+TDpbuR3DVP8LxB9MT1ApA2bqhg101hPzhWJWIK3Siotkb7eAlsiWgVhdNr8t5eZBPOOnjLiU6PNCF6ZGCZRJHQ6q4xQ2DycGug8OfwMw63yewLEYmVBppeAlStnPUfDWURlJ8=
+ - secure: >-
+ X7cWxU13zLWoahM/BNBPSvgnI396zMmLzVHDHX6zAQL7gFL+PS4Dz6WjooO3Jx79ks2E8REUzWB8IFY3FohVAncXA///PAaqNLX0k2et4aOTOs5tcsCWL4nj8tKA6vjZIZ61rCjWTyNRR+o2QPsY9QQgi3Y+6bzLguWoPuycRbE=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-iconset-svg/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/README.md b/catapult/third_party/polymer/components/iron-iconset-svg/README.md
new file mode 100644
index 00000000..971ab0c7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/README.md
@@ -0,0 +1,56 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-iconset-svg.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-iconset-svg.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-iconset-svg)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-iconset-svg)_
+
+
+##&lt;iron-iconset-svg&gt;
+
+The `iron-iconset-svg` element allows users to define their own icon sets
+that contain svg icons. The svg icon elements should be children of the
+`iron-iconset-svg` element. Multiple icons should be given distinct id's.
+
+Using svg elements to create icons has a few advantages over traditional
+bitmap graphics like jpg or png. Icons that use svg are vector based so
+they are resolution independent and should look good on any device. They
+are stylable via css. Icons can be themed, colorized, and even animated.
+
+Example:
+
+```html
+<iron-iconset-svg name="my-svg-icons" size="24">
+ <svg>
+ <defs>
+ <g id="shape">
+ <rect x="12" y="0" width="12" height="24" />
+ <circle cx="12" cy="12" r="12" />
+ </g>
+ </defs>
+ </svg>
+</iron-iconset-svg>
+```
+
+This will automatically register the icon set "my-svg-icons" to the iconset
+database. To use these icons from within another element, make a
+`iron-iconset` element and call the `byId` method
+to retrieve a given iconset. To apply a particular icon inside an
+element use the `applyIcon` method. For example:
+
+```javascript
+iconset.applyIcon(iconNode, 'car');
+```
+
+
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/bower.json b/catapult/third_party/polymer/components/iron-iconset-svg/bower.json
new file mode 100644
index 00000000..6f4a1ffb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/bower.json
@@ -0,0 +1,34 @@
+{
+ "name": "iron-iconset-svg",
+ "description": "Manages a set of svg icons",
+ "version": "1.1.2",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "icon"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-iconset-svg.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0",
+ "iron-meta": "PolymerElements/iron-meta#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "main": "iron-iconset-svg.html",
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/demo/index.html b/catapult/third_party/polymer/components/iron-iconset-svg/demo/index.html
new file mode 100644
index 00000000..53b291aa
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/demo/index.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-iconset-svg</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link href="../../paper-styles/demo-pages.html" rel="import">
+
+ <link rel="import" href="svg-sample-icons.html">
+ <style is="custom-style">
+
+ .centered {
+ text-align: center;
+ }
+
+ iron-icon {
+ height: 64px;
+ width: 64px;
+ margin: auto 1em;
+ }
+
+ iron-icon:nth-of-type(1) {
+ fill: orange;
+ }
+
+ iron-icon:nth-of-type(2) {
+ fill: green;
+ }
+
+ iron-icon:nth-of-type(3) {
+ fill: navy;
+ }
+
+ iron-icon:nth-of-type(4) {
+ fill: red;
+ }
+
+ iron-icon {
+ transition: all 0.5s;
+ -webkit-transition: all 0.5s;
+ }
+
+ iron-icon:hover {
+ -webkit-filter: drop-shadow( 2px 2px 2px var(--google-grey-700) );
+ filter: drop-shadow( 2px 2px 2px var(--google-grey-700) );
+ }
+ </style>
+</head>
+<body>
+
+ <div class="vertical-section vertical-section-container centered">
+ <iron-icon icon="svg-sample-icons:codepen"></iron-icon>
+ <iron-icon icon="svg-sample-icons:twitter"></iron-icon>
+ <iron-icon icon="svg-sample-icons:youtube"></iron-icon>
+ <iron-icon icon="inline:shape"></iron-icon>
+ </div>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/demo/svg-sample-icons.html b/catapult/third_party/polymer/components/iron-iconset-svg/demo/svg-sample-icons.html
new file mode 100644
index 00000000..3eed843f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/demo/svg-sample-icons.html
@@ -0,0 +1,81 @@
+
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-iconset-svg.html">
+
+<iron-iconset-svg name="svg-sample-icons" size="100">
+ <svg>
+ <defs>
+ <g id="codepen">
+ <path class="outer-ring" d="M50,0C22.385,0,0,22.385,0,50c0,27.615,22.385,50,50,50c27.614,0,50-22.385,50-50C100,22.385,77.615,0,50,0z M50,91.789
+ C26.958,91.789,8.212,73.042,8.212,50C8.212,26.958,26.958,8.212,50,8.212c23.042,0,41.788,18.747,41.788,41.789
+ C91.788,73.042,73.042,91.789,50,91.789z"></path>
+ <path class="inner-logo" d="M80.893,40.234c-0.006-0.039-0.016-0.076-0.022-0.115c-0.013-0.075-0.027-0.15-0.046-0.223
+ c-0.012-0.044-0.028-0.086-0.042-0.128c-0.021-0.065-0.042-0.13-0.068-0.193c-0.018-0.044-0.039-0.088-0.059-0.13
+ c-0.028-0.06-0.057-0.119-0.09-0.175c-0.024-0.042-0.051-0.083-0.076-0.124c-0.036-0.055-0.073-0.109-0.112-0.161
+ c-0.029-0.039-0.06-0.078-0.091-0.115c-0.042-0.049-0.086-0.098-0.132-0.143c-0.035-0.036-0.069-0.072-0.106-0.104
+ c-0.049-0.044-0.099-0.086-0.15-0.127c-0.04-0.031-0.079-0.062-0.12-0.091c-0.016-0.01-0.029-0.023-0.044-0.033L51.474,19.531
+ c-0.893-0.595-2.055-0.595-2.947,0L20.267,38.371c-0.015,0.01-0.028,0.023-0.044,0.033c-0.042,0.029-0.081,0.06-0.12,0.091
+ c-0.052,0.041-0.102,0.083-0.15,0.127c-0.037,0.032-0.071,0.068-0.106,0.104c-0.046,0.045-0.09,0.094-0.132,0.143
+ c-0.031,0.038-0.062,0.077-0.092,0.115c-0.039,0.052-0.076,0.106-0.111,0.161c-0.027,0.041-0.052,0.082-0.076,0.124
+ c-0.033,0.057-0.062,0.115-0.09,0.175c-0.021,0.042-0.042,0.086-0.06,0.13c-0.026,0.063-0.047,0.128-0.068,0.193
+ c-0.014,0.042-0.029,0.084-0.042,0.128c-0.02,0.073-0.032,0.148-0.046,0.223c-0.006,0.039-0.016,0.076-0.021,0.115
+ c-0.016,0.114-0.024,0.229-0.024,0.346V59.42c0,0.117,0.009,0.233,0.024,0.348c0.005,0.038,0.015,0.077,0.021,0.114
+ c0.014,0.075,0.027,0.149,0.046,0.223c0.012,0.043,0.028,0.086,0.042,0.128c0.021,0.065,0.042,0.13,0.068,0.195
+ c0.018,0.044,0.039,0.086,0.06,0.129c0.028,0.06,0.058,0.118,0.09,0.177c0.024,0.041,0.049,0.082,0.076,0.122
+ c0.035,0.056,0.072,0.109,0.111,0.161c0.029,0.041,0.061,0.078,0.092,0.115c0.042,0.049,0.086,0.098,0.132,0.144
+ c0.035,0.036,0.069,0.071,0.106,0.104c0.048,0.044,0.099,0.086,0.15,0.127c0.039,0.031,0.078,0.062,0.12,0.091
+ c0.016,0.01,0.029,0.023,0.044,0.032l28.259,18.84c0.446,0.297,0.96,0.447,1.474,0.447c0.513,0,1.027-0.149,1.473-0.447
+ l28.259-18.84c0.015-0.009,0.028-0.022,0.044-0.032c0.042-0.029,0.081-0.06,0.12-0.091c0.051-0.041,0.102-0.083,0.15-0.127
+ c0.037-0.033,0.071-0.068,0.106-0.104c0.046-0.046,0.09-0.095,0.132-0.144c0.031-0.037,0.062-0.075,0.091-0.115
+ c0.04-0.052,0.076-0.105,0.112-0.161c0.025-0.041,0.051-0.081,0.076-0.122c0.033-0.059,0.062-0.117,0.09-0.177
+ c0.02-0.042,0.041-0.085,0.059-0.129c0.026-0.065,0.047-0.13,0.068-0.195c0.014-0.042,0.03-0.085,0.042-0.128
+ c0.02-0.074,0.033-0.148,0.046-0.223c0.006-0.037,0.016-0.076,0.022-0.114c0.014-0.115,0.023-0.231,0.023-0.348V40.581
+ C80.916,40.464,80.907,40.348,80.893,40.234z M52.657,26.707l20.817,13.877l-9.298,6.221l-11.519-7.706V26.707z M47.343,26.707
+ v12.393l-11.518,7.706l-9.299-6.221L47.343,26.707z M24.398,45.554L31.046,50l-6.648,4.446V45.554z M47.343,73.294L26.525,59.417
+ l9.299-6.219l11.518,7.704V73.294z M50,56.286L40.603,50L50,43.715L59.397,50L50,56.286z M52.657,73.294V60.902l11.519-7.704
+ l9.298,6.219L52.657,73.294z M75.602,54.447L68.955,50l6.647-4.446V54.447z"></path>
+ </g>
+
+ <path id="twitter" d="M100.001,17.942c-3.681,1.688-7.633,2.826-11.783,3.339
+ c4.236-2.624,7.49-6.779,9.021-11.73c-3.965,2.432-8.354,4.193-13.026,5.146C80.47,10.575,75.138,8,69.234,8
+ c-11.33,0-20.518,9.494-20.518,21.205c0,1.662,0.183,3.281,0.533,4.833c-17.052-0.884-32.168-9.326-42.288-22.155
+ c-1.767,3.133-2.778,6.773-2.778,10.659c0,7.357,3.622,13.849,9.127,17.65c-3.363-0.109-6.525-1.064-9.293-2.651
+ c-0.002,0.089-0.002,0.178-0.002,0.268c0,10.272,7.072,18.845,16.458,20.793c-1.721,0.484-3.534,0.744-5.405,0.744
+ c-1.322,0-2.606-0.134-3.859-0.379c2.609,8.424,10.187,14.555,19.166,14.726c-7.021,5.688-15.867,9.077-25.48,9.077
+ c-1.656,0-3.289-0.102-4.895-0.297C9.08,88.491,19.865,92,31.449,92c37.737,0,58.374-32.312,58.374-60.336
+ c0-0.92-0.02-1.834-0.059-2.743C93.771,25.929,97.251,22.195,100.001,17.942L100.001,17.942z"></path>
+
+ <g id="youtube">
+ <path class="youtube" d="M98.77,27.492c-1.225-5.064-5.576-8.799-10.811-9.354C75.561,16.818,63.01,15.993,50.514,16
+ c-12.495-0.007-25.045,0.816-37.446,2.139c-5.235,0.557-9.583,4.289-10.806,9.354C0.522,34.704,0.5,42.574,0.5,50.001
+ c0,7.426,0,15.296,1.741,22.509c1.224,5.061,5.572,8.799,10.807,9.352c12.399,1.32,24.949,2.145,37.446,2.14
+ c12.494,0.005,25.047-0.817,37.443-2.14c5.234-0.555,9.586-4.291,10.81-9.352c1.741-7.213,1.753-15.083,1.753-22.509
+ S100.51,34.704,98.77,27.492 M67.549,52.203L43.977,64.391c-2.344,1.213-4.262,0.119-4.262-2.428V38.036
+ c0-2.548,1.917-3.644,4.262-2.429l23.572,12.188C69.896,49.008,69.896,50.992,67.549,52.203"></path>
+ </g>
+
+ </defs>
+
+ </svg>
+</iron-iconset-svg>
+
+<iron-iconset-svg name="inline" size="24">
+ <svg>
+ <defs>
+ <g id="shape">
+ <rect x="12" y="0" width="12" height="24" />
+ <circle cx="12" cy="12" r="12" />
+ </g>
+ </defs>
+ </svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/index.html b/catapult/third_party/polymer/components/iron-iconset-svg/index.html
new file mode 100644
index 00000000..487bb5c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/iron-iconset-svg.html b/catapult/third_party/polymer/components/iron-iconset-svg/iron-iconset-svg.html
new file mode 100644
index 00000000..ab07e88c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/iron-iconset-svg.html
@@ -0,0 +1,242 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-meta/iron-meta.html">
+
+<script>
+ /**
+ * The `iron-iconset-svg` element allows users to define their own icon sets
+ * that contain svg icons. The svg icon elements should be children of the
+ * `iron-iconset-svg` element. Multiple icons should be given distinct id's.
+ *
+ * Using svg elements to create icons has a few advantages over traditional
+ * bitmap graphics like jpg or png. Icons that use svg are vector based so
+ * they are resolution independent and should look good on any device. They
+ * are stylable via css. Icons can be themed, colorized, and even animated.
+ *
+ * Example:
+ *
+ * <iron-iconset-svg name="my-svg-icons" size="24">
+ * <svg>
+ * <defs>
+ * <g id="shape">
+ * <rect x="12" y="0" width="12" height="24" />
+ * <circle cx="12" cy="12" r="12" />
+ * </g>
+ * </defs>
+ * </svg>
+ * </iron-iconset-svg>
+ *
+ * This will automatically register the icon set "my-svg-icons" to the iconset
+ * database. To use these icons from within another element, make a
+ * `iron-iconset` element and call the `byId` method
+ * to retrieve a given iconset. To apply a particular icon inside an
+ * element use the `applyIcon` method. For example:
+ *
+ * iconset.applyIcon(iconNode, 'car');
+ *
+ * @element iron-iconset-svg
+ * @demo demo/index.html
+ * @implements {Polymer.Iconset}
+ */
+ Polymer({
+ is: 'iron-iconset-svg',
+
+ properties: {
+
+ /**
+ * The name of the iconset.
+ */
+ name: {
+ type: String,
+ observer: '_nameChanged'
+ },
+
+ /**
+ * The size of an individual icon. Note that icons must be square.
+ */
+ size: {
+ type: Number,
+ value: 24
+ },
+
+ /**
+ * Set to true to enable mirroring of icons where specified when they are
+ * stamped. Icons that should be mirrored should be decorated with a
+ * `mirror-in-rtl` attribute.
+ *
+ * NOTE: For performance reasons, direction will be resolved once per
+ * document per iconset, so moving icons in and out of RTL subtrees will
+ * not cause their mirrored state to change.
+ */
+ rtlMirroring: {
+ type: Boolean,
+ value: false
+ }
+ },
+
+ attached: function() {
+ this.style.display = 'none';
+ },
+
+ /**
+ * Construct an array of all icon names in this iconset.
+ *
+ * @return {!Array} Array of icon names.
+ */
+ getIconNames: function() {
+ this._icons = this._createIconMap();
+ return Object.keys(this._icons).map(function(n) {
+ return this.name + ':' + n;
+ }, this);
+ },
+
+ /**
+ * Applies an icon to the given element.
+ *
+ * An svg icon is prepended to the element's shadowRoot if it exists,
+ * otherwise to the element itself.
+ *
+ * If RTL mirroring is enabled, and the icon is marked to be mirrored in
+ * RTL, the element will be tested (once and only once ever for each
+ * iconset) to determine the direction of the subtree the element is in.
+ * This direction will apply to all future icon applications, although only
+ * icons marked to be mirrored will be affected.
+ *
+ * @method applyIcon
+ * @param {Element} element Element to which the icon is applied.
+ * @param {string} iconName Name of the icon to apply.
+ * @return {?Element} The svg element which renders the icon.
+ */
+ applyIcon: function(element, iconName) {
+ // insert svg element into shadow root, if it exists
+ element = element.root || element;
+ // Remove old svg element
+ this.removeIcon(element);
+ // install new svg element
+ var svg = this._cloneIcon(iconName,
+ this.rtlMirroring && this._targetIsRTL(element));
+ if (svg) {
+ var pde = Polymer.dom(element);
+ pde.insertBefore(svg, pde.childNodes[0]);
+ return element._svgIcon = svg;
+ }
+ return null;
+ },
+
+ /**
+ * Remove an icon from the given element by undoing the changes effected
+ * by `applyIcon`.
+ *
+ * @param {Element} element The element from which the icon is removed.
+ */
+ removeIcon: function(element) {
+ // Remove old svg element
+ element = element.root || element;
+ if (element._svgIcon) {
+ Polymer.dom(element).removeChild(element._svgIcon);
+ element._svgIcon = null;
+ }
+ },
+
+ /**
+ * Measures and memoizes the direction of the element. Note that this
+ * measurement is only done once and the result is memoized for future
+ * invocations.
+ */
+ _targetIsRTL: function(target) {
+ if (this.__targetIsRTL == null) {
+ if (target && target.nodeType !== Node.ELEMENT_NODE) {
+ target = target.host;
+ }
+
+ this.__targetIsRTL = target &&
+ window.getComputedStyle(target)['direction'] === 'rtl';
+ }
+
+ return this.__targetIsRTL;
+ },
+
+ /**
+ *
+ * When name is changed, register iconset metadata
+ *
+ */
+ _nameChanged: function() {
+ new Polymer.IronMeta({type: 'iconset', key: this.name, value: this});
+ this.async(function() {
+ this.fire('iron-iconset-added', this, {node: window});
+ });
+ },
+
+ /**
+ * Create a map of child SVG elements by id.
+ *
+ * @return {!Object} Map of id's to SVG elements.
+ */
+ _createIconMap: function() {
+ // Objects chained to Object.prototype (`{}`) have members. Specifically,
+ // on FF there is a `watch` method that confuses the icon map, so we
+ // need to use a null-based object here.
+ var icons = Object.create(null);
+ Polymer.dom(this).querySelectorAll('[id]')
+ .forEach(function(icon) {
+ icons[icon.id] = icon;
+ });
+ return icons;
+ },
+
+ /**
+ * Produce installable clone of the SVG element matching `id` in this
+ * iconset, or `undefined` if there is no matching element.
+ *
+ * @return {Element} Returns an installable clone of the SVG element
+ * matching `id`.
+ */
+ _cloneIcon: function(id, mirrorAllowed) {
+ // create the icon map on-demand, since the iconset itself has no discrete
+ // signal to know when it's children are fully parsed
+ this._icons = this._icons || this._createIconMap();
+ return this._prepareSvgClone(this._icons[id], this.size, mirrorAllowed);
+ },
+
+ /**
+ * @param {Element} sourceSvg
+ * @param {number} size
+ * @param {Boolean} mirrorAllowed
+ * @return {Element}
+ */
+ _prepareSvgClone: function(sourceSvg, size, mirrorAllowed) {
+ if (sourceSvg) {
+ var content = sourceSvg.cloneNode(true),
+ svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
+ viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size,
+ cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;';
+
+ if (mirrorAllowed && content.hasAttribute('mirror-in-rtl')) {
+ cssText += '-webkit-transform:scale(-1,1);transform:scale(-1,1);';
+ }
+
+ svg.setAttribute('viewBox', viewBox);
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+ svg.setAttribute('focusable', 'false');
+ // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136
+ // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root
+ svg.style.cssText = cssText;
+ svg.appendChild(content).removeAttribute('id');
+ return svg;
+ }
+ return null;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/test/index.html b/catapult/third_party/polymer/components/iron-iconset-svg/test/index.html
new file mode 100644
index 00000000..65e4abfd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/test/index.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+</head>
+<body>
+
+ <script>
+ WCT.loadSuites([
+ 'iron-iconset-svg.html',
+ 'iron-iconset-svg.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-iconset-svg/test/iron-iconset-svg.html b/catapult/third_party/polymer/components/iron-iconset-svg/test/iron-iconset-svg.html
new file mode 100644
index 00000000..c982abcf
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-iconset-svg/test/iron-iconset-svg.html
@@ -0,0 +1,233 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-iconset-svg</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../iron-iconset-svg.html">
+ <link rel="import" href="../../iron-meta/iron-meta.html">
+ <link rel="import" href="../../iron-icon/iron-icon.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+
+</head>
+<body>
+
+ <test-fixture id="TrivialIconsetSvg">
+ <template>
+ <iron-iconset-svg name="foo"></iron-iconset-svg>
+ <iron-meta type="iconset"></iron-meta>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="MirroredIconsetSvg">
+ <template>
+ <iron-iconset-svg name="mirrored-icons" rtl-mirroring>
+ <circle id="circle" cx="20" cy="20" r="10"></circle>
+ <symbol id="rect" viewBox="0 0 50 25" mirror-in-rtl>
+ <rect x="0" y="0" width="50" height="25"></rect>
+ </symbol>
+ </iron-iconset-svg>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="RtlContainer">
+ <template>
+ <div dir="rtl"></div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="StandardIconsetSvg">
+ <template>
+ <iron-iconset-svg name="my-icons" size="20">
+ <svg>
+ <defs>
+ <circle id="circle" cx="20" cy="20" r="10"></circle>
+ <rect id="square" x="0" y="0" width="20" height="20"></rect>
+ <symbol id="rect" viewBox="0 0 50 25">
+ <rect x="0" y="0" width="50" height="25"></rect>
+ </symbol>
+ </defs>
+ </svg>
+ </iron-iconset-svg>
+ <div></div>
+ <iron-icon></iron-icon>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('<iron-iconset>', function () {
+
+ suite('basic behavior', function () {
+ var iconset;
+ var meta;
+ var loadedPromise;
+
+ setup(function () {
+ loadedPromise = new Promise(function(resolve) {
+ window.addEventListener('iron-iconset-added', function(ev) {
+ if (ev && ev.detail === iconset) {
+ resolve();
+ }
+ });
+ });
+ var elements = fixture('TrivialIconsetSvg');
+ iconset = elements[0];
+ meta = elements[1];
+ });
+
+ test('it can be accessed via iron-meta', function () {
+ expect(meta.byKey('foo')).to.be.equal(iconset);
+ });
+
+ test('it does not have a size', function () {
+ var rect = iconset.getBoundingClientRect();
+ expect(rect.width).to.be.equal(0);
+ expect(rect.height).to.be.equal(0);
+ });
+
+ test('it fires an iron-iconset-added event on the window', function() {
+ return loadedPromise;
+ });
+ });
+
+ suite('when stamping in an RTL context', function() {
+ var iconset;
+ var rtlContainer;
+
+ setup(function() {
+ iconset = fixture('MirroredIconsetSvg');
+ rtlContainer = fixture('RtlContainer');
+ });
+
+ test('icons marked as mirror-in-rtl are mirrored', function() {
+ iconset.applyIcon(rtlContainer, 'rect');
+ var svg = rtlContainer.firstElementChild;
+ var computedStyle = window.getComputedStyle(svg);
+ var transform = computedStyle.transform || computedStyle.webkitTransform;
+ expect(transform).to.be.eql('matrix(-1, 0, 0, 1, 0, 0)');
+ });
+
+ test('icons not marked as mirror-in-rtl are not mirrored', function() {
+ iconset.applyIcon(rtlContainer, 'circle');
+ var svg = rtlContainer.firstElementChild;
+ var computedStyle = window.getComputedStyle(svg);
+ var transform = computedStyle.transform || computedStyle.webkitTransform;
+ expect(transform).to.be.eql('none');
+ });
+
+ test('many mirrored icons only call getComputedStyle once', function() {
+ sinon.spy(window, 'getComputedStyle');
+
+ for (var i = 0; i < 3; ++i) {
+ iconset.applyIcon(rtlContainer, 'rect');
+ }
+
+ expect(window.getComputedStyle.callCount).to.be.eql(1);
+ window.getComputedStyle.restore();
+ });
+ });
+
+ suite('when paired with a size and SVG definition', function () {
+ var iconset;
+ var div;
+
+ setup(function () {
+ var elements = fixture('StandardIconsetSvg');
+ iconset = elements[0];
+ div = elements[1];
+ });
+
+ test('it does not have a size', function () {
+ var rect = iconset.getBoundingClientRect();
+ expect(rect.width).to.be.equal(0);
+ expect(rect.height).to.be.equal(0);
+ });
+
+ test('appends a child to the target element', function () {
+ expect(div.firstElementChild).to.not.be.ok;
+ iconset.applyIcon(div, 'circle');
+ expect(div.firstElementChild).to.be.ok;
+ });
+
+ test('can be queried for all available icons', function () {
+ expect(iconset.getIconNames()).to.deep.eql(['my-icons:circle', 'my-icons:square', 'my-icons:rect']);
+ });
+
+ test('supports any icon defined in the svg', function () {
+ var lastSvgIcon;
+
+ iconset.getIconNames().forEach(function (iconName) {
+ iconset.applyIcon(div, iconName.split(':').pop());
+ expect(div.firstElementChild).to.not.be.equal(lastSvgIcon);
+ lastSvgIcon = div.firstElementChild;
+ });
+ });
+
+ test('prefers a viewBox attribute over the iconset size', function () {
+ iconset.applyIcon(div, 'rect');
+ expect(div.firstElementChild.getAttribute('viewBox')).to.be.equal('0 0 50 25');
+ });
+
+ test('uses the iconset size when viewBox is not defined on the element', function () {
+ iconset.applyIcon(div, 'circle');
+ expect(div.firstElementChild.getAttribute('viewBox')).to.be.equal('0 0 20 20');
+ });
+ });
+
+ suite('Adding / removal from iron-icon', function () {
+ var iconset;
+ var div;
+ var ironIcon;
+
+ setup(function () {
+ var elements = fixture('StandardIconsetSvg');
+ iconset = elements[0];
+ div = elements[1];
+ ironIcon = elements[2];
+ });
+
+ test('be able to remove an iconset from a standard DOM element', function () {
+ iconset.applyIcon(div, 'circle');
+ Polymer.dom.flush();
+ expect(div.children.length).to.be.equal(1);
+ iconset.removeIcon(div);
+ Polymer.dom.flush();
+ expect(div.children.length).to.be.equal(0);
+ });
+
+ test('be able to remove an iconset from a Polymer element', function () {
+ var baseLength = Polymer.dom(ironIcon.root).children.length;
+ iconset.applyIcon(ironIcon, 'circle');
+ Polymer.dom.flush();
+ expect(Polymer.dom(ironIcon.root).children.length - baseLength).to.be.equal(1);
+ iconset.removeIcon(ironIcon);
+ Polymer.dom.flush();
+ expect(Polymer.dom(ironIcon.root).children.length - baseLength).to.be.equal(0);
+ });
+
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-image/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-image/.github/ISSUE_TEMPLATE.md
new file mode 100755
index 00000000..d7459c59
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-image/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-image/.gitignore b/catapult/third_party/polymer/components/iron-image/.gitignore
new file mode 100755
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-image/.travis.yml b/catapult/third_party/polymer/components/iron-image/.travis.yml
new file mode 100755
index 00000000..74afb743
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/.travis.yml
@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: citR35FLXNRA4C4o5Gl7gb6yvFhOq3rTd9+IQ8i9Zcf2TV2Ip2wCNANNXOvJV8szsu2fhZLttSclpW+MVePsW3ORYWOVm9neZeTczzNZkUExRKOztAyaujzzIgRd+f5ClJOsjUfGVLWKKWgKMJ8UT4pNRgqwKe4V73oW7LhYpto=
+ - secure: bWeuwHFJSBQu3v2K5I2++tCWh3K05I8NEfi6mywbrxuiOCGNvCWFLTO42+aqUp/yAnolNIKYR5NJFtty5CX2YD4oaRbryk2gzv7UtpIXRx9Jqhe7b/UBzfHIxPoT12TFS/iub+oRnZPAVPPoDrXwwoHkWltZHZwpOVTp86T6DPI=
+node_js: stable
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+ - xvfb-run wct
+ - "if [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then wct -s 'default'; fi"
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-image/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-image/CONTRIBUTING.md
new file mode 100755
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-image/README.md b/catapult/third_party/polymer/components/iron-image/README.md
new file mode 100755
index 00000000..37979626
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/README.md
@@ -0,0 +1,86 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-image.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-image.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-image)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-image)_
+
+
+##&lt;iron-image&gt;
+
+`iron-image` is an element for displaying an image that provides useful sizing and
+preloading options not found on the standard `<img>` tag.
+
+The `sizing` option allows the image to be either cropped (`cover`) or
+letterboxed (`contain`) to fill a fixed user-size placed on the element.
+
+The `preload` option prevents the browser from rendering the image until the
+image is fully loaded. In the interim, either the element's CSS `background-color`
+can be be used as the placeholder, or the `placeholder` property can be
+set to a URL (preferably a data-URI, for instant rendering) for an
+placeholder image.
+
+The `fade` option (only valid when `preload` is set) will cause the placeholder
+image/color to be faded out once the image is rendered.
+
+Examples:
+
+ Basically identical to `<img src="...">` tag:
+
+```html
+<iron-image src="http://lorempixel.com/400/400"></iron-image>
+```
+
+ Will letterbox the image to fit:
+
+```html
+<iron-image style="width:400px; height:400px;" sizing="contain"
+ src="http://lorempixel.com/600/400"></iron-image>
+```
+
+ Will crop the image to fit:
+
+```html
+<iron-image style="width:400px; height:400px;" sizing="cover"
+ src="http://lorempixel.com/600/400"></iron-image>
+```
+
+ Will show light-gray background until the image loads:
+
+```html
+<iron-image style="width:400px; height:400px; background-color: lightgray;"
+ sizing="cover" preload src="http://lorempixel.com/600/400"></iron-image>
+```
+
+ Will show a base-64 encoded placeholder image until the image loads:
+
+```html
+<iron-image style="width:400px; height:400px;" placeholder="data:image/gif;base64,..."
+ sizing="cover" preload src="http://lorempixel.com/600/400"></iron-image>
+```
+
+ Will fade the light-gray background out once the image is loaded:
+
+```html
+<iron-image style="width:400px; height:400px; background-color: lightgray;"
+ sizing="cover" preload fade src="http://lorempixel.com/600/400"></iron-image>
+```
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--iron-image-placeholder` | Mixin applied to #placeholder | `{}` |
+| `--iron-image-width` | Sets the width of the wrapped image | `auto` |
+| `--iron-image-height` | Sets the height of the wrapped image | `auto` |
+
+
diff --git a/catapult/third_party/polymer/components/iron-image/bower.json b/catapult/third_party/polymer/components/iron-image/bower.json
new file mode 100755
index 00000000..9ac7b5d6
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "iron-image",
+ "version": "1.2.3",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "An image-displaying element with lots of convenient features",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "media"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-image.git"
+ },
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.4",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "iron-image.html",
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-image/demo/index.html b/catapult/third_party/polymer/components/iron-image/demo/index.html
new file mode 100755
index 00000000..06c533cf
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/demo/index.html
@@ -0,0 +1,266 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>iron-image demo</title>
+
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../iron-image.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ .example {
+ margin: 4px;
+ flex: 1;
+ }
+
+ code {
+ white-space: nowrap;
+ }
+ </style>
+
+ <script>
+ function load(id) {
+ document.getElementById(id).src = "./polymer.svg?" + Math.random();
+ }
+ </script>
+
+ </head>
+ <body unresolved>
+
+ <div class="vertical-section-container centered">
+
+ <h3>A plain <code>iron-image</code>.</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <iron-image alt="The Polymer logo." src="./polymer.svg"></iron-image>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ <code>sizing="cover"</code> expands the image to cover all of its
+ specified size.
+ </h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ #example-sizing-cover {
+ width: 150px;
+ height: 150px;
+ background: #ddd;
+ }
+ </style>
+
+ <iron-image sizing="cover" id="example-sizing-cover" alt="The Polymer logo." src="./polymer.svg"></iron-image>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ <code>sizing="contain"</code> expands the image to fit within its
+ specified size.
+ </h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ #example-sizing-contain {
+ width: 150px;
+ height: 150px;
+ background: #ddd;
+ }
+ </style>
+
+ <iron-image sizing="contain" id="example-sizing-contain" alt="The Polymer logo." src="./polymer.svg"></iron-image>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Use the <code>--iron-image-width</code> property to set the width of
+ the image wrapped by the <code>iron-image</code>.
+ </h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ #example-full-width-container {
+ width: 200px;
+ border: 2px solid #444;
+ background: #444;
+ }
+
+ #example-full-width-container iron-image {
+ background: #ddd;
+ }
+
+ #example-full-width {
+ width: 100%;
+ --iron-image-width: 100%;
+ }
+
+ #example-half-width {
+ width: 50%;
+ --iron-image-width: 100%;
+ }
+ </style>
+
+
+ <div id="example-full-width-container">
+ <iron-image id="example-full-width" alt="The Polymer logo." src="./polymer.svg"></iron-image>
+ <iron-image id="example-half-width" alt="The Polymer logo." src="./polymer.svg"></iron-image>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Use the <code>--iron-image-height</code> property to set the height of
+ the image wrapped by the <code>iron-image</code>.
+ </h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ #example-full-height-container {
+ height: 150px;
+ border: 2px solid #444;
+ background: #444;
+ }
+
+ #example-full-height-container iron-image{
+ background: #ddd;
+ }
+
+ #example-full-height {
+ height: 100%;
+ --iron-image-height: 100%;
+ }
+
+ #example-half-height {
+ height: 50%;
+ --iron-image-height: 100%;
+ }
+ </style>
+
+
+ <div id="example-full-height-container">
+ <iron-image id="example-full-height" alt="The Polymer logo." src="./polymer.svg"></iron-image>
+ <iron-image id="example-half-height" alt="The Polymer logo." src="./polymer.svg"></iron-image>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ No placeholder is shown by default.
+ </h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ .example.without-preload iron-image {
+ width: 150px;
+ height: 150px;
+ background: #ddd;
+ }
+ </style>
+
+ <div class="example without-preload">
+ <button onclick="load('example-without-preload-1')">
+ Load image
+ </button>
+ <br>
+ <iron-image sizing="contain" alt="The Polymer logo." id="example-without-preload-1"></iron-image>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ The <code>preload</code> attribute shows a placeholder element in front
+ of the image before it has loaded. Use the
+ <code>--iron-image-placeholder</code> CSS mixin to style it.
+ </h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ .example.preload iron-image {
+ width: 150px;
+ height: 150px;
+ background: #ddd;
+ --iron-image-placeholder: {
+ background: #939ed5;
+ };
+ }
+ </style>
+
+ <div class="example preload">
+ <button onclick="load('example-preload-1')">
+ Load image
+ </button>
+ <br>
+ <iron-image preload id="example-preload-1" alt="The Polymer logo." class="sized" sizing="contain"></iron-image>
+ <br>
+ Without the <code>fade</code> attribute, the placeholder element is
+ hidden with no transition when the image loads.
+ </div>
+ <div class="example preload">
+ <button onclick="load('example-preload-2')">
+ Load image
+ </button>
+ <br>
+ <iron-image preload fade id="example-preload-2" alt="The Polymer logo." class="sized" sizing="contain"></iron-image>
+ <br>
+ With the <code>fade</code> attribute, the placeholder element is
+ fades away when the image loads.
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Use the <code>placeholder</code> attribute to specify a background image
+ for the placeholder element.
+ </h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ .example.preload-image iron-image {
+ width: 150px;
+ height: 150px;
+ background: #ddd;
+ }
+ </style>
+
+ <div class="example preload-image">
+ <button onclick="load('example-preload-image-1')">
+ Load image
+ </button>
+ <br>
+ <iron-image preload placeholder="./loading.png" id="example-preload-image-1" alt="The Polymer logo." class="sized" sizing="contain"></iron-image>
+ <br>
+ (without <code>fade</code> attribute)
+ </div>
+ <div class="example preload-image">
+ <button onclick="load('example-preload-image-2')">
+ Load image
+ </button>
+ <br>
+ <iron-image preload placeholder="./loading.png" fade id="example-preload-image-2" alt="The Polymer logo." class="sized" sizing="contain"></iron-image>
+ <br>
+ (with <code>fade</code> attribute)
+ </div>
+ </template>
+ </demo-snippet>
+
+ </div>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-image/demo/loading.png b/catapult/third_party/polymer/components/iron-image/demo/loading.png
new file mode 100644
index 00000000..a87e1719
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/demo/loading.png
Binary files differ
diff --git a/catapult/third_party/polymer/components/iron-image/demo/polymer.svg b/catapult/third_party/polymer/components/iron-image/demo/polymer.svg
new file mode 100755
index 00000000..f7a815dd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/demo/polymer.svg
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="208px" height="143px" viewBox="0 0 416 286" enable-background="new 0 0 416 286" xml:space="preserve">
+<g>
+ <g>
+ <polygon fill="#303F9F" points="84.157,143 42.878,214.5 84.157,286 125.436,214.5 "/>
+ <polygon fill="#3F51B5" points="331.842,0 290.561,71.5 331.842,143 373.121,71.5 "/>
+ <polygon fill="#7986CB" points="373.121,71.5 249.278,286 331.842,286 414.4,143 "/>
+ <polygon fill="#FF4081" points="249.278,0 84.157,286 166.721,286 331.842,0 "/>
+ <polygon fill="#536DFE" points="84.157,0 1.596,143 42.878,214.5 166.721,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="249.278,0 290.561,71.5 331.842,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="208,71.5 249.278,0 290.561,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="208,71.5 249.278,143 290.561,71.5 "/>
+ <polygon fill-opacity="0.1" points="166.721,143 208,71.5 249.278,143 "/>
+ <polygon fill-opacity="0.2" points="166.721,143 208,214.5 249.278,143 "/>
+ <polygon fill-opacity="0.3" points="125.438,214.5 166.721,143 208,214.5 "/>
+ <polygon fill-opacity="0.4" points="125.438,214.5 166.721,286 208,214.5 "/>
+ <polygon fill-opacity="0.5" points="84.157,286 125.438,214.5 166.721,286 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="84.157,0 125.438,71.5 166.721,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="42.878,71.5 84.157,0 125.438,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="42.878,71.5 84.157,143 125.438,71.5 "/>
+ <polygon fill-opacity="0.1" points="1.598,143 42.878,71.5 84.157,143 "/>
+ <polygon fill-opacity="0.2" points="1.598,143 42.878,214.5 84.157,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="125.438,214.5 84.157,143 42.878,214.5 "/>
+ <polygon fill-opacity="0.2" points="125.438,214.5 84.157,286 42.878,214.5 "/>
+ <polygon fill-opacity="0.2" points="373.121,71.5 331.842,0 290.561,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="373.121,71.5 331.842,143 290.561,71.5 "/>
+ <g>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="331.842,143 373.121,71.5 414.4,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="331.842,143 373.121,214.5 414.4,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="290.561,214.5 331.842,143 373.121,214.5 "/>
+ <polygon fill-opacity="0.1" points="290.561,214.5 331.842,286 373.121,214.5 "/>
+ <polygon fill-opacity="0.2" points="249.278,286 290.561,214.5 331.842,286 "/>
+ </g>
+ </g>
+ <rect y="-65" fill="none" width="416" height="416"/>
+</g>
+<g display="none">
+ <g display="inline">
+ <polygon fill="#303F9F" points="84.157,143 42.878,214.5 84.157,286 166.721,286 "/>
+ <polygon fill="#3F51B5" points="331.842,0 249.278,0 331.842,143 373.121,71.5 "/>
+ <polygon fill="#7986CB" points="373.121,71.5 249.278,286 331.842,286 414.4,143 "/>
+ <polygon fill="#536DFE" points="84.157,0 1.596,143 42.878,214.5 166.721,0 "/>
+ <polygon fill-opacity="0.5" points="249.278,0 290.561,71.5 331.842,0 "/>
+ <polygon fill-opacity="0.5" points="84.157,286 125.438,214.5 166.721,286 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="84.157,0 125.438,71.5 166.721,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="42.878,71.5 84.157,0 125.438,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="42.878,71.5 84.157,143 125.438,71.5 "/>
+ <polygon fill-opacity="0.1" points="1.598,143 42.878,71.5 84.157,143 "/>
+ <polygon fill-opacity="0.2" points="1.598,143 42.878,214.5 84.157,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="125.438,214.5 84.157,143 42.878,214.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="373.121,71.5 331.842,143 290.561,71.5 "/>
+ <g>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="331.842,143 373.121,71.5 414.4,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="331.842,143 373.121,214.5 414.4,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="290.561,214.5 331.842,143 373.121,214.5 "/>
+ <polygon fill-opacity="0.1" points="290.561,214.5 331.842,286 373.121,214.5 "/>
+ <polygon fill-opacity="0.2" points="249.278,286 290.561,214.5 331.842,286 "/>
+ </g>
+ <polygon fill-opacity="0.2" points="125.438,214.5 84.157,286 42.878,214.5 "/>
+ <polygon fill-opacity="0.2" points="373.121,71.5 331.842,0 290.561,71.5 "/>
+ </g>
+ <rect y="-65" display="inline" fill="none" width="416" height="416"/>
+</g>
+<g display="none">
+ <g display="inline">
+ <polygon fill="#FF4081" points="249.279,0 84.157,286 166.721,286 331.843,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="249.279,0 290.558,71.5 331.843,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="208,71.5 249.279,0 290.558,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="208,71.5 249.279,143 290.558,71.5 "/>
+ <polygon fill-opacity="0.2" points="166.721,143 208,214.5 249.279,143 "/>
+ <polygon fill-opacity="0.3" points="125.439,214.5 166.721,143 208,214.5 "/>
+ <polygon fill-opacity="0.4" points="125.439,214.5 166.721,286 208,214.5 "/>
+ <polygon fill-opacity="0.5" points="84.157,286 125.439,214.5 166.721,286 "/>
+ <polygon fill-opacity="0.1" points="166.721,143 208,71.5 249.279,143 "/>
+ </g>
+ <g display="inline">
+ <polygon fill="#FF4081" points="331.84,0 166.718,286 249.279,286 373.121,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="290.558,71.5 331.84,0 373.121,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="290.558,71.5 331.84,143 373.121,71.5 "/>
+ <polygon fill-opacity="0.2" points="249.279,143 290.558,214.5 331.84,143 "/>
+ <polygon fill-opacity="0.3" points="208,214.5 249.279,143 290.558,214.5 "/>
+ <polygon fill-opacity="0.4" points="208,214.5 249.279,286 290.558,214.5 "/>
+ <polygon fill-opacity="0.5" points="166.718,286 208,214.5 249.279,286 "/>
+ <polygon fill-opacity="0.1" points="249.279,143 290.558,71.5 331.84,143 "/>
+ </g>
+ <g display="inline">
+ <polygon fill="#FF4081" points="166.718,0 42.878,214.5 84.16,286 249.279,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="166.718,0 208,71.5 249.279,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="125.439,71.5 166.718,0 208,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="125.439,71.5 166.718,143 208,71.5 "/>
+ <polygon fill-opacity="0.2" points="84.16,143 125.439,214.5 166.718,143 "/>
+ <polygon fill-opacity="0.3" points="42.878,214.5 84.16,143 125.439,214.5 "/>
+ <polygon fill-opacity="0.4" points="42.878,214.5 84.16,286 125.439,214.5 "/>
+ <polygon fill-opacity="0.1" points="84.16,143 125.439,71.5 166.718,143 "/>
+ </g>
+ <rect y="-65" display="inline" fill="none" width="416" height="416"/>
+ <g display="inline">
+ <polygon fill="#303F9F" points="84.157,143 42.878,214.5 84.157,286 166.721,286 "/>
+ <polygon fill="#3F51B5" points="331.843,0 249.279,0 331.843,143 373.121,71.5 "/>
+ <polygon fill="#7986CB" points="373.121,71.5 249.279,286 331.843,286 414.4,143 "/>
+ <polygon fill="#536DFE" points="84.157,0 1.597,143 42.878,214.5 166.721,0 "/>
+ <polygon fill-opacity="0.5" points="249.279,0 290.558,71.5 331.843,0 "/>
+ <polygon fill-opacity="0.5" points="84.157,286 125.439,214.5 166.721,286 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="84.157,0 125.439,71.5 166.721,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="42.878,71.5 84.157,0 125.439,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="42.878,71.5 84.157,143 125.439,71.5 "/>
+ <polygon fill-opacity="0.1" points="1.6,143 42.878,71.5 84.157,143 "/>
+ <polygon fill-opacity="0.2" points="1.6,143 42.878,214.5 84.157,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="125.439,214.5 84.157,143 42.878,214.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="373.121,71.5 331.843,143 290.558,71.5 "/>
+ <g>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="331.843,143 373.121,71.5 414.4,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="331.843,143 373.121,214.5 414.4,143 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="290.558,214.5 331.843,143 373.121,214.5 "/>
+ <polygon fill-opacity="0.1" points="290.558,214.5 331.843,286 373.121,214.5 "/>
+ <polygon fill-opacity="0.2" points="249.279,286 290.558,214.5 331.843,286 "/>
+ </g>
+ <polygon fill-opacity="0.2" points="125.439,214.5 84.157,286 42.878,214.5 "/>
+ <polygon fill-opacity="0.2" points="373.121,71.5 331.843,0 290.558,71.5 "/>
+ </g>
+</g>
+<g display="none">
+ <g display="inline">
+ <polygon fill="#9F499B" points="249.279,0 84.157,286 166.721,286 331.843,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="249.279,0 290.558,71.5 331.843,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="208,71.5 249.279,0 290.558,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="208,71.5 249.279,143 290.558,71.5 "/>
+ <polygon fill-opacity="0.2" points="166.721,143 208,214.5 249.279,143 "/>
+ <polygon fill-opacity="0.3" points="125.439,214.5 166.721,143 208,214.5 "/>
+ <polygon fill-opacity="0.4" points="125.439,214.5 166.721,286 208,214.5 "/>
+ <polygon fill-opacity="0.5" points="84.157,286 125.439,214.5 166.721,286 "/>
+ <polygon fill-opacity="0.1" points="166.721,143 208,71.5 249.279,143 "/>
+ </g>
+ <g display="inline">
+ <polygon fill="#9F499B" points="331.84,0 166.718,286 249.279,286 373.121,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="290.558,71.5 331.84,0 373.121,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="290.558,71.5 331.84,143 373.121,71.5 "/>
+ <polygon fill-opacity="0.2" points="249.279,143 290.558,214.5 331.84,143 "/>
+ <polygon fill-opacity="0.3" points="208,214.5 249.279,143 290.558,214.5 "/>
+ <polygon fill-opacity="0.4" points="208,214.5 249.279,286 290.558,214.5 "/>
+ <polygon fill-opacity="0.5" points="166.718,286 208,214.5 249.279,286 "/>
+ <polygon fill-opacity="0.1" points="249.279,143 290.558,71.5 331.84,143 "/>
+ </g>
+ <g display="inline">
+ <polygon fill="#9F499B" points="373.121,71.5 249.279,286 331.843,286 414.4,143 "/>
+ <polygon fill-opacity="0.2" points="331.843,143 373.121,214.5 414.4,143 "/>
+ <polygon fill-opacity="0.3" points="290.558,214.5 331.843,143 373.121,214.5 "/>
+ <polygon fill-opacity="0.4" points="290.558,214.5 331.843,286 373.121,214.5 "/>
+ <polygon fill-opacity="0.5" points="249.279,286 290.558,214.5 331.843,286 "/>
+ <polygon fill-opacity="0.1" points="331.843,143 373.121,71.5 414.4,143 "/>
+ </g>
+ <g display="inline">
+ <polygon fill="#9F499B" points="166.718,0 42.878,214.5 84.16,286 249.279,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="166.718,0 208,71.5 249.279,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="125.439,71.5 166.718,0 208,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="125.439,71.5 166.718,143 208,71.5 "/>
+ <polygon fill-opacity="0.2" points="84.16,143 125.439,214.5 166.718,143 "/>
+ <polygon fill-opacity="0.3" points="42.878,214.5 84.16,143 125.439,214.5 "/>
+ <polygon fill-opacity="0.4" points="42.878,214.5 84.16,286 125.439,214.5 "/>
+ <polygon fill-opacity="0.1" points="84.16,143 125.439,71.5 166.718,143 "/>
+ </g>
+ <g display="inline">
+ <polygon fill="#9F499B" points="84.157,0 1.6,143 42.878,214.5 166.721,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.2" points="84.157,0 125.439,71.5 166.721,0 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0.1" points="42.878,71.5 84.157,0 125.439,71.5 "/>
+ <polygon fill="#FFFFFF" fill-opacity="0" points="42.878,71.5 84.157,143 125.439,71.5 "/>
+ <polygon fill-opacity="0.2" points="1.6,143 42.878,214.5 84.157,143 "/>
+ <polygon fill-opacity="0.1" points="1.6,143 42.878,71.5 84.157,143 "/>
+ </g>
+ <rect y="-65" display="inline" fill="none" width="416" height="416"/>
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-image/index.html b/catapult/third_party/polymer/components/iron-image/index.html
new file mode 100755
index 00000000..bb7da82e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-image</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-image/iron-image.html b/catapult/third_party/polymer/components/iron-image/iron-image.html
new file mode 100755
index 00000000..bf774d6c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/iron-image.html
@@ -0,0 +1,403 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+
+<!--
+`iron-image` is an element for displaying an image that provides useful sizing and
+preloading options not found on the standard `<img>` tag.
+
+The `sizing` option allows the image to be either cropped (`cover`) or
+letterboxed (`contain`) to fill a fixed user-size placed on the element.
+
+The `preload` option prevents the browser from rendering the image until the
+image is fully loaded. In the interim, either the element's CSS `background-color`
+can be be used as the placeholder, or the `placeholder` property can be
+set to a URL (preferably a data-URI, for instant rendering) for an
+placeholder image.
+
+The `fade` option (only valid when `preload` is set) will cause the placeholder
+image/color to be faded out once the image is rendered.
+
+Examples:
+
+ Basically identical to `<img src="...">` tag:
+
+ <iron-image src="http://lorempixel.com/400/400"></iron-image>
+
+ Will letterbox the image to fit:
+
+ <iron-image style="width:400px; height:400px;" sizing="contain"
+ src="http://lorempixel.com/600/400"></iron-image>
+
+ Will crop the image to fit:
+
+ <iron-image style="width:400px; height:400px;" sizing="cover"
+ src="http://lorempixel.com/600/400"></iron-image>
+
+ Will show light-gray background until the image loads:
+
+ <iron-image style="width:400px; height:400px; background-color: lightgray;"
+ sizing="cover" preload src="http://lorempixel.com/600/400"></iron-image>
+
+ Will show a base-64 encoded placeholder image until the image loads:
+
+ <iron-image style="width:400px; height:400px;" placeholder="data:image/gif;base64,..."
+ sizing="cover" preload src="http://lorempixel.com/600/400"></iron-image>
+
+ Will fade the light-gray background out once the image is loaded:
+
+ <iron-image style="width:400px; height:400px; background-color: lightgray;"
+ sizing="cover" preload fade src="http://lorempixel.com/600/400"></iron-image>
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--iron-image-placeholder` | Mixin applied to #placeholder | `{}`
+`--iron-image-width` | Sets the width of the wrapped image | `auto`
+`--iron-image-height` | Sets the height of the wrapped image | `auto`
+
+@group Iron Elements
+@element iron-image
+@demo demo/index.html
+-->
+
+<dom-module id="iron-image">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ overflow: hidden;
+ position: relative;
+ }
+
+ #sizedImgDiv {
+ @apply(--layout-fit);
+
+ display: none;
+ }
+
+ #img {
+ display: block;
+ width: var(--iron-image-width, auto);
+ height: var(--iron-image-height, auto);
+ }
+
+ :host([sizing]) #sizedImgDiv {
+ display: block;
+ }
+
+ :host([sizing]) #img {
+ display: none;
+ }
+
+ #placeholder {
+ @apply(--layout-fit);
+
+ background-color: inherit;
+ opacity: 1;
+
+ @apply(--iron-image-placeholder);
+ }
+
+ #placeholder.faded-out {
+ transition: opacity 0.5s linear;
+ opacity: 0;
+ }
+ </style>
+
+ <div id="sizedImgDiv"
+ role="img"
+ hidden$="[[_computeImgDivHidden(sizing)]]"
+ aria-hidden$="[[_computeImgDivARIAHidden(alt)]]"
+ aria-label$="[[_computeImgDivARIALabel(alt, src)]]"></div>
+ <img id="img" alt$="[[alt]]" hidden$="[[_computeImgHidden(sizing)]]">
+ <div id="placeholder"
+ hidden$="[[_computePlaceholderHidden(preload, fade, loading, loaded)]]"
+ class$="[[_computePlaceholderClassName(preload, fade, loading, loaded)]]"></div>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'iron-image',
+
+ properties: {
+ /**
+ * The URL of an image.
+ */
+ src: {
+ observer: '_srcChanged',
+ type: String,
+ value: ''
+ },
+
+ /**
+ * A short text alternative for the image.
+ */
+ alt: {
+ type: String,
+ value: null
+ },
+
+ /**
+ * When true, the image is prevented from loading and any placeholder is
+ * shown. This may be useful when a binding to the src property is known to
+ * be invalid, to prevent 404 requests.
+ */
+ preventLoad: {
+ type: Boolean,
+ value: false,
+ observer: '_preventLoadChanged'
+ },
+
+ /**
+ * Sets a sizing option for the image. Valid values are `contain` (full
+ * aspect ratio of the image is contained within the element and
+ * letterboxed) or `cover` (image is cropped in order to fully cover the
+ * bounds of the element), or `null` (default: image takes natural size).
+ */
+ sizing: {
+ type: String,
+ value: null,
+ reflectToAttribute: true
+ },
+
+ /**
+ * When a sizing option is used (`cover` or `contain`), this determines
+ * how the image is aligned within the element bounds.
+ */
+ position: {
+ type: String,
+ value: 'center'
+ },
+
+ /**
+ * When `true`, any change to the `src` property will cause the `placeholder`
+ * image to be shown until the new image has loaded.
+ */
+ preload: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * This image will be used as a background/placeholder until the src image has
+ * loaded. Use of a data-URI for placeholder is encouraged for instant rendering.
+ */
+ placeholder: {
+ type: String,
+ value: null,
+ observer: '_placeholderChanged'
+ },
+
+ /**
+ * When `preload` is true, setting `fade` to true will cause the image to
+ * fade into place.
+ */
+ fade: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Read-only value that is true when the image is loaded.
+ */
+ loaded: {
+ notify: true,
+ readOnly: true,
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Read-only value that tracks the loading state of the image when the `preload`
+ * option is used.
+ */
+ loading: {
+ notify: true,
+ readOnly: true,
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Read-only value that indicates that the last set `src` failed to load.
+ */
+ error: {
+ notify: true,
+ readOnly: true,
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Can be used to set the width of image (e.g. via binding); size may also be
+ * set via CSS.
+ */
+ width: {
+ observer: '_widthChanged',
+ type: Number,
+ value: null
+ },
+
+ /**
+ * Can be used to set the height of image (e.g. via binding); size may also be
+ * set via CSS.
+ *
+ * @attribute height
+ * @type number
+ * @default null
+ */
+ height: {
+ observer: '_heightChanged',
+ type: Number,
+ value: null
+ },
+ },
+
+ observers: [
+ '_transformChanged(sizing, position)'
+ ],
+
+ ready: function() {
+ var img = this.$.img;
+
+ img.onload = function() {
+ if (this.$.img.src !== this._resolveSrc(this.src)) return;
+
+ this._setLoading(false);
+ this._setLoaded(true);
+ this._setError(false);
+ }.bind(this);
+
+ img.onerror = function() {
+ if (this.$.img.src !== this._resolveSrc(this.src)) return;
+
+ this._reset();
+
+ this._setLoading(false);
+ this._setLoaded(false);
+ this._setError(true);
+ }.bind(this);
+
+ this._resolvedSrc = '';
+ },
+
+ _load: function(src) {
+ if (src) {
+ this.$.img.src = src;
+ } else {
+ this.$.img.removeAttribute('src');
+ }
+ this.$.sizedImgDiv.style.backgroundImage = src ? 'url("' + src + '")' : '';
+
+ this._setLoading(!!src);
+ this._setLoaded(false);
+ this._setError(false);
+ },
+
+ _reset: function() {
+ this.$.img.removeAttribute('src');
+ this.$.sizedImgDiv.style.backgroundImage = '';
+
+ this._setLoading(false);
+ this._setLoaded(false);
+ this._setError(false);
+ },
+
+ _computePlaceholderHidden: function() {
+ return !this.preload || (!this.fade && !this.loading && this.loaded);
+ },
+
+ _computePlaceholderClassName: function() {
+ return (this.preload && this.fade && !this.loading && this.loaded) ? 'faded-out' : '';
+ },
+
+ _computeImgDivHidden: function() {
+ return !this.sizing;
+ },
+
+ _computeImgDivARIAHidden: function() {
+ return this.alt === '' ? 'true' : undefined;
+ },
+
+ _computeImgDivARIALabel: function() {
+ if (this.alt !== null) {
+ return this.alt;
+ }
+
+ // Polymer.ResolveUrl.resolveUrl will resolve '' relative to a URL x to
+ // that URL x, but '' is the default for src.
+ if (this.src === '') {
+ return '';
+ }
+
+ var pathComponents = (new URL(this._resolveSrc(this.src))).pathname.split("/");
+ return pathComponents[pathComponents.length - 1];
+ },
+
+ _computeImgHidden: function() {
+ return !!this.sizing;
+ },
+
+ _widthChanged: function() {
+ this.style.width = isNaN(this.width) ? this.width : this.width + 'px';
+ },
+
+ _heightChanged: function() {
+ this.style.height = isNaN(this.height) ? this.height : this.height + 'px';
+ },
+
+ _preventLoadChanged: function() {
+ if (this.preventLoad || this.loaded) return;
+
+ this._reset();
+ this._load(this.src);
+ },
+
+ _srcChanged: function(newSrc, oldSrc) {
+ var newResolvedSrc = this._resolveSrc(newSrc);
+ if (newResolvedSrc === this._resolvedSrc) return;
+ this._resolvedSrc = newResolvedSrc;
+
+ this._reset();
+ if (!this.preventLoad) {
+ this._load(newSrc);
+ }
+ },
+
+ _placeholderChanged: function() {
+ this.$.placeholder.style.backgroundImage =
+ this.placeholder ? 'url("' + this.placeholder + '")' : '';
+ },
+
+ _transformChanged: function() {
+ var sizedImgDivStyle = this.$.sizedImgDiv.style;
+ var placeholderStyle = this.$.placeholder.style;
+
+ sizedImgDivStyle.backgroundSize =
+ placeholderStyle.backgroundSize =
+ this.sizing;
+
+ sizedImgDivStyle.backgroundPosition =
+ placeholderStyle.backgroundPosition =
+ this.sizing ? this.position : '';
+
+ sizedImgDivStyle.backgroundRepeat =
+ placeholderStyle.backgroundRepeat =
+ this.sizing ? 'no-repeat' : '';
+ },
+
+ _resolveSrc: function(testSrc) {
+ return Polymer.ResolveUrl.resolveUrl(testSrc, this.ownerDocument.baseURI);
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-image/test/index.html b/catapult/third_party/polymer/components/iron-image/test/index.html
new file mode 100755
index 00000000..7145c47c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/test/index.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="utf-8">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'iron-image.html',
+ 'iron-image.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-image/test/iron-image.html b/catapult/third_party/polymer/components/iron-image/test/iron-image.html
new file mode 100755
index 00000000..162ff4ee
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-image/test/iron-image.html
@@ -0,0 +1,338 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>iron-image</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-image.html">
+
+ <style is="custom-style">
+ .fixed-width-container {
+ width: 500px;
+ }
+
+ .fixed-width-container iron-image {
+ width: 100%;
+ --iron-image-width: 100%;
+ }
+
+ .fixed-height-container {
+ height: 500px;
+ }
+
+ .fixed-height-container iron-image {
+ height: 100%;
+ --iron-image-height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+ <test-fixture id="TrivialImage">
+ <template>
+ <iron-image></iron-image>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="FixedWidthContainer">
+ <template>
+ <div class="fixed-width-container">
+ <iron-image></iron-image>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="FixedHeightContainer">
+ <template>
+ <div class="fixed-height-container">
+ <iron-image></iron-image>
+ </div>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('<iron-image>', function() {
+ function randomImageUrl () {
+ return '../demo/polymer.svg?' + Math.random();
+ }
+
+ var image;
+
+ suite('basic behavior', function() {
+ setup(function() {
+ image = fixture('TrivialImage');
+ });
+
+ test('loading, loaded, error are false before any src is set', function() {
+ expect(image.loading).to.be.eql(false);
+ expect(image.loaded).to.be.eql(false);
+ expect(image.error).to.be.eql(false);
+ });
+
+ test('loading, loaded, error are false when src is set to empty string', function(done) {
+ image.addEventListener('loaded-changed', function onLoadedChanged() {
+ if (image.loaded) {
+ image.removeEventListener('loaded-changed', onLoadedChanged);
+ image.addEventListener('loaded-changed', function onLoadedChanged2() {
+ image.removeEventListener('loaded-changed', onLoadedChanged2);
+
+ expect(image.loading).to.be.eql(false);
+ expect(image.loaded).to.be.eql(false);
+ expect(image.error).to.be.eql(false);
+ done();
+ });
+
+ expect(image.loading).to.be.eql(false);
+ expect(image.loaded).to.be.eql(true);
+ expect(image.error).to.be.eql(false);
+ image.src = '';
+ }
+ });
+ image.src = randomImageUrl();
+ });
+
+ test('can load images given a src', function(done) {
+ image.addEventListener('loaded-changed', function onLoadedChanged() {
+ image.removeEventListener('loaded-changed', onLoadedChanged);
+
+ try {
+ expect(image.loaded).to.be.eql(true);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ image.src = randomImageUrl();
+ });
+
+ test('will reload images when src changes', function(done) {
+ var loadCount = 0;
+
+ image.addEventListener('loaded-changed', function onLoadedChanged() {
+ if (image.loaded === true) {
+ loadCount++;
+
+ if (loadCount === 2) {
+ image.removeEventListener('loaded-changed', onLoadedChanged);
+ done();
+ } else {
+ image.src = randomImageUrl();
+ }
+ }
+ });
+
+ image.src = randomImageUrl();
+ });
+
+ test('error property is set when the image fails to load', function(done) {
+ image.addEventListener('error-changed', function onErrorChanged() {
+ assert(image.error, 'image has error property set');
+ image.removeEventListener('error-changed', onErrorChanged);
+ done();
+ });
+
+ image.src = '/this_image_should_not_exist.jpg';
+ });
+
+ // Test for PolymerElements/iron-image#16.
+ test('placeholder is hidden after loading when src is changed from invalid to valid', function(done) {
+ image.preload = true;
+
+ image.addEventListener('error-changed', function onErrorChanged() {
+ image.removeEventListener('error-changed', onErrorChanged);
+
+ assert.equal(image.loading, false, 'errored image loading = false');
+ assert.equal(image.loaded, false, 'errored image loaded = false');
+ assert.equal(image.error, true, 'errored image error = true');
+
+ image.addEventListener('loaded-changed', function onLoadedChanged() {
+ if (!image.loaded) return;
+
+ image.removeEventListener('loaded-changed', onLoadedChanged);
+
+ assert.equal(image.loading, false, 'ok image loading = false');
+ assert.equal(image.loaded, true, 'ok image loaded = true');
+ assert.equal(image.error, false, 'ok image error = false');
+ assert.equal(getComputedStyle(image.$.placeholder).display, 'none', 'placeholder has style.display = none');
+
+ done();
+ });
+
+ image.src = randomImageUrl();
+ });
+
+ image.src = '/this_image_should_not_exist.jpg';
+ });
+
+ // Test for PolymerElements/iron-image#23.
+ test('image is not shown below placeholder if previous image was loaded with' +
+ ' sizing on and current image fails to load', function(done) {
+ image.preload = true;
+ image.sizing = 'cover';
+
+ image.addEventListener('loaded-changed', function onLoadedChanged() {
+ if (!image.loaded) return;
+ image.removeEventListener('loaded-changed', onLoadedChanged);
+
+ assert.notEqual(getComputedStyle(image.$.sizedImgDiv).backgroundImage, 'none', 'image visible after successful load');
+ assert.equal(getComputedStyle(image.$.placeholder).display, 'none', 'placeholder hidden after successful load');
+
+ image.addEventListener('error-changed', function onErrorChanged() {
+ if (!image.error) return;
+ image.removeEventListener('error-changed', onErrorChanged);
+
+ assert.equal(getComputedStyle(image.$.sizedImgDiv).backgroundImage, 'none', 'image hidden after failed load');
+ assert.notEqual(getComputedStyle(image.$.placeholder).display, 'none', 'placeholder visible after failed load');
+
+ done();
+ });
+
+ image.src = '/this_image_should_not_exist.jpg';
+ });
+
+ image.src = randomImageUrl();
+ });
+ });
+
+ suite('--iron-image-width, --iron-image-height', function() {
+ var fixedWidthContainer;
+ var fixedWidthIronImage;
+ var fixedHeightContainer;
+ var fixedHeightIronImage;
+
+ setup(function() {
+ fixedWidthContainer = fixture('FixedWidthContainer');
+ fixedWidthIronImage = fixedWidthContainer.querySelector('iron-image');
+ fixedHeightContainer = fixture('FixedHeightContainer');
+ fixedHeightIronImage = fixedHeightContainer.querySelector('iron-image');
+ });
+
+ test('100% width image fills container', function(done) {
+ fixedWidthIronImage.$.img.addEventListener('load', function onLoadedChanged(e) {
+ fixedWidthIronImage.$.img.removeEventListener('load', onLoadedChanged);
+ Polymer.updateStyles();
+
+ var containerRect = fixedWidthContainer.getBoundingClientRect();
+ var ironImageRect = fixedWidthIronImage.getBoundingClientRect();
+ var wrappedImageRect = fixedWidthIronImage.$.img.getBoundingClientRect();
+
+ expect(containerRect.width).to.be.closeTo(500, 0.5);
+ expect(ironImageRect.width).to.be.closeTo(500, 0.5);
+ expect(wrappedImageRect.width).to.be.closeTo(500, 0.5);
+
+ done();
+ });
+
+ fixedWidthIronImage.src = randomImageUrl();
+ });
+
+ test('100% height image fills container', function(done) {
+ fixedHeightIronImage.$.img.addEventListener('load', function onLoadedChanged(e) {
+ fixedHeightIronImage.$.img.removeEventListener('load', onLoadedChanged);
+ Polymer.updateStyles();
+
+ var containerRect = fixedHeightContainer.getBoundingClientRect();
+ var ironImageRect = fixedHeightIronImage.getBoundingClientRect();
+ var wrappedImageRect = fixedHeightIronImage.$.img.getBoundingClientRect();
+
+ expect(containerRect.height).to.be.closeTo(500, 0.5);
+ expect(ironImageRect.height).to.be.closeTo(500, 0.5);
+ expect(wrappedImageRect.height).to.be.closeTo(500, 0.5);
+
+ done();
+ });
+
+ fixedHeightIronImage.src = randomImageUrl();
+ });
+ });
+
+ suite('accessibility', function() {
+ suite('sizing inactive', function() {
+ var image;
+
+ setup(function() {
+ image = fixture('TrivialImage');
+ });
+
+ test('#sizedImgDiv is hidden', function() {
+ var sizedImgDivStyle = window.getComputedStyle(image.$.sizedImgDiv);
+ assert.strictEqual(sizedImgDivStyle.display, 'none');
+ });
+
+ test('img has no alt text by default', function() {
+ assert.isFalse(image.$.img.hasAttribute('alt'));
+ });
+
+ test('img alt text is empty string when iron-image alt text is empty string', function() {
+ image.alt = '';
+
+ assert.isTrue(image.$.img.hasAttribute('alt'));
+ assert.strictEqual(image.$.img.getAttribute('alt'), '');
+ });
+
+ test('img alt text matches iron-image alt text when defined', function() {
+ image.alt = 'alt text value';
+
+ assert.isTrue(image.$.img.hasAttribute('alt'));
+ assert.strictEqual(image.$.img.getAttribute('alt'), 'alt text value');
+ });
+ });
+
+ suite('sizing active', function() {
+ var image;
+
+ setup(function() {
+ image = fixture('TrivialImage');
+ image.sizing = 'cover';
+ });
+
+ test('inner img is hidden', function() {
+ var imgStyle = window.getComputedStyle(image.$.img);
+ assert.strictEqual(imgStyle.display, 'none');
+ });
+
+ test('#sizedImgDiv has empty aria-label text by default', function() {
+ assert.isTrue(image.$.sizedImgDiv.hasAttribute('aria-label'));
+ assert.strictEqual(image.$.sizedImgDiv.getAttribute('aria-label'), '');
+ });
+
+ test('#sizedImgDiv has aria-hidden when iron-image alt text is empty string', function() {
+ image.alt = '';
+
+ assert.isTrue(image.$.sizedImgDiv.hasAttribute('aria-hidden'));
+ var hiddenValue = image.$.sizedImgDiv.getAttribute('aria-hidden');
+ assert.isTrue(hiddenValue === '' || hiddenValue === 'true');
+ });
+
+ test('#sizedImgDiv aria-label matches iron-image alt text when defined', function() {
+ image.alt = 'alt text value';
+
+ assert.isTrue(image.$.sizedImgDiv.hasAttribute('aria-label'));
+ assert.strictEqual(image.$.sizedImgDiv.getAttribute('aria-label'), 'alt text value');
+ });
+
+ test('#sizedImgDiv aria-label text is last path component of src when iron-image alt text is undefined', function() {
+ image.src = '/some/path.components/file.jpg?a=b&c=d#anchor';
+
+ assert.isTrue(image.$.sizedImgDiv.hasAttribute('aria-label'));
+ assert.strictEqual(image.$.sizedImgDiv.getAttribute('aria-label'), 'file.jpg');
+ });
+ });
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-input/.bower.json b/catapult/third_party/polymer/components/iron-input/.bower.json
new file mode 100644
index 00000000..403a62d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/.bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "iron-input",
+ "version": "1.0.11",
+ "description": "An input element with data binding",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "input"
+ ],
+ "main": "iron-input.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-input.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-input",
+ "ignore": [],
+ "dependencies": {
+ "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#^1.0.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.0.11",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.11",
+ "commit": "9b991e43576064227053ab73eeb351be60d82a40"
+ },
+ "_source": "https://github.com/PolymerElements/iron-input.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-input"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-input/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-input/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..b01fd73e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-input/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-input/.gitignore b/catapult/third_party/polymer/components/iron-input/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-input/.travis.yml b/catapult/third_party/polymer/components/iron-input/.travis.yml
new file mode 100644
index 00000000..b29bddcd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ NQunbnhwQFU/uRU1Dz8VIYXpG9cq4wEldllph4To1XJqEa21pG1Q9+a/M4+aOkW6iIPRgfI/bQ6mPWaRnE/KGrsusARqgYDytq42dJpSoLOiLYbVdRKIHI3t9cr1MiZKUxwz/vpOsKcMK8dW5RmR6zJ8k5roijNAmjFRlzfGOKY=
+ - secure: >-
+ Ge6m7GkHZdhv6Ru7uNEVS9jUQ15CsCa9ZBcBQncA/w8zEmDfM2tzcTUJ54piZ03mo9qnWOcV4IcJj78nXsyKcYZHiGpe63w5R5NWhO3uZ72C48SMPbWQxL0Kyt6PN4u7i5MNCMJ3+a+ttyet8H9qTzxAwOLVSFCUr3On1/B1APU=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-input/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-input/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-input/README.md b/catapult/third_party/polymer/components/iron-input/README.md
new file mode 100644
index 00000000..24563f47
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/README.md
@@ -0,0 +1,59 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-input.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-input.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-input)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-input)_
+
+
+## &lt;iron-input&gt;
+
+`<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior`
+to `<input>`.
+
+### Two-way binding
+
+By default you can only get notified of changes to an `input`'s `value` due to user input:
+
+```html
+<input value="{{myValue::input}}">
+```
+
+`iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used
+for two-way data binding. `bind-value` will notify if it is changed either by user input or by script.
+
+```html
+<input is="iron-input" bind-value="{{myValue}}">
+```
+
+### Custom validators
+
+You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`.
+
+```html
+<input is="iron-input" validator="my-custom-validator">
+```
+
+### Stopping invalid input
+
+It may be desirable to only allow users to enter certain characters. You can use the
+`prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature
+is separate from validation, and `allowed-pattern` does not affect how the input is validated.
+
+```html
+<!-- only allow characters that match [0-9] -->
+<input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]">
+```
+
+
diff --git a/catapult/third_party/polymer/components/iron-input/bower.json b/catapult/third_party/polymer/components/iron-input/bower.json
new file mode 100644
index 00000000..8428fd13
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "iron-input",
+ "version": "1.0.11",
+ "description": "An input element with data binding",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "input"
+ ],
+ "main": "iron-input.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-input.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-input",
+ "ignore": [],
+ "dependencies": {
+ "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#^1.0.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-input/demo/index.html b/catapult/third_party/polymer/components/iron-input/demo/index.html
new file mode 100644
index 00000000..59d37b2d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/demo/index.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>iron-input demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../iron-input.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../../paper-styles/typography.html">
+
+ <style is="custom-style">
+
+ .vertical-section {
+ @apply(--paper-font-body1);
+
+ line-height: 40px;
+ }
+
+ code {
+ color: var(--google-grey-700);
+ }
+
+ input[is=iron-input] {
+ width: 100%;
+ box-sizing: border-box;
+ }
+
+ input, button {
+ font-size: 20px;
+ padding: 0.2em;
+ }
+
+ </style>
+</head>
+<body>
+
+ <div class="vertical-section vertical-section-container centered">
+ <template is="dom-bind">
+ <p>
+ <input is="iron-input" bind-value="{{bindValue}}" value="{{value::input}}">
+ <br>
+ bind to <code>bind-value</code>: <b>[[bindValue]]</b>
+ <br>
+ bind to <code>value::input</code>: <b>{{value}}</b>
+ </p>
+
+ <p on-click="setValue">
+ set bind-value to: <input> <button is="paper-button" value="bindValue">set</button>
+ <br>
+ set value to: <input> <button value="value">set</button>
+ </p>
+ </template>
+ <p>only allows these characters:
+ <code>!@#0123456789</code></p>
+ <input is="iron-input" allowed-pattern="[!@#0-9]" prevent-invalid-input>
+
+ </div>
+
+ <script>
+ var scope = document.querySelector('template[is=dom-bind]');
+
+ scope.setValue = function(event) {
+ if (!(event.target instanceof HTMLButtonElement)) {
+ return;
+ }
+ document.querySelector('input[is=iron-input]')[event.target.value] = event.target.previousElementSibling.value;
+ }
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-input/hero.svg b/catapult/third_party/polymer/components/iron-input/hero.svg
new file mode 100755
index 00000000..e72ebd30
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/hero.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <rect x="49" y="53" width="2" height="18"/>
+ <path d="M188,78H37V44h151V78z M39,76h147V46H39V76z"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-input/index.html b/catapult/third_party/polymer/components/iron-input/index.html
new file mode 100644
index 00000000..ca0dac06
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-input</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-input/iron-input.html b/catapult/third_party/polymer/components/iron-input/iron-input.html
new file mode 100644
index 00000000..ddb5277e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/iron-input.html
@@ -0,0 +1,311 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-announcer/iron-a11y-announcer.html">
+<link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html">
+
+<script>
+
+/*
+`<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior`
+to `<input>`.
+
+### Two-way binding
+
+By default you can only get notified of changes to an `input`'s `value` due to user input:
+
+ <input value="{{myValue::input}}">
+
+`iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used
+for two-way data binding. `bind-value` will notify if it is changed either by user input or by script.
+
+ <input is="iron-input" bind-value="{{myValue}}">
+
+### Custom validators
+
+You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`.
+
+ <input is="iron-input" validator="my-custom-validator">
+
+### Stopping invalid input
+
+It may be desirable to only allow users to enter certain characters. You can use the
+`prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature
+is separate from validation, and `allowed-pattern` does not affect how the input is validated.
+
+ <!-- only allow characters that match [0-9] -->
+ <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]">
+
+@hero hero.svg
+@demo demo/index.html
+*/
+
+ Polymer({
+
+ is: 'iron-input',
+
+ extends: 'input',
+
+ behaviors: [
+ Polymer.IronValidatableBehavior
+ ],
+
+ properties: {
+
+ /**
+ * Use this property instead of `value` for two-way data binding.
+ */
+ bindValue: {
+ observer: '_bindValueChanged',
+ type: String
+ },
+
+ /**
+ * Set to true to prevent the user from entering invalid input. If `allowedPattern` is set,
+ * any character typed by the user will be matched against that pattern, and rejected if it's not a match.
+ * Pasted input will have each character checked individually; if any character
+ * doesn't match `allowedPattern`, the entire pasted string will be rejected.
+ * If `allowedPattern` is not set, it will use the `type` attribute (only supported for `type=number`).
+ */
+ preventInvalidInput: {
+ type: Boolean
+ },
+
+ /**
+ * Regular expression that list the characters allowed as input.
+ * This pattern represents the allowed characters for the field; as the user inputs text,
+ * each individual character will be checked against the pattern (rather than checking
+ * the entire value as a whole). The recommended format should be a list of allowed characters;
+ * for example, `[a-zA-Z0-9.+-!;:]`
+ */
+ allowedPattern: {
+ type: String,
+ observer: "_allowedPatternChanged"
+ },
+
+ _previousValidInput: {
+ type: String,
+ value: ''
+ },
+
+ _patternAlreadyChecked: {
+ type: Boolean,
+ value: false
+ }
+
+ },
+
+ listeners: {
+ 'input': '_onInput',
+ 'keypress': '_onKeypress'
+ },
+
+ registered: function() {
+ // Feature detect whether we need to patch dispatchEvent (i.e. on FF and IE).
+ if (!this._canDispatchEventOnDisabled()) {
+ this._origDispatchEvent = this.dispatchEvent;
+ this.dispatchEvent = this._dispatchEventFirefoxIE;
+ }
+ },
+
+ created: function() {
+ Polymer.IronA11yAnnouncer.requestAvailability();
+ },
+
+ _canDispatchEventOnDisabled: function() {
+ var input = document.createElement('input');
+ var canDispatch = false;
+ input.disabled = true;
+
+ input.addEventListener('feature-check-dispatch-event', function() {
+ canDispatch = true;
+ });
+
+ try {
+ input.dispatchEvent(new Event('feature-check-dispatch-event'));
+ } catch(e) {}
+
+ return canDispatch;
+ },
+
+ /**
+ * @this {Node}
+ * @param {!Event} event
+ * @return {boolean}
+ */
+ _dispatchEventFirefoxIE: function(event) {
+ // Due to Firefox bug, events fired on disabled form controls can throw
+ // errors; furthermore, neither IE nor Firefox will actually dispatch
+ // events from disabled form controls; as such, we toggle disable around
+ // the dispatch to allow notifying properties to notify
+ // See issue #47 for details
+ var disabled = this.disabled;
+ this.disabled = false;
+ var defaultPrevented = this._origDispatchEvent(event);
+ this.disabled = disabled;
+ return defaultPrevented
+ },
+
+ get _patternRegExp() {
+ var pattern;
+ if (this.allowedPattern) {
+ pattern = new RegExp(this.allowedPattern);
+ } else {
+ switch (this.type) {
+ case 'number':
+ pattern = /[0-9.,e-]/;
+ break;
+ }
+ }
+ return pattern;
+ },
+
+ ready: function() {
+ this.bindValue = this.value;
+ },
+
+ /**
+ * @suppress {checkTypes}
+ */
+ _bindValueChanged: function() {
+ if (this.value !== this.bindValue) {
+ this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue;
+ }
+ // manually notify because we don't want to notify until after setting value
+ this.fire('bind-value-changed', {value: this.bindValue});
+ },
+
+ _allowedPatternChanged: function() {
+ // Force to prevent invalid input when an `allowed-pattern` is set
+ this.preventInvalidInput = this.allowedPattern ? true : false;
+ },
+
+ _onInput: function() {
+ // Need to validate each of the characters pasted if they haven't
+ // been validated inside `_onKeypress` already.
+ if (this.preventInvalidInput && !this._patternAlreadyChecked) {
+ var valid = this._checkPatternValidity();
+ if (!valid) {
+ this._announceInvalidCharacter('Invalid string of characters not entered.');
+ this.value = this._previousValidInput;
+ }
+ }
+
+ this.bindValue = this.value;
+ this._previousValidInput = this.value;
+ this._patternAlreadyChecked = false;
+ },
+
+ _isPrintable: function(event) {
+ // What a control/printable character is varies wildly based on the browser.
+ // - most control characters (arrows, backspace) do not send a `keypress` event
+ // in Chrome, but the *do* on Firefox
+ // - in Firefox, when they do send a `keypress` event, control chars have
+ // a charCode = 0, keyCode = xx (for ex. 40 for down arrow)
+ // - printable characters always send a keypress event.
+ // - in Firefox, printable chars always have a keyCode = 0. In Chrome, the keyCode
+ // always matches the charCode.
+ // None of this makes any sense.
+
+ // For these keys, ASCII code == browser keycode.
+ var anyNonPrintable =
+ (event.keyCode == 8) || // backspace
+ (event.keyCode == 9) || // tab
+ (event.keyCode == 13) || // enter
+ (event.keyCode == 27); // escape
+
+ // For these keys, make sure it's a browser keycode and not an ASCII code.
+ var mozNonPrintable =
+ (event.keyCode == 19) || // pause
+ (event.keyCode == 20) || // caps lock
+ (event.keyCode == 45) || // insert
+ (event.keyCode == 46) || // delete
+ (event.keyCode == 144) || // num lock
+ (event.keyCode == 145) || // scroll lock
+ (event.keyCode > 32 && event.keyCode < 41) || // page up/down, end, home, arrows
+ (event.keyCode > 111 && event.keyCode < 124); // fn keys
+
+ return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable);
+ },
+
+ _onKeypress: function(event) {
+ if (!this.preventInvalidInput && this.type !== 'number') {
+ return;
+ }
+ var regexp = this._patternRegExp;
+ if (!regexp) {
+ return;
+ }
+
+ // Handle special keys and backspace
+ if (event.metaKey || event.ctrlKey || event.altKey)
+ return;
+
+ // Check the pattern either here or in `_onInput`, but not in both.
+ this._patternAlreadyChecked = true;
+
+ var thisChar = String.fromCharCode(event.charCode);
+ if (this._isPrintable(event) && !regexp.test(thisChar)) {
+ event.preventDefault();
+ this._announceInvalidCharacter('Invalid character ' + thisChar + ' not entered.');
+ }
+ },
+
+ _checkPatternValidity: function() {
+ var regexp = this._patternRegExp;
+ if (!regexp) {
+ return true;
+ }
+ for (var i = 0; i < this.value.length; i++) {
+ if (!regexp.test(this.value[i])) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ /**
+ * Returns true if `value` is valid. The validator provided in `validator` will be used first,
+ * then any constraints.
+ * @return {boolean} True if the value is valid.
+ */
+ validate: function() {
+ // First, check what the browser thinks. Some inputs (like type=number)
+ // behave weirdly and will set the value to "" if something invalid is
+ // entered, but will set the validity correctly.
+ var valid = this.checkValidity();
+
+ // Only do extra checking if the browser thought this was valid.
+ if (valid) {
+ // Empty, required input is invalid
+ if (this.required && this.value === '') {
+ valid = false;
+ } else if (this.hasValidator()) {
+ valid = Polymer.IronValidatableBehavior.validate.call(this, this.value);
+ }
+ }
+
+ this.invalid = !valid;
+ this.fire('iron-input-validate');
+ return valid;
+ },
+
+ _announceInvalidCharacter: function(message) {
+ this.fire('iron-announce', { text: message });
+ }
+ });
+
+ /*
+ The `iron-input-validate` event is fired whenever `validate()` is called.
+ @event iron-input-validate
+ */
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-input/test/disabled-input.html b/catapult/third_party/polymer/components/iron-input/test/disabled-input.html
new file mode 100644
index 00000000..06503e59
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/test/disabled-input.html
@@ -0,0 +1,32 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+
+<dom-module id="disabled-input">
+ <template>
+ <input is="iron-input" bind-value="{{myValue}}" invalid="{{myInvalid}}" disabled id="input">
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'disabled-input',
+ properties: {
+ myValue: {
+ value: 'foo'
+ },
+
+ myInvalid: {
+ value: false
+ }
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-input/test/index.html b/catapult/third_party/polymer/components/iron-input/test/index.html
new file mode 100644
index 00000000..0e3c37ca
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2014 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>iron-input tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'iron-input.html',
+ 'iron-input.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-input/test/iron-input.html b/catapult/third_party/polymer/components/iron-input/test/iron-input.html
new file mode 100644
index 00000000..fddf9ae9
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/test/iron-input.html
@@ -0,0 +1,281 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-input tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../iron-input.html">
+ <link rel="import" href="letters-only.html">
+ <link rel="import" href="disabled-input.html">
+
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <input is="iron-input">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="has-value">
+ <template>
+ <input is="iron-input" value="foobar">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="has-bind-value">
+ <template>
+ <input is="iron-input" bind-value="foobar">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="prevent-invalid-input">
+ <template>
+ <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="prevent-invalid-input-with-pattern">
+ <template>
+ <input is="iron-input" prevent-invalid-input pattern="[a-zA-Z]{3}[0-9]*">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="prevent-invalid-input-has-value">
+ <template>
+ <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]" value="foobar">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="prevent-invalid-input-has-bind-value">
+ <template>
+ <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]" bind-value="foobar">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="automatically-prevent-invalid-input-if-allowed-pattern">
+ <template>
+ <input is="iron-input" allowed-pattern="[0-9]">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="no-validator">
+ <template>
+ <input is="iron-input" pattern="[a-zA-Z]{3}[0-9]*">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="has-validator">
+ <template>
+ <letters-only></letters-only>
+ <input is="iron-input" validator="letters-only" pattern="[0-9]*">
+ </template>
+ </test-fixture>
+
+ <test-fixture id="native-and-custom-validator">
+ <template>
+ <letters-only></letters-only>
+ <input is="iron-input" validator="letters-only" pattern="[a-c]*">
+ </template>
+ </test-fixture>
+
+ <template is="dom-bind" id="bind-to-object">
+ <input is="iron-input" id="input" bind-value="{{foo}}">
+ </template>
+
+ <test-fixture id="disabled-input">
+ <template>
+ <disabled-input></disabled-input>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('setting bindValue sets value', function() {
+ var input = fixture('basic');
+ input.bindValue = 'foobar';
+ assert.equal(input.value, input.bindValue, 'value equals to bindValue');
+ });
+
+ test('changing the input triggers an event', function(done) {
+ var input = fixture('basic');
+
+ input.addEventListener('bind-value-changed', function(value) {
+ assert.equal(input.value, input.bindValue, 'value equals to bindValue');
+ done();
+ });
+
+ input.value = "foo";
+ input._onInput();
+ });
+
+ test('default value sets bindValue', function() {
+ var input = fixture('has-value');
+ assert.equal(input.bindValue, input.value, 'bindValue equals value');
+ });
+
+ test('default bindValue sets value', function() {
+ var input = fixture('has-bind-value');
+ assert.equal(input.value, input.bindValue, 'value equals to bindValue');
+ });
+
+ test('set bindValue to undefined', function() {
+ var scope = document.getElementById('bind-to-object');
+ scope.foo = undefined;
+ assert.ok(!scope.$.input.bindValue, 'bindValue is falsy');
+ assert.ok(!scope.$.input.value, 'value is falsy');
+ });
+
+ test('can validate using a complex regex', function() {
+ var input = fixture('no-validator');
+ input.value = '123';
+ input.validate();
+ assert.isTrue(input.invalid, 'input is invalid');
+ input.value = 'foo';
+ input.validate();
+ assert.isFalse(input.invalid, 'input is valid');
+ input.value = 'foo123';
+ input.validate();
+ assert.isFalse(input.invalid, 'input is valid');
+ });
+
+ test('set bindValue to false', function() {
+ var scope = document.getElementById('bind-to-object');
+ scope.foo = false;
+ assert.isFalse(scope.$.input.bindValue);
+ assert.equal(scope.$.input.value, 'false');
+ });
+
+ test('validator used instead of constraints api if provided', function() {
+ var input = fixture('has-validator')[1];
+ input.value = '123';
+ input.validate();
+ assert.isTrue(input.invalid, 'input is invalid');
+ });
+
+ test('prevent invalid input works in _onInput', function() {
+ var input = fixture('prevent-invalid-input');
+ input.value = '123';
+ input._onInput();
+ assert.equal(input.bindValue, '123');
+
+ input.value = '123foo';
+ input._onInput();
+ assert.equal(input.bindValue, '123');
+ });
+
+ test('inputs can be validated', function() {
+ var input = fixture('prevent-invalid-input-with-pattern');
+ input.value = '123';
+ input._onInput();
+ assert.equal(input.bindValue, '123');
+ input.validate();
+ assert.isTrue(input.invalid, 'input is invalid');
+
+ input.value = 'foo';
+ input._onInput();
+ assert.equal(input.bindValue, 'foo');
+ input.validate();
+ assert.isFalse(input.invalid, 'input is valid');
+
+ input.value = 'foo123';
+ input._onInput();
+ assert.equal(input.bindValue, 'foo123');
+ input.validate();
+ assert.isFalse(input.invalid, 'input is valid');
+ });
+
+ test('prevent invalid input works automatically when allowed pattern is set', function() {
+ var input = fixture('automatically-prevent-invalid-input-if-allowed-pattern');
+ input.value = '123';
+ input._onInput();
+ assert.equal(input.bindValue, '123');
+
+ input.value = '123foo';
+ input._onInput();
+ assert.equal(input.bindValue, '123');
+
+ input.allowedPattern = '';
+ input.value = '#123foo BAR!';
+ input._onInput();
+ assert.equal(input.bindValue, '#123foo BAR!');
+
+ input.allowedPattern = '[a-zA-Z]';
+ input.value = 'foo';
+ input._onInput();
+ input.value = 'bar123';
+ input._onInput();
+ assert.equal(input.bindValue, 'foo');
+ });
+
+ test('disabled input doesn\'t throw on Firefox', function() {
+ var el = fixture('disabled-input');
+ var input = el.$.input;
+
+ assert.equal(input.bindValue, 'foo');
+
+ assert.isFalse(el.myInvalid);
+ assert.isTrue(input.disabled);
+ });
+
+ test('browser validation beats custom validation', function() {
+ var input = fixture('native-and-custom-validator')[1];
+ // The input allows any letters, but the browser only allows one
+ // of [abc].
+ input.value = 'aaaa';
+ input.validate();
+ assert.isFalse(input.invalid, 'input is valid');
+
+ // The validator allows this, but the browser doesn't.
+ input.value = 'zzz';
+ input.validate();
+ assert.isTrue(input.invalid, 'input is invalid');
+ });
+ });
+
+ suite('a11y', function() {
+ test('announces invalid characters when _onInput is called', function() {
+ var input = fixture('prevent-invalid-input');
+ input.addEventListener('iron-announce', function(event) {
+ assert.equal(event.detail.text, 'Invalid string of characters not entered.');
+ });
+ input.value = 'foo';
+ input._onInput();
+ });
+
+ test('announces invalid characters on keypress', function() {
+ var input = fixture('prevent-invalid-input');
+ input.addEventListener('iron-announce', function(event) {
+ assert.equal(event.detail.text, 'Invalid character a not entered.');
+ });
+
+ // Simulate key press event.
+ var event = new CustomEvent('keypress', {
+ bubbles: true,
+ cancelable: true
+ });
+ event.charCode = 97 /* a */;
+ input.dispatchEvent(event);
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-input/test/letters-only.html b/catapult/third_party/polymer/components/iron-input/test/letters-only.html
new file mode 100644
index 00000000..bfc301c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-input/test/letters-only.html
@@ -0,0 +1,30 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-validator-behavior/iron-validator-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'letters-only',
+
+ behaviors: [
+ Polymer.IronValidatorBehavior
+ ],
+
+ validate: function(value) {
+ return !value || value.match(/^[a-zA-Z]*$/) !== null;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/.bower.json b/catapult/third_party/polymer/components/iron-jsonp-library/.bower.json
new file mode 100644
index 00000000..9c383b6b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/.bower.json
@@ -0,0 +1,66 @@
+{
+ "name": "iron-jsonp-library",
+ "version": "2.0.0",
+ "description": "Loads jsonp libraries",
+ "authors": [
+ "Aleks Totic <a@totic.org>",
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-component",
+ "polymer",
+ "behavior"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-jsonp-library.git"
+ },
+ "main": "iron-jsonp-library.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-jsonp-library/",
+ "ignore": [
+ "/.*",
+ "/test/"
+ ],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2",
+ "test-fixture": "PolymerElements/test-fixture#^3.0.0-rc.1",
+ "web-component-tester": "^6.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#1 - 2",
+ "paper-spinner": "PolymerElements/paper-spinner#1 - 2"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.9"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.2",
+ "paper-spinner": "PolymerElements/paper-spinner#^1.0.1",
+ "web-component-tester": "Polymer/web-component-tester#^4.0.0"
+ },
+ "resolutions": {
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ },
+ "_release": "2.0.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v2.0.0",
+ "commit": "61d47ed327b76bd597d1e1800d3040b2279ae329"
+ },
+ "_source": "https://github.com/PolymerElements/iron-jsonp-library.git",
+ "_target": "1 - 2",
+ "_originalSource": "PolymerElements/iron-jsonp-library"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-jsonp-library/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/README.md b/catapult/third_party/polymer/components/iron-jsonp-library/README.md
new file mode 100644
index 00000000..c93e1d6b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/README.md
@@ -0,0 +1,51 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-jsonp-library.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-jsonp-library.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-jsonp-library)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-jsonp-library)_
+
+
+## &lt;iron-jsonp-library&gt;
+
+Loads specified jsonp library.
+
+Example:
+
+```html
+<iron-jsonp-library
+ library-url="https://apis.google.com/js/plusone.js?onload=%%callback%%"
+ notify-event="api-load"
+ library-loaded="{{loaded}}"></iron-jsonp-library>
+```
+
+Will emit 'api-load' event when loaded, and set 'loaded' to true
+
+Implemented by Polymer.IronJsonpLibraryBehavior. Use it
+to create specific library loader elements.
+
+
+
+## Polymer.IronJsonpLibraryBehavior
+
+`Polymer.IronJsonpLibraryBehavior` loads a jsonp library.
+Multiple components can request same library, only one copy will load.
+
+Some libraries require a specific global function be defined.
+If this is the case, specify the `callbackName` property.
+
+You should use an HTML Import to load library dependencies
+when possible instead of using this element.
+
+
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/bower.json b/catapult/third_party/polymer/components/iron-jsonp-library/bower.json
new file mode 100644
index 00000000..ef4c8b7d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/bower.json
@@ -0,0 +1,57 @@
+{
+ "name": "iron-jsonp-library",
+ "version": "2.0.0",
+ "description": "Loads jsonp libraries",
+ "authors": [
+ "Aleks Totic <a@totic.org>",
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-component",
+ "polymer",
+ "behavior"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-jsonp-library.git"
+ },
+ "main": "iron-jsonp-library.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-jsonp-library/",
+ "ignore": [
+ "/.*",
+ "/test/"
+ ],
+ "dependencies": {
+ "polymer": "Polymer/polymer#1.9 - 2"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#1 - 2",
+ "test-fixture": "PolymerElements/test-fixture#^3.0.0-rc.1",
+ "web-component-tester": "^6.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#1 - 2",
+ "paper-spinner": "PolymerElements/paper-spinner#1 - 2"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.9"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.2",
+ "paper-spinner": "PolymerElements/paper-spinner#^1.0.1",
+ "web-component-tester": "Polymer/web-component-tester#^4.0.0"
+ },
+ "resolutions": {
+ "webcomponentsjs": "^0.7"
+ }
+ }
+ },
+ "resolutions": {
+ "webcomponentsjs": "^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/demo/index.html b/catapult/third_party/polymer/components/iron-jsonp-library/demo/index.html
new file mode 100644
index 00000000..13abb9a7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/demo/index.html
@@ -0,0 +1,133 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>iron-jsonp-library Demo</title>
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../../paper-spinner/paper-spinner.html">
+ <link rel="import" href="../iron-jsonp-library.html">
+ <style is="custom-style">
+
+ .loading {
+ color: var(--google-grey-500);
+ }
+
+ .success {
+ color: var(--paper-green-800);
+ }
+
+ .failure {
+ color: var(--paper-red-800);
+ }
+
+ paper-spinner {
+ --paper-spinner-layer-1-color: var(--google-grey-500);
+ --paper-spinner-layer-2-color: var(--google-grey-500);
+ --paper-spinner-layer-3-color: var(--google-grey-500);
+ --paper-spinner-layer-4-color: var(--google-grey-500);
+ }
+ </style>
+ </head>
+ <body>
+
+ <div class="vertical-section vertical-section-container centered">
+ <h1>&lt;iron-jsonp-library&gt;</h1>
+ <dom-bind>
+ <template is="dom-bind">
+ <h3>Good loader</h3>
+ <iron-jsonp-library
+ library-url="https://apis.google.com/js/plusone.js?onload=%%callback%%"
+ notify-event="api-load"
+ library-loaded="{{loaded}}"
+ library-error-message="{{errorMessage}}"></iron-jsonp-library>
+ <template is="dom-if" if="{{loaded}}">
+ <p class="success">The <code>Google+ API</code> has been loaded</p>
+ </template>
+ <template is="dom-if" if="{{!loaded}}">
+ <template is="dom-if" if="{{errorMessage}}">
+ <p class="failure">{{errorMessage}}</p>
+ </template>
+ <template is="dom-if" if="{{!errorMessage}}">
+ <p class="loading">Loading...</p>
+ </template>
+ </template>
+ </template>
+ </dom-bind>
+
+ <hr>
+ <dom-bind>
+ <template is="dom-bind">
+ <h3>Bad loader</h3>
+ <iron-jsonp-library
+ library-url="https://badapis.google.com/js/plusone.js?onload=%%callback%%"
+ notify-event="api-load"
+ library-loaded="{{loaded}}"
+ library-error-message="{{errorMessage}}"></iron-jsonp-library>
+ <template is="dom-if" if="{{loaded}}">
+ <p><code>badapis</code> has been loaded</p>
+ </template>
+ <template is="dom-if" if="{{!loaded}}">
+ <template is="dom-if" if="{{errorMessage}}">
+ <p class="failure">{{errorMessage}}</p>
+ </template>
+ <template is="dom-if" if="{{!errorMessage}}">
+ <p class="loading">Loading...</p>
+ </template>
+ </template>
+ </template>
+ </dom-bind>
+
+ <hr>
+ <dom-bind>
+ <template is="dom-bind" id="delayedLoader">
+ <h3>Delayed libraryUrl loader</h3>
+ <iron-jsonp-library
+ library-url="{{libraryUrl}}"
+ library-loaded="{{loaded}}"
+ library-error-message="{{errorMessage}}">
+ </iron-jsonp-library>
+ <template is="dom-if" if="{{loaded}}">
+ <p><code>{{libraryUrl}}</code> has been loaded</p>
+ </template>
+ <template is="dom-if" if="{{!loaded}}">
+ <template is="dom-if" if="{{errorMessage}}">
+ <p class="failure">{{errorMessage}}</p>
+ </template>
+ <template is="dom-if" if="{{!errorMessage}}">
+ <p class="loading">Loading...<code>{{libraryUrl}}</code></p>
+ </template>
+ </template>
+ </template>
+ </dom-bind>
+
+ </div>
+
+ <script>
+ // kick off delayed loader by setting libraryUrl
+ window.setTimeout(function() {
+ var t = document.querySelector('#delayedLoader');
+
+ if (Polymer.Element) {
+ // value is set on the dom-bind in 2.x
+ t.parentNode.libraryUrl = 'https://apis.google.com/js/drive-realtime.js?onload=%%callback%%';
+ } else {
+ // value is set on template in Polymer 1.x
+ t.libraryUrl = 'https://apis.google.com/js/drive-realtime.js?onload=%%callback%%';
+ }
+ },
+ 1000);
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/hero.svg b/catapult/third_party/polymer/components/iron-jsonp-library/hero.svg
new file mode 100755
index 00000000..b02c565b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/hero.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <circle cx="112" cy="36" r="4"/>
+ <circle cx="112" cy="90" r="4"/>
+ <circle cx="91" cy="98" r="4"/>
+ <circle cx="91" cy="29" r="4"/>
+ <circle cx="133" cy="29" r="4"/>
+ <circle cx="133" cy="97" r="4"/>
+ <circle cx="56" cy="63" r="4"/>
+ <circle cx="168" cy="63" r="4"/>
+ <circle cx="99" cy="63" r="4"/>
+ <circle cx="125" cy="63" r="4"/>
+ <path d="M90.8,98.5c-19.6,0-35.5-15.9-35.5-35.5s15.9-35.5,35.5-35.5s35.5,15.9,35.5,35.5S110.3,98.5,90.8,98.5z M90.8,29.5
+ c-18.5,0-33.5,15-33.5,33.5s15,33.5,33.5,33.5s33.5-15,33.5-33.5S109.2,29.5,90.8,29.5z"/>
+ <path d="M133.2,98.5c-19.6,0-35.5-15.9-35.5-35.5s15.9-35.5,35.5-35.5s35.5,15.9,35.5,35.5S152.8,98.5,133.2,98.5z M133.2,29.5
+ c-18.5,0-33.5,15-33.5,33.5s15,33.5,33.5,33.5s33.5-15,33.5-33.5S151.7,29.5,133.2,29.5z"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/index.html b/catapult/third_party/polymer/components/iron-jsonp-library/index.html
new file mode 100644
index 00000000..487bb5c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-jsonp-library/iron-jsonp-library.html b/catapult/third_party/polymer/components/iron-jsonp-library/iron-jsonp-library.html
new file mode 100644
index 00000000..09277da4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-jsonp-library/iron-jsonp-library.html
@@ -0,0 +1,271 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+(function() {
+ "use strict";
+ /**
+ * `Polymer.IronJsonpLibraryBehavior` loads a jsonp library.
+ * Multiple components can request same library, only one copy will load.
+ *
+ * Some libraries require a specific global function be defined.
+ * If this is the case, specify the `callbackName` property.
+ *
+ * You should use an HTML Import to load library dependencies
+ * when possible instead of using this element.
+ *
+ * @hero hero.svg
+ * @demo demo/index.html
+ * @polymerBehavior
+ */
+ Polymer.IronJsonpLibraryBehavior = {
+
+ properties: {
+ /**
+ * True if library has been successfully loaded
+ */
+ libraryLoaded: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ readOnly: true
+ },
+ /**
+ * Not null if library has failed to load
+ */
+ libraryErrorMessage: {
+ type: String,
+ value: null,
+ notify: true,
+ readOnly: true
+ }
+ // Following properties are to be set by behavior users
+ /**
+ * Library url. Must contain string `%%callback%%`.
+ *
+ * `%%callback%%` is a placeholder for jsonp wrapper function name
+ *
+ * Ex: https://maps.googleapis.com/maps/api/js?callback=%%callback%%
+ * @property libraryUrl
+ */
+ /**
+ * Set if library requires specific callback name.
+ * Name will be automatically generated if not set.
+ * @property callbackName
+ */
+ /**
+ * name of event to be emitted when library loads. Standard is `api-load`
+ * @property notifyEvent
+ */
+ /**
+ * event with name specified in `notifyEvent` attribute
+ * will fire upon successful load2
+ * @event `notifyEvent`
+ */
+ },
+
+ observers: [
+ '_libraryUrlChanged(libraryUrl)'
+ ],
+
+ _libraryUrlChanged: function(libraryUrl) {
+ // can't load before ready because notifyEvent might not be set
+ if (this._isReady && this.libraryUrl)
+ this._loadLibrary();
+ },
+
+ _libraryLoadCallback: function(err, result) {
+ if (err) {
+ Polymer.Base._warn("Library load failed:", err.message);
+ this._setLibraryErrorMessage(err.message);
+ }
+ else {
+ this._setLibraryErrorMessage(null);
+ this._setLibraryLoaded(true);
+ if (this.notifyEvent)
+ this.fire(this.notifyEvent, result, {composed: true});
+ }
+ },
+
+ /** loads the library, and fires this.notifyEvent upon completion */
+ _loadLibrary: function() {
+ LoaderMap.require(
+ this.libraryUrl,
+ this._libraryLoadCallback.bind(this),
+ this.callbackName
+ );
+ },
+
+ ready: function() {
+ this._isReady = true;
+ if (this.libraryUrl)
+ this._loadLibrary();
+ }
+ };
+
+ /**
+ * LoaderMap keeps track of all Loaders
+ */
+ var LoaderMap = {
+ apiMap: {}, // { hash -> Loader }
+
+ /**
+ * @param {Function} notifyCallback loaded callback fn(result)
+ * @param {string} jsonpCallbackName name of jsonpcallback. If API does not provide it, leave empty. Optional.
+ */
+ require: function(url, notifyCallback, jsonpCallbackName) {
+
+ // make hashable string form url
+ var name = this.nameFromUrl(url);
+
+ // create a loader as needed
+ if (!this.apiMap[name])
+ this.apiMap[name] = new Loader(name, url, jsonpCallbackName);
+
+ // ask for notification
+ this.apiMap[name].requestNotify(notifyCallback);
+ },
+
+ nameFromUrl: function(url) {
+ return url.replace(/[\:\/\%\?\&\.\=\-\,]/g, '_') + '_api';
+ }
+ };
+
+ /** @constructor */
+ var Loader = function(name, url, callbackName) {
+ this.notifiers = []; // array of notifyFn [ notifyFn* ]
+
+ // callback is specified either as callback name
+ // or computed dynamically if url has callbackMacro in it
+ if (!callbackName) {
+ if (url.indexOf(this.callbackMacro) >= 0) {
+ callbackName = name + '_loaded';
+ url = url.replace(this.callbackMacro, callbackName);
+ } else {
+ this.error = new Error('IronJsonpLibraryBehavior a %%callback%% parameter is required in libraryUrl');
+ // TODO(sjmiles): we should probably fallback to listening to script.load
+ return;
+ }
+ }
+ this.callbackName = callbackName;
+ window[this.callbackName] = this.success.bind(this);
+ this.addScript(url);
+ };
+
+ Loader.prototype = {
+
+ callbackMacro: '%%callback%%',
+ loaded: false,
+
+ addScript: function(src) {
+ var script = document.createElement('script');
+ script.src = src;
+ script.onerror = this.handleError.bind(this);
+ var s = document.querySelector('script') || document.body;
+ s.parentNode.insertBefore(script, s);
+ this.script = script;
+ },
+
+ removeScript: function() {
+ if (this.script.parentNode) {
+ this.script.parentNode.removeChild(this.script);
+ }
+ this.script = null;
+ },
+
+ handleError: function(ev) {
+ this.error = new Error("Library failed to load");
+ this.notifyAll();
+ this.cleanup();
+ },
+
+ success: function() {
+ this.loaded = true;
+ this.result = Array.prototype.slice.call(arguments);
+ this.notifyAll();
+ this.cleanup();
+ },
+
+ cleanup: function() {
+ delete window[this.callbackName];
+ },
+
+ notifyAll: function() {
+ this.notifiers.forEach( function(notifyCallback) {
+ notifyCallback(this.error, this.result);
+ }.bind(this));
+ this.notifiers = [];
+ },
+
+ requestNotify: function(notifyCallback) {
+ if (this.loaded || this.error) {
+ notifyCallback( this.error, this.result);
+ } else {
+ this.notifiers.push(notifyCallback);
+ }
+ }
+ };
+})();
+</script>
+
+<!--
+ Loads specified jsonp library.
+
+ Example:
+
+ <iron-jsonp-library
+ library-url="https://apis.google.com/js/plusone.js?onload=%%callback%%"
+ notify-event="api-load"
+ library-loaded="{{loaded}}"></iron-jsonp-library>
+
+ Will emit 'api-load' event when loaded, and set 'loaded' to true
+
+ Implemented by Polymer.IronJsonpLibraryBehavior. Use it
+ to create specific library loader elements.
+
+ @demo
+-->
+<script>
+ Polymer({
+
+ is: 'iron-jsonp-library',
+
+ behaviors: [ Polymer.IronJsonpLibraryBehavior ],
+
+ properties: {
+ /**
+ * Library url. Must contain string `%%callback%%`.
+ *
+ * `%%callback%%` is a placeholder for jsonp wrapper function name
+ *
+ * Ex: https://maps.googleapis.com/maps/api/js?callback=%%callback%%
+ */
+ libraryUrl: String,
+ /**
+ * Set if library requires specific callback name.
+ * Name will be automatically generated if not set.
+ */
+ callbackName: String,
+ /**
+ * event with name specified in 'notifyEvent' attribute
+ * will fire upon successful load
+ */
+ notifyEvent: String
+ /**
+ * event with name specified in 'notifyEvent' attribute
+ * will fire upon successful load
+ * @event `notifyEvent`
+ */
+
+ }
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-location/.bower.json b/catapult/third_party/polymer/components/iron-location/.bower.json
new file mode 100644
index 00000000..c5d8ceb3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/.bower.json
@@ -0,0 +1,49 @@
+{
+ "name": "iron-location",
+ "version": "0.8.11",
+ "description": "Bidirectional data binding into the page's URL.",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "routing"
+ ],
+ "main": [
+ "iron-location.html",
+ "iron-query-params.html"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-location.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-location",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "paper-input": "polymerelements/paper-input#^1.0.0",
+ "paper-slider": "polymerelements/paper-slider#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.3"
+ },
+ "_release": "0.8.11",
+ "_resolution": {
+ "type": "version",
+ "tag": "v0.8.11",
+ "commit": "0df572a3ba0a6f81cd624693b1486bde0d187100"
+ },
+ "_source": "https://github.com/PolymerElements/iron-location.git",
+ "_target": "^0.8.1",
+ "_originalSource": "PolymerElements/iron-location"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-location/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-location/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..e3b4e289
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-location/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/lebawa/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-location/.gitignore b/catapult/third_party/polymer/components/iron-location/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-location/.travis.yml b/catapult/third_party/polymer/components/iron-location/.travis.yml
new file mode 100644
index 00000000..90371c3b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+env:
+ global:
+ - secure: >-
+ LSHof0ip5tJswHk4wAIXXsf8Z0WL44Y1pB1XmqFtIsjQLl+2K5bdhRq6Hd6RUZYcpTL29cMStCLQJEG1hxnMks37omHuVFWRtk0NaYlGnJY4i/m/ukUBmM7Yr7LROWRnBgU75PVUvc4YMbCjvlQ9KKjdgBbQBFH2KjhKr9GUN+ihB1yaKRMADvFtC6JWisG4RPv1pRxF6UgvGkOsaCdVb9H9AbKA0psAI1cpg8IMGgUBslbwhYw+viMEQdZ5lW++T3Tb9cwzCVaTqBgvkQCXW4I6T4En9yvUWbV0Ry8uChchvs6BJfwpgN/nksYnLCbCLmN+Rw5eU0+XyWZDLiki/HpsNQblqVEW7vHo1hmtpK24GUv2g8kuE1OjjW7K/l+Q20D8FJFIcPdFjmZ/XgLWxdtXbgNC8n4Z1XBEqKuV1BJ29ojvZk5a5/gFBf9PWdMNTVwSepk7+LoEVQW17vzSHT/HDF7WYEs14uZ0x9I0AV2+339RG7iCcVFMZKYgIif0DmZVysnm0Z9j+wRRk227JpiF9MHeVkFUaXl3+3XIQMz04t+H1wOXiDRlJLJG9IUrzFs+KYuCA+OxNc0XrxjA6Zkl75AQAjAY5vMEWYD+N2OGjkfwD7dB4JtltmZ4llMhEZUt/RPLeZJvL4Sus8hfA6SNG9/tdVgef0mQ91acxZg=
+ - secure: >-
+ h0j4eT4lJqbDkEVz4OjkPuAHxxT0Cy7bWbT71oDllhDAvl9zXxIn5BHSnCaUfO0xUhXVpwNuVmDzOE6yQETJ3MamoqL2Hrbg0VCr4hux9/2ipa4tlCTjQ2Y/YaIoID7CpCchdoOQAjrxeg0ZhpMnwxV18NHTqlxNrsdoSDDSmCdffWQwgjVI07O4h1ISIWx6oY0i1Z+9nJI001iZIw5vGopK8zACa5hfSu9my8Nog5gYOBOEoYmT8lNBaKkNuZacGAkHmXPmeeWiflM49sRO5MfyCgVvNtn6yohXs5D1QD2KUZs4mTzVBebT3vPuV7I9NFd+ZY9GT9YeH9BLuxKpYhGlnjL2HhGAvGjIcKX8315EOCpVvgwX3VE1f9PJe3K9q6LP/sFUqf8cUrbj9FhiAVp79IPJkvLUc+1hUG3iQ8X83mjQxcrCt6I50BINOLgjsfzCmnvv12g3FuRry2EBRxEultvPLRCNc2bJCPqol6kiWzs8ySOPXCYOcXqW5/6Z4DYLMTYevapkJZhErcTBuIH7rXEX4tGsdBjtGjTepcW7k2dsCElrul79czZA3AwdLEK/fuygxXR8eMCeS8Tdh4+Ba4M9TfXtAjjnR30NayTOCwyWW9IvTJeWeXKd6SilJcGVO7r546ayGWJ2G+Fltyi7RGixMplRMm8y6YfeM9k=
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-location/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-location/CONTRIBUTING.md
new file mode 100644
index 00000000..cfda1a55
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/lebawa/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/lebawa/edit?html,output](https://jsbin.com/lebawa/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-location/README.md b/catapult/third_party/polymer/components/iron-location/README.md
new file mode 100644
index 00000000..702bd5eb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/README.md
@@ -0,0 +1,58 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-location.html iron-query-params.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-location.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-location)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-location)_
+
+
+##&lt;iron-location&gt;
+
+The `iron-location` element manages binding to and from the current URL.
+
+iron-location is the first, and lowest level element in the Polymer team's
+routing system. This is a beta release of iron-location as we continue work
+on higher level elements, and as such iron-location may undergo breaking
+changes.
+
+#### Properties
+
+When the URL is: `/search?query=583#details` iron-location's properties will be:
+
+* path: `'/search'`
+* query: `'query=583'`
+* hash: `'details'`
+
+These bindings are bidirectional. Modifying them will in turn modify the URL.
+
+iron-location is only active while it is attached to the document.
+
+#### Links
+
+While iron-location is active in the document it will intercept clicks on links
+within your site, updating the URL pushing the updated URL out through the
+databinding system. iron-location only intercepts clicks with the intent to
+open in the same window, so middle mouse clicks and ctrl/cmd clicks work fine.
+
+You can customize this behavior with the `urlSpaceRegex`.
+
+#### Dwell Time
+
+iron-location protects against accidental history spamming by only adding
+entries to the user's history if the URL stays unchanged for `dwellTime`
+milliseconds.
+
+
+
+<!-- No docs for <iron-query-params> found. -->
diff --git a/catapult/third_party/polymer/components/iron-location/bower.json b/catapult/third_party/polymer/components/iron-location/bower.json
new file mode 100644
index 00000000..9935b0b9
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "iron-location",
+ "version": "0.8.11",
+ "description": "Bidirectional data binding into the page's URL.",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "routing"
+ ],
+ "main": [
+ "iron-location.html",
+ "iron-query-params.html"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-location.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-location",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+ "paper-input": "polymerelements/paper-input#^1.0.0",
+ "paper-slider": "polymerelements/paper-slider#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.3"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-location/demo/index.html b/catapult/third_party/polymer/components/iron-location/demo/index.html
new file mode 100644
index 00000000..19d80bbd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/demo/index.html
@@ -0,0 +1,126 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-location</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../iron-location.html">
+ <link rel="import" href="../../paper-styles/typography.html">
+ <link rel="import" href="../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../paper-input/paper-input.html">
+ <link rel="import" href="../../paper-slider/paper-slider.html">
+ <link rel="import" href="../../iron-demo-helpers/url-bar.html">
+ <link rel="stylesheet" href="../../paper-styles/demo.css">
+</head>
+<body>
+ <url-bar></url-bar>
+
+ <dom-module id="iron-location-demo">
+ <template>
+ <style>
+ div.inputs {
+ margin-bottom: 15px;
+ }
+ label {
+ display: inline-block;
+ width: 100px;
+ }
+
+ h3 {
+ padding: 0;
+ margin: 0;
+ }
+
+ .inputs, .history_entries {
+ @apply(--layout-vertical);
+ @apply(--layout-flex);
+ padding: 20px;
+ max-width: 500px;
+ }
+
+ .container {
+ @apply(--layout-horizontal);
+ }
+ </style>
+ <iron-location path="{{path}}" hash="{{hash}}" query="{{query}}"
+ dwell-time="{{dwellTime}}">
+ </iron-location>
+
+ <div class="container">
+ <div class="inputs">
+ <h3>URL</h3>
+ <paper-input label="path" value="{{path}}" always-float-label>
+ </paper-input>
+ <paper-input label="hash" value="{{hash}}" always-float-label>
+ </paper-input>
+ <paper-input label='query' value='{{query}}' always-float-label>
+ </paper-input>
+ </div>
+ <div class="history_entries">
+ <h3>Dwell Time</h3>
+ <p>
+ iron-location won't add extraneous entries to the browser's history
+ when changes come in quick succession.
+ </p>
+ <p>
+ A new history entry will only be added if iron-location stays in
+ the same state longer than dwellTime.
+ </p>
+ <div>
+ <label>History added: </label>
+ {{historyElementsAdded}} entries
+ </div>
+ <div>
+ <label>Dwell time:</label>
+ {{inSeconds(dwellTime)}}s
+ </div>
+ <paper-slider min="-1" max="5000" value="2000" step="100"
+ immediate-value="{{dwellTime}}">
+ </paper-slider>
+ </div>
+ </div>
+ </template>
+ <script>
+ window.addEventListener('WebComponentsReady', function() {
+ Polymer({
+ is: 'iron-location-demo',
+ properties: {
+ historyElementsAdded: {
+ type: Number
+ }
+ },
+ observers: [
+ 'checkHistorySize(path, hash, query, startingHistoryLength)'
+ ],
+ ready: function() {
+ this.startingHistoryLength = window.history.length;
+ },
+ checkHistorySize: function() {
+ this.historyElementsAdded =
+ window.history.length - this.startingHistoryLength;
+ },
+ inSeconds: function(dwellTime) {
+ if (dwellTime === -1) {
+ return -1;
+ }
+ return (Math.round(dwellTime / 100) / 10)
+ }
+ });
+ });
+ </script>
+ </dom-module>
+
+ <iron-location-demo></iron-location-demo>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-location/demo/iron-query-params.html b/catapult/third_party/polymer/components/iron-location/demo/iron-query-params.html
new file mode 100644
index 00000000..a7d753a7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/demo/iron-query-params.html
@@ -0,0 +1,119 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-query-params</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-query-params.html">
+ <link rel="import" href="../../paper-styles/classes/typography.html">
+ <link rel="import" href="../../iron-flex-layout/classes/iron-flex-layout.html">
+ <link rel="import" href="../../paper-input/paper-input.html">
+ <link rel="stylesheet" href="../../paper-styles/demo.css">
+</head>
+<body>
+
+ <dom-module id='iron-query-params-demo'>
+ <template>
+ <style>
+ div.inputs {
+ margin-bottom: 15px;
+ }
+ label {
+ display: inline-block;
+ width: 100px;
+ }
+ span.seconds {
+ }
+ [layout] {
+ @apply(--layout);
+ }
+ [layout][horizontal] {
+ @apply(--layout-horizontal);
+ }
+ [layout][horizontal] > div {
+ padding: 20px;
+ max-width: 500px;
+ }
+ [layout][vertical] {
+ @apply(--layout-vertical);
+ }
+ [layout][flex] {
+ @apply(--layout-flex);
+ }
+ h3 {
+ padding: 0;
+ margin: 0;
+ }
+ </style>
+ <iron-query-params
+ params-string='{{paramString}}' params-object='{{params}}'>
+ </iron-query-params>
+
+ <div layout horizontal>
+ <div layout vertical flex class='inputs'>
+ <paper-input label='params as string'
+ value='{{paramString}}' always-float-label>
+ </paper-input>
+ <paper-input label='params as object' value='{{paramsString}}'
+ invalid='{{paramsInvalid}}'
+ error-message='Should be legal JSON'
+ always-float-label>
+ </paper-input>
+ </div>
+ </div>
+ </template>
+ <script>
+ window.addEventListener('WebComponentsReady', function() {
+ Polymer({
+ is: 'iron-query-params-demo',
+ properties: {
+ paramsString: {
+ observer: 'paramsStringChanged'
+ },
+ params: {
+ observer: 'paramsChanged'
+ },
+ paramsInvalid: {
+ value: false,
+ },
+ },
+ paramsStringChanged: function() {
+ if (this.ignore) {
+ return;
+ }
+ this.ignore = true;
+ try {
+ this.params = JSON.parse(this.paramsString);
+ this.paramsInvalid = false;
+ } catch(_) {
+ this.paramsInvalid = true;
+ }
+ this.ignore = false;
+ },
+ paramsChanged: function() {
+ if (this.ignore) {
+ return;
+ }
+ this.ignore = true;
+ this.paramsString = JSON.stringify(this.params);
+ this.paramsInvalid = false;
+ this.ignore = false;
+ }
+ });
+ });
+ </script>
+ </dom-module>
+
+ <iron-query-params-demo></iron-query-params-demo>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-location/index.html b/catapult/third_party/polymer/components/iron-location/index.html
new file mode 100644
index 00000000..8ded2107
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-location</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-location/iron-location.html b/catapult/third_party/polymer/components/iron-location/iron-location.html
new file mode 100644
index 00000000..eda14063
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/iron-location.html
@@ -0,0 +1,333 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!--
+
+The `iron-location` element manages binding to and from the current URL.
+
+iron-location is the first, and lowest level element in the Polymer team's
+routing system. This is a beta release of iron-location as we continue work
+on higher level elements, and as such iron-location may undergo breaking
+changes.
+
+#### Properties
+
+When the URL is: `/search?query=583#details` iron-location's properties will be:
+
+ - path: `'/search'`
+ - query: `'query=583'`
+ - hash: `'details'`
+
+These bindings are bidirectional. Modifying them will in turn modify the URL.
+
+iron-location is only active while it is attached to the document.
+
+#### Links
+
+While iron-location is active in the document it will intercept clicks on links
+within your site, updating the URL pushing the updated URL out through the
+databinding system. iron-location only intercepts clicks with the intent to
+open in the same window, so middle mouse clicks and ctrl/cmd clicks work fine.
+
+You can customize this behavior with the `urlSpaceRegex`.
+
+#### Dwell Time
+
+iron-location protects against accidental history spamming by only adding
+entries to the user's history if the URL stays unchanged for `dwellTime`
+milliseconds.
+
+@demo demo/index.html
+
+ -->
+<script>
+ (function() {
+ 'use strict';
+
+ Polymer({
+ is: 'iron-location',
+ properties: {
+ /**
+ * The pathname component of the URL.
+ */
+ path: {
+ type: String,
+ notify: true,
+ value: function() {
+ return window.decodeURIComponent(window.location.pathname);
+ }
+ },
+ /**
+ * The query string portion of the URL.
+ */
+ query: {
+ type: String,
+ notify: true,
+ value: function() {
+ return window.location.search.slice(1);
+ }
+ },
+ /**
+ * The hash component of the URL.
+ */
+ hash: {
+ type: String,
+ notify: true,
+ value: function() {
+ return window.decodeURIComponent(window.location.hash.slice(1));
+ }
+ },
+ /**
+ * If the user was on a URL for less than `dwellTime` milliseconds, it
+ * won't be added to the browser's history, but instead will be replaced
+ * by the next entry.
+ *
+ * This is to prevent large numbers of entries from clogging up the user's
+ * browser history. Disable by setting to a negative number.
+ */
+ dwellTime: {
+ type: Number,
+ value: 2000
+ },
+
+ /**
+ * A regexp that defines the set of URLs that should be considered part
+ * of this web app.
+ *
+ * Clicking on a link that matches this regex won't result in a full page
+ * navigation, but will instead just update the URL state in place.
+ *
+ * This regexp is given everything after the origin in an absolute
+ * URL. So to match just URLs that start with /search/ do:
+ * url-space-regex="^/search/"
+ *
+ * @type {string|RegExp}
+ */
+ urlSpaceRegex: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * urlSpaceRegex, but coerced into a regexp.
+ *
+ * @type {RegExp}
+ */
+ _urlSpaceRegExp: {
+ computed: '_makeRegExp(urlSpaceRegex)'
+ },
+
+ _lastChangedAt: {
+ type: Number
+ },
+
+ _initialized: {
+ type: Boolean,
+ value: false
+ }
+ },
+ hostAttributes: {
+ hidden: true
+ },
+ observers: [
+ '_updateUrl(path, query, hash)'
+ ],
+ attached: function() {
+ this.listen(window, 'hashchange', '_hashChanged');
+ this.listen(window, 'location-changed', '_urlChanged');
+ this.listen(window, 'popstate', '_urlChanged');
+ this.listen(/** @type {!HTMLBodyElement} */(document.body), 'click', '_globalOnClick');
+ // Give a 200ms grace period to make initial redirects without any
+ // additions to the user's history.
+ this._lastChangedAt = window.performance.now() - (this.dwellTime - 200);
+
+ this._initialized = true;
+ this._urlChanged();
+ },
+ detached: function() {
+ this.unlisten(window, 'hashchange', '_hashChanged');
+ this.unlisten(window, 'location-changed', '_urlChanged');
+ this.unlisten(window, 'popstate', '_urlChanged');
+ this.unlisten(/** @type {!HTMLBodyElement} */(document.body), 'click', '_globalOnClick');
+ this._initialized = false;
+ },
+ _hashChanged: function() {
+ this.hash = window.decodeURIComponent(window.location.hash.substring(1));
+ },
+ _urlChanged: function() {
+ // We want to extract all info out of the updated URL before we
+ // try to write anything back into it.
+ //
+ // i.e. without _dontUpdateUrl we'd overwrite the new path with the old
+ // one when we set this.hash. Likewise for query.
+ this._dontUpdateUrl = true;
+ this._hashChanged();
+ this.path = window.decodeURIComponent(window.location.pathname);
+ this.query = window.location.search.substring(1);
+ this._dontUpdateUrl = false;
+ this._updateUrl();
+ },
+ _getUrl: function() {
+ var partiallyEncodedPath = window.encodeURI(
+ this.path).replace(/\#/g, '%23').replace(/\?/g, '%3F');
+ var partiallyEncodedQuery = '';
+ if (this.query) {
+ partiallyEncodedQuery = '?' + this.query.replace(/\#/g, '%23');
+ }
+ var partiallyEncodedHash = '';
+ if (this.hash) {
+ partiallyEncodedHash = '#' + window.encodeURI(this.hash);
+ }
+ return (
+ partiallyEncodedPath + partiallyEncodedQuery + partiallyEncodedHash);
+ },
+ _updateUrl: function() {
+ if (this._dontUpdateUrl || !this._initialized) {
+ return;
+ }
+ if (this.path === window.decodeURIComponent(window.location.pathname) &&
+ this.query === window.location.search.substring(1) &&
+ this.hash === window.decodeURIComponent(
+ window.location.hash.substring(1))) {
+ // Nothing to do, the current URL is a representation of our properties.
+ return;
+ }
+ var newUrl = this._getUrl();
+ // Need to use a full URL in case the containing page has a base URI.
+ var fullNewUrl = new URL(
+ newUrl, window.location.protocol + '//' + window.location.host).href;
+ var now = window.performance.now();
+ var shouldReplace =
+ this._lastChangedAt + this.dwellTime > now;
+ this._lastChangedAt = now;
+ if (shouldReplace) {
+ window.history.replaceState({}, '', fullNewUrl);
+ } else {
+ window.history.pushState({}, '', fullNewUrl);
+ }
+ this.fire('location-changed', {}, {node: window});
+ },
+ /**
+ * A necessary evil so that links work as expected. Does its best to
+ * bail out early if possible.
+ *
+ * @param {MouseEvent} event .
+ */
+ _globalOnClick: function(event) {
+ // If another event handler has stopped this event then there's nothing
+ // for us to do. This can happen e.g. when there are multiple
+ // iron-location elements in a page.
+ if (event.defaultPrevented) {
+ return;
+ }
+ var href = this._getSameOriginLinkHref(event);
+ if (!href) {
+ return;
+ }
+ event.preventDefault();
+ // If the navigation is to the current page we shouldn't add a history
+ // entry or fire a change event.
+ if (href === window.location.href) {
+ return;
+ }
+ window.history.pushState({}, '', href);
+ this.fire('location-changed', {}, {node: window});
+ },
+ /**
+ * Returns the absolute URL of the link (if any) that this click event
+ * is clicking on, if we can and should override the resulting full
+ * page navigation. Returns null otherwise.
+ *
+ * @param {MouseEvent} event .
+ * @return {string?} .
+ */
+ _getSameOriginLinkHref: function(event) {
+ // We only care about left-clicks.
+ if (event.button !== 0) {
+ return null;
+ }
+ // We don't want modified clicks, where the intent is to open the page
+ // in a new tab.
+ if (event.metaKey || event.ctrlKey) {
+ return null;
+ }
+ var eventPath = Polymer.dom(event).path;
+ var anchor = null;
+ for (var i = 0; i < eventPath.length; i++) {
+ var element = eventPath[i];
+ if (element.tagName === 'A' && element.href) {
+ anchor = element;
+ break;
+ }
+ }
+
+ // If there's no link there's nothing to do.
+ if (!anchor) {
+ return null;
+ }
+
+ // Target blank is a new tab, don't intercept.
+ if (anchor.target === '_blank') {
+ return null;
+ }
+ // If the link is for an existing parent frame, don't intercept.
+ if ((anchor.target === '_top' ||
+ anchor.target === '_parent') &&
+ window.top !== window) {
+ return null;
+ }
+
+ var href = anchor.href;
+
+ // It only makes sense for us to intercept same-origin navigations.
+ // pushState/replaceState don't work with cross-origin links.
+ var url;
+ if (document.baseURI != null) {
+ url = new URL(href, /** @type {string} */(document.baseURI));
+ } else {
+ url = new URL(href);
+ }
+
+ var origin;
+
+ // IE Polyfill
+ if (window.location.origin) {
+ origin = window.location.origin;
+ } else {
+ origin = window.location.protocol + '//' + window.location.hostname;
+
+ if (window.location.port) {
+ origin += ':' + window.location.port;
+ }
+ }
+
+ if (url.origin !== origin) {
+ return null;
+ }
+ var normalizedHref = url.pathname + url.search + url.hash;
+
+ // If we've been configured not to handle this url... don't handle it!
+ if (this._urlSpaceRegExp &&
+ !this._urlSpaceRegExp.test(normalizedHref)) {
+ return null;
+ }
+ // Need to use a full URL in case the containing page has a base URI.
+ var fullNormalizedHref = new URL(
+ normalizedHref, window.location.href).href;
+ return fullNormalizedHref;
+ },
+ _makeRegExp: function(urlSpaceRegex) {
+ return RegExp(urlSpaceRegex);
+ }
+ });
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-location/iron-query-params.html b/catapult/third_party/polymer/components/iron-location/iron-query-params.html
new file mode 100644
index 00000000..edcc3e1f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/iron-query-params.html
@@ -0,0 +1,89 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+
+<script>
+ 'use strict';
+
+ Polymer({
+ is: 'iron-query-params',
+ properties: {
+ paramsString: {
+ type: String,
+ notify: true,
+ observer: 'paramsStringChanged',
+ },
+ paramsObject: {
+ type: Object,
+ notify: true,
+ value: function() {
+ return {};
+ }
+ },
+ _dontReact: {
+ type: Boolean,
+ value: false
+ }
+ },
+ hostAttributes: {
+ hidden: true
+ },
+ observers: [
+ 'paramsObjectChanged(paramsObject.*)'
+ ],
+ paramsStringChanged: function() {
+ this._dontReact = true;
+ this.paramsObject = this._decodeParams(this.paramsString);
+ this._dontReact = false;
+ },
+ paramsObjectChanged: function() {
+ if (this._dontReact) {
+ return;
+ }
+ this.paramsString = this._encodeParams(this.paramsObject)
+ .replace(/%3F/g, '?').replace(/%2F/g, '/').replace(/'/g, '%27');
+ },
+ _encodeParams: function(params) {
+ var encodedParams = [];
+ for (var key in params) {
+ var value = params[key];
+ if (value === '') {
+ encodedParams.push(encodeURIComponent(key));
+ } else if (value) {
+ encodedParams.push(
+ encodeURIComponent(key) +
+ '=' +
+ encodeURIComponent(value.toString())
+ );
+ }
+ }
+ return encodedParams.join('&');
+ },
+ _decodeParams: function(paramString) {
+ var params = {};
+
+ // Work around a bug in decodeURIComponent where + is not
+ // converted to spaces:
+ paramString = (paramString || '').replace(/\+/g, '%20');
+
+ var paramList = paramString.split('&');
+ for (var i = 0; i < paramList.length; i++) {
+ var param = paramList[i].split('=');
+ if (param[0]) {
+ params[decodeURIComponent(param[0])] =
+ decodeURIComponent(param[1] || '');
+ }
+ }
+ return params;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-location/test/index.html b/catapult/third_party/polymer/components/iron-location/test/index.html
new file mode 100644
index 00000000..ce67fdf3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'iron-location.html',
+ 'iron-query-params.html',
+ 'initialization-tests.html',
+ 'integration.html'
+ ]);
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-location/test/initialization-cases.html b/catapult/third_party/polymer/components/iron-location/test/initialization-cases.html
new file mode 100644
index 00000000..f8bde4f5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/initialization-cases.html
@@ -0,0 +1,277 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel='import' href='../iron-location.html'>
+<link rel='import' href='../../polymer/polymer.html'>
+
+<script>
+ Polymer({
+ is: 'default-value',
+ properties: {
+ val: {
+ type: String,
+ notify: true,
+ value: 'default-value'
+ }
+ },
+ });
+
+ Polymer({
+ is: 'on-attached',
+ properties: {
+ val: {
+ type: String,
+ notify: true,
+ value: 'on-attached-default-value'
+ }
+ },
+ attached: function() {
+ if (this.val === 'on-attached-default-value') {
+ this.val = 'on-attached';
+ }
+ }
+ });
+
+ Polymer({
+ is: 'on-ready',
+ properties: {
+ val: {
+ type: String,
+ notify: true,
+ value: 'on-ready-default-value'
+ }
+ },
+ ready: function() {
+ this.val = 'on-ready';
+ }
+ });
+
+ Polymer({
+ is: 'on-timeout',
+ properties: {
+ val: {
+ type: String,
+ notify: true,
+ value: 'on-timeout-default-value'
+ }
+ },
+ attached: function() {
+ setTimeout(function() {
+ this.val = 'on-timeout';
+ }.bind(this), 10);
+ }
+ })
+</script>
+
+<dom-module id='default-before'>
+ <template>
+ <default-value value='{{val}}'></default-value>
+ <iron-location query='{{val}}'></iron-location>
+
+ </template>
+ <script>Polymer({is: 'default-before', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='attached-before'>
+ <template>
+ <on-attached val='{{val}}'></on-attached>
+ <iron-location query='{{val}}'></iron-location>
+ </template>
+ <script>Polymer({is: 'attached-before', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='ready-before'>
+ <template>
+ <on-ready val='{{val}}'></on-ready>
+ <iron-location query='{{val}}'></iron-location>
+ </template>
+ <script>Polymer({is: 'ready-before', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='timeout-before'>
+ <template>
+ <on-timeout val='{{val}}'></on-timeout>
+ <iron-location query='{{val}}'></iron-location>
+ </template>
+ <script>Polymer({is: 'timeout-before', properties: {val: {type: String}}});</script>
+</dom-module>
+
+
+<dom-module id='default-after'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ <default-value value='{{val}}'></default-value>
+ </template>
+ <script>Polymer({is: 'default-after', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='attached-after'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ <on-attached val='{{val}}'></on-attached>
+ </template>
+ <script>Polymer({is: 'attached-after', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='ready-after'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ <on-ready val='{{val}}'></on-ready>
+ </template>
+ <script>Polymer({is: 'ready-after', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='timeout-after'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ <on-timeout val='{{val}}'></on-timeout>
+ </template>
+ <script>Polymer({is: 'timeout-after', properties: {val: {type: String}}});</script>
+</dom-module>
+
+
+<dom-module id='default-below'>
+ <template>
+ <iron-location query='{{val}}'>
+ <default-value value='{{val}}'></default-value>
+ </iron-location>
+ </template>
+ <script>Polymer({is: 'default-below', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='attached-below'>
+ <template>
+ <iron-location query='{{val}}'>
+ <on-attached val='{{val}}'></on-attached>
+ </iron-location>
+ </template>
+ <script>Polymer({is: 'attached-below', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='ready-below'>
+ <template>
+ <iron-location query='{{val}}'>
+ <on-ready val='{{val}}'></on-ready>
+ </iron-location>
+ </template>
+ <script>Polymer({is: 'ready-below', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='timeout-below'>
+ <template>
+ <iron-location query='{{val}}'>
+ <on-timeout val='{{val}}'></on-timeout>
+ </iron-location>
+ </template>
+ <script>Polymer({is: 'timeout-below', properties: {val: {type: String}}});</script>
+</dom-module>
+
+
+<dom-module id='default-above'>
+ <template>
+ <default-value value='{{val}}'>
+ <iron-location query='{{val}}'></iron-location>
+ </default-value>
+ </template>
+ <script>Polymer({is: 'default-above', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='attached-above'>
+ <template>
+ <on-attached val='{{val}}'>
+ <iron-location query='{{val}}'>
+ </iron-location>
+ </on-attached>
+ </template>
+ <script>Polymer({is: 'attached-above', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='ready-above'>
+ <template>
+ <on-ready val='{{val}}'>
+ <iron-location query='{{val}}'>
+ </iron-location>
+ </on-ready>
+ </template>
+ <script>Polymer({is: 'ready-above', properties: {val: {type: String}}});</script>
+</dom-module>
+
+<dom-module id='timeout-above'>
+ <template>
+ <on-timeout val='{{val}}'>
+ <iron-location query='{{val}}'></iron-location>
+ </on-timeout>
+ </template>
+ <script>Polymer({is: 'timeout-above', properties: {val: {type: String}}});</script>
+</dom-module>
+
+
+<dom-module id='default-container'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ </template>
+ <script>
+ Polymer({
+ is: 'default-container',
+ properties: {val: {type: String, value: 'default-container-val'}}
+ });
+ </script>
+</dom-module>
+
+<dom-module id='attached-container'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ </template>
+ <script>
+ Polymer({
+ is: 'attached-container',
+ properties: {val: {type: String, value: 'container-attached-default-val'}},
+ attached: function() {
+ if (this.val === 'container-attached-default-val') {
+ this.val = 'attached-container-val';
+ }
+ }
+ });
+ </script>
+</dom-module>
+
+<dom-module id='ready-container'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ </template>
+ <script>
+ Polymer({
+ is: 'ready-container', properties: {val: {type: String}},
+ ready: function() {
+ this.val = 'ready-container-val';
+ }
+ });
+ </script>
+</dom-module>
+
+<dom-module id='timeout-container'>
+ <template>
+ <iron-location query='{{val}}'></iron-location>
+ </template>
+ <script>Polymer({
+ is: 'timeout-container',
+ properties: {
+ val: {
+ type: String,
+ notify: true
+ }
+ },
+ attached: function() {
+ setTimeout(function() {
+ this.val = 'on-timeout';
+ }.bind(this), 10);
+ }
+ });</script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-location/test/initialization-iframe.html b/catapult/third_party/polymer/components/iron-location/test/initialization-iframe.html
new file mode 100644
index 00000000..564bf262
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/initialization-iframe.html
@@ -0,0 +1,61 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Base source for injecting into an iframe for tests</title>
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <link rel='import' href='./initialization-cases.html'>
+ </head>
+ <body>
+ <script>
+ window.addEventListener("message", messageReceived, false);
+
+ window.addEventListener('WebComponentsReady', function() {
+ window.parent.postMessage({
+ 'type': 'ready'
+ }, '*');
+ });
+
+ var appendBodyReceived = false;
+ function messageReceived(msg) {
+ if (!msg.data) {
+ console.error('got invalid msg?');
+ }
+ // the parent can (at any time) ask for our URL.
+ if (msg.data.type === 'urlQuery') {
+ msg.source.postMessage({
+ 'type': 'urlQueryResponse',
+ 'href': window.location.href,
+ 'pathname': window.location.pathname,
+ 'hash': window.location.hash,
+ 'search': window.location.search
+ }, '*');
+ } else if (msg.data.type === 'appendBody') {
+ if (appendBodyReceived) {
+ throw new Error('should only receive at most one appendBody call');
+ }
+ var element = document.createElement(msg.data.tagName);
+ document.body.appendChild(element);
+ appendBodyReceived = true;
+ }
+ }
+
+ window.addEventListener('error', function(e) {
+ window.parent.postMessage({
+ 'type': 'error',
+ 'error': e.message
+ }, '*');
+ });
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-location/test/initialization-tests.html b/catapult/third_party/polymer/components/iron-location/test/initialization-tests.html
new file mode 100644
index 00000000..16c91119
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/initialization-tests.html
@@ -0,0 +1,145 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title></title>
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-location.html">
+ </head>
+
+ <body>
+ <script>
+ 'use strict';
+
+ function getIframe() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement('iframe');
+ var result = getMessageMatching(iframe, function(message) {
+ return message.type === 'ready';
+ });
+ iframe.src = './initialization-iframe.html';
+ document.body.appendChild(iframe);
+ iframe.addEventListener('error', reject);
+ result.then(function() {resolve(iframe)}, reject);
+ });
+ }
+
+ function onMessage(iframe, callback) {
+ var f = function(event) {
+ if (event.source === iframe.contentWindow) {
+ callback(event.data);
+ }
+ };
+ window.addEventListener('message', f, false);
+ return function() {
+ window.removeEventListener('message', f);
+ }
+ }
+
+ function getMessageMatching(iframe, predicate) {
+ var revoke = function() {};
+ var result = new Promise(function(resolve, reject) {
+ revoke = onMessage(iframe, function(message) {
+ if (predicate(message)) {
+ resolve(message);
+ }
+ });
+ });
+ result.then(revoke, revoke);
+ return result;
+ }
+
+ function getUrl(iframe) {
+ var result = getMessageMatching(iframe, function(message) {
+ return message.type === 'urlQueryResponse';
+ })
+ var revoke = function() {};
+ var result = new Promise(function(resolve, reject) {
+ revoke = onMessage(iframe, resolve);
+ });
+ result.then(revoke, revoke);
+ iframe.contentWindow.postMessage({type: 'urlQuery'}, '*');
+ return result;
+ }
+
+ function timePasses(ms) {
+ return new Promise(function(resolve) {
+ window.setTimeout(function() {
+ resolve();
+ }, ms);
+ });
+ }
+
+ suite('<iron-location>\'s initialization', function() {
+ var iframe;
+ var error;
+ setup(function() {
+ return getIframe().then(function(i) {
+ iframe = i;
+ function isError(m) {return m.type === 'error'}
+ getMessageMatching(iframe, isError).then(function(m) {
+ error = m.error;
+ });
+ });
+ });
+ teardown(function() {
+ if (iframe) {
+ document.body.removeChild(iframe);
+ }
+ var e = error;
+ iframe = null;
+ error = null;
+ if (e) {
+ throw new Error('Error message from contained iframe: ' + e);
+ }
+ });
+ var cases = [
+ 'default-before', 'attached-before', 'ready-before',
+ 'default-after', 'attached-after', 'ready-after',
+ 'default-below', 'attached-below', 'ready-below',
+ 'default-above', 'attached-above', 'ready-above',
+ 'default-container', 'attached-container', 'ready-container'
+ ];
+ cases.forEach(function(caseName) {
+ test('the url takes priority in ' + caseName + ' initialization', function() {
+ return getUrl(iframe).then(function(url) {
+ expect(url.search).to.be.eq('');
+ iframe.contentWindow.postMessage({type: 'appendBody', tagName: caseName}, '*');
+ return timePasses(10).then(function() {return getUrl(iframe)});
+ }).then(function(url) {
+ expect(url.search).to.be.eq('');
+ });
+ });
+ });
+ var expectedFailureCases = ['timeout-before', 'timeout-after', 'timeout-below', 'timeout-above', 'timeout-container'];
+ expectedFailureCases.forEach(function(caseName) {
+ test('the url does not take priority in ' + caseName + ' initialization', function() {
+ return getUrl(iframe).then(function(url) {
+ expect(url.search).to.be.eq('');
+ iframe.contentWindow.postMessage({type: 'appendBody', tagName: caseName}, '*');
+ return timePasses(60).then(function() {return getUrl(iframe)});
+ }).then(function(url) {
+ expect(url.search).to.be.eq('?on-timeout');
+ });
+ });
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-location/test/integration.html b/catapult/third_party/polymer/components/iron-location/test/integration.html
new file mode 100644
index 00000000..9518d175
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/integration.html
@@ -0,0 +1,135 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+ <title>iron-location</title>
+
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../iron-query-params.html">
+ <link rel="import" href="../iron-location.html">
+</head>
+<body>
+ <dom-module id="integration-element">
+ <template>
+ <iron-location
+ id="location"
+ path="{{path}}"
+ query="{{query}}"
+ hash="{{hash}}">
+ </iron-location>
+ <iron-query-params
+ id="queryParams"
+ params-string="{{query}}"
+ params-object="{{queryParams}}">
+ </iron-query-params>
+ </template>
+ <script>
+ HTMLImports.whenReady(function() {
+ Polymer({
+ is: 'integration-element'
+ });
+ });
+ </script>
+ </dom-module>
+
+ <test-fixture id="Integration">
+ <template>
+ <integration-element></integration-element>
+ </template>
+ </test-fixture>
+
+ <script>
+ 'use strict';
+
+ suite('Integration tests', function () {
+
+ var integration;
+ var ironLocation;
+ var ironQueryParams;
+
+ setup(function() {
+ integration = fixture('Integration');
+ ironLocation = integration.$.location;
+ ironQueryParams = integration.$.queryParams;
+ });
+
+ test('propagations from location to iqp', function() {
+ var queryEncodingExamples = {
+ 'foo': '?foo',
+ '': '',
+ 'foo bar': '?foo%20bar',
+ 'foo#bar': '?foo%23bar',
+ 'foo?bar': '?foo?bar',
+ '/foo\'bar\'baz': ['?/foo%27bar%27baz', '?/foo\'bar\'baz'],
+ 'foo/bar/baz': '?foo/bar/baz'
+ };
+ for (var plainTextQuery in queryEncodingExamples) {
+ var encodedQueries = queryEncodingExamples[plainTextQuery];
+ var ironLocationQuery = encodedQueries;
+ if (typeof encodedQueries === 'string') {
+ encodedQueries = [encodedQueries];
+ ironLocationQuery = [ironLocationQuery.substring(1)];
+ } else {
+ ironLocationQuery = ironLocationQuery.map(function(value) {
+ return value.substring(1);
+ });
+ }
+
+ ironLocation.query = plainTextQuery;
+ expect(encodedQueries).to.contain(window.location.search);
+ expect(ironLocationQuery).to.contain(ironLocation.query);
+ expect(ironLocationQuery).to.contain(ironQueryParams.paramsString);
+ if (plainTextQuery) {
+ expect('').to.be.equal(ironQueryParams.paramsObject[plainTextQuery])
+ } else {
+ expect(ironQueryParams.paramsObject[plainTextQuery]).to.be.undefined;
+ }
+ }
+ });
+
+ test('propagations from iqp to location', function() {
+ var queryEncodingExamples = {
+ 'foo': '?foo',
+ '': '',
+ 'foo bar': '?foo%20bar',
+ 'foo#bar': '?foo%23bar',
+ 'foo?bar': '?foo?bar',
+ '/foo\'bar\'baz': ['?/foo%27bar%27baz', '?/foo\'bar\'baz'],
+ 'foo/bar/baz': '?foo/bar/baz'
+ };
+ for (var plainTextQuery in queryEncodingExamples) {
+ var encodedQueries = queryEncodingExamples[plainTextQuery];
+ var ironLocationQuery = encodedQueries;
+ if (typeof encodedQueries === 'string') {
+ encodedQueries = [encodedQueries];
+ ironLocationQuery = [ironLocationQuery.substring(1)];
+ } else {
+ ironLocationQuery = ironLocationQuery.map(function(value) {
+ return value.substring(1);
+ });
+ }
+
+ var newParamsObject = {};
+ newParamsObject[plainTextQuery] = '';
+
+ ironQueryParams.paramsObject = newParamsObject;
+ expect(encodedQueries).to.contain(window.location.search);
+ expect(ironLocationQuery).to.contain(ironLocation.query);
+ }
+ });
+ });
+
+ </script>
+</body>
diff --git a/catapult/third_party/polymer/components/iron-location/test/iron-location.html b/catapult/third_party/polymer/components/iron-location/test/iron-location.html
new file mode 100644
index 00000000..4eebc611
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/iron-location.html
@@ -0,0 +1,499 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-location</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../iron-location.html">
+ <link rel="import" href="./redirection.html">
+ <style>
+ #safari-cooldown {
+ font-size: 18px;
+ max-width: 300px;
+ }
+ #safari-cooldown div {
+ margin-bottom: 20px;
+ }
+ </style>
+</head>
+<body>
+
+ <div id='safari-cooldown' hidden>
+ <div>Danger: URL overheating.</div>
+ <div>
+ Safari requires a cooldown period before we call
+ pushState/replaceState any more.
+ </div>
+ <div>Testing will resume after 30 seconds.</div>
+ <div>
+ <a href="https://forums.developer.apple.com/thread/36650">More info</a>
+ </div>
+ </div>
+
+<test-fixture id='Solo'>
+ <template>
+ <iron-location></iron-location>
+ </template>
+</test-fixture>
+<test-fixture id='Together'>
+ <template>
+ <div>
+ <iron-location id='one'></iron-location>
+ <iron-location id='two'></iron-location>
+ </div>
+ </template>
+</test-fixture>
+<test-fixture id='RedirectHash'>
+ <template>
+ <redirect-hash></redirect-hash>
+ </template>
+</test-fixture>
+<test-fixture id='RedirectPath'>
+ <template>
+ <redirect-path></redirect-path>
+ </template>
+</test-fixture>
+<test-fixture id='RedirectQuery'>
+ <template>
+ <redirect-query></redirect-query>
+ </template>
+</test-fixture>
+
+<script>
+ 'use strict';
+
+ function timePasses(ms) {
+ return new Promise(function(resolve) {
+ window.setTimeout(function() {
+ resolve();
+ }, ms);
+ });
+ }
+
+ function makeAbsoluteUrl(path) {
+ return window.location.protocol + '//' + window.location.host + path;
+ }
+
+ // A window.history.replaceState wrapper that's smart about baseURI.
+ function replaceState(path) {
+ window.history.replaceState({}, '', makeAbsoluteUrl(path));
+ }
+
+ /**
+ * We run the iron location tests with a couple different page configurations
+ * (e.g. with and withoug a base URI), so we define the test set as a function
+ * that we call in each of these configurations.
+ */
+ function ironLocationTests() {
+ suite('when used solo', function() {
+ var urlElem;
+ var toRemove;
+ setup(function() {
+ replaceState('/');
+ urlElem = fixture('Solo');
+ toRemove = [];
+ });
+ teardown(function() {
+ for (var i = 0; i < toRemove.length; i++) {
+ document.body.removeChild(toRemove[i]);
+ }
+ });
+ test('basic functionality with #hash urls', function() {
+ // Initialized to the window location's hash.
+ expect(window.location.hash).to.be.equal(urlElem.hash);
+
+ // Changing the urlElem's hash changes the URL
+ urlElem.hash = '/lol/ok';
+ expect(window.location.hash).to.be.equal('#/lol/ok');
+
+ // Changing the hash via normal means changes the urlElem.
+ var anchor = document.createElement('a');
+ var base =
+ window.location.protocol + '//' + window.location.host +
+ window.location.pathname;
+ anchor.href = base + '#/wat';
+ document.body.appendChild(anchor);
+ anchor.click();
+ // In IE10 it appears that the hashchange event is asynchronous.
+ return timePasses(10).then(function() {
+ expect(urlElem.hash).to.be.equal('/wat');
+ });
+ });
+ test('basic functionality with paths', function() {
+ // Initialized to the window location's path.
+ expect(window.location.pathname).to.be.equal(urlElem.path);
+
+ // Changing the urlElem's path changes the URL
+ urlElem.path = '/foo/bar';
+ expect(window.location.pathname).to.be.equal('/foo/bar');
+
+ // Changing the path and sending a custom event on the window changes
+ // the urlElem.
+ replaceState('/baz');
+ window.dispatchEvent(new CustomEvent('location-changed'));
+ expect(urlElem.path).to.be.equal('/baz');
+ });
+ function makeTemporaryIronLocation() {
+ var ironLocation = document.createElement('iron-location');
+ document.body.appendChild(ironLocation);
+ toRemove.push(ironLocation);
+ return ironLocation
+ }
+ test('dealing with paths with unusual characters', function() {
+ var pathEncodingExamples = {
+ '/foo': '/foo',
+ '/': '/',
+ '/foo bar': '/foo%20bar',
+ '/foo#bar': '/foo%23bar',
+ '/foo?xyz': '/foo%3Fxyz',
+ '/foo\'bar\'baz': '/foo\'bar\'baz',
+ };
+
+ for (var plainTextPath in pathEncodingExamples) {
+ var encodedPath = pathEncodingExamples[plainTextPath];
+
+ urlElem.path = plainTextPath;
+ expect(window.location.pathname).to.be.equal(encodedPath);
+ expect(urlElem.path).to.be.equal(plainTextPath);
+ var temporaryIronLocation = makeTemporaryIronLocation();
+ expect(temporaryIronLocation.path).to.be.equal(plainTextPath);
+ }
+ });
+ test('dealing with hashes with unusual characters', function() {
+ var hashEncodingExamples = {
+ 'foo': '#foo',
+ '': '',
+ 'foo bar': ['#foo%20bar', '#foo bar'],
+ 'foo#bar': '#foo#bar',
+ 'foo?bar': '#foo?bar',
+ '/foo\'bar\'baz': ['#/foo%27bar%27baz', '#/foo\'bar\'baz'],
+ };
+ for (var plainTextHash in hashEncodingExamples) {
+ var encodedHashes = hashEncodingExamples[plainTextHash];
+ if (typeof encodedHashes === 'string') {
+ encodedHashes = [encodedHashes];
+ }
+
+ urlElem.hash = plainTextHash;
+ expect(encodedHashes).to.contain(window.location.hash);
+ expect(urlElem.hash).to.be.equal(plainTextHash);
+ expect(makeTemporaryIronLocation().hash).to.be.equal(plainTextHash);
+ }
+ });
+ test('dealing with queries with unusual characters', function() {
+ var queryEncodingExamples = {
+ 'foo': '?foo',
+ '': '',
+ 'foo bar': '?foo%20bar',
+ 'foo#bar': '?foo%23bar',
+ 'foo?bar': '?foo?bar',
+ '/foo\'bar\'baz': ['?/foo%27bar%27baz', '?/foo\'bar\'baz'],
+ 'foo/bar/baz': '?foo/bar/baz'
+ };
+ for (var plainTextQuery in queryEncodingExamples) {
+ var encodedQueries = queryEncodingExamples[plainTextQuery];
+ if (typeof encodedQueries === 'string') {
+ encodedQueries = [encodedQueries];
+ }
+
+ var ironLocationQuery = encodedQueries.map(function(value) {
+ return value.substring(1);
+ });
+
+ expect(urlElem._initialized).to.be.eq(true);
+ urlElem.query = plainTextQuery;
+ expect(encodedQueries).to.contain(window.location.search);
+ expect(ironLocationQuery).to.contain(urlElem.query);
+ expect(ironLocationQuery).to.contain(makeTemporaryIronLocation().query);
+
+ urlElem.query = 'dummyValue';
+ urlElem.query = ironLocationQuery[0];
+
+ expect(encodedQueries).to.contain(window.location.search);
+ expect(ironLocationQuery).to.contain(urlElem.query);
+ expect(ironLocationQuery).to.contain(makeTemporaryIronLocation().query);
+ }
+ });
+ test('assigning to a relative path URL', function() {
+ urlElem.path = '/foo/bar';
+ expect(window.location.pathname).to.be.equal('/foo/bar');
+
+ // A relative path is treated as an absolute one, just with a
+ // missing leading slash.
+ urlElem.path = 'baz';
+ expect(window.location.pathname).to.be.equal('/baz');
+ });
+ test('basic functionality with ?key=value params', function() {
+ // Initialized to the window location's params.
+ expect(urlElem.query).to.be.eq('');
+
+ // Changing location.search and sending a custom event on the window
+ // changes the urlElem.
+ replaceState('/?greeting=hello&target=world');
+ window.dispatchEvent(new CustomEvent('location-changed'));
+ expect(urlElem.query).to.be.equal('greeting=hello&target=world');
+
+ // Changing the urlElem's query changes the URL.
+ urlElem.query = 'greeting=hello&target=world&another=key';
+ expect(window.location.search).to.be.equal(
+ '?greeting=hello&target=world&another=key');
+ });
+ });
+ suite('does not spam the user\'s history', function() {
+ var replaceStateCalls, pushStateCalls;
+ var nativeReplaceState, nativePushState;
+ setup(function() {
+ replaceStateCalls = pushStateCalls = 0;
+ nativeReplaceState = window.history.replaceState;
+ nativePushState = window.history.pushState;
+ window.history.replaceState = function() {
+ replaceStateCalls++;
+ };
+ window.history.pushState = function() {
+ pushStateCalls++;
+ };
+ });
+ teardown(function() {
+ window.history.replaceState = nativeReplaceState;
+ window.history.pushState = nativePushState;
+ });
+ test('when a change happens immediately after ' +
+ 'the iron-location is attached', function() {
+ var ironLocation = fixture('Solo');
+ expect(pushStateCalls).to.be.equal(0);
+ expect(replaceStateCalls).to.be.equal(0);
+ ironLocation.path = '/foo';
+ expect(replaceStateCalls).to.be.equal(1);
+ expect(pushStateCalls).to.be.equal(0);
+ });
+
+ suite('when intercepting links', function() {
+ /**
+ * Clicks the given link while an iron-location element with the given
+ * urlSpaceRegex is in the same document. Returns whether the
+ * iron-location prevented the default behavior of the click.
+ *
+ * No matter what, it prevents the default behavior of the click itself
+ * to ensure that no navigation occurs (as that would interrupt
+ * running and reporting these tests!)
+ */
+ function isClickCaptured(anchor, urlSpaceRegex) {
+ var defaultWasPrevented;
+ function handler(event) {
+ expect(event.target).to.be.eq(anchor);
+ defaultWasPrevented = event.defaultPrevented;
+ event.preventDefault();
+ expect(event.defaultPrevented).to.be.eq(true);
+ }
+ window.addEventListener('click', handler);
+ var ironLocation = fixture('Solo');
+ if (urlSpaceRegex != null) {
+ ironLocation.urlSpaceRegex = urlSpaceRegex;
+ }
+ document.body.appendChild(anchor);
+ anchor.click();
+ document.body.removeChild(anchor);
+ window.removeEventListener('click', handler);
+ return defaultWasPrevented;
+ }
+
+ test('simple link to / is intercepted', function() {
+ var anchor = document.createElement('a');
+ if (document.baseURI !== window.location.href) {
+ anchor.href = makeAbsoluteUrl('/');
+ } else {
+ anchor.href = '/';
+ }
+
+ expect(isClickCaptured(anchor)).to.be.eq(true);
+ expect(pushStateCalls).to.be.equal(1);
+ });
+
+ test('link that matches url space is intercepted', function() {
+ var anchor = document.createElement('a');
+ anchor.href = makeAbsoluteUrl('/foo');
+
+ expect(isClickCaptured(anchor, '/fo+')).to.be.eq(true);
+ expect(pushStateCalls).to.be.equal(1);
+ });
+
+ test('link that doesn\'t match url space isn\'t intercepted', function() {
+ var anchor = document.createElement('a');
+ anchor.href = makeAbsoluteUrl('/bar');
+
+ expect(isClickCaptured(anchor, '/fo+')).to.be.eq(false);
+ expect(pushStateCalls).to.be.equal(0);
+ });
+
+ test('link to another domain isn\'t intercepted', function() {
+ var anchor = document.createElement('a');
+ anchor.href = 'http://example.com/';
+
+ expect(isClickCaptured(anchor)).to.be.eq(false);
+ expect(pushStateCalls).to.be.equal(0);
+ });
+
+ test('a link with target=_blank isn\'t intercepted', function() {
+ var anchor = document.createElement('a');
+ anchor.href = makeAbsoluteUrl('/');
+ anchor.target = '_blank';
+
+ expect(isClickCaptured(anchor)).to.be.eq(false);
+ expect(pushStateCalls).to.be.equal(0);
+ });
+
+ test('a link with an href to the ' +
+ 'current page shouldn\'t add to history.', function() {
+ var anchor = document.createElement('a');
+ anchor.href = window.location.href;
+
+ // The click is captured, but it doesn't add to history.
+ expect(isClickCaptured(anchor)).to.be.equal(true);
+ expect(pushStateCalls).to.be.equal(0);
+ });
+
+ test('a click that has already been defaultPrevented ' +
+ 'shouldn\'t result in a navigation', function() {
+ fixture('Solo');
+ var anchor = document.createElement('a');
+ anchor.href = makeAbsoluteUrl('/');
+ anchor.addEventListener('click', function(event) {
+ event.preventDefault();
+ });
+ document.body.appendChild(anchor);
+
+ var originalPushState = window.history.pushState;
+ var count = 0;
+ window.history.pushState = function() {
+ count++;
+ }
+ anchor.click();
+ window.history.pushState = originalPushState;
+
+ expect(count).to.be.equal(0);
+ })
+ });
+ });
+ suite('when used with other iron-location elements', function() {
+ var otherUrlElem;
+ var urlElem;
+ setup(function() {
+ var elems = fixture('Together');
+ urlElem = elems.querySelector('#one');
+ otherUrlElem = elems.querySelector('#two');
+ });
+ function assertHaveSameUrls(urlElemOne, urlElemTwo) {
+ expect(urlElemOne.path).to.be.equal(urlElemTwo.path);
+ expect(urlElemOne.hash).to.be.equal(urlElemTwo.hash);
+ expect(urlElemOne.query).to.be.equal(urlElemTwo.query);
+ }
+ test('coordinate their changes (by firing events on window)', function() {
+ assertHaveSameUrls(urlElem, otherUrlElem);
+
+ urlElem.path = '/a/b/c';
+ assertHaveSameUrls(urlElem, otherUrlElem);
+
+ otherUrlElem.query = 'alsdkjflaksjfd=alksjfdlkajsdfl';
+ assertHaveSameUrls(urlElem, otherUrlElem);
+
+ urlElem.hash = 'lkjljifosjkldfjlksjfldsjf';
+ assertHaveSameUrls(urlElem, otherUrlElem);
+ });
+ });
+
+ suite('supports doing synchronous redirection', function() {
+ test('of the hash portion of the URL', function() {
+ expect(window.location.hash).to.be.equal('');
+ var redirector = fixture('RedirectHash');
+ expect(window.location.hash).to.be.equal('#redirectedTo');
+ expect(redirector.hash).to.be.equal('redirectedTo');
+ redirector.hash = 'newHash';
+ expect(window.location.hash).to.be.equal('#redirectedTo');
+ expect(redirector.hash).to.be.equal('redirectedTo');
+ });
+
+ test('of the path portion of the URL', function() {
+ expect(window.location.pathname).to.not.be.equal('/redirectedTo');
+ var redirector = fixture('RedirectPath');
+ expect(window.location.pathname).to.be.equal('/redirectedTo');
+ expect(redirector.path).to.be.equal('/redirectedTo');
+ redirector.path = '/newPath';
+ expect(window.location.pathname).to.be.equal('/redirectedTo');
+ expect(redirector.path).to.be.equal('/redirectedTo');
+ });
+
+ test('of the query portion of the URL', function() {
+ expect(window.location.search).to.be.equal('');
+ var redirector = fixture('RedirectQuery');
+ expect(window.location.search).to.be.equal('?redirectedTo');
+ expect(redirector.query).to.be.equal('redirectedTo');
+ redirector.query = 'newQuery';
+ expect(window.location.search).to.be.equal('?redirectedTo');
+ expect(redirector.query).to.be.equal('redirectedTo');
+ });
+ });
+ }
+
+ suite('<iron-location>', function () {
+ var initialUrl;
+ setup(function() {
+ initialUrl = window.location.href;
+ });
+ teardown(function(){
+ window.history.replaceState({}, '', initialUrl);
+ });
+
+ // This is as dumb as it looks. See #safari-cooldown in the dom above.
+ var cooldownFunction = function() {};
+ if (/^Apple/.test(navigator.vendor)) {
+ cooldownFunction = function(done) {
+ var cooldownPeriod = 30 * 1000;
+ this.timeout(cooldownPeriod + 5000);
+ var cooldownMessage = document.querySelector('#safari-cooldown');
+ cooldownMessage.removeAttribute('hidden');
+ setTimeout(function() {
+ done();
+ cooldownMessage.setAttribute('hidden', 'hidden');
+ }, cooldownPeriod);
+ };
+ }
+
+ suite('without a base URI', function() {
+ ironLocationTests();
+
+ suiteTeardown(cooldownFunction);
+ });
+
+ suite('with a base URI', function() {
+ var baseElem;
+ setup(function() {
+ expect(document.baseURI).to.be.equal(window.location.href);
+ baseElem = document.createElement('base');
+ var href = 'https://example.com/i/dont/exist/obviously'
+ baseElem.href = href;
+ document.head.appendChild(baseElem);
+ expect(document.baseURI).to.be.equal(href);
+ });
+ teardown(function() {
+ document.head.removeChild(baseElem);
+ });
+ suiteTeardown(cooldownFunction);
+ ironLocationTests();
+ });
+ });
+
+</script>
+</body>
diff --git a/catapult/third_party/polymer/components/iron-location/test/iron-query-params.html b/catapult/third_party/polymer/components/iron-location/test/iron-query-params.html
new file mode 100644
index 00000000..8b276427
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/iron-query-params.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>iron-location</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
+ <link rel="import" href="../iron-query-params.html">
+</head>
+<body>
+
+ <test-fixture id="BasicQueryParams">
+ <template>
+ <iron-query-params></iron-query-params>
+ </template>
+ </test-fixture>
+
+ <script>
+ 'use strict';
+
+ suite('<iron-query-params>', function () {
+
+ var paramsElem;
+ setup(function() {
+ paramsElem = fixture('BasicQueryParams');
+ });
+
+ test('basic functionality with ?key=value params', function() {
+ // Check initialization
+ expect(paramsElem.paramsString).to.be.eq('');
+ expect(paramsElem.paramsObject).to.deep.eq({});
+
+ // Check the mapping from paramsString to paramsObject.
+ paramsElem.paramsString = 'greeting=hello&target=world';
+ expect(paramsElem.paramsObject).to.deep.equal(
+ {greeting: 'hello', target: 'world'});
+
+ // Check the mapping from paramsObject back to paramsString.
+ paramsElem.set('paramsObject.another', 'key');
+ expect(paramsElem.paramsString).to.be.equal(
+ 'greeting=hello&target=world&another=key');
+ });
+ test('encoding of params', function() {
+ var mappings = [
+ {
+ string: 'a=b',
+ object: {'a': 'b'}
+ },
+ {
+ string: 'debug&ok',
+ object: {'debug': '', 'ok': ''}
+ },
+ {
+ string: 'monster%20kid%3A=%F0%9F%98%BF&quotes=%27%27',
+ object: {'monster kid:': '😿', 'quotes': '\'\''}
+ },
+ {
+ string: 'yes%2C%20ok?%20what%20is%20up%20with%20%CB%9Athiiis%CB%9A=%E2%98%83',
+ object: {'yes, ok? what is up with ˚thiiis˚': '☃'}
+ },
+ ];
+
+ mappings.forEach(function(mapping) {
+ // Clear
+ paramsElem.paramsObject = {};
+ expect(paramsElem.paramsString).to.be.equal('');
+
+ // Test going from string to object
+ paramsElem.paramsString = mapping.string;
+ expect(paramsElem.paramsObject).to.deep.equal(mapping.object);
+
+ // Clear again.
+ paramsElem.paramsObject = {};
+ expect(paramsElem.paramsString).to.be.equal('');
+
+ // Test going from object to string
+ paramsElem.paramsObject = mapping.object;
+ expect(paramsElem.paramsString).to.be.equal(mapping.string);
+ });
+ });
+ test('query strings with alternative encodings', function() {
+ // Check the mapping for querystrings with + instead of %20.
+ paramsElem.paramsString = 'key=value+with+spaces';
+ expect(paramsElem.paramsObject).to.deep.equal(
+ {key: 'value with spaces'});
+ });
+ });
+
+ </script>
+</body>
diff --git a/catapult/third_party/polymer/components/iron-location/test/redirection.html b/catapult/third_party/polymer/components/iron-location/test/redirection.html
new file mode 100644
index 00000000..3b21f267
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-location/test/redirection.html
@@ -0,0 +1,68 @@
+<!--
+@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
+-->
+<dom-module id='redirect-hash'>
+ <template>
+ <iron-location hash='{{hash}}'></iron-location>
+ </template>
+ <script>
+ Polymer({
+ is: 'redirect-hash',
+ properties: {
+ hash: {
+ value: '',
+ observer: 'hashChanged'
+ }
+ },
+ hashChanged: function(hash) {
+ this.hash = 'redirectedTo';
+ }
+ });
+ </script>
+</dom-module>
+
+<dom-module id='redirect-path'>
+ <template>
+ <iron-location path='{{path}}'></iron-location>
+ </template>
+ <script>
+ Polymer({
+ is: 'redirect-path',
+ properties: {
+ path: {
+ value: '',
+ observer: 'pathChanged'
+ }
+ },
+ pathChanged: function(path) {
+ this.path = '/redirectedTo';
+ }
+ });
+ </script>
+</dom-module>
+
+<dom-module id='redirect-query'>
+ <template>
+ <iron-location query='{{query}}'></iron-location>
+ </template>
+ <script>
+ Polymer({
+ is: 'redirect-query',
+ properties: {
+ query: {
+ value: '',
+ observer: 'queryChanged'
+ }
+ },
+ queryChanged: function(query) {
+ this.query = 'redirectedTo';
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/.bower.json b/catapult/third_party/polymer/components/iron-menu-behavior/.bower.json
new file mode 100644
index 00000000..704911bd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/.bower.json
@@ -0,0 +1,46 @@
+{
+ "name": "iron-menu-behavior",
+ "version": "1.3.1",
+ "description": "Provides accessible menu behavior",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior",
+ "menu"
+ ],
+ "main": [
+ "iron-menu-behavior.html",
+ "iron-menubar-behavior.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-menu-behavior"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-menu-behavior",
+ "ignore": [],
+ "dependencies": {
+ "iron-selector": "PolymerElements/iron-selector#^1.0.0",
+ "polymer": "Polymer/polymer#^1.2.4",
+ "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.3.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.3.1",
+ "commit": "e5222093effd1637d6f2453005cf18a2e31552f7"
+ },
+ "_source": "https://github.com/PolymerElements/iron-menu-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-menu-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-menu-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..da2172d0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-menu-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/.gitignore b/catapult/third_party/polymer/components/iron-menu-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-menu-behavior/.travis.yml
new file mode 100644
index 00000000..ee56b18a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ CbYi/0VAtpLB+NDHdD/I9q2ldILrmyc3wxKdO5vEtMvRKYgsddQ/hXGovV3c6Hy9sAXD5sKtNi60BBG5E2XuydshjYAZiytfeNjFIvDu5627Xljjt90e/r1hg3tNHRRQihH73nPECfp/X+g+yBNCX3f0+2ExAh0DMs1DXt7Dl7Q=
+ - secure: >-
+ kLFlOTh9IjctY7DIJ3KEw5OPrqHNTzoArdabfAtisBMWahuJptKFmYCp/t+zPSL27IVqJakaqPrwGrBUi+4h3wVWredNhfl2lCpMfQfBMcHC5kBVkf2xjJyDa5Y3bP7jPq6YnWYAqEl6pBWYiHU6yWBc6BEdJ6FsTWFbLFTnY7w=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-menu-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/README.md b/catapult/third_party/polymer/components/iron-menu-behavior/README.md
new file mode 100644
index 00000000..f8d90c65
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/README.md
@@ -0,0 +1,30 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-menu-behavior.html iron-menubar-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-menu-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-menu-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-menu-behavior)_
+
+
+##Polymer.IronMenuBehavior
+
+`Polymer.IronMenuBehavior` implements accessible menu behavior.
+
+
+
+##Polymer.IronMenubarBehavior
+
+`Polymer.IronMenubarBehavior` implements accessible menubar behavior.
+
+
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/bower.json b/catapult/third_party/polymer/components/iron-menu-behavior/bower.json
new file mode 100644
index 00000000..e00194e5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/bower.json
@@ -0,0 +1,37 @@
+{
+ "name": "iron-menu-behavior",
+ "version": "1.3.1",
+ "description": "Provides accessible menu behavior",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior",
+ "menu"
+ ],
+ "main": [
+ "iron-menu-behavior.html",
+ "iron-menubar-behavior.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-menu-behavior"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-menu-behavior",
+ "ignore": [],
+ "dependencies": {
+ "iron-selector": "PolymerElements/iron-selector#^1.0.0",
+ "polymer": "Polymer/polymer#^1.2.4",
+ "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-menu-behavior/demo/index.html
new file mode 100644
index 00000000..11da8329
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/demo/index.html
@@ -0,0 +1,118 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>iron-menu-behavior demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../../paper-styles/default-theme.html">
+ <link rel="import" href="simple-menu.html">
+ <link rel="import" href="simple-menubar.html">
+
+ <style is="custom-style">
+ simple-menu,
+ simple-menubar {
+ display: inline-block;
+ width: 100%;
+ }
+
+ simple-menu a {
+ display: block;
+ }
+
+ simple-menubar a,
+ simple-menu a {
+ padding: 15px 20px;
+ color: var(--primary-text-color);
+ text-decoration: none;
+ }
+
+ simple-menu a[disabled],
+ simple-menubar a[disabled] {
+ color: var(--google-grey-300);
+ }
+
+ .horizontal-section {
+ padding: 0;
+ margin-bottom: 20px;
+ }
+
+ .row {
+ height: 120px;
+ }
+ </style>
+</head>
+<body unresolved>
+
+ <div class="horizontal-section-container">
+ <div>
+ <h3>Simple menu</h3>
+ <div class="horizontal-section">
+ <simple-menu>
+ <a href="javascript:void(0)" role="menuitem">Item 0</a>
+ <a href="javascript:void(0)" role="menuitem">Item 1</a>
+ <a href="javascript:void(0)" role="menuitem" disabled>Item 2</a>
+ <a href="javascript:void(0)" role="menuitem" hidden>Ghost</a>
+ <a href="javascript:void(0)" role="menuitem">Item 3</a>
+ <a href="javascript:void(0)" role="menuitem" style="display:none">Another ghost</a>
+ </simple-menu>
+ </div>
+ </div>
+
+ <div>
+ <h3>Multi-select menu</h3>
+ <div class="horizontal-section">
+ <simple-menu multi>
+ <a href="javascript:void(0)" role="menuitem">Item 0</a>
+ <a href="javascript:void(0)" role="menuitem" disabled>Item 1</a>
+ <a href="javascript:void(0)" role="menuitem" hidden>Ghost</a>
+ <a href="javascript:void(0)" role="menuitem">Item 2</a>
+ <a href="javascript:void(0)" role="menuitem">Item 3</a>
+ <a href="javascript:void(0)" role="menuitem" style="display:none">Another ghost</a>
+ </simple-menu>
+ </div>
+ </div>
+
+ <div>
+ <div class="row">
+ <h3>Simple menubar</h3>
+ <div class="horizontal-section">
+ <simple-menubar>
+ <a href="javascript:void(0)" role="menuitem">Item 0</a>
+ <a href="javascript:void(0)" role="menuitem">Item 1</a>
+ <a href="javascript:void(0)" role="menuitem" disabled>Item 2</a>
+ <a href="javascript:void(0)" role="menuitem">Item 3</a>
+ </simple-menubar>
+ </div>
+ </div>
+ <div class="row">
+ <h3>Multi-select menubar</h3>
+ <div class="horizontal-section">
+ <simple-menubar multi>
+ <a href="javascript:void(0)" role="menuitem">Item 0</a>
+ <a href="javascript:void(0)" role="menuitem">Item 1</a>
+ <a href="javascript:void(0)" role="menuitem">Item 2</a>
+ <a href="javascript:void(0)" role="menuitem">Item 3</a>
+ </simple-menubar>
+ </div>
+ </div>
+ </div>
+
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menu.html b/catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menu.html
new file mode 100644
index 00000000..da16eeba
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menu.html
@@ -0,0 +1,48 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-menu-behavior.html">
+<link rel="import" href="../../paper-styles/color.html">
+
+<dom-module id="simple-menu">
+ <template>
+ <style>
+ .content ::content > .iron-selected {
+ color: white;
+ background-color: var(--google-blue-500);
+ }
+ </style>
+
+ <div class="content">
+ <content></content>
+ </div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'simple-menu',
+
+ behaviors: [
+ Polymer.IronMenuBehavior
+ ]
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menubar.html b/catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menubar.html
new file mode 100644
index 00000000..0176ad0f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/demo/simple-menubar.html
@@ -0,0 +1,52 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-menubar-behavior.html">
+<link rel="import" href="../../paper-styles/color.html">
+
+<dom-module id="simple-menubar">
+ <template>
+ <style>
+ .content ::content > .iron-selected {
+ color: white;
+ background-color: var(--google-red-500);
+ }
+
+ .content ::content > * {
+ display: inline-block;
+ }
+ </style>
+
+ <div class="content">
+ <content></content>
+ </div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'simple-menubar',
+
+ behaviors: [
+ Polymer.IronMenubarBehavior
+ ]
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/index.html b/catapult/third_party/polymer/components/iron-menu-behavior/index.html
new file mode 100644
index 00000000..2c643c4a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-menu-behavior</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page src="iron-menubar-behavior.html"></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/iron-menu-behavior.html b/catapult/third_party/polymer/components/iron-menu-behavior/iron-menu-behavior.html
new file mode 100644
index 00000000..92a24450
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/iron-menu-behavior.html
@@ -0,0 +1,396 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-selector/iron-multi-selectable.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+
+<script>
+
+ /**
+ * `Polymer.IronMenuBehavior` implements accessible menu behavior.
+ *
+ * @demo demo/index.html
+ * @polymerBehavior Polymer.IronMenuBehavior
+ */
+ Polymer.IronMenuBehaviorImpl = {
+
+ properties: {
+
+ /**
+ * Returns the currently focused item.
+ * @type {?Object}
+ */
+ focusedItem: {
+ observer: '_focusedItemChanged',
+ readOnly: true,
+ type: Object
+ },
+
+ /**
+ * The attribute to use on menu items to look up the item title. Typing the first
+ * letter of an item when the menu is open focuses that item. If unset, `textContent`
+ * will be used.
+ */
+ attrForItemTitle: {
+ type: String
+ },
+
+ disabled: {
+ type: Boolean,
+ value: false,
+ observer: '_disabledChanged',
+ },
+ },
+
+ _SEARCH_RESET_TIMEOUT_MS: 1000,
+
+ _previousTabIndex: 0,
+
+ hostAttributes: {
+ 'role': 'menu',
+ },
+
+ observers: [
+ '_updateMultiselectable(multi)'
+ ],
+
+ listeners: {
+ 'focus': '_onFocus',
+ 'keydown': '_onKeydown',
+ 'iron-items-changed': '_onIronItemsChanged'
+ },
+
+ keyBindings: {
+ 'up': '_onUpKey',
+ 'down': '_onDownKey',
+ 'esc': '_onEscKey',
+ 'shift+tab:keydown': '_onShiftTabDown'
+ },
+
+ attached: function() {
+ this._resetTabindices();
+ },
+
+ /**
+ * Selects the given value. If the `multi` property is true, then the selected state of the
+ * `value` will be toggled; otherwise the `value` will be selected.
+ *
+ * @param {string|number} value the value to select.
+ */
+ select: function(value) {
+ // Cancel automatically focusing a default item if the menu received focus
+ // through a user action selecting a particular item.
+ if (this._defaultFocusAsync) {
+ this.cancelAsync(this._defaultFocusAsync);
+ this._defaultFocusAsync = null;
+ }
+ var item = this._valueToItem(value);
+ if (item && item.hasAttribute('disabled')) return;
+ this._setFocusedItem(item);
+ Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments);
+ },
+
+ /**
+ * Resets all tabindex attributes to the appropriate value based on the
+ * current selection state. The appropriate value is `0` (focusable) for
+ * the default selected item, and `-1` (not keyboard focusable) for all
+ * other items.
+ */
+ _resetTabindices: function() {
+ var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem;
+
+ this.items.forEach(function(item) {
+ item.setAttribute('tabindex', item === selectedItem ? '0' : '-1');
+ }, this);
+ },
+
+ /**
+ * Sets appropriate ARIA based on whether or not the menu is meant to be
+ * multi-selectable.
+ *
+ * @param {boolean} multi True if the menu should be multi-selectable.
+ */
+ _updateMultiselectable: function(multi) {
+ if (multi) {
+ this.setAttribute('aria-multiselectable', 'true');
+ } else {
+ this.removeAttribute('aria-multiselectable');
+ }
+ },
+
+ /**
+ * Given a KeyboardEvent, this method will focus the appropriate item in the
+ * menu (if there is a relevant item, and it is possible to focus it).
+ *
+ * @param {KeyboardEvent} event A KeyboardEvent.
+ */
+ _focusWithKeyboardEvent: function(event) {
+ this.cancelDebouncer('_clearSearchText');
+
+ var searchText = this._searchText || '';
+ var key = event.key && event.key.length == 1 ? event.key :
+ String.fromCharCode(event.keyCode);
+ searchText += key.toLocaleLowerCase();
+
+ var searchLength = searchText.length;
+
+ for (var i = 0, item; item = this.items[i]; i++) {
+ if (item.hasAttribute('disabled')) {
+ continue;
+ }
+
+ var attr = this.attrForItemTitle || 'textContent';
+ var title = (item[attr] || item.getAttribute(attr) || '').trim();
+
+ if (title.length < searchLength) {
+ continue;
+ }
+
+ if (title.slice(0, searchLength).toLocaleLowerCase() == searchText) {
+ this._setFocusedItem(item);
+ break;
+ }
+ }
+
+ this._searchText = searchText;
+ this.debounce('_clearSearchText', this._clearSearchText,
+ this._SEARCH_RESET_TIMEOUT_MS);
+ },
+
+ _clearSearchText: function() {
+ this._searchText = '';
+ },
+
+ /**
+ * Focuses the previous item (relative to the currently focused item) in the
+ * menu, disabled items will be skipped.
+ * Loop until length + 1 to handle case of single item in menu.
+ */
+ _focusPrevious: function() {
+ var length = this.items.length;
+ var curFocusIndex = Number(this.indexOf(this.focusedItem));
+
+ for (var i = 1; i < length + 1; i++) {
+ var item = this.items[(curFocusIndex - i + length) % length];
+ if (!item.hasAttribute('disabled')) {
+ var owner = Polymer.dom(item).getOwnerRoot() || document;
+ this._setFocusedItem(item);
+
+ // Focus might not have worked, if the element was hidden or not
+ // focusable. In that case, try again.
+ if (Polymer.dom(owner).activeElement == item) {
+ return;
+ }
+ }
+ }
+ },
+
+ /**
+ * Focuses the next item (relative to the currently focused item) in the
+ * menu, disabled items will be skipped.
+ * Loop until length + 1 to handle case of single item in menu.
+ */
+ _focusNext: function() {
+ var length = this.items.length;
+ var curFocusIndex = Number(this.indexOf(this.focusedItem));
+
+ for (var i = 1; i < length + 1; i++) {
+ var item = this.items[(curFocusIndex + i) % length];
+ if (!item.hasAttribute('disabled')) {
+ var owner = Polymer.dom(item).getOwnerRoot() || document;
+ this._setFocusedItem(item);
+
+ // Focus might not have worked, if the element was hidden or not
+ // focusable. In that case, try again.
+ if (Polymer.dom(owner).activeElement == item) {
+ return;
+ }
+ }
+ }
+ },
+
+ /**
+ * Mutates items in the menu based on provided selection details, so that
+ * all items correctly reflect selection state.
+ *
+ * @param {Element} item An item in the menu.
+ * @param {boolean} isSelected True if the item should be shown in a
+ * selected state, otherwise false.
+ */
+ _applySelection: function(item, isSelected) {
+ if (isSelected) {
+ item.setAttribute('aria-selected', 'true');
+ } else {
+ item.removeAttribute('aria-selected');
+ }
+ Polymer.IronSelectableBehavior._applySelection.apply(this, arguments);
+ },
+
+ /**
+ * Discretely updates tabindex values among menu items as the focused item
+ * changes.
+ *
+ * @param {Element} focusedItem The element that is currently focused.
+ * @param {?Element} old The last element that was considered focused, if
+ * applicable.
+ */
+ _focusedItemChanged: function(focusedItem, old) {
+ old && old.setAttribute('tabindex', '-1');
+ if (focusedItem && !focusedItem.hasAttribute('disabled') && !this.disabled) {
+ focusedItem.setAttribute('tabindex', '0');
+ focusedItem.focus();
+ }
+ },
+
+ /**
+ * A handler that responds to mutation changes related to the list of items
+ * in the menu.
+ *
+ * @param {CustomEvent} event An event containing mutation records as its
+ * detail.
+ */
+ _onIronItemsChanged: function(event) {
+ if (event.detail.addedNodes.length) {
+ this._resetTabindices();
+ }
+ },
+
+ /**
+ * Handler that is called when a shift+tab keypress is detected by the menu.
+ *
+ * @param {CustomEvent} event A key combination event.
+ */
+ _onShiftTabDown: function(event) {
+ var oldTabIndex = this.getAttribute('tabindex');
+
+ Polymer.IronMenuBehaviorImpl._shiftTabPressed = true;
+
+ this._setFocusedItem(null);
+
+ this.setAttribute('tabindex', '-1');
+
+ this.async(function() {
+ this.setAttribute('tabindex', oldTabIndex);
+ Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
+ // NOTE(cdata): polymer/polymer#1305
+ }, 1);
+ },
+
+ /**
+ * Handler that is called when the menu receives focus.
+ *
+ * @param {FocusEvent} event A focus event.
+ */
+ _onFocus: function(event) {
+ if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) {
+ // do not focus the menu itself
+ return;
+ }
+
+ // Do not focus the selected tab if the deepest target is part of the
+ // menu element's local DOM and is focusable.
+ var rootTarget = /** @type {?HTMLElement} */(
+ Polymer.dom(event).rootTarget);
+ if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !this.isLightDescendant(rootTarget)) {
+ return;
+ }
+
+ // clear the cached focus item
+ this._defaultFocusAsync = this.async(function() {
+ // focus the selected item when the menu receives focus, or the first item
+ // if no item is selected
+ var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem;
+
+ this._setFocusedItem(null);
+
+ if (selectedItem) {
+ this._setFocusedItem(selectedItem);
+ } else if (this.items[0]) {
+ // We find the first none-disabled item (if one exists)
+ this._focusNext();
+ }
+ });
+ },
+
+ /**
+ * Handler that is called when the up key is pressed.
+ *
+ * @param {CustomEvent} event A key combination event.
+ */
+ _onUpKey: function(event) {
+ // up and down arrows moves the focus
+ this._focusPrevious();
+ event.detail.keyboardEvent.preventDefault();
+ },
+
+ /**
+ * Handler that is called when the down key is pressed.
+ *
+ * @param {CustomEvent} event A key combination event.
+ */
+ _onDownKey: function(event) {
+ this._focusNext();
+ event.detail.keyboardEvent.preventDefault();
+ },
+
+ /**
+ * Handler that is called when the esc key is pressed.
+ *
+ * @param {CustomEvent} event A key combination event.
+ */
+ _onEscKey: function(event) {
+ // esc blurs the control
+ this.focusedItem.blur();
+ },
+
+ /**
+ * Handler that is called when a keydown event is detected.
+ *
+ * @param {KeyboardEvent} event A keyboard event.
+ */
+ _onKeydown: function(event) {
+ if (!this.keyboardEventMatchesKeys(event, 'up down esc')) {
+ // all other keys focus the menu item starting with that character
+ this._focusWithKeyboardEvent(event);
+ }
+ event.stopPropagation();
+ },
+
+ // override _activateHandler
+ _activateHandler: function(event) {
+ Polymer.IronSelectableBehavior._activateHandler.call(this, event);
+ event.stopPropagation();
+ },
+
+ /**
+ * Updates this element's tab index when it's enabled/disabled.
+ * @param {boolean} disabled
+ */
+ _disabledChanged: function(disabled) {
+ if (disabled) {
+ this._previousTabIndex = this.hasAttribute('tabindex') ? this.tabIndex : 0;
+ this.removeAttribute('tabindex'); // No tabindex means not tab-able or select-able.
+ } else if (!this.hasAttribute('tabindex')) {
+ this.setAttribute('tabindex', this._previousTabIndex);
+ }
+ }
+ };
+
+ Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
+
+ /** @polymerBehavior Polymer.IronMenuBehavior */
+ Polymer.IronMenuBehavior = [
+ Polymer.IronMultiSelectableBehavior,
+ Polymer.IronA11yKeysBehavior,
+ Polymer.IronMenuBehaviorImpl
+ ];
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/iron-menubar-behavior.html b/catapult/third_party/polymer/components/iron-menu-behavior/iron-menubar-behavior.html
new file mode 100644
index 00000000..f3b65186
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/iron-menubar-behavior.html
@@ -0,0 +1,81 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="iron-menu-behavior.html">
+
+<script>
+
+ /**
+ * `Polymer.IronMenubarBehavior` implements accessible menubar behavior.
+ *
+ * @polymerBehavior Polymer.IronMenubarBehavior
+ */
+ Polymer.IronMenubarBehaviorImpl = {
+
+ hostAttributes: {
+ 'role': 'menubar'
+ },
+
+ keyBindings: {
+ 'left': '_onLeftKey',
+ 'right': '_onRightKey'
+ },
+
+ _onUpKey: function(event) {
+ this.focusedItem.click();
+ event.detail.keyboardEvent.preventDefault();
+ },
+
+ _onDownKey: function(event) {
+ this.focusedItem.click();
+ event.detail.keyboardEvent.preventDefault();
+ },
+
+ get _isRTL() {
+ return window.getComputedStyle(this)['direction'] === 'rtl';
+ },
+
+ _onLeftKey: function(event) {
+ if (this._isRTL) {
+ this._focusNext();
+ } else {
+ this._focusPrevious();
+ }
+ event.detail.keyboardEvent.preventDefault();
+ },
+
+ _onRightKey: function(event) {
+ if (this._isRTL) {
+ this._focusPrevious();
+ } else {
+ this._focusNext();
+ }
+ event.detail.keyboardEvent.preventDefault();
+ },
+
+ _onKeydown: function(event) {
+ if (this.keyboardEventMatchesKeys(event, 'up down left right esc')) {
+ return;
+ }
+
+ // all other keys focus the menu item starting with that character
+ this._focusWithKeyboardEvent(event);
+ }
+
+ };
+
+ /** @polymerBehavior Polymer.IronMenubarBehavior */
+ Polymer.IronMenubarBehavior = [
+ Polymer.IronMenuBehavior,
+ Polymer.IronMenubarBehaviorImpl
+ ];
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/test/index.html b/catapult/third_party/polymer/components/iron-menu-behavior/test/index.html
new file mode 100644
index 00000000..851835ba
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/test/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>iron-menu-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'iron-menu-behavior.html',
+ 'iron-menubar-behavior.html',
+ 'iron-menu-behavior.html?dom=shadow',
+ 'iron-menubar-behavior.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menu-behavior.html b/catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menu-behavior.html
new file mode 100644
index 00000000..07c3f92e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menu-behavior.html
@@ -0,0 +1,617 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>iron-menu-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+ <link rel="import" href="test-menu.html">
+ <link rel="import" href="test-nested-menu.html">
+
+ <style>
+ .ghost, [hidden] {
+ display: none;
+ }
+ .invisible {
+ visibility: hidden;
+ }
+ </style>
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <test-menu>
+ <div>item 1</div>
+ <div>item 2</div>
+ <div>item 3</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="single-item">
+ <template>
+ <test-menu>
+ <div>item 1</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="disabled">
+ <template>
+ <test-menu>
+ <div>a item 1</div>
+ <div disabled>b item 2</div>
+ <div>b item 3</div>
+ <div disabled>c item 4</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="invisible">
+ <template>
+ <test-menu>
+ <div>item 1</div>
+ <div hidden>item 2</div>
+ <div class="ghost">item 3</div>
+ <div class="invisible">item 3.1</div>
+ <div>item 4</div>
+ <div hidden>item 5</div>
+ <div class="ghost">item 6</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="nested-invisible">
+ <template>
+ <test-nested-menu></test-nested-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="only-disabled">
+ <template>
+ <test-menu>
+ <div disabled>disabled item</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="multi">
+ <template>
+ <test-menu multi>
+ <div>item 1</div>
+ <div>item 2</div>
+ <div>item 3</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="nested">
+ <template>
+ <test-menu>
+ <test-menu>
+ <div>item 1</div>
+ <div>item 2</div>
+ <div>item 3</div>
+ </test-menu>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="empty">
+ <template>
+ <test-menu></test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="disabled-group-and-options">
+ <template>
+ <test-menu disabled>
+ <div disabled>one</div>
+ <div disabled>two</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="nonzero-tabindex">
+ <template>
+ <test-menu tabindex="32">
+ <div>One</div>
+ <div>Two</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="countries">
+ <template>
+ <test-menu>
+ <div>Ghana</div>
+ <div>Uganda</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="bogus-attr-for-item-title">
+ <template>
+ <test-menu attr-for-item-title="totally-doesnt-exist">
+ <div>Focused by default</div>
+ <div>I'm not entitled!</div>
+ </test-menu>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('menu a11y tests', function() {
+ test('menu has role="menu"', function() {
+ var menu = fixture('basic');
+ assert.equal(menu.getAttribute('role'), 'menu', 'has role="menu"');
+ });
+
+ test('first item gets focus when menu is focused', function(done) {
+ var menu = fixture('basic');
+ MockInteractions.focus(menu);
+ Polymer.Base.async(function() {
+ var ownerRoot = Polymer.dom(menu.firstElementChild).getOwnerRoot() || document;
+ var activeElement = Polymer.dom(ownerRoot).activeElement;
+ assert.equal(activeElement, menu.firstElementChild, 'menu.firstElementChild is focused');
+ done();
+ });
+ });
+
+ test('first item gets focus when menu is focused in a single item menu', function(done) {
+ var menu = fixture('single-item');
+ MockInteractions.focus(menu);
+ Polymer.Base.async(function() {
+ var ownerRoot = Polymer.dom(menu.firstElementChild).getOwnerRoot() || document;
+ var activeElement = Polymer.dom(ownerRoot).activeElement;
+ assert.equal(activeElement, menu.firstElementChild, 'menu.firstElementChild is focused');
+ done();
+ });
+ });
+
+ test('selected item gets focus when menu is focused', function(done) {
+ var menu = fixture('basic');
+ menu.selected = 1;
+ MockInteractions.focus(menu);
+ Polymer.Base.async(function() {
+ var ownerRoot = Polymer.dom(menu.selectedItem).getOwnerRoot() || document;
+ var activeElement = Polymer.dom(ownerRoot).activeElement;
+ assert.equal(activeElement, menu.selectedItem, 'menu.selectedItem is focused');
+ done();
+ });
+ });
+
+ test('focusing on next item skips disabled items', function(done) {
+ var menu = fixture('disabled');
+ MockInteractions.focus(menu);
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press down
+ MockInteractions.keyDownOn(menu, 40);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[2], 'menu.items[2] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on next item skips invisible items', function(done) {
+ var menu = fixture('invisible');
+
+ MockInteractions.focus(menu);
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press down
+ MockInteractions.keyDownOn(menu, 40);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[4], 'menu.items[4] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on next item skips nested invisible items', function(done) {
+ var nestedMenu = fixture('nested-invisible');
+ var menu = nestedMenu.$.actualMenu;
+
+ MockInteractions.focus(menu);
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press down
+ MockInteractions.keyDownOn(menu, 40);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[4], 'menu.items[4] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on next item in empty menu', function(done) {
+ var menu = fixture('empty');
+ MockInteractions.focus(menu);
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press down
+ MockInteractions.keyDownOn(menu, 40);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, undefined, 'no focused item');
+ done();
+ });
+ });
+ });
+
+ test('focusing on next item in all disabled menu', function(done) {
+ var menu = fixture('only-disabled');
+ MockInteractions.focus(menu);
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press down
+ MockInteractions.keyDownOn(menu, 40);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, undefined, 'no focused item');
+ done();
+ });
+ });
+ });
+
+ test('focusing on previous item skips disabled items', function(done) {
+ var menu = fixture('disabled');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press up
+ MockInteractions.keyDownOn(menu, 38);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[2], 'menu.items[2] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on previous item skips invisible items', function(done) {
+ var menu = fixture('invisible');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press up
+ MockInteractions.keyDownOn(menu, 38);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[4], 'menu.items[4] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on previous item skips nested invisible items', function(done) {
+ var nestedMenu = fixture('nested-invisible');
+ var menu = nestedMenu.$.actualMenu;
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press up
+ MockInteractions.keyDownOn(menu, 38);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[4], 'menu.items[4] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on previous item in empty menu', function(done) {
+ var menu = fixture('empty');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press up
+ MockInteractions.keyDownOn(menu, 38);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, undefined, 'no focused item');
+ done();
+ });
+ });
+ });
+
+ test('focusing on previous item in all disabled menu', function(done) {
+ var menu = fixture('only-disabled');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press up
+ MockInteractions.keyDownOn(menu, 38);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, undefined, 'no focused item');
+ done();
+ });
+ });
+ });
+
+ test('focusing on item using key press skips disabled items', function(done) {
+ var menu = fixture('disabled');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press 'b'
+ MockInteractions.keyDownOn(menu, 66);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[2], 'menu.items[2] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on item using key press ignores disabled items', function(done) {
+ var menu = fixture('disabled');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press 'c'
+ MockInteractions.keyDownOn(menu, 67);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[0], 'menu.items[0] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on item using key press in all disabled items', function(done) {
+ var menu = fixture('only-disabled');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press 'c'
+ MockInteractions.keyDownOn(menu, 67);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, undefined, 'no focused item');
+ done();
+ });
+ });
+ });
+
+ test('focusing on item with multi-char, quick input', function(done) {
+ var menu = fixture('countries');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press 'u'
+ MockInteractions.keyDownOn(menu, 85);
+
+ // Key press 'g'
+ MockInteractions.keyDownOn(menu, 71);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[1], 'menu.items[1] is focused');
+ done();
+ });
+ });
+ });
+
+ test('focusing on item with bogus attr-for-item-title', function(done) {
+ var menu = fixture('bogus-attr-for-item-title');
+ MockInteractions.focus(menu);
+
+ // Wait for async focus
+ Polymer.Base.async(function() {
+ // Key press 'i'
+ MockInteractions.keyDownOn(menu, 73);
+
+ Polymer.Base.async(function() {
+ var focusedItem = Polymer.dom(menu).node.focusedItem;
+ assert.equal(focusedItem, menu.items[0], 'menu.items[0] is still focused');
+ done();
+ });
+ });
+
+ });
+
+ test('focusing non-item content does not auto-focus an item', function(done) {
+ var menu = fixture('basic');
+ menu.extraContent.focus();
+ Polymer.Base.async(function() {
+ var menuOwnerRoot = Polymer.dom(menu.extraContent).getOwnerRoot() || document;
+ var menuActiveElement = Polymer.dom(menuOwnerRoot).activeElement;
+ assert.equal(menuActiveElement, menu.extraContent, 'menu.extraContent is focused');
+ assert.equal(Polymer.dom(document).activeElement, menu, 'menu is document.activeElement');
+ done();
+ });
+ });
+
+ test('last activated item in a multi select menu is focused', function(done) {
+ var menu = fixture('multi');
+ menu.selected = 0;
+ menu.items[1].click();
+ Polymer.Base.async(function() {
+ var ownerRoot = Polymer.dom(menu.items[1]).getOwnerRoot() || document;
+ var activeElement = Polymer.dom(ownerRoot).activeElement;
+ assert.equal(activeElement, menu.items[1], 'menu.items[1] is focused');
+ done();
+ });
+ });
+
+ test('deselection in a multi select menu focuses deselected item', function(done) {
+ var menu = fixture('multi');
+ menu.selected = 0;
+ menu.items[0].click();
+ Polymer.Base.async(function() {
+ var ownerRoot = Polymer.dom(menu.items[0]).getOwnerRoot() || document;
+ var activeElement = Polymer.dom(ownerRoot).activeElement;
+ assert.equal(activeElement, menu.items[0], 'menu.items[0] is focused');
+ done();
+ });
+ });
+
+ test('keyboard events should not bubble', function(done) {
+ var menu = fixture('nested');
+ var keyCounter = 0;
+
+ menu.addEventListener('keydown', function(event) {
+ if (menu.keyboardEventMatchesKeys(event, 'esc')) {
+ keyCounter++;
+ }
+ if (menu.keyboardEventMatchesKeys(event, 'up')) {
+ keyCounter++;
+ }
+ if (menu.keyboardEventMatchesKeys(event, 'down')) {
+ keyCounter++;
+ }
+ });
+
+ // up
+ MockInteractions.keyDownOn(menu.firstElementChild, 38);
+ // down
+ MockInteractions.keyDownOn(menu.firstElementChild, 40);
+ // esc
+ MockInteractions.keyDownOn(menu.firstElementChild, 27);
+
+ Polymer.Base.async(function() {
+ assert.equal(menu.firstElementChild.tagName, 'TEST-MENU');
+ assert.equal(keyCounter, 0);
+ done();
+ });
+ });
+
+ test('empty menus don\'t unfocus themselves', function(done) {
+ var menu = fixture('empty');
+
+ menu.focus();
+ Polymer.Base.async(function() {
+ assert.equal(Polymer.dom(document).activeElement, menu);
+ done();
+ });
+ });
+
+ test('A disabled menu should not be focusable', function(done) {
+ var menu = fixture('disabled-group-and-options');
+ menu.focus();
+ Polymer.Base.async(function() {
+ assert.notEqual(Polymer.dom(document).activeElement, menu);
+ assert.notEqual(Polymer.dom(document).activeElement, menu.items[0]);
+ assert.notEqual(Polymer.dom(document).activeElement, menu.items[1]);
+ done();
+ });
+ });
+
+ test('A disabled menu will not have a tab index.', function() {
+ var menu = fixture('countries');
+ assert.equal(menu.getAttribute('tabindex'), '0');
+ menu.disabled = true;
+ assert.equal(menu.getAttribute('tabindex'), null);
+ menu.disabled = false;
+ assert.equal(menu.getAttribute('tabindex'), '0');
+ });
+
+ test('Updated tab index of disabled element should remain.', function() {
+ var menu = fixture('countries');
+ assert.equal(menu.getAttribute('tabindex'), '0');
+ menu.disabled = true;
+ assert.equal(menu.getAttribute('tabindex'), null);
+ menu.setAttribute('tabindex', 15);
+ assert.equal(menu.getAttribute('tabindex'), '15');
+ menu.disabled = false;
+ assert.equal(menu.getAttribute('tabindex'), '15');
+ });
+
+ test('A disabled menu will regain its non-zero tab index when re-enabled.', function() {
+ var menu = fixture('nonzero-tabindex');
+ assert.equal(menu.getAttribute('tabindex'), '32');
+ menu.disabled = true;
+ assert.equal(menu.getAttribute('tabindex'), null);
+ menu.disabled = false;
+ assert.equal(menu.getAttribute('tabindex'), '32');
+ });
+
+ test('`tabIndex` properties of all items are updated when items change', function(done) {
+ var menu = fixture('basic');
+
+ function assertTabIndexCounts(nodes, expected) {
+ var tabIndexCounts = {};
+ for (var i = 0; i < nodes.length; i++) {
+ var tabIndex = nodes[i].tabIndex;
+ if (tabIndexCounts[tabIndex]) {
+ tabIndexCounts[tabIndex]++;
+ } else {
+ tabIndexCounts[tabIndex] = 1;
+ }
+ }
+
+ assert.equal(Object.keys(tabIndexCounts).length, Object.keys(expected).length);
+ Object.keys(expected).forEach(function(key) {
+ assert.equal(tabIndexCounts[key], expected[key]);
+ });
+ }
+
+ function divWithTabIndex(tabIndex) {
+ var div = document.createElement('div');
+ div.tabIndex = tabIndex;
+ return div;
+ }
+
+ // Only the selected item will have tabIndex 0.
+ menu.select(0);
+ assertTabIndexCounts(menu.items, {"-1": 2, "0": 1});
+
+ Polymer.dom(menu).appendChild(divWithTabIndex(1));
+ Polymer.dom(menu).appendChild(divWithTabIndex(2));
+ Polymer.dom(menu).appendChild(divWithTabIndex(3));
+
+ // Async wait for `observeNodes`.
+ Polymer.Base.async(function() {
+ assertTabIndexCounts(menu.items, {"-1": 5, "0": 1});
+ done();
+ });
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menubar-behavior.html b/catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menubar-behavior.html
new file mode 100644
index 00000000..78af4ea4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/test/iron-menubar-behavior.html
@@ -0,0 +1,162 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>iron-menubar-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+ <link rel="import" href="test-menubar.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <test-menubar>
+ <div>item 1</div>
+ <div>item 2</div>
+ <div>item 3</div>
+ </test-menubar>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="multi">
+ <template>
+ <test-menubar multi>
+ <div>item 1</div>
+ <div>item 2</div>
+ <div>item 3</div>
+ </test-menubar>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="rtl">
+ <template>
+ <div dir="rtl">
+ <test-menubar>
+ <div>item 1</div>
+ <div>item 2</div>
+ <div>item 3</div>
+ </test-menubar>
+ </div>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('menubar a11y tests', function() {
+
+ test('menubar has role="menubar"', function() {
+ var menubar = fixture('basic');
+ assert.equal(menubar.getAttribute('role'), 'menubar', 'has role="menubar"');
+ });
+
+ test('first item gets focus when menubar is focused', function(done) {
+ var menubar = fixture('basic');
+ MockInteractions.focus(menubar);
+ Polymer.Base.async(function() {
+ assert.equal(Polymer.dom(document).activeElement, menubar.firstElementChild, 'document.activeElement is first item')
+ done();
+ });
+ });
+
+ test('selected item gets focus when menubar is focused', function(done) {
+ var menubar = fixture('basic');
+ menubar.selected = 1;
+ MockInteractions.focus(menubar);
+ Polymer.Base.async(function() {
+ assert.equal(Polymer.dom(document).activeElement, menubar.selectedItem, 'document.activeElement is selected item');
+ done();
+ });
+ });
+
+ test('focusing non-item content does not auto-focus an item', function(done) {
+ var menubar = fixture('basic');
+ menubar.extraContent.focus();
+ Polymer.Base.async(function() {
+ var ownerRoot = Polymer.dom(menubar.extraContent).getOwnerRoot() || document;
+ var activeElement = Polymer.dom(ownerRoot).activeElement;
+ assert.equal(activeElement, menubar.extraContent, 'menubar.extraContent is focused');
+ assert.equal(Polymer.dom(document).activeElement, menubar, 'menubar is document.activeElement');
+ done();
+ });
+ });
+
+ test('last activated item in a multi select menubar is focused', function(done) {
+ var menubar = fixture('multi');
+ menubar.selected = 0;
+ menubar.items[1].click();
+ Polymer.Base.async(function() {
+ assert.equal(Polymer.dom(document).activeElement, menubar.items[1], 'document.activeElement is last activated item');
+ done();
+ });
+ });
+
+ test('deselection in a multi select menubar focuses deselected item', function(done) {
+ var menubar = fixture('multi');
+ menubar.selected = 0;
+ menubar.items[0].click();
+ Polymer.Base.async(function() {
+ assert.equal(Polymer.dom(document).activeElement, menubar.items[0], 'document.activeElement is last activated item');
+ done();
+ });
+ });
+
+ suite('left / right keys are reversed when the menubar has RTL directionality', function() {
+ var LEFT = 37;
+ var RIGHT = 39;
+
+ test('left key moves to the next item', function() {
+ var rtlContainer = fixture('rtl');
+ var menubar = rtlContainer.querySelector('test-menubar');
+ menubar.selected = 0;
+ menubar.items[1].click();
+
+ assert.equal(Polymer.dom(document).activeElement, menubar.items[1]);
+
+ MockInteractions.pressAndReleaseKeyOn(menubar, LEFT);
+
+ assert.equal(Polymer.dom(document).activeElement, menubar.items[2],
+ '`document.activeElement` should be the next item.');
+ assert.equal(menubar.selected, 1,
+ '`menubar.selected` should not change.');
+ });
+
+ test('right key moves to the previous item', function() {
+ var rtlContainer = fixture('rtl');
+ var menubar = rtlContainer.querySelector('test-menubar');
+ menubar.selected = 0;
+ menubar.items[1].click();
+
+ assert.equal(Polymer.dom(document).activeElement, menubar.items[1]);
+
+ MockInteractions.pressAndReleaseKeyOn(menubar, RIGHT);
+
+ assert.equal(Polymer.dom(document).activeElement, menubar.items[0],
+ '`document.activeElement` should be the previous item');
+ assert.equal(menubar.selected, 1,
+ '`menubar.selected` should not change.');
+ });
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/test/test-menu.html b/catapult/third_party/polymer/components/iron-menu-behavior/test/test-menu.html
new file mode 100644
index 00000000..aa8eab28
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/test/test-menu.html
@@ -0,0 +1,46 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-menu-behavior.html">
+
+<dom-module id="test-menu">
+
+ <template>
+
+ <content></content>
+
+ <div id="extraContent" tabindex="-1">focusable extra content</div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'test-menu',
+
+ behaviors: [
+ Polymer.IronMenuBehavior
+ ],
+
+ get extraContent() {
+ return this.$.extraContent;
+ }
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/test/test-menubar.html b/catapult/third_party/polymer/components/iron-menu-behavior/test/test-menubar.html
new file mode 100644
index 00000000..66ce6fde
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/test/test-menubar.html
@@ -0,0 +1,46 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-menubar-behavior.html">
+
+<dom-module id="test-menubar">
+
+ <template>
+
+ <content></content>
+
+ <div id="extraContent" tabindex="-1">focusable extra content</div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'test-menubar',
+
+ behaviors: [
+ Polymer.IronMenubarBehavior
+ ],
+
+ get extraContent() {
+ return this.$.extraContent;
+ }
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-menu-behavior/test/test-nested-menu.html b/catapult/third_party/polymer/components/iron-menu-behavior/test/test-nested-menu.html
new file mode 100644
index 00000000..9eab85f2
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-menu-behavior/test/test-nested-menu.html
@@ -0,0 +1,44 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="test-menu.html">
+
+<dom-module id="test-nested-menu">
+ <template>
+ <style>
+ .ghost, [hidden] {
+ display: none !important;
+ }
+ .invisible {
+ visibility: hidden;
+ }
+ </style>
+ <test-menu id="actualMenu">
+ <div>item 1</div>
+ <div hidden>item 2</div>
+ <div class="ghost">item 3</div>
+ <div class="invisible">item 3.1</div>
+ <div>item 4</div>
+ <div hidden>item 5</div>
+ <div class="ghost">item 6</div>
+ </test-menu>
+ </template>
+</dom-module>
+
+<script>
+
+(function() {
+ Polymer({
+ is: 'test-nested-menu',
+ });
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-meta/.bower.json b/catapult/third_party/polymer/components/iron-meta/.bower.json
new file mode 100644
index 00000000..b8b8970a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/.bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "iron-meta",
+ "version": "1.1.3",
+ "keywords": [
+ "web-components",
+ "polymer"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Useful for sharing information across a DOM tree",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-meta.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.4",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "iron-meta.html",
+ "ignore": [],
+ "homepage": "https://github.com/polymerelements/iron-meta",
+ "_release": "1.1.3",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.3",
+ "commit": "ec0afd727a0af36509264a78b691484123dc7bf6"
+ },
+ "_source": "https://github.com/polymerelements/iron-meta.git",
+ "_target": "^1.0.0",
+ "_originalSource": "polymerelements/iron-meta"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-meta/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-meta/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..2e817113
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-meta/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-meta/.gitignore b/catapult/third_party/polymer/components/iron-meta/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-meta/.travis.yml b/catapult/third_party/polymer/components/iron-meta/.travis.yml
new file mode 100644
index 00000000..0ee194ad
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ mjikUCoN+UpTbFHwbGXUoKp4vtZ2qNN1JYW79WcOik7fCFmvdFzfYQPDa6y9aJvU3kgkDndGdR/ynLG4kejZjmqTS5fYtdHEwpPVPapbVYnquJvCJKbMN4S2QpGCoq51pjKQ8U3Ys6G5HkmdcDfw3SKk1uMgVzKV7fEI+6WnZ/M=
+ - secure: >-
+ LHDnBtwK7yO2X4GNmIaAl7t85WWc1U189OiPqemD27+jTcKml0by1n9Mu/yrg94jYgeXab9mHgU3uMtIdQstNNwTDu8CgmmIP4H2EWopHrTi3KM7Z7aeofPgMJsVFXwg+WhNlcCfhEsygHZWTxjJXM4fcGOrFPDa4+BTgRa2hEE=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-meta/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-meta/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-meta/README.md b/catapult/third_party/polymer/components/iron-meta/README.md
new file mode 100644
index 00000000..3f2ebb32
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/README.md
@@ -0,0 +1,110 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-meta.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-meta.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-meta)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-meta)_
+
+
+##&lt;iron-meta&gt;
+
+`iron-meta` is a generic element you can use for sharing information across the DOM tree.
+It uses [monostate pattern](http://c2.com/cgi/wiki?MonostatePattern) such that any
+instance of iron-meta has access to the shared
+information. You can use `iron-meta` to share whatever you want (or create an extension
+[like x-meta] for enhancements).
+
+The `iron-meta` instances containing your actual data can be loaded in an import,
+or constructed in any way you see fit. The only requirement is that you create them
+before you try to access them.
+
+Examples:
+
+If I create an instance like this:
+
+```html
+<iron-meta key="info" value="foo/bar"></iron-meta>
+```
+
+Note that value="foo/bar" is the metadata I've defined. I could define more
+attributes or use child nodes to define additional metadata.
+
+Now I can access that element (and it's metadata) from any iron-meta instance
+via the byKey method, e.g.
+
+```javascript
+meta.byKey('info');
+```
+
+Pure imperative form would be like:
+
+```javascript
+document.createElement('iron-meta').byKey('info');
+```
+
+Or, in a Polymer element, you can include a meta in your template:
+
+```html
+<iron-meta id="meta"></iron-meta>
+...
+this.$.meta.byKey('info');
+```
+
+
+
+##&lt;iron-meta-query&gt;
+
+`iron-meta` is a generic element you can use for sharing information across the DOM tree.
+It uses [monostate pattern](http://c2.com/cgi/wiki?MonostatePattern) such that any
+instance of iron-meta has access to the shared
+information. You can use `iron-meta` to share whatever you want (or create an extension
+[like x-meta] for enhancements).
+
+The `iron-meta` instances containing your actual data can be loaded in an import,
+or constructed in any way you see fit. The only requirement is that you create them
+before you try to access them.
+
+Examples:
+
+If I create an instance like this:
+
+```html
+<iron-meta key="info" value="foo/bar"></iron-meta>
+```
+
+Note that value="foo/bar" is the metadata I've defined. I could define more
+attributes or use child nodes to define additional metadata.
+
+Now I can access that element (and it's metadata) from any iron-meta instance
+via the byKey method, e.g.
+
+```javascript
+meta.byKey('info');
+```
+
+Pure imperative form would be like:
+
+```javascript
+document.createElement('iron-meta').byKey('info');
+```
+
+Or, in a Polymer element, you can include a meta in your template:
+
+```html
+<iron-meta id="meta"></iron-meta>
+...
+this.$.meta.byKey('info');
+```
+
+
diff --git a/catapult/third_party/polymer/components/iron-meta/bower.json b/catapult/third_party/polymer/components/iron-meta/bower.json
new file mode 100644
index 00000000..5ab0c849
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/bower.json
@@ -0,0 +1,30 @@
+{
+ "name": "iron-meta",
+ "version": "1.1.3",
+ "keywords": [
+ "web-components",
+ "polymer"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Useful for sharing information across a DOM tree",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-meta.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "polymerelements/paper-styles#^1.0.4",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "iron-meta.html",
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-meta/demo/index.html b/catapult/third_party/polymer/components/iron-meta/demo/index.html
new file mode 100644
index 00000000..0fc39e30
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/demo/index.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <title>iron-meta</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../iron-meta.html">
+</head>
+<body>
+
+ <div class="vertical-section centered">
+ <h1>&lt;iron-meta&gt;</h1>
+ <h2>Key Query</h2>
+
+ <iron-meta key="info" value="foo/bar"></iron-meta>
+
+ The <code>value</code> stored at <code>key="info"</code> is <code><meta-test></meta-test></code>.
+ </div>
+
+ <div class="vertical-section centered">
+ <h2>Type Query</h2>
+
+ <iron-meta type="type1" key="a" value="Polymer"></iron-meta>
+ <iron-meta type="type1" key="b" value="is"></iron-meta>
+ <iron-meta type="type2" key="a" value="wonderful"></iron-meta>
+ <iron-meta type="type2" key="b" value="<3"></iron-meta>
+
+ The <code>value(s)</code> stored at <code>type="type1"</code> are:<br><code><type-one></type-one></code>.<br><br>The <code>value(s)</code> stored at <code>type="type2"</code> are:<br><code><type-two></type-two></code>.
+ </div>
+
+ <script>
+ document.addEventListener('WebComponentsReady', function() {
+ Polymer({
+ is: 'meta-test',
+ ready: function() {
+ this.textContent = new Polymer.IronMetaQuery({key: 'info'}).value;
+ }
+ });
+ });
+
+ Polymer({
+ is: 'type-one',
+
+ ready: function() {
+ var resultList = new Polymer.IronMetaQuery({type: "type1"}).list;
+ this.textContent = JSON.stringify(resultList);
+ }
+ });
+
+ Polymer({
+ is: 'type-two',
+
+ ready: function() {
+ var resultList = new Polymer.IronMetaQuery({type: "type2"}).list;
+ this.textContent = JSON.stringify(resultList);
+ }
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-meta/hero.svg b/catapult/third_party/polymer/components/iron-meta/hero.svg
new file mode 100755
index 00000000..8d36c506
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/hero.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <circle cx="22" cy="85" r="4"/>
+ <circle cx="88" cy="98" r="4"/>
+ <path d="M87.5,100c-3.8-0.3-5.5-2.8-7-5c-1.4-2.1-2.7-3.9-5.5-4.2c-2.8-0.3-4.4,1.3-6.2,3.1c-1.9,1.9-4,4-7.8,3.7
+ c-3.8-0.3-5.5-2.8-7-5c-1.4-2.1-2.7-3.9-5.5-4.2c-2.8-0.3-4.4,1.3-6.2,3.1c-1.9,1.9-4,4-7.8,3.7c-3.8-0.3-5.5-2.8-7-5
+ c-1.4-2.1-2.7-3.9-5.5-4.2l0.2-2c3.8,0.3,5.5,2.8,7,5c1.4,2.1,2.7,3.9,5.5,4.2c2.8,0.3,4.4-1.3,6.2-3.1c1.9-1.9,4-4,7.8-3.7
+ c3.8,0.3,5.5,2.8,7,5c1.4,2.1,2.7,3.9,5.5,4.2c2.8,0.3,4.4-1.3,6.2-3.1c1.9-1.9,4-4,7.8-3.7c3.8,0.3,5.5,2.8,7,5
+ c1.4,2.1,2.7,3.9,5.5,4.2L87.5,100z"/>
+ <circle cx="96" cy="86" r="4"/>
+ <circle cx="162" cy="98" r="4"/>
+ <rect x="95.5" y="91" transform="matrix(0.9839 0.1789 -0.1789 0.9839 18.5382 -21.5923)" width="67.1" height="2"/>
+ <g>
+ <path d="M27,41.5l4.5,13.4l4.9-13.4h5.4v32h-4.4V61l0.4-13.4l-5.4,14.5h-2L25.6,48L26,61v12.5h-4.4v-32H27z"/>
+ <path d="M67.5,58.7H53.4V70h16.4v3.5H49v-32h20.6V45H53.4v10.2h14.2V58.7z"/>
+ <path d="M98.5,45H88.3v28.5h-4.4V45H73.6v-3.5h24.9V45z"/>
+ <path d="M116.2,65.3H105l-2.6,8.2h-4.5l10.9-32h3.8l10.6,32h-4.5L116.2,65.3z M106.2,61.6h8.9l-4.4-14.2L106.2,61.6z"/>
+ </g>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-meta/index.html b/catapult/third_party/polymer/components/iron-meta/index.html
new file mode 100644
index 00000000..dce83623
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>iron-meta</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-meta/iron-meta.html b/catapult/third_party/polymer/components/iron-meta/iron-meta.html
new file mode 100644
index 00000000..73d66ccc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/iron-meta.html
@@ -0,0 +1,333 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!--
+`iron-meta` is a generic element you can use for sharing information across the DOM tree.
+It uses [monostate pattern](http://c2.com/cgi/wiki?MonostatePattern) such that any
+instance of iron-meta has access to the shared
+information. You can use `iron-meta` to share whatever you want (or create an extension
+[like x-meta] for enhancements).
+
+The `iron-meta` instances containing your actual data can be loaded in an import,
+or constructed in any way you see fit. The only requirement is that you create them
+before you try to access them.
+
+Examples:
+
+If I create an instance like this:
+
+ <iron-meta key="info" value="foo/bar"></iron-meta>
+
+Note that value="foo/bar" is the metadata I've defined. I could define more
+attributes or use child nodes to define additional metadata.
+
+Now I can access that element (and it's metadata) from any iron-meta instance
+via the byKey method, e.g.
+
+ meta.byKey('info');
+
+Pure imperative form would be like:
+
+ document.createElement('iron-meta').byKey('info');
+
+Or, in a Polymer element, you can include a meta in your template:
+
+ <iron-meta id="meta"></iron-meta>
+ ...
+ this.$.meta.byKey('info');
+
+@group Iron Elements
+@demo demo/index.html
+@hero hero.svg
+@element iron-meta
+-->
+
+<script>
+
+ (function() {
+
+ // monostate data
+ var metaDatas = {};
+ var metaArrays = {};
+ var singleton = null;
+
+ Polymer.IronMeta = Polymer({
+
+ is: 'iron-meta',
+
+ properties: {
+
+ /**
+ * The type of meta-data. All meta-data of the same type is stored
+ * together.
+ */
+ type: {
+ type: String,
+ value: 'default',
+ observer: '_typeChanged'
+ },
+
+ /**
+ * The key used to store `value` under the `type` namespace.
+ */
+ key: {
+ type: String,
+ observer: '_keyChanged'
+ },
+
+ /**
+ * The meta-data to store or retrieve.
+ */
+ value: {
+ type: Object,
+ notify: true,
+ observer: '_valueChanged'
+ },
+
+ /**
+ * If true, `value` is set to the iron-meta instance itself.
+ */
+ self: {
+ type: Boolean,
+ observer: '_selfChanged'
+ },
+
+ /**
+ * Array of all meta-data values for the given type.
+ */
+ list: {
+ type: Array,
+ notify: true
+ }
+
+ },
+
+ hostAttributes: {
+ hidden: true
+ },
+
+ /**
+ * Only runs if someone invokes the factory/constructor directly
+ * e.g. `new Polymer.IronMeta()`
+ *
+ * @param {{type: (string|undefined), key: (string|undefined), value}=} config
+ */
+ factoryImpl: function(config) {
+ if (config) {
+ for (var n in config) {
+ switch(n) {
+ case 'type':
+ case 'key':
+ case 'value':
+ this[n] = config[n];
+ break;
+ }
+ }
+ }
+ },
+
+ created: function() {
+ // TODO(sjmiles): good for debugging?
+ this._metaDatas = metaDatas;
+ this._metaArrays = metaArrays;
+ },
+
+ _keyChanged: function(key, old) {
+ this._resetRegistration(old);
+ },
+
+ _valueChanged: function(value) {
+ this._resetRegistration(this.key);
+ },
+
+ _selfChanged: function(self) {
+ if (self) {
+ this.value = this;
+ }
+ },
+
+ _typeChanged: function(type) {
+ this._unregisterKey(this.key);
+ if (!metaDatas[type]) {
+ metaDatas[type] = {};
+ }
+ this._metaData = metaDatas[type];
+ if (!metaArrays[type]) {
+ metaArrays[type] = [];
+ }
+ this.list = metaArrays[type];
+ this._registerKeyValue(this.key, this.value);
+ },
+
+ /**
+ * Retrieves meta data value by key.
+ *
+ * @method byKey
+ * @param {string} key The key of the meta-data to be returned.
+ * @return {*}
+ */
+ byKey: function(key) {
+ return this._metaData && this._metaData[key];
+ },
+
+ _resetRegistration: function(oldKey) {
+ this._unregisterKey(oldKey);
+ this._registerKeyValue(this.key, this.value);
+ },
+
+ _unregisterKey: function(key) {
+ this._unregister(key, this._metaData, this.list);
+ },
+
+ _registerKeyValue: function(key, value) {
+ this._register(key, value, this._metaData, this.list);
+ },
+
+ _register: function(key, value, data, list) {
+ if (key && data && value !== undefined) {
+ data[key] = value;
+ list.push(value);
+ }
+ },
+
+ _unregister: function(key, data, list) {
+ if (key && data) {
+ if (key in data) {
+ var value = data[key];
+ delete data[key];
+ this.arrayDelete(list, value);
+ }
+ }
+ }
+
+ });
+
+ Polymer.IronMeta.getIronMeta = function getIronMeta() {
+ if (singleton === null) {
+ singleton = new Polymer.IronMeta();
+ }
+ return singleton;
+ };
+
+ /**
+ `iron-meta-query` can be used to access infomation stored in `iron-meta`.
+
+ Examples:
+
+ If I create an instance like this:
+
+ <iron-meta key="info" value="foo/bar"></iron-meta>
+
+ Note that value="foo/bar" is the metadata I've defined. I could define more
+ attributes or use child nodes to define additional metadata.
+
+ Now I can access that element (and it's metadata) from any `iron-meta-query` instance:
+
+ var value = new Polymer.IronMetaQuery({key: 'info'}).value;
+
+ @group Polymer Iron Elements
+ @element iron-meta-query
+ */
+ Polymer.IronMetaQuery = Polymer({
+
+ is: 'iron-meta-query',
+
+ properties: {
+
+ /**
+ * The type of meta-data. All meta-data of the same type is stored
+ * together.
+ */
+ type: {
+ type: String,
+ value: 'default',
+ observer: '_typeChanged'
+ },
+
+ /**
+ * Specifies a key to use for retrieving `value` from the `type`
+ * namespace.
+ */
+ key: {
+ type: String,
+ observer: '_keyChanged'
+ },
+
+ /**
+ * The meta-data to store or retrieve.
+ */
+ value: {
+ type: Object,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * Array of all meta-data values for the given type.
+ */
+ list: {
+ type: Array,
+ notify: true
+ }
+
+ },
+
+ /**
+ * Actually a factory method, not a true constructor. Only runs if
+ * someone invokes it directly (via `new Polymer.IronMeta()`);
+ *
+ * @param {{type: (string|undefined), key: (string|undefined)}=} config
+ */
+ factoryImpl: function(config) {
+ if (config) {
+ for (var n in config) {
+ switch(n) {
+ case 'type':
+ case 'key':
+ this[n] = config[n];
+ break;
+ }
+ }
+ }
+ },
+
+ created: function() {
+ // TODO(sjmiles): good for debugging?
+ this._metaDatas = metaDatas;
+ this._metaArrays = metaArrays;
+ },
+
+ _keyChanged: function(key) {
+ this._setValue(this._metaData && this._metaData[key]);
+ },
+
+ _typeChanged: function(type) {
+ this._metaData = metaDatas[type];
+ this.list = metaArrays[type];
+ if (this.key) {
+ this._keyChanged(this.key);
+ }
+ },
+
+ /**
+ * Retrieves meta data value by key.
+ * @param {string} key The key of the meta-data to be returned.
+ * @return {*}
+ */
+ byKey: function(key) {
+ return this._metaData && this._metaData[key];
+ }
+
+ });
+
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-meta/test/basic.html b/catapult/third_party/polymer/components/iron-meta/test/basic.html
new file mode 100644
index 00000000..c561dc3c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/test/basic.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-meta-basic</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../iron-meta.html">
+
+</head>
+<body>
+
+ <iron-meta key="info" value="foo/bar"></iron-meta>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('byKey', function() {
+ var meta = document.createElement('iron-meta');
+ assert.equal(meta.byKey('info'), 'foo/bar');
+ });
+
+ test('list', function() {
+ var meta = document.createElement('iron-meta');
+ assert.equal(meta.list.length, 1);
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-meta/test/index.html b/catapult/third_party/polymer/components/iron-meta/test/index.html
new file mode 100644
index 00000000..2fb0d690
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/test/index.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+</head>
+<body>
+
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'iron-meta.html',
+ 'basic.html?dom=shadow',
+ 'iron-meta.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-meta/test/iron-meta.html b/catapult/third_party/polymer/components/iron-meta/test/iron-meta.html
new file mode 100644
index 00000000..c4dc2500
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-meta/test/iron-meta.html
@@ -0,0 +1,195 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-meta</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../iron-meta.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="TrivialMeta">
+ <template>
+ <iron-meta self key="info"></iron-meta>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ManyMetas">
+ <template>
+ <iron-meta self key="default1"></iron-meta>
+ <iron-meta self key="default2"></iron-meta>
+ <iron-meta self key="default3"></iron-meta>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="DifferentTypedMetas">
+ <template>
+ <iron-meta self type="foo" key="foobarKey"></iron-meta>
+ <iron-meta self type="bar" key="foobarKey"></iron-meta>
+ <iron-meta self key="defaultKey"></iron-meta>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ClashingMetas">
+ <template>
+ <iron-meta self key="baz"></iron-meta>
+ <iron-meta self key="baz"></iron-meta>
+ </template>
+ </test-fixture>
+
+ <script>
+suite('<iron-meta>', function () {
+ suite('basic behavior', function () {
+ var meta;
+
+ setup(function () {
+ meta = fixture('TrivialMeta');
+ });
+
+ teardown(function () {
+ meta.key = null;
+ });
+
+ test('uses itself as the default value', function () {
+ expect(meta.value).to.be.equal(meta);
+ });
+
+ test('can be assigned alternative values', function () {
+ meta.value = 'foobar';
+
+ expect(meta.list[0]).to.be.equal('foobar');
+ });
+
+ test('can access same-type meta values by key', function () {
+ expect(meta.byKey(meta.key)).to.be.equal(meta.value);
+ });
+
+ test('yields a list of same-type meta data', function () {
+ expect(meta.list).to.be.ok;
+ expect(meta.list.length).to.be.equal(1);
+ expect(meta.list[0]).to.be.equal(meta);
+ });
+ });
+
+ suite('many same-typed metas', function () {
+ var metas;
+
+ setup(function () {
+ metas = fixture('ManyMetas');
+ });
+
+ teardown(function () {
+ metas.forEach(function (meta) {
+ meta.key = null;
+ });
+ });
+
+ test('all cache all meta values', function () {
+ metas.forEach(function (meta, index) {
+ expect(meta.list.length).to.be.equal(metas.length);
+ expect(meta.list[index].value).to.be.equal(meta.value);
+ });
+ });
+
+ test('can be unregistered individually', function () {
+ metas[0].key = null;
+
+ expect(metas[0].list.length).to.be.equal(2);
+ expect(metas[0].list).to.be.deep.equal([metas[1], metas[2]])
+ });
+
+ test('can access each others value by key', function () {
+ expect(metas[0].byKey('default2')).to.be.equal(metas[1].value);
+ });
+ });
+
+ suite('different-typed metas', function () {
+ var metas;
+
+ setup(function () {
+ metas = fixture('DifferentTypedMetas');
+ });
+
+ teardown(function () {
+ metas.forEach(function (meta) {
+ meta.key = null;
+ });
+ });
+
+ test('cache their values separately', function () {
+ var fooMeta = metas[0];
+ var barMeta = metas[1];
+
+ expect(fooMeta.value).to.not.be.equal(barMeta.value);
+ expect(fooMeta.byKey('foobarKey')).to.be.equal(fooMeta.value);
+ expect(barMeta.byKey('foobarKey')).to.be.equal(barMeta.value);
+ });
+
+ test('cannot access values of other types', function () {
+ var defaultMeta = metas[2];
+
+ expect(defaultMeta.byKey('foobarKey')).to.be.equal(undefined);
+ });
+
+ test('only list values of their type', function () {
+ metas.forEach(function (meta) {
+ expect(meta.list.length).to.be.equal(1);
+ expect(meta.list[0]).to.be.equal(meta.value);
+ })
+ });
+ });
+
+ suite('metas with clashing keys', function () {
+ var metaPair;
+
+ setup(function () {
+ metaPair = fixture('ClashingMetas');
+ });
+
+ teardown(function () {
+ metaPair.forEach(function (meta) {
+ meta.key = null;
+ });
+ });
+
+ test('let the last value win registration against the key', function () {
+ var registeredValue = metaPair[0].byKey(metaPair[0].key);
+ var firstValue = metaPair[0].value;
+ var secondValue = metaPair[1].value;
+
+ expect(registeredValue).to.not.be.equal(firstValue);
+ expect(registeredValue).to.be.equal(secondValue);
+ });
+ });
+
+ suite('singleton', function () {
+
+ test('only one ironmeta created', function () {
+ var first = Polymer.IronMeta.getIronMeta();
+ var second = Polymer.IronMeta.getIronMeta();
+ expect(first).to.be.equal(second);
+ });
+ });
+});
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/.bower.json b/catapult/third_party/polymer/components/iron-overlay-behavior/.bower.json
new file mode 100644
index 00000000..9111f7a3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/.bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "iron-overlay-behavior",
+ "version": "1.10.4",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Provides a behavior for making an element an overlay",
+ "private": true,
+ "main": "iron-overlay-behavior.html",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior",
+ "overlay"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-overlay-behavior.git"
+ },
+ "dependencies": {
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^1.0.0",
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.2",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-overlay-behavior",
+ "_release": "1.10.4",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.10.4",
+ "commit": "67713dbb5a65f18ae4ee01407e7264cc286cabca"
+ },
+ "_source": "https://github.com/PolymerElements/iron-overlay-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-overlay-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-overlay-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..9925d374
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-overlay-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/.gitignore b/catapult/third_party/polymer/components/iron-overlay-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-overlay-behavior/.travis.yml
new file mode 100644
index 00000000..cc9cef45
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ ZE4MrCc8vi6hb2WDctjBf9Y7wmYm0tbknPCX0TR+ks0eEXt2ZkWOvMGCw6sKvL1SsrUzhSOQiAc3IFCZBZ6vA1DUCo6S+4O8IvH+VawsK34arI7PIN7UhTDRmudzDwYKJHToRGVFffM1Y5vmNOBNj7hyiQp/UP0hz8vWLpQKyNw=
+ - secure: >-
+ IDwaZdFRon3lTOOZsoGXFUTJayvthgm5JTigYlOtVq6CVlJQBShveehe08fHAhb0XkpFyTFxBsricOUc2DOlyK9ds+DE9rRlIml+BuCrmvfCFvxX4loRg5tibKrRT9mrHNhJRwZiL4WbsGTskMpWGorgkO9qMJSJ9sAabjCJrmM=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-overlay-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/README.md b/catapult/third_party/polymer/components/iron-overlay-behavior/README.md
new file mode 100644
index 00000000..d4ac2af5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/README.md
@@ -0,0 +1,78 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-overlay-backdrop.html iron-overlay-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-overlay-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-overlay-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-overlay-behavior)_
+
+
+##Polymer.IronOverlayBehavior
+
+Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays
+on top of other content. It includes an optional backdrop, and can be used to implement a variety
+of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once.
+
+See the [demo source code](https://github.com/PolymerElements/iron-overlay-behavior/blob/master/demo/simple-overlay.html)
+for an example.
+
+### Closing and canceling
+
+An overlay may be hidden by closing or canceling. The difference between close and cancel is user
+intent. Closing generally implies that the user acknowledged the content on the overlay. By default,
+it will cancel whenever the user taps outside it or presses the escape key. This behavior is
+configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties.
+`close()` should be called explicitly by the implementer when the user interacts with a control
+in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled'
+event. Call `preventDefault` on this event to prevent the overlay from closing.
+
+### Positioning
+
+By default the element is sized and positioned to fit and centered inside the window. You can
+position and size it manually using CSS. See `Polymer.IronFitBehavior`.
+
+### Backdrop
+
+Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is
+appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling
+options.
+
+In addition, `with-backdrop` will wrap the focus within the content in the light DOM.
+Override the [`_focusableNodes` getter](#Polymer.IronOverlayBehavior:property-_focusableNodes)
+to achieve a different behavior.
+
+### Limitations
+
+The element is styled to appear on top of other content by setting its `z-index` property. You
+must ensure no element has a stacking context with a higher `z-index` than its parent stacking
+context. You should place this element as a child of `<body>` whenever possible.
+
+
+
+##&lt;iron-overlay-backdrop&gt;
+
+`iron-overlay-backdrop` is a backdrop used by `Polymer.IronOverlayBehavior`. It should be a
+singleton.
+
+### Styling
+
+The following custom properties and mixins are available for styling.
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--iron-overlay-backdrop-background-color` | Backdrop background color | #000 |
+| `--iron-overlay-backdrop-opacity` | Backdrop opacity | 0.6 |
+| `--iron-overlay-backdrop` | Mixin applied to `iron-overlay-backdrop`. | {} |
+| `--iron-overlay-backdrop-opened` | Mixin applied to `iron-overlay-backdrop` when it is displayed | {} |
+
+
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/bower.json b/catapult/third_party/polymer/components/iron-overlay-behavior/bower.json
new file mode 100644
index 00000000..621f28b4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/bower.json
@@ -0,0 +1,37 @@
+{
+ "name": "iron-overlay-behavior",
+ "version": "1.10.4",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Provides a behavior for making an element an overlay",
+ "private": true,
+ "main": "iron-overlay-behavior.html",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior",
+ "overlay"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-overlay-behavior.git"
+ },
+ "dependencies": {
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^1.0.0",
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.2",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-overlay-behavior/demo/index.html
new file mode 100644
index 00000000..4933bf7d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/demo/index.html
@@ -0,0 +1,188 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+
+ <title>simple-overlay demo</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="simple-overlay.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ demo-snippet {
+ --demo-snippet-code: {
+ max-height: 250px;
+ }
+ }
+ </style>
+
+</head>
+
+<body unresolved class="centered">
+
+ <h3>An element with <code>IronOverlayBehavior</code> can be opened, closed, toggled.</h3>
+ <demo-snippet>
+ <template>
+ <button onclick="plain.open()">Plain overlay</button>
+ <simple-overlay id="plain" tabindex=-1>
+ <h2>Hello world!</h2>
+ <p>This can be closed by pressing the ESC key too.</p>
+ <button onclick="plain.close()">Close</button>
+ </simple-overlay>
+ </template>
+ </demo-snippet>
+
+ <h3>Use <code>with-backdrop</code> to add a backdrop to your overlay. Tabbing will be trapped within the overlay.</h3>
+ <demo-snippet>
+ <template>
+ <button onclick="backdrop.open()">Overlay with backdrop</button>
+ <simple-overlay id="backdrop" with-backdrop>
+ <p>Hello world!</p>
+ <button>Button</button>
+ <button onclick="backdrop.close()">Close</button>
+ </simple-overlay>
+ </template>
+ </demo-snippet>
+
+ <h3>Use <code>restore-focus-on-close</code> to return the focus to the element that opened the overlay when it gets closed.</h3>
+ <demo-snippet>
+ <template>
+ <button onclick="returnFocus.open()">Overlay that restores focus</button>
+ <simple-overlay id="returnFocus" restore-focus-on-close>
+ <p>Hello world!</p>
+ <button onclick="returnFocus.close()">Close</button>
+ </simple-overlay>
+ </template>
+ </demo-snippet>
+
+ <h3>The child with <code>autofocus</code> gets focused when opening the overlay.</h3>
+ <demo-snippet>
+ <template>
+ <button onclick="withAutofocus.open()">Overlay with autofocus child</button>
+ <simple-overlay id="withAutofocus">
+ <p>Hello world!</p>
+ <button autofocus>autofocus</button>
+ <button onclick="withAutofocus.close()">Close</button>
+ </simple-overlay>
+ </template>
+ </demo-snippet>
+
+ <h3>Multiple overlays can be opened.</h3>
+ <demo-snippet>
+ <template>
+ <button onclick="multiple.open()">Open first overlay</button>
+ <simple-overlay id="multiple" tabindex=-1>
+ <p>click to open another overlay</p>
+ <button onclick="multiple2.open()">Open second overlay</button>
+ <button onclick="multiple.close()">Close this</button>
+ </simple-overlay>
+ <simple-overlay id="multiple2" tabindex=-1>
+ <h2>Hi!</h2>
+ <button onclick="multiple2.close()">Close</button>
+ </simple-overlay>
+ </template>
+ </demo-snippet>
+
+ <h3>Use <code>always-on-top</code> to keep the overlay on top of others.</h3>
+ <demo-snippet>
+ <template>
+ <button onclick="onTop.open()">Open always-on-top</button>
+ <simple-overlay id="onTop" always-on-top tabindex=-1>
+ <h2>Always on top</h2>
+ <button onclick="backdrop2.open()">Open with backdrop</button>
+ <button onclick="onTop.close()">Close this</button>
+ </simple-overlay>
+ <simple-overlay id="backdrop2" with-backdrop tabindex=-1>
+ <h2>With backdrop</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <button onclick="backdrop2.close()">Close</button>
+ </simple-overlay>
+ </template>
+ </demo-snippet>
+
+ <h3>An element with <code>IronOverlayBehavior</code> can be scrollable or contain scrollable content.</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .with-margin {
+ margin: 24px 40px;
+ }
+ .scrollable {
+ border: 1px solid lightgray;
+ padding: 24px;
+ @apply(--layout-scroll);
+ }
+ </style>
+ <button onclick="scrolling.open()">Scrolling overlay</button>
+
+ <simple-overlay id="scrolling" class="with-margin scrollable" tabindex=-1>
+ <h2>This overlay scrolls internally.</h2>
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </p>
+ <button onclick="scrolling.close()">Close</button>
+ </simple-overlay>
+
+ <button onclick="scrolling2.open()">Scrolling content</button>
+
+ <simple-overlay id="scrolling2" class="with-margin" tabindex=-1>
+ <h2>This overlay has a scrolling child.</h2>
+ <p class="scrollable">
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </p>
+ <button onclick="scrolling2.close()">Close</button>
+ </simple-overlay>
+
+ <script>
+ // .scrollable is the element that determines the size of the overlay.
+ scrolling2.sizingTarget = Polymer.dom(scrolling2).querySelector('.scrollable');
+ </script>
+ </template>
+ </demo-snippet>
+
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/demo/simple-overlay.html b/catapult/third_party/polymer/components/iron-overlay-behavior/demo/simple-overlay.html
new file mode 100644
index 00000000..047f0477
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/demo/simple-overlay.html
@@ -0,0 +1,46 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-overlay-behavior.html">
+
+<dom-module id="simple-overlay">
+ <template>
+ <style>
+ :host {
+ background: white;
+ color: black;
+ padding: 24px;
+ box-shadow: rgba(0, 0, 0, 0.24) -2px 5px 12px 0px, rgba(0, 0, 0, 0.12) 0px 0px 12px 0px;
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'simple-overlay',
+
+ behaviors: [
+ Polymer.IronOverlayBehavior
+ ]
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/index.html b/catapult/third_party/polymer/components/iron-overlay-behavior/index.html
new file mode 100644
index 00000000..d69e3044
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-overlay-behavior</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/iron-focusables-helper.html b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-focusables-helper.html
new file mode 100644
index 00000000..084ad7d2
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-focusables-helper.html
@@ -0,0 +1,220 @@
+<!doctype html>
+<!--
+@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
+-->
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+ (function() {
+ 'use strict';
+
+ var p = Element.prototype;
+ var matches = p.matches || p.matchesSelector || p.mozMatchesSelector ||
+ p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
+
+ Polymer.IronFocusablesHelper = {
+
+ /**
+ * Returns a sorted array of tabbable nodes, including the root node.
+ * It searches the tabbable nodes in the light and shadow dom of the chidren,
+ * sorting the result by tabindex.
+ * @param {!Node} node
+ * @return {Array<HTMLElement>}
+ */
+ getTabbableNodes: function(node) {
+ var result = [];
+ // If there is at least one element with tabindex > 0, we need to sort
+ // the final array by tabindex.
+ var needsSortByTabIndex = this._collectTabbableNodes(node, result);
+ if (needsSortByTabIndex) {
+ return this._sortByTabIndex(result);
+ }
+ return result;
+ },
+
+ /**
+ * Returns if a element is focusable.
+ * @param {!HTMLElement} element
+ * @return {boolean}
+ */
+ isFocusable: function(element) {
+ // From http://stackoverflow.com/a/1600194/4228703:
+ // There isn't a definite list, it's up to the browser. The only
+ // standard we have is DOM Level 2 HTML https://www.w3.org/TR/DOM-Level-2-HTML/html.html,
+ // according to which the only elements that have a focus() method are
+ // HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement and
+ // HTMLAnchorElement. This notably omits HTMLButtonElement and
+ // HTMLAreaElement.
+ // Referring to these tests with tabbables in different browsers
+ // http://allyjs.io/data-tables/focusable.html
+
+ // Elements that cannot be focused if they have [disabled] attribute.
+ if (matches.call(element, 'input, select, textarea, button, object')) {
+ return matches.call(element, ':not([disabled])');
+ }
+ // Elements that can be focused even if they have [disabled] attribute.
+ return matches.call(element,
+ 'a[href], area[href], iframe, [tabindex], [contentEditable]');
+ },
+
+ /**
+ * Returns if a element is tabbable. To be tabbable, a element must be
+ * focusable, visible, and with a tabindex !== -1.
+ * @param {!HTMLElement} element
+ * @return {boolean}
+ */
+ isTabbable: function(element) {
+ return this.isFocusable(element) &&
+ matches.call(element, ':not([tabindex="-1"])') &&
+ this._isVisible(element);
+ },
+
+ /**
+ * Returns the normalized element tabindex. If not focusable, returns -1.
+ * It checks for the attribute "tabindex" instead of the element property
+ * `tabIndex` since browsers assign different values to it.
+ * e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
+ * @param {!HTMLElement} element
+ * @return {!number}
+ * @private
+ */
+ _normalizedTabIndex: function(element) {
+ if (this.isFocusable(element)) {
+ var tabIndex = element.getAttribute('tabindex') || 0;
+ return Number(tabIndex);
+ }
+ return -1;
+ },
+
+ /**
+ * Searches for nodes that are tabbable and adds them to the `result` array.
+ * Returns if the `result` array needs to be sorted by tabindex.
+ * @param {!Node} node The starting point for the search; added to `result`
+ * if tabbable.
+ * @param {!Array<HTMLElement>} result
+ * @return {boolean}
+ * @private
+ */
+ _collectTabbableNodes: function(node, result) {
+ // If not an element or not visible, no need to explore children.
+ if (node.nodeType !== Node.ELEMENT_NODE || !this._isVisible(node)) {
+ return false;
+ }
+ var element = /** @type {HTMLElement} */ (node);
+ var tabIndex = this._normalizedTabIndex(element);
+ var needsSortByTabIndex = tabIndex > 0;
+ if (tabIndex >= 0) {
+ result.push(element);
+ }
+
+ // In ShadowDOM v1, tab order is affected by the order of distrubution.
+ // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B];
+ // in ShadowDOM v0 tab order is not affected by the distrubution order,
+ // in fact getTabbableNodes(#root) returns [#B, #A].
+ // <div id="root">
+ // <!-- shadow -->
+ // <slot name="a">
+ // <slot name="b">
+ // <!-- /shadow -->
+ // <input id="A" slot="a">
+ // <input id="B" slot="b" tabindex="1">
+ // </div>
+ // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0.
+ var children;
+ if (element.localName === 'content') {
+ children = Polymer.dom(element).getDistributedNodes();
+ } else {
+ // Use shadow root if possible, will check for distributed nodes.
+ children = Polymer.dom(element.root || element).children;
+ }
+ for (var i = 0; i < children.length; i++) {
+ // Ensure method is always invoked to collect tabbable children.
+ var needsSort = this._collectTabbableNodes(children[i], result);
+ needsSortByTabIndex = needsSortByTabIndex || needsSort;
+ }
+ return needsSortByTabIndex;
+ },
+
+ /**
+ * Returns false if the element has `visibility: hidden` or `display: none`
+ * @param {!HTMLElement} element
+ * @return {boolean}
+ * @private
+ */
+ _isVisible: function(element) {
+ // Check inline style first to save a re-flow. If looks good, check also
+ // computed style.
+ var style = element.style;
+ if (style.visibility !== 'hidden' && style.display !== 'none') {
+ style = window.getComputedStyle(element);
+ return (style.visibility !== 'hidden' && style.display !== 'none');
+ }
+ return false;
+ },
+
+ /**
+ * Sorts an array of tabbable elements by tabindex. Returns a new array.
+ * @param {!Array<HTMLElement>} tabbables
+ * @return {Array<HTMLElement>}
+ * @private
+ */
+ _sortByTabIndex: function(tabbables) {
+ // Implement a merge sort as Array.prototype.sort does a non-stable sort
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
+ var len = tabbables.length;
+ if (len < 2) {
+ return tabbables;
+ }
+ var pivot = Math.ceil(len / 2);
+ var left = this._sortByTabIndex(tabbables.slice(0, pivot));
+ var right = this._sortByTabIndex(tabbables.slice(pivot));
+ return this._mergeSortByTabIndex(left, right);
+ },
+
+ /**
+ * Merge sort iterator, merges the two arrays into one, sorted by tab index.
+ * @param {!Array<HTMLElement>} left
+ * @param {!Array<HTMLElement>} right
+ * @return {Array<HTMLElement>}
+ * @private
+ */
+ _mergeSortByTabIndex: function(left, right) {
+ var result = [];
+ while ((left.length > 0) && (right.length > 0)) {
+ if (this._hasLowerTabOrder(left[0], right[0])) {
+ result.push(right.shift());
+ } else {
+ result.push(left.shift());
+ }
+ }
+
+ return result.concat(left, right);
+ },
+
+ /**
+ * Returns if element `a` has lower tab order compared to element `b`
+ * (both elements are assumed to be focusable and tabbable).
+ * Elements with tabindex = 0 have lower tab order compared to elements
+ * with tabindex > 0.
+ * If both have same tabindex, it returns false.
+ * @param {!HTMLElement} a
+ * @param {!HTMLElement} b
+ * @return {boolean}
+ * @private
+ */
+ _hasLowerTabOrder: function(a, b) {
+ // Normalize tabIndexes
+ // e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
+ var ati = Math.max(a.tabIndex, 0);
+ var bti = Math.max(b.tabIndex, 0);
+ return (ati === 0 || bti === 0) ? bti > ati : ati > bti;
+ }
+ };
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-backdrop.html b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-backdrop.html
new file mode 100644
index 00000000..432a57e1
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-backdrop.html
@@ -0,0 +1,168 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<!--
+`iron-overlay-backdrop` is a backdrop used by `Polymer.IronOverlayBehavior`. It should be a
+singleton.
+
+### Styling
+
+The following custom properties and mixins are available for styling.
+
+Custom property | Description | Default
+-------------------------------------------|------------------------|---------
+`--iron-overlay-backdrop-background-color` | Backdrop background color | #000
+`--iron-overlay-backdrop-opacity` | Backdrop opacity | 0.6
+`--iron-overlay-backdrop` | Mixin applied to `iron-overlay-backdrop`. | {}
+`--iron-overlay-backdrop-opened` | Mixin applied to `iron-overlay-backdrop` when it is displayed | {}
+-->
+
+<dom-module id="iron-overlay-backdrop">
+
+ <template>
+ <style>
+ :host {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: var(--iron-overlay-backdrop-background-color, #000);
+ opacity: 0;
+ transition: opacity 0.2s;
+ pointer-events: none;
+ @apply(--iron-overlay-backdrop);
+ }
+
+ :host(.opened) {
+ opacity: var(--iron-overlay-backdrop-opacity, 0.6);
+ pointer-events: auto;
+ @apply(--iron-overlay-backdrop-opened);
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+(function() {
+'use strict';
+
+ Polymer({
+
+ is: 'iron-overlay-backdrop',
+
+ properties: {
+
+ /**
+ * Returns true if the backdrop is opened.
+ */
+ opened: {
+ reflectToAttribute: true,
+ type: Boolean,
+ value: false,
+ observer: '_openedChanged'
+ }
+
+ },
+
+ listeners: {
+ 'transitionend': '_onTransitionend'
+ },
+
+ created: function() {
+ // Used to cancel previous requestAnimationFrame calls when opened changes.
+ this.__openedRaf = null;
+ },
+
+ attached: function() {
+ this.opened && this._openedChanged(this.opened);
+ },
+
+ /**
+ * Appends the backdrop to document body if needed.
+ */
+ prepare: function() {
+ if (this.opened && !this.parentNode) {
+ Polymer.dom(document.body).appendChild(this);
+ }
+ },
+
+ /**
+ * Shows the backdrop.
+ */
+ open: function() {
+ this.opened = true;
+ },
+
+ /**
+ * Hides the backdrop.
+ */
+ close: function() {
+ this.opened = false;
+ },
+
+ /**
+ * Removes the backdrop from document body if needed.
+ */
+ complete: function() {
+ if (!this.opened && this.parentNode === document.body) {
+ Polymer.dom(this.parentNode).removeChild(this);
+ }
+ },
+
+ _onTransitionend: function(event) {
+ if (event && event.target === this) {
+ this.complete();
+ }
+ },
+
+ /**
+ * @param {boolean} opened
+ * @private
+ */
+ _openedChanged: function(opened) {
+ if (opened) {
+ // Auto-attach.
+ this.prepare();
+ } else {
+ // Animation might be disabled via the mixin or opacity custom property.
+ // If it is disabled in other ways, it's up to the user to call complete.
+ var cs = window.getComputedStyle(this);
+ if (cs.transitionDuration === '0s' || cs.opacity == 0) {
+ this.complete();
+ }
+ }
+
+ if (!this.isAttached) {
+ return;
+ }
+
+ // Always cancel previous requestAnimationFrame.
+ if (this.__openedRaf) {
+ window.cancelAnimationFrame(this.__openedRaf);
+ this.__openedRaf = null;
+ }
+ // Force relayout to ensure proper transitions.
+ this.scrollTop = this.scrollTop;
+ this.__openedRaf = window.requestAnimationFrame(function() {
+ this.__openedRaf = null;
+ this.toggleClass('opened', this.opened);
+ }.bind(this));
+ }
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-behavior.html b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-behavior.html
new file mode 100644
index 00000000..ae085c53
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-behavior.html
@@ -0,0 +1,637 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-fit-behavior/iron-fit-behavior.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="iron-overlay-manager.html">
+<link rel="import" href="iron-focusables-helper.html">
+
+<script>
+(function() {
+ 'use strict';
+
+ /** @polymerBehavior */
+ Polymer.IronOverlayBehaviorImpl = {
+
+ properties: {
+
+ /**
+ * True if the overlay is currently displayed.
+ */
+ opened: {
+ observer: '_openedChanged',
+ type: Boolean,
+ value: false,
+ notify: true
+ },
+
+ /**
+ * True if the overlay was canceled when it was last closed.
+ */
+ canceled: {
+ observer: '_canceledChanged',
+ readOnly: true,
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to display a backdrop behind the overlay. It traps the focus
+ * within the light DOM of the overlay.
+ */
+ withBackdrop: {
+ observer: '_withBackdropChanged',
+ type: Boolean
+ },
+
+ /**
+ * Set to true to disable auto-focusing the overlay or child nodes with
+ * the `autofocus` attribute` when the overlay is opened.
+ */
+ noAutoFocus: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable canceling the overlay with the ESC key.
+ */
+ noCancelOnEscKey: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable canceling the overlay by clicking outside it.
+ */
+ noCancelOnOutsideClick: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Contains the reason(s) this overlay was last closed (see `iron-overlay-closed`).
+ * `IronOverlayBehavior` provides the `canceled` reason; implementers of the
+ * behavior can provide other reasons in addition to `canceled`.
+ */
+ closingReason: {
+ // was a getter before, but needs to be a property so other
+ // behaviors can override this.
+ type: Object
+ },
+
+ /**
+ * Set to true to enable restoring of focus when overlay is closed.
+ */
+ restoreFocusOnClose: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to keep overlay always on top.
+ */
+ alwaysOnTop: {
+ type: Boolean
+ },
+
+ /**
+ * Shortcut to access to the overlay manager.
+ * @private
+ * @type {Polymer.IronOverlayManagerClass}
+ */
+ _manager: {
+ type: Object,
+ value: Polymer.IronOverlayManager
+ },
+
+ /**
+ * The node being focused.
+ * @type {?Node}
+ */
+ _focusedChild: {
+ type: Object
+ }
+
+ },
+
+ listeners: {
+ 'iron-resize': '_onIronResize'
+ },
+
+ /**
+ * The backdrop element.
+ * @type {Element}
+ */
+ get backdropElement() {
+ return this._manager.backdropElement;
+ },
+
+ /**
+ * Returns the node to give focus to.
+ * @type {Node}
+ */
+ get _focusNode() {
+ return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this;
+ },
+
+ /**
+ * Array of nodes that can receive focus (overlay included), ordered by `tabindex`.
+ * This is used to retrieve which is the first and last focusable nodes in order
+ * to wrap the focus for overlays `with-backdrop`.
+ *
+ * If you know what is your content (specifically the first and last focusable children),
+ * you can override this method to return only `[firstFocusable, lastFocusable];`
+ * @type {Array<Node>}
+ * @protected
+ */
+ get _focusableNodes() {
+ return Polymer.IronFocusablesHelper.getTabbableNodes(this);
+ },
+
+ ready: function() {
+ // Used to skip calls to notifyResize and refit while the overlay is animating.
+ this.__isAnimating = false;
+ // with-backdrop needs tabindex to be set in order to trap the focus.
+ // If it is not set, IronOverlayBehavior will set it, and remove it if with-backdrop = false.
+ this.__shouldRemoveTabIndex = false;
+ // Used for wrapping the focus on TAB / Shift+TAB.
+ this.__firstFocusableNode = this.__lastFocusableNode = null;
+ // Used by __onNextAnimationFrame to cancel any previous callback.
+ this.__raf = null;
+ // Focused node before overlay gets opened. Can be restored on close.
+ this.__restoreFocusNode = null;
+ this._ensureSetup();
+ },
+
+ attached: function() {
+ // Call _openedChanged here so that position can be computed correctly.
+ if (this.opened) {
+ this._openedChanged(this.opened);
+ }
+ this._observer = Polymer.dom(this).observeNodes(this._onNodesChange);
+ },
+
+ detached: function() {
+ Polymer.dom(this).unobserveNodes(this._observer);
+ this._observer = null;
+ if (this.__raf) {
+ window.cancelAnimationFrame(this.__raf);
+ this.__raf = null;
+ }
+ this._manager.removeOverlay(this);
+ },
+
+ /**
+ * Toggle the opened state of the overlay.
+ */
+ toggle: function() {
+ this._setCanceled(false);
+ this.opened = !this.opened;
+ },
+
+ /**
+ * Open the overlay.
+ */
+ open: function() {
+ this._setCanceled(false);
+ this.opened = true;
+ },
+
+ /**
+ * Close the overlay.
+ */
+ close: function() {
+ this._setCanceled(false);
+ this.opened = false;
+ },
+
+ /**
+ * Cancels the overlay.
+ * @param {Event=} event The original event
+ */
+ cancel: function(event) {
+ var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: true});
+ if (cancelEvent.defaultPrevented) {
+ return;
+ }
+
+ this._setCanceled(true);
+ this.opened = false;
+ },
+
+ /**
+ * Invalidates the cached tabbable nodes. To be called when any of the focusable
+ * content changes (e.g. a button is disabled).
+ */
+ invalidateTabbables: function() {
+ this.__firstFocusableNode = this.__lastFocusableNode = null;
+ },
+
+ _ensureSetup: function() {
+ if (this._overlaySetup) {
+ return;
+ }
+ this._overlaySetup = true;
+ this.style.outline = 'none';
+ this.style.display = 'none';
+ },
+
+ /**
+ * Called when `opened` changes.
+ * @param {boolean=} opened
+ * @protected
+ */
+ _openedChanged: function(opened) {
+ if (opened) {
+ this.removeAttribute('aria-hidden');
+ } else {
+ this.setAttribute('aria-hidden', 'true');
+ }
+
+ // Defer any animation-related code on attached
+ // (_openedChanged gets called again on attached).
+ if (!this.isAttached) {
+ return;
+ }
+
+ this.__isAnimating = true;
+
+ // Use requestAnimationFrame for non-blocking rendering.
+ this.__onNextAnimationFrame(this.__openedChanged);
+ },
+
+ _canceledChanged: function() {
+ this.closingReason = this.closingReason || {};
+ this.closingReason.canceled = this.canceled;
+ },
+
+ _withBackdropChanged: function() {
+ // If tabindex is already set, no need to override it.
+ if (this.withBackdrop && !this.hasAttribute('tabindex')) {
+ this.setAttribute('tabindex', '-1');
+ this.__shouldRemoveTabIndex = true;
+ } else if (this.__shouldRemoveTabIndex) {
+ this.removeAttribute('tabindex');
+ this.__shouldRemoveTabIndex = false;
+ }
+ if (this.opened && this.isAttached) {
+ this._manager.trackBackdrop();
+ }
+ },
+
+ /**
+ * tasks which must occur before opening; e.g. making the element visible.
+ * @protected
+ */
+ _prepareRenderOpened: function() {
+ // Store focused node.
+ this.__restoreFocusNode = this._manager.deepActiveElement;
+
+ // Needed to calculate the size of the overlay so that transitions on its size
+ // will have the correct starting points.
+ this._preparePositioning();
+ this.refit();
+ this._finishPositioning();
+
+ // Safari will apply the focus to the autofocus element when displayed
+ // for the first time, so we make sure to return the focus where it was.
+ if (this.noAutoFocus && document.activeElement === this._focusNode) {
+ this._focusNode.blur();
+ this.__restoreFocusNode.focus();
+ }
+ },
+
+ /**
+ * Tasks which cause the overlay to actually open; typically play an animation.
+ * @protected
+ */
+ _renderOpened: function() {
+ this._finishRenderOpened();
+ },
+
+ /**
+ * Tasks which cause the overlay to actually close; typically play an animation.
+ * @protected
+ */
+ _renderClosed: function() {
+ this._finishRenderClosed();
+ },
+
+ /**
+ * Tasks to be performed at the end of open action. Will fire `iron-overlay-opened`.
+ * @protected
+ */
+ _finishRenderOpened: function() {
+ this.notifyResize();
+ this.__isAnimating = false;
+
+ this.fire('iron-overlay-opened');
+ },
+
+ /**
+ * Tasks to be performed at the end of close action. Will fire `iron-overlay-closed`.
+ * @protected
+ */
+ _finishRenderClosed: function() {
+ // Hide the overlay.
+ this.style.display = 'none';
+ // Reset z-index only at the end of the animation.
+ this.style.zIndex = '';
+ this.notifyResize();
+ this.__isAnimating = false;
+ this.fire('iron-overlay-closed', this.closingReason);
+ },
+
+ _preparePositioning: function() {
+ this.style.transition = this.style.webkitTransition = 'none';
+ this.style.transform = this.style.webkitTransform = 'none';
+ this.style.display = '';
+ },
+
+ _finishPositioning: function() {
+ // First, make it invisible & reactivate animations.
+ this.style.display = 'none';
+ // Force reflow before re-enabling animations so that they don't start.
+ // Set scrollTop to itself so that Closure Compiler doesn't remove this.
+ this.scrollTop = this.scrollTop;
+ this.style.transition = this.style.webkitTransition = '';
+ this.style.transform = this.style.webkitTransform = '';
+ // Now that animations are enabled, make it visible again
+ this.style.display = '';
+ // Force reflow, so that following animations are properly started.
+ // Set scrollTop to itself so that Closure Compiler doesn't remove this.
+ this.scrollTop = this.scrollTop;
+ },
+
+ /**
+ * Applies focus according to the opened state.
+ * @protected
+ */
+ _applyFocus: function() {
+ if (this.opened) {
+ if (!this.noAutoFocus) {
+ this._focusNode.focus();
+ }
+ }
+ else {
+ this._focusNode.blur();
+ this._focusedChild = null;
+ // Restore focus.
+ if (this.restoreFocusOnClose && this.__restoreFocusNode) {
+ this.__restoreFocusNode.focus();
+ }
+ this.__restoreFocusNode = null;
+ // If many overlays get closed at the same time, one of them would still
+ // be the currentOverlay even if already closed, and would call _applyFocus
+ // infinitely, so we check for this not to be the current overlay.
+ var currentOverlay = this._manager.currentOverlay();
+ if (currentOverlay && this !== currentOverlay) {
+ currentOverlay._applyFocus();
+ }
+ }
+ },
+
+ /**
+ * Cancels (closes) the overlay. Call when click happens outside the overlay.
+ * @param {!Event} event
+ * @protected
+ */
+ _onCaptureClick: function(event) {
+ if (!this.noCancelOnOutsideClick) {
+ this.cancel(event);
+ }
+ },
+
+ /**
+ * Keeps track of the focused child. If withBackdrop, traps focus within overlay.
+ * @param {!Event} event
+ * @protected
+ */
+ _onCaptureFocus: function (event) {
+ if (!this.withBackdrop) {
+ return;
+ }
+ var path = Polymer.dom(event).path;
+ if (path.indexOf(this) === -1) {
+ event.stopPropagation();
+ this._applyFocus();
+ } else {
+ this._focusedChild = path[0];
+ }
+ },
+
+ /**
+ * Handles the ESC key event and cancels (closes) the overlay.
+ * @param {!Event} event
+ * @protected
+ */
+ _onCaptureEsc: function(event) {
+ if (!this.noCancelOnEscKey) {
+ this.cancel(event);
+ }
+ },
+
+ /**
+ * Handles TAB key events to track focus changes.
+ * Will wrap focus for overlays withBackdrop.
+ * @param {!Event} event
+ * @protected
+ */
+ _onCaptureTab: function(event) {
+ if (!this.withBackdrop) {
+ return;
+ }
+ this.__ensureFirstLastFocusables();
+ // TAB wraps from last to first focusable.
+ // Shift + TAB wraps from first to last focusable.
+ var shift = event.shiftKey;
+ var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode;
+ var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode;
+ var shouldWrap = false;
+ if (nodeToCheck === nodeToSet) {
+ // If nodeToCheck is the same as nodeToSet, it means we have an overlay
+ // with 0 or 1 focusables; in either case we still need to trap the
+ // focus within the overlay.
+ shouldWrap = true;
+ } else {
+ // In dom=shadow, the manager will receive focus changes on the main
+ // root but not the ones within other shadow roots, so we can't rely on
+ // _focusedChild, but we should check the deepest active element.
+ var focusedNode = this._manager.deepActiveElement;
+ // If the active element is not the nodeToCheck but the overlay itself,
+ // it means the focus is about to go outside the overlay, hence we
+ // should prevent that (e.g. user opens the overlay and hit Shift+TAB).
+ shouldWrap = (focusedNode === nodeToCheck || focusedNode === this);
+ }
+
+ if (shouldWrap) {
+ // When the overlay contains the last focusable element of the document
+ // and it's already focused, pressing TAB would move the focus outside
+ // the document (e.g. to the browser search bar). Similarly, when the
+ // overlay contains the first focusable element of the document and it's
+ // already focused, pressing Shift+TAB would move the focus outside the
+ // document (e.g. to the browser search bar).
+ // In both cases, we would not receive a focus event, but only a blur.
+ // In order to achieve focus wrapping, we prevent this TAB event and
+ // force the focus. This will also prevent the focus to temporarily move
+ // outside the overlay, which might cause scrolling.
+ event.preventDefault();
+ this._focusedChild = nodeToSet;
+ this._applyFocus();
+ }
+ },
+
+ /**
+ * Refits if the overlay is opened and not animating.
+ * @protected
+ */
+ _onIronResize: function() {
+ if (this.opened && !this.__isAnimating) {
+ this.__onNextAnimationFrame(this.refit);
+ }
+ },
+
+ /**
+ * Will call notifyResize if overlay is opened.
+ * Can be overridden in order to avoid multiple observers on the same node.
+ * @protected
+ */
+ _onNodesChange: function() {
+ if (this.opened && !this.__isAnimating) {
+ // It might have added focusable nodes, so invalidate cached values.
+ this.invalidateTabbables();
+ this.notifyResize();
+ }
+ },
+
+ /**
+ * Will set first and last focusable nodes if any of them is not set.
+ * @private
+ */
+ __ensureFirstLastFocusables: function() {
+ if (!this.__firstFocusableNode || !this.__lastFocusableNode) {
+ var focusableNodes = this._focusableNodes;
+ this.__firstFocusableNode = focusableNodes[0];
+ this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];
+ }
+ },
+
+ /**
+ * Tasks executed when opened changes: prepare for the opening, move the
+ * focus, update the manager, render opened/closed.
+ * @private
+ */
+ __openedChanged: function() {
+ if (this.opened) {
+ // Make overlay visible, then add it to the manager.
+ this._prepareRenderOpened();
+ this._manager.addOverlay(this);
+ // Move the focus to the child node with [autofocus].
+ this._applyFocus();
+
+ this._renderOpened();
+ } else {
+ // Remove overlay, then restore the focus before actually closing.
+ this._manager.removeOverlay(this);
+ this._applyFocus();
+
+ this._renderClosed();
+ }
+ },
+
+ /**
+ * Executes a callback on the next animation frame, overriding any previous
+ * callback awaiting for the next animation frame. e.g.
+ * `__onNextAnimationFrame(callback1) && __onNextAnimationFrame(callback2)`;
+ * `callback1` will never be invoked.
+ * @param {!Function} callback Its `this` parameter is the overlay itself.
+ * @private
+ */
+ __onNextAnimationFrame: function(callback) {
+ if (this.__raf) {
+ window.cancelAnimationFrame(this.__raf);
+ }
+ var self = this;
+ this.__raf = window.requestAnimationFrame(function nextAnimationFrame() {
+ self.__raf = null;
+ callback.call(self);
+ });
+ }
+
+ };
+
+ /**
+ Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays
+ on top of other content. It includes an optional backdrop, and can be used to implement a variety
+ of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once.
+
+ See the [demo source code](https://github.com/PolymerElements/iron-overlay-behavior/blob/master/demo/simple-overlay.html)
+ for an example.
+
+ ### Closing and canceling
+
+ An overlay may be hidden by closing or canceling. The difference between close and cancel is user
+ intent. Closing generally implies that the user acknowledged the content on the overlay. By default,
+ it will cancel whenever the user taps outside it or presses the escape key. This behavior is
+ configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties.
+ `close()` should be called explicitly by the implementer when the user interacts with a control
+ in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled'
+ event. Call `preventDefault` on this event to prevent the overlay from closing.
+
+ ### Positioning
+
+ By default the element is sized and positioned to fit and centered inside the window. You can
+ position and size it manually using CSS. See `Polymer.IronFitBehavior`.
+
+ ### Backdrop
+
+ Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is
+ appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling
+ options.
+
+ In addition, `with-backdrop` will wrap the focus within the content in the light DOM.
+ Override the [`_focusableNodes` getter](#Polymer.IronOverlayBehavior:property-_focusableNodes)
+ to achieve a different behavior.
+
+ ### Limitations
+
+ The element is styled to appear on top of other content by setting its `z-index` property. You
+ must ensure no element has a stacking context with a higher `z-index` than its parent stacking
+ context. You should place this element as a child of `<body>` whenever possible.
+
+ @demo demo/index.html
+ @polymerBehavior
+ */
+ Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
+
+ /**
+ * Fired after the overlay opens.
+ * @event iron-overlay-opened
+ */
+
+ /**
+ * Fired when the overlay is canceled, but before it is closed.
+ * @event iron-overlay-canceled
+ * @param {Event} event The closing of the overlay can be prevented
+ * by calling `event.preventDefault()`. The `event.detail` is the original event that
+ * originated the canceling (e.g. ESC keyboard event or click event outside the overlay).
+ */
+
+ /**
+ * Fired after the overlay closes.
+ * @event iron-overlay-closed
+ * @param {Event} event The `event.detail` is the `closingReason` property
+ * (contains `canceled`, whether the overlay was canceled).
+ */
+
+})();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-manager.html b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-manager.html
new file mode 100644
index 00000000..53fb265b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/iron-overlay-manager.html
@@ -0,0 +1,366 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="iron-overlay-backdrop.html">
+
+<script>
+
+ /**
+ * @struct
+ * @constructor
+ * @private
+ */
+ Polymer.IronOverlayManagerClass = function() {
+ /**
+ * Used to keep track of the opened overlays.
+ * @private {Array<Element>}
+ */
+ this._overlays = [];
+
+ /**
+ * iframes have a default z-index of 100,
+ * so this default should be at least that.
+ * @private {number}
+ */
+ this._minimumZ = 101;
+
+ /**
+ * Memoized backdrop element.
+ * @private {Element|null}
+ */
+ this._backdropElement = null;
+
+ // Enable document-wide tap recognizer.
+ // NOTE: Use useCapture=true to avoid accidentally prevention of the closing
+ // of an overlay via event.stopPropagation(). The only way to prevent
+ // closing of an overlay should be through its APIs.
+ // NOTE: enable tap on <html> to workaround Polymer/polymer#4459
+ Polymer.Gestures.add(document.documentElement, 'tap', null);
+ document.addEventListener('tap', this._onCaptureClick.bind(this), true);
+ document.addEventListener('focus', this._onCaptureFocus.bind(this), true);
+ document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true);
+ };
+
+ Polymer.IronOverlayManagerClass.prototype = {
+
+ constructor: Polymer.IronOverlayManagerClass,
+
+ /**
+ * The shared backdrop element.
+ * @type {!Element} backdropElement
+ */
+ get backdropElement() {
+ if (!this._backdropElement) {
+ this._backdropElement = document.createElement('iron-overlay-backdrop');
+ }
+ return this._backdropElement;
+ },
+
+ /**
+ * The deepest active element.
+ * @type {!Element} activeElement the active element
+ */
+ get deepActiveElement() {
+ // document.activeElement can be null
+ // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
+ // In case of null, default it to document.body.
+ var active = document.activeElement || document.body;
+ while (active.root && Polymer.dom(active.root).activeElement) {
+ active = Polymer.dom(active.root).activeElement;
+ }
+ return active;
+ },
+
+ /**
+ * Brings the overlay at the specified index to the front.
+ * @param {number} i
+ * @private
+ */
+ _bringOverlayAtIndexToFront: function(i) {
+ var overlay = this._overlays[i];
+ if (!overlay) {
+ return;
+ }
+ var lastI = this._overlays.length - 1;
+ var currentOverlay = this._overlays[lastI];
+ // Ensure always-on-top overlay stays on top.
+ if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
+ lastI--;
+ }
+ // If already the top element, return.
+ if (i >= lastI) {
+ return;
+ }
+ // Update z-index to be on top.
+ var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);
+ if (this._getZ(overlay) <= minimumZ) {
+ this._applyOverlayZ(overlay, minimumZ);
+ }
+
+ // Shift other overlays behind the new on top.
+ while (i < lastI) {
+ this._overlays[i] = this._overlays[i + 1];
+ i++;
+ }
+ this._overlays[lastI] = overlay;
+ },
+
+ /**
+ * Adds the overlay and updates its z-index if it's opened, or removes it if it's closed.
+ * Also updates the backdrop z-index.
+ * @param {!Element} overlay
+ */
+ addOrRemoveOverlay: function(overlay) {
+ if (overlay.opened) {
+ this.addOverlay(overlay);
+ } else {
+ this.removeOverlay(overlay);
+ }
+ },
+
+ /**
+ * Tracks overlays for z-index and focus management.
+ * Ensures the last added overlay with always-on-top remains on top.
+ * @param {!Element} overlay
+ */
+ addOverlay: function(overlay) {
+ var i = this._overlays.indexOf(overlay);
+ if (i >= 0) {
+ this._bringOverlayAtIndexToFront(i);
+ this.trackBackdrop();
+ return;
+ }
+ var insertionIndex = this._overlays.length;
+ var currentOverlay = this._overlays[insertionIndex - 1];
+ var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ);
+ var newZ = this._getZ(overlay);
+
+ // Ensure always-on-top overlay stays on top.
+ if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
+ // This bumps the z-index of +2.
+ this._applyOverlayZ(currentOverlay, minimumZ);
+ insertionIndex--;
+ // Update minimumZ to match previous overlay's z-index.
+ var previousOverlay = this._overlays[insertionIndex - 1];
+ minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ);
+ }
+
+ // Update z-index and insert overlay.
+ if (newZ <= minimumZ) {
+ this._applyOverlayZ(overlay, minimumZ);
+ }
+ this._overlays.splice(insertionIndex, 0, overlay);
+
+ this.trackBackdrop();
+ },
+
+ /**
+ * @param {!Element} overlay
+ */
+ removeOverlay: function(overlay) {
+ var i = this._overlays.indexOf(overlay);
+ if (i === -1) {
+ return;
+ }
+ this._overlays.splice(i, 1);
+
+ this.trackBackdrop();
+ },
+
+ /**
+ * Returns the current overlay.
+ * @return {Element|undefined}
+ */
+ currentOverlay: function() {
+ var i = this._overlays.length - 1;
+ return this._overlays[i];
+ },
+
+ /**
+ * Returns the current overlay z-index.
+ * @return {number}
+ */
+ currentOverlayZ: function() {
+ return this._getZ(this.currentOverlay());
+ },
+
+ /**
+ * Ensures that the minimum z-index of new overlays is at least `minimumZ`.
+ * This does not effect the z-index of any existing overlays.
+ * @param {number} minimumZ
+ */
+ ensureMinimumZ: function(minimumZ) {
+ this._minimumZ = Math.max(this._minimumZ, minimumZ);
+ },
+
+ focusOverlay: function() {
+ var current = /** @type {?} */ (this.currentOverlay());
+ if (current) {
+ current._applyFocus();
+ }
+ },
+
+ /**
+ * Updates the backdrop z-index.
+ */
+ trackBackdrop: function() {
+ var overlay = this._overlayWithBackdrop();
+ // Avoid creating the backdrop if there is no overlay with backdrop.
+ if (!overlay && !this._backdropElement) {
+ return;
+ }
+ this.backdropElement.style.zIndex = this._getZ(overlay) - 1;
+ this.backdropElement.opened = !!overlay;
+ },
+
+ /**
+ * @return {Array<Element>}
+ */
+ getBackdrops: function() {
+ var backdrops = [];
+ for (var i = 0; i < this._overlays.length; i++) {
+ if (this._overlays[i].withBackdrop) {
+ backdrops.push(this._overlays[i]);
+ }
+ }
+ return backdrops;
+ },
+
+ /**
+ * Returns the z-index for the backdrop.
+ * @return {number}
+ */
+ backdropZ: function() {
+ return this._getZ(this._overlayWithBackdrop()) - 1;
+ },
+
+ /**
+ * Returns the first opened overlay that has a backdrop.
+ * @return {Element|undefined}
+ * @private
+ */
+ _overlayWithBackdrop: function() {
+ for (var i = 0; i < this._overlays.length; i++) {
+ if (this._overlays[i].withBackdrop) {
+ return this._overlays[i];
+ }
+ }
+ },
+
+ /**
+ * Calculates the minimum z-index for the overlay.
+ * @param {Element=} overlay
+ * @private
+ */
+ _getZ: function(overlay) {
+ var z = this._minimumZ;
+ if (overlay) {
+ var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).zIndex);
+ // Check if is a number
+ // Number.isNaN not supported in IE 10+
+ if (z1 === z1) {
+ z = z1;
+ }
+ }
+ return z;
+ },
+
+ /**
+ * @param {!Element} element
+ * @param {number|string} z
+ * @private
+ */
+ _setZ: function(element, z) {
+ element.style.zIndex = z;
+ },
+
+ /**
+ * @param {!Element} overlay
+ * @param {number} aboveZ
+ * @private
+ */
+ _applyOverlayZ: function(overlay, aboveZ) {
+ this._setZ(overlay, aboveZ + 2);
+ },
+
+ /**
+ * Returns the deepest overlay in the path.
+ * @param {Array<Element>=} path
+ * @return {Element|undefined}
+ * @suppress {missingProperties}
+ * @private
+ */
+ _overlayInPath: function(path) {
+ path = path || [];
+ for (var i = 0; i < path.length; i++) {
+ if (path[i]._manager === this) {
+ return path[i];
+ }
+ }
+ },
+
+ /**
+ * Ensures the click event is delegated to the right overlay.
+ * @param {!Event} event
+ * @private
+ */
+ _onCaptureClick: function(event) {
+ var overlay = /** @type {?} */ (this.currentOverlay());
+ // Check if clicked outside of top overlay.
+ if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) {
+ overlay._onCaptureClick(event);
+ }
+ },
+
+ /**
+ * Ensures the focus event is delegated to the right overlay.
+ * @param {!Event} event
+ * @private
+ */
+ _onCaptureFocus: function(event) {
+ var overlay = /** @type {?} */ (this.currentOverlay());
+ if (overlay) {
+ overlay._onCaptureFocus(event);
+ }
+ },
+
+ /**
+ * Ensures TAB and ESC keyboard events are delegated to the right overlay.
+ * @param {!Event} event
+ * @private
+ */
+ _onCaptureKeyDown: function(event) {
+ var overlay = /** @type {?} */ (this.currentOverlay());
+ if (overlay) {
+ if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) {
+ overlay._onCaptureEsc(event);
+ } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'tab')) {
+ overlay._onCaptureTab(event);
+ }
+ }
+ },
+
+ /**
+ * Returns if the overlay1 should be behind overlay2.
+ * @param {!Element} overlay1
+ * @param {!Element} overlay2
+ * @return {boolean}
+ * @suppress {missingProperties}
+ * @private
+ */
+ _shouldBeBehindOverlay: function(overlay1, overlay2) {
+ return !overlay1.alwaysOnTop && overlay2.alwaysOnTop;
+ }
+ };
+
+ Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/index.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/index.html
new file mode 100644
index 00000000..2dac121a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>iron-overlay-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'iron-overlay-behavior.html',
+ 'iron-overlay-behavior.html?dom=shadow',
+ 'iron-focusables-helper.html',
+ 'iron-focusables-helper.html?dom=shadow',
+ 'iron-overlay-backdrop.html',
+ 'iron-overlay-backdrop.html?dom=shadow',
+ ]);
+ </script>
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-focusables-helper.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-focusables-helper.html
new file mode 100644
index 00000000..a400b4c5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-focusables-helper.html
@@ -0,0 +1,182 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+
+ <title>iron-focusables-helper tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+ <link rel="import" href="../iron-focusables-helper.html">
+ <link rel="import" href="test-buttons.html">
+ <link rel="import" href="test-buttons-wrapper.html">
+
+ <style is="custom-style">
+ .hidden {
+ visibility: hidden;
+ }
+
+ .no-display {
+ display: none;
+ }
+ </style>
+</head>
+
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <div>
+ <h2>Focusables (no tabindex)</h2>
+ <div>
+ <input class="focusable1" placeholder="1 (nested)">
+ </div>
+ <a href="#" class="focusable2">2</a>
+ <button disabled> disabled button</button>
+ <input disabled tabindex="0" value="disabled input with tabindex">
+ <div tabindex="-1">not focusable</div>
+ <div contenteditable class="focusable3">3</div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="tabindex">
+ <template>
+ <div>
+ <h2>Focusables (with tabindex)</h2>
+ <div tabindex="0" class="focusable7">7</div>
+ <div tabindex="0" class="focusable8">8</div>
+ <div tabindex="0" class="focusable9">9</div>
+ <div tabindex="0" class="focusable10">10</div>
+ <div tabindex="0" class="focusable11">11</div>
+ <div tabindex="0" class="focusable12">12</div>
+ <div tabindex="-1">not focusable</div>
+ <div tabindex="3" class="focusable3">3</div>
+ <div tabindex="4" class="focusable4">4</div>
+ <div tabindex="5" class="focusable5">5</div>
+ <div>
+ <div tabindex="1" class="focusable1">1 (nested)</div>
+ <div tabindex="6" class="focusable6">6 (nested)</div>
+ </div>
+ <div tabindex="2" class="focusable2">2</div>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="shadow">
+ <template>
+ <test-buttons>
+ <h2>focusables in ShadowDOM</h2>
+ <input placeholder="type something..">
+ </test-buttons>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="composed">
+ <template>
+ <test-buttons-wrapper>
+ <input placeholder="type something..">
+ </test-buttons-wrapper>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('getTabbableNodes', function() {
+
+ test('returns tabbable nodes', function() {
+ var node = fixture('basic');
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(node);
+ assert.equal(focusableNodes.length, 3, '3 nodes are focusable');
+ assert.equal(focusableNodes[0], Polymer.dom(node).querySelector('.focusable1'));
+ assert.equal(focusableNodes[1], Polymer.dom(node).querySelector('.focusable2'));
+ assert.equal(focusableNodes[2], Polymer.dom(node).querySelector('.focusable3'));
+ });
+
+ test('includes the root if it has a valid tabindex', function() {
+ var node = fixture('basic');
+ node.setAttribute('tabindex', '0');
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(node);
+ assert.equal(focusableNodes.length, 4, '4 focusable nodes');
+ assert.notEqual(focusableNodes.indexOf(node), -1, 'root is included');
+ });
+
+ test('excludes visibility: hidden elements', function() {
+ var node = fixture('basic');
+ var focusable = Polymer.dom(node).querySelector('.focusable1');
+ focusable.classList.add('hidden');
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(node);
+ assert.equal(focusableNodes.length, 2, '2 focusable nodes');
+ assert.equal(focusableNodes.indexOf(focusable), -1, 'hidden element is not included');
+ });
+
+ test('excludes display: none elements', function() {
+ var node = fixture('basic');
+ var focusable = Polymer.dom(node).querySelector('.focusable1');
+ focusable.classList.add('no-display');
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(node);
+ assert.equal(focusableNodes.length, 2, '2 focusable nodes');
+ assert.equal(focusableNodes.indexOf(focusable), -1, 'hidden element is not included');
+ });
+
+ test('respects the tabindex order', function() {
+ var node = fixture('tabindex');
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(node);
+ assert.equal(focusableNodes.length, 12, '12 nodes are focusable');
+ for (var i = 0; i < 12; i++) {
+ assert.equal(focusableNodes[i], Polymer.dom(node).querySelector('.focusable' + (i + 1)));
+ }
+ });
+
+ test('includes tabbable elements in the shadow dom', function() {
+ var node = fixture('shadow');
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(node);
+ assert.equal(focusableNodes.length, 4, '4 nodes are focusable');
+ assert.equal(focusableNodes[0], node.$.button0);
+ assert.equal(focusableNodes[1], node.$.button1);
+ assert.equal(focusableNodes[2], Polymer.dom(node).querySelector('input'));
+ assert.equal(focusableNodes[3], node.$.button2);
+ });
+
+ test('handles composition', function() {
+ var node = fixture('composed');
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(node);
+ assert.equal(focusableNodes.length, 6, '6 nodes are focusable');
+ assert.equal(focusableNodes[0], node.$.select);
+ assert.equal(focusableNodes[1], node.$.wrapped.$.button0);
+ assert.equal(focusableNodes[2], node.$.wrapped.$.button1);
+ assert.equal(focusableNodes[3], Polymer.dom(node).querySelector('input'));
+ assert.equal(focusableNodes[4], node.$.wrapped.$.button2);
+ assert.equal(focusableNodes[5], node.$.focusableDiv);
+ });
+
+ test('handles distributed nodes', function() {
+ var node = fixture('composed');
+ var wrapped = node.$.wrapped;
+ var focusableNodes = Polymer.IronFocusablesHelper.getTabbableNodes(wrapped);
+ assert.equal(focusableNodes.length, 4, '4 nodes are focusable');
+ assert.equal(focusableNodes[0], wrapped.$.button0);
+ assert.equal(focusableNodes[1], wrapped.$.button1);
+ assert.equal(focusableNodes[2], Polymer.dom(node).querySelector('input'));
+ assert.equal(focusableNodes[3], wrapped.$.button2);
+ });
+ });
+ </script>
+
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-backdrop.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-backdrop.html
new file mode 100644
index 00000000..89c2e9af
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-backdrop.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+
+<head>
+
+ <title>iron-overlay-backdrop tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+ <link rel="import" href="test-overlay.html">
+
+ <style>
+ html,
+ body {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ min-width: 0;
+ }
+ .sizer {
+ width: 4000px;
+ height: 5000px;
+ }
+ </style>
+
+</head>
+
+<body>
+
+<div class="sizer"></div>
+
+<test-fixture id="backdrop">
+ <template>
+ <test-overlay with-backdrop>
+ Overlay with backdrop
+ </test-overlay>
+ </template>
+</test-fixture>
+
+<script>
+ function runAfterOpen(overlay, callback) {
+ overlay.addEventListener('iron-overlay-opened', callback);
+ overlay.open();
+ }
+
+ suite('overlay with backdrop', function() {
+ var overlay;
+
+ setup(function() {
+ overlay = fixture('backdrop');
+ });
+
+ test('backdrop size matches parent size', function(done) {
+ runAfterOpen(overlay, function() {
+ // Flush so we are sure backdrop is added in the DOM.
+ Polymer.dom.flush();
+ var backdrop = overlay.backdropElement;
+ var parent = backdrop.parentElement;
+ assert.strictEqual(backdrop.offsetWidth, parent.clientWidth, 'backdrop width matches parent width');
+ assert.strictEqual(backdrop.offsetHeight, parent.clientHeight, 'backdrop height matches parent height');
+ done();
+ });
+ });
+
+ });
+</script>
+
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-behavior.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-behavior.html
new file mode 100644
index 00000000..f6c3621a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/iron-overlay-behavior.html
@@ -0,0 +1,1282 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+ <head>
+
+ <title>iron-overlay-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+ <link rel="import" href="test-overlay.html">
+ <link rel="import" href="test-overlay2.html">
+ <link rel="import" href="test-buttons.html">
+ <link rel="import" href="test-menu-button.html">
+
+ <style is="custom-style">
+ iron-overlay-backdrop {
+ /* For quicker tests */
+ --iron-overlay-backdrop: {
+ transition: none;
+ }
+ }
+ </style>
+
+ </head>
+
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <test-overlay>
+ Basic Overlay
+ </test-overlay>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="opened">
+ <template>
+ <test-overlay opened>
+ Basic Overlay
+ </test-overlay>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="autofocus">
+ <template>
+ <test-overlay>
+ Autofocus
+ <button autofocus>button</button>
+ </test-overlay>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="focusables">
+ <template>
+ <test-overlay tabindex="-1">
+ <h2>Focusables (no tabindex)</h2>
+ <div>
+ <input class="focusable1" placeholder="1 (nested)">
+ </div>
+ <button class="focusable2">1</button>
+ <button disabled> disabled button</button>
+ <div tabindex="-1">not focusable</div>
+ <button class="focusable3">2</button>
+ </test-overlay>
+ <test-overlay tabindex="-1">
+ <h2>Focusables (with tabindex)</h2>
+ <div tabindex="-1">not focusable</div>
+ <div tabindex="3" class="focusable3">3</div>
+ <div tabindex="4" class="focusable4">4</div>
+ <div tabindex="5" class="focusable5">5</div>
+ <div>
+ <div tabindex="1" class="focusable1">1 (nested)</div>
+ <div tabindex="6" class="focusable6">6 (nested)</div>
+ </div>
+ <div tabindex="2" class="focusable2">2</div>
+ </test-overlay>
+ <test-overlay2>
+ Overlay with optimized focusableNodes getter
+ <button class="focusable1">1</button>
+ </test-overlay2>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="backdrop">
+ <template>
+ <test-overlay with-backdrop>
+ Overlay with backdrop
+ </test-overlay>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="multiple">
+ <template>
+ <test-overlay class="overlay-1">
+ Test overlay 1
+ </test-overlay>
+ <test-overlay class="overlay-2">
+ Test overlay 2
+ <button>Click</button>
+ </test-overlay>
+ <test-overlay2 class="overlay-3">
+ Other overlay 3
+ </test-overlay2>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="composed">
+ <template>
+ <test-menu-button></test-menu-button>
+ </template>
+ </test-fixture>
+
+ <test-buttons id="buttons"></test-buttons>
+ <input id="focusInput" placeholder="focus input">
+
+ <script>
+
+ function runAfterOpen(overlay, callback) {
+ overlay.addEventListener('iron-overlay-opened', callback);
+ overlay.open();
+ }
+
+ function runAfterClose(overlay, callback) {
+ overlay.addEventListener('iron-overlay-closed', callback);
+ overlay.close();
+ }
+
+ suite('basic overlay', function() {
+ var overlay;
+
+ setup(function() {
+ overlay = fixture('basic');
+ });
+
+ test('overlay starts hidden', function() {
+ assert.isFalse(overlay.opened, 'overlay starts closed');
+ assert.equal(getComputedStyle(overlay).display, 'none', 'overlay starts hidden');
+ });
+
+ test('_renderOpened called only after is attached', function(done) {
+ var overlay = document.createElement('test-overlay');
+ // The overlay is ready at this point, but not yet attached.
+ var spy = sinon.spy(overlay, '_renderOpened');
+ // This triggers _openedChanged.
+ overlay.opened = true;
+ // Wait long enough for requestAnimationFrame callback.
+ overlay.async(function() {
+ assert.isFalse(spy.called, '_renderOpened not called');
+ // Because not attached yet, overlay should not be the current overlay!
+ assert.isNotOk(overlay._manager.currentOverlay(), 'currentOverlay not set');
+ done();
+ }, 100);
+ });
+
+ test('overlay open/close events', function(done) {
+ var nevents = 0;
+
+ overlay.addEventListener('iron-overlay-opened', function() {
+ nevents += 1;
+ overlay.opened = false;
+ });
+
+ overlay.addEventListener('iron-overlay-closed', function() {
+ nevents += 1;
+ assert.equal(nevents, 2, 'opened and closed events fired');
+ done();
+ });
+
+ overlay.opened = true;
+ });
+
+ test('open() refits overlay only once', function(done) {
+ var spy = sinon.spy(overlay, 'refit');
+ runAfterOpen(overlay, function() {
+ assert.equal(spy.callCount, 1, 'overlay did refit only once');
+ done();
+ });
+ });
+
+ test('open overlay refits on iron-resize', function(done) {
+ runAfterOpen(overlay, function() {
+ var spy = sinon.spy(overlay, 'refit');
+ overlay.fire('iron-resize');
+ Polymer.dom.flush();
+ requestAnimationFrame(function() {
+ assert.isTrue(spy.called, 'overlay did refit');
+ done();
+ });
+ });
+ });
+
+ test('closed overlay does not refit on iron-resize', function(done) {
+ var spy = sinon.spy(overlay, 'refit');
+ overlay.fire('iron-resize');
+ Polymer.dom.flush();
+ requestAnimationFrame(function() {
+ assert.isFalse(spy.called, 'overlay should not refit');
+ done();
+ });
+ });
+
+ test('open() triggers iron-resize', function(done) {
+ var callCount = 0;
+ // Ignore iron-resize triggered by window resize.
+ window.addEventListener('resize', function() { callCount--; }, true);
+ overlay.addEventListener('iron-resize', function() { callCount++; });
+ runAfterOpen(overlay, function() {
+ assert.equal(callCount, 1, 'iron-resize called once before iron-overlay-opened');
+ done();
+ });
+ });
+
+ test('close() triggers iron-resize', function(done) {
+ runAfterOpen(overlay, function() {
+ var spy = sinon.stub();
+ overlay.addEventListener('iron-resize', spy);
+ runAfterClose(overlay, function() {
+ assert.equal(spy.callCount, 1, 'iron-resize called once before iron-overlay-closed');
+ done();
+ });
+ });
+ });
+
+ test('closed overlay does not trigger iron-resize when its content changes', function() {
+ // Ignore iron-resize triggered by window resize.
+ var callCount = 0;
+ window.addEventListener('resize', function() { callCount--; }, true);
+ overlay.addEventListener('iron-resize', function() { callCount++; });
+ Polymer.dom(overlay).appendChild(document.createElement('div'));
+ Polymer.dom.flush();
+ assert.equal(callCount, 0, 'iron-resize should not be called');
+ });
+
+ test('open overlay triggers iron-resize when its content changes', function(done) {
+ runAfterOpen(overlay, function() {
+ var spy = sinon.stub();
+ overlay.addEventListener('iron-resize', spy);
+ Polymer.dom(overlay).appendChild(document.createElement('div'));
+ Polymer.dom.flush();
+ assert.equal(spy.callCount, 1, 'iron-resize should be called once');
+ done();
+ });
+ });
+
+ test('close an overlay quickly after open', function(done) {
+ // first, open the overlay
+ overlay.open();
+ overlay.async(function() {
+ // during the opening transition, close the overlay
+ this.close();
+ // wait for any exceptions to be thrown until the transition is done
+ this.async(function() {
+ done();
+ }, 300);
+ });
+ });
+
+ test('clicking an overlay does not close it', function(done) {
+ runAfterOpen(overlay, function() {
+ var spy = sinon.stub();
+ overlay.addEventListener('iron-overlay-closed', spy);
+ MockInteractions.tap(overlay);
+ overlay.async(function() {
+ assert.isFalse(spy.called, 'iron-overlay-closed should not fire');
+ done();
+ }, 10);
+ });
+ });
+
+ test('open overlay on mousedown does not close it', function(done) {
+ var btn = document.createElement('button');
+ btn.addEventListener('mousedown', overlay.open.bind(overlay));
+ document.body.appendChild(btn);
+ // It triggers mousedown, mouseup, and click.
+ MockInteractions.tap(btn);
+ document.body.removeChild(btn);
+
+ assert.isTrue(overlay.opened, 'overlay opened');
+ overlay.async(function() {
+ assert.isTrue(overlay.opened, 'overlay is still open');
+ done();
+ }, 10);
+ });
+
+ test('clicking outside fires iron-overlay-canceled', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-canceled', function(event) {
+ assert.equal(event.detail.target, document.body, 'detail contains original click event');
+ done();
+ });
+ MockInteractions.tap(document.body);
+ });
+ });
+
+ test('clicking outside closes the overlay', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-closed', function(event) {
+ assert.isTrue(event.detail.canceled, 'overlay is canceled');
+ done();
+ });
+ MockInteractions.tap(document.body);
+ });
+ });
+
+ test('iron-overlay-canceled event can be prevented', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-canceled', function(event) {
+ event.preventDefault();
+ });
+ var spy = sinon.stub();
+ overlay.addEventListener('iron-overlay-closed', spy);
+ MockInteractions.tap(document.body);
+ Polymer.Base.async(function() {
+ assert.isTrue(overlay.opened, 'overlay is still open');
+ assert.isFalse(spy.called, 'iron-overlay-closed not fired');
+ done();
+ }, 10);
+ });
+ });
+
+ test('cancel an overlay with esc key', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-canceled', function(event) {
+ assert.equal(event.detail.type, 'keydown');
+ done();
+ });
+ MockInteractions.pressAndReleaseKeyOn(document, 27);
+ });
+ });
+
+ test('close an overlay with esc key', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-closed', function(event) {
+ assert.isTrue(event.detail.canceled, 'overlay is canceled');
+ done();
+ });
+ MockInteractions.pressAndReleaseKeyOn(document, 27);
+ });
+ });
+
+ test('no-cancel-on-outside-click property', function(done) {
+ overlay.noCancelOnOutsideClick = true;
+ runAfterOpen(overlay, function() {
+ var spy = sinon.stub();
+ overlay.addEventListener('iron-overlay-closed', spy);
+ MockInteractions.tap(document.body);
+ Polymer.Base.async(function() {
+ assert.isFalse(spy.called, 'iron-overlay-closed should not fire');
+ done();
+ }, 10);
+ });
+ });
+
+ test('no-cancel-on-esc-key property', function(done) {
+ overlay.noCancelOnEscKey = true;
+ runAfterOpen(overlay, function() {
+ var spy = sinon.stub();
+ overlay.addEventListener('iron-overlay-closed', spy);
+ MockInteractions.pressAndReleaseKeyOn(document, 27);
+ Polymer.Base.async(function() {
+ assert.isFalse(spy.called, 'iron-overlay-cancel should not fire');
+ done();
+ }, 10);
+ });
+ });
+
+ test('with-backdrop sets tabindex=-1 and removes it', function() {
+ overlay.withBackdrop = true;
+ assert.equal(overlay.getAttribute('tabindex'), '-1', 'tabindex is -1');
+ overlay.withBackdrop = false;
+ assert.isFalse(overlay.hasAttribute('tabindex'), 'tabindex removed');
+ });
+
+ test('with-backdrop does not override tabindex if already set', function() {
+ overlay.setAttribute('tabindex', '1');
+ overlay.withBackdrop = true;
+ assert.equal(overlay.getAttribute('tabindex'), '1', 'tabindex is 1');
+ overlay.withBackdrop = false;
+ assert.equal(overlay.getAttribute('tabindex'), '1', 'tabindex is still 1');
+ });
+
+ });
+
+ suite('keyboard event listener', function() {
+ var overlay;
+
+ var preventKeyDown = function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ suiteSetup(function() {
+ // Worst case scenario: listener with useCapture = true that prevents & stops propagation
+ // added before the overlay is initialized.
+ document.addEventListener('keydown', preventKeyDown, true);
+ });
+
+ setup(function() {
+ overlay = fixture('basic');
+ });
+
+ suiteTeardown(function() {
+ document.removeEventListener('keydown', preventKeyDown, true);
+ });
+
+ test('cancel an overlay with esc key even if event is prevented by other listeners', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-canceled', function(event) {
+ done();
+ });
+ MockInteractions.pressAndReleaseKeyOn(document, 27);
+ });
+ });
+ });
+
+ suite('tap event listener', function() {
+ var overlay;
+
+ var preventTap = function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ suiteSetup(function() {
+ // Worst case scenario: listener with useCapture = true that prevents & stops propagation
+ // added before the overlay is initialized.
+ document.body.addEventListener('tap', preventTap, true);
+ });
+
+ setup(function() {
+ overlay = fixture('basic');
+ });
+
+ suiteTeardown(function() {
+ document.body.removeEventListener('tap', preventTap, true);
+ });
+
+ test('cancel an overlay with tap outside even if event is prevented by other listeners', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-canceled', function(event) {
+ done();
+ });
+ MockInteractions.tap(document.body);
+ });
+ });
+ });
+
+ suite('opened overlay', function() {
+ var overlay;
+
+ setup(function() {
+ overlay = fixture('opened');
+ });
+
+ test('overlay open by default', function(done) {
+ overlay.addEventListener('iron-overlay-opened', function() {
+ assert.isTrue(overlay.opened, 'overlay starts opened');
+ assert.notEqual(getComputedStyle(overlay).display, 'none', 'overlay starts showing');
+ done();
+ });
+ });
+
+ test('overlay positioned & sized properly', function(done) {
+ overlay.addEventListener('iron-overlay-opened', function() {
+ var s = getComputedStyle(overlay);
+ assert.closeTo(parseFloat(s.left), (window.innerWidth - overlay.offsetWidth) / 2, 1, 'centered horizontally');
+ assert.closeTo(parseFloat(s.top), (window.innerHeight - overlay.offsetHeight) / 2, 1, 'centered vertically');
+ done();
+ });
+ });
+ });
+
+ suite('focus handling', function() {
+ var overlay;
+
+ setup(function() {
+ // Ensure focus is set to document.body
+ document.body.focus();
+ overlay = fixture('autofocus');
+ });
+
+ test('node with autofocus is focused', function(done) {
+ runAfterOpen(overlay, function() {
+ assert.equal(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> is focused');
+ done();
+ });
+ });
+
+ test('no-auto-focus will not focus node with autofocus', function(done) {
+ overlay.noAutoFocus = true;
+ runAfterOpen(overlay, function() {
+ assert.notEqual(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> not focused after opened');
+ done();
+ });
+ // In Safari the element with autofocus will immediately receive focus when displayed for the first time http://jsbin.com/woroci/2/
+ // Ensure this is not the case for overlay.
+ assert.notEqual(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> not immediately focused');
+ });
+
+ test('no-cancel-on-outside-click property; focus stays on overlay when click outside', function(done) {
+ overlay.noCancelOnOutsideClick = true;
+ runAfterOpen(overlay, function() {
+ MockInteractions.tap(document.body);
+ Polymer.Base.async(function() {
+ assert.equal(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> is focused');
+ done();
+ }, 10);
+ });
+ });
+
+ test('with-backdrop traps the focus within the overlay', function(done) {
+ var focusSpy = sinon.stub();
+ var button = document.createElement('button');
+ document.body.appendChild(button);
+ button.addEventListener('focus', focusSpy, true);
+
+ overlay.withBackdrop = true;
+ runAfterOpen(overlay, function() {
+ // Try to steal the focus
+ MockInteractions.focus(button);
+ assert.equal(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, '<button autofocus> is focused');
+ assert.equal(focusSpy.callCount, 0, 'button in body did not get the focus');
+ document.body.removeChild(button);
+ done();
+ });
+ });
+
+ test('overlay with-backdrop and 1 focusable: prevent TAB and trap the focus', function(done) {
+ overlay.withBackdrop = true;
+ runAfterOpen(overlay, function() {
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function() {
+ // Spy keydown.
+ var tabSpy = sinon.spy();
+ document.addEventListener('keydown', tabSpy);
+ // Simulate TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9);
+ assert.equal(Polymer.dom(overlay).querySelector('[autofocus]'), document.activeElement, 'focus stays on button');
+ assert.isTrue(tabSpy.calledOnce, 'keydown spy called');
+ assert.isTrue(tabSpy.getCall(0).args[0].defaultPrevented, 'keydown default prevented');
+ // Cleanup.
+ document.removeEventListener('keydown', tabSpy);
+ done();
+ }, 1);
+ });
+ });
+
+ test('empty overlay with-backdrop: prevent TAB and trap the focus', function(done) {
+ overlay = fixture('basic');
+ overlay.withBackdrop = true;
+ runAfterOpen(overlay, function() {
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function() {
+ // Spy keydown.
+ var tabSpy = sinon.spy();
+ document.addEventListener('keydown', tabSpy);
+ // Simulate TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9);
+ assert.equal(overlay, document.activeElement, 'focus stays on overlay');
+ assert.isTrue(tabSpy.calledOnce, 'keydown spy called');
+ assert.isTrue(tabSpy.getCall(0).args[0].defaultPrevented, 'keydown default prevented');
+ // Cleanup.
+ document.removeEventListener('keydown', tabSpy);
+ done();
+ }, 1);
+ });
+ });
+
+ });
+
+ suite('focusable nodes', function() {
+ var overlay, overlayWithTabIndex, overlayFocusableNodes;
+
+ setup(function() {
+ var f = fixture('focusables');
+ overlay = f[0];
+ overlayWithTabIndex = f[1];
+ overlayFocusableNodes = f[2];
+ });
+
+ test('_focusableNodes returns nodes that are focusable', function(done) {
+ runAfterOpen(overlay, function() {
+ var focusableNodes = overlay._focusableNodes;
+ assert.equal(focusableNodes.length, 3, '3 nodes are focusable');
+ assert.equal(focusableNodes[0], Polymer.dom(overlay).querySelector('.focusable1'));
+ assert.equal(focusableNodes[1], Polymer.dom(overlay).querySelector('.focusable2'));
+ assert.equal(focusableNodes[2], Polymer.dom(overlay).querySelector('.focusable3'));
+ done();
+ });
+ });
+
+ test('_focusableNodes includes overlay if it has a valid tabindex', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.setAttribute('tabindex', '0');
+ var focusableNodes = overlay._focusableNodes;
+ assert.equal(focusableNodes.length, 4, '4 focusable nodes');
+ assert.notEqual(focusableNodes.indexOf(overlay), -1, 'overlay is included');
+ done();
+ });
+ });
+
+ test('_focusableNodes respects the tabindex order', function(done) {
+ runAfterOpen(overlayWithTabIndex, function() {
+ var focusableNodes = overlayWithTabIndex._focusableNodes;
+ assert.equal(focusableNodes.length, 6, '6 nodes are focusable');
+ assert.equal(focusableNodes[0], Polymer.dom(overlayWithTabIndex).querySelector('.focusable1'));
+ assert.equal(focusableNodes[1], Polymer.dom(overlayWithTabIndex).querySelector('.focusable2'));
+ assert.equal(focusableNodes[2], Polymer.dom(overlayWithTabIndex).querySelector('.focusable3'));
+ assert.equal(focusableNodes[3], Polymer.dom(overlayWithTabIndex).querySelector('.focusable4'));
+ assert.equal(focusableNodes[4], Polymer.dom(overlayWithTabIndex).querySelector('.focusable5'));
+ assert.equal(focusableNodes[5], Polymer.dom(overlayWithTabIndex).querySelector('.focusable6'));
+ done();
+ });
+ });
+
+ test('_focusableNodes can be overridden', function(done) {
+ runAfterOpen(overlayFocusableNodes, function() {
+ // It has 1 focusable in the light dom, and 2 in the shadow dom.
+ var focusableNodes = overlayFocusableNodes._focusableNodes;
+ assert.equal(focusableNodes.length, 2, 'length ok');
+ assert.equal(focusableNodes[0], overlayFocusableNodes.$.first, 'first ok');
+ assert.equal(focusableNodes[1], overlayFocusableNodes.$.last, 'last ok');
+ done();
+ });
+ });
+
+ test('with-backdrop: TAB & Shift+TAB wrap focus', function(done) {
+ overlay.withBackdrop = true;
+ runAfterOpen(overlay, function() {
+ var focusableNodes = overlay._focusableNodes;
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function() {
+ // Go to last element.
+ focusableNodes[focusableNodes.length-1].focus();
+ // Spy keydown.
+ var tabSpy = sinon.spy();
+ document.addEventListener('keydown', tabSpy);
+ // Simulate TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9);
+ assert.equal(focusableNodes[0], document.activeElement, 'focus wrapped to first focusable');
+ assert.isTrue(tabSpy.calledOnce, 'keydown spy called');
+ assert.isTrue(tabSpy.getCall(0).args[0].defaultPrevented, 'keydown default prevented');
+ // Simulate Shift+TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
+ assert.equal(focusableNodes[focusableNodes.length-1], document.activeElement, 'focus wrapped to last focusable');
+ assert.isTrue(tabSpy.calledTwice, 'keydown spy called again');
+ assert.isTrue(tabSpy.getCall(1).args[0].defaultPrevented, 'keydown default prevented again');
+ // Cleanup.
+ document.removeEventListener('keydown', tabSpy);
+ done();
+ }, 1);
+ });
+ });
+
+ test('with-backdrop: TAB & Shift+TAB wrap focus respecting tabindex', function(done) {
+ overlayWithTabIndex.withBackdrop = true;
+ runAfterOpen(overlayWithTabIndex, function() {
+ var focusableNodes = overlayWithTabIndex._focusableNodes;
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function() {
+ // Go to last element.
+ focusableNodes[focusableNodes.length-1].focus();
+ // Simulate TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9);
+ assert.equal(focusableNodes[0], document.activeElement, 'focus wrapped to first focusable');
+ // Simulate Shift+TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
+ assert.equal(focusableNodes[focusableNodes.length-1], document.activeElement, 'focus wrapped to last focusable');
+ done();
+ }, 1);
+ });
+ });
+
+ test('with-backdrop: Shift+TAB after open wrap focus', function(done) {
+ overlay.withBackdrop = true;
+ runAfterOpen(overlay, function() {
+ var focusableNodes = overlay._focusableNodes;
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function() {
+ // Spy keydown.
+ var tabSpy = sinon.spy();
+ document.addEventListener('keydown', tabSpy);
+ // Simulate Shift+TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
+ assert.equal(focusableNodes[focusableNodes.length-1], document.activeElement, 'focus wrapped to last focusable');
+ assert.isTrue(tabSpy.calledOnce, 'keydown spy called');
+ assert.isTrue(tabSpy.getCall(0).args[0].defaultPrevented, 'keydown default prevented');
+ // Cleanup.
+ document.removeEventListener('keydown', tabSpy);
+ done();
+ }, 1);
+ });
+ });
+
+ test('with-backdrop: after open, update last focusable node and then Shift+TAB', function(done) {
+ overlay.withBackdrop = true;
+ runAfterOpen(overlay, function() {
+ var focusableNodes = overlay._focusableNodes;
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function() {
+ // Before tabbing, make lastFocusable non-tabbable. This will make
+ // the one before it (focusableNodes.length - 2), the new last focusable node.
+ focusableNodes[focusableNodes.length-1].setAttribute('tabindex', '-1');
+ overlay.invalidateTabbables();
+ // Simulate Shift+TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
+ assert.equal(focusableNodes[focusableNodes.length-2], document.activeElement, 'focus wrapped correctly');
+ done();
+ }, 1);
+ });
+ });
+
+ test('with-backdrop: Shift+TAB wrap focus in shadowDOM', function(done) {
+ overlayFocusableNodes.withBackdrop = true;
+ runAfterOpen(overlayFocusableNodes, function() {
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function() {
+ // Spy keydown.
+ var tabSpy = sinon.spy();
+ document.addEventListener('keydown', tabSpy);
+ // Simulate Shift+TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
+ assert.equal(overlayFocusableNodes.$.last, Polymer.IronOverlayManager.deepActiveElement, 'focus wrapped to last focusable in the shadowDOM');
+ assert.isTrue(tabSpy.calledOnce, 'keydown spy called');
+ assert.isTrue(tabSpy.getCall(0).args[0].defaultPrevented, 'keydown default prevented');
+ // Cleanup.
+ document.removeEventListener('keydown', tabSpy);
+ done();
+ }, 1);
+ });
+ });
+
+ });
+
+ suite('Polymer.IronOverlayManager.deepActiveElement', function() {
+
+ test('handles document.body', function () {
+ document.body.focus();
+ assert.equal(Polymer.IronOverlayManager.deepActiveElement, document.body);
+ });
+
+ test('handles light dom', function () {
+ var focusable = document.getElementById('focusInput');
+ focusable.focus();
+ assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable, 'input is handled');
+ focusable.blur();
+ });
+
+ test('handles shadow dom', function () {
+ var focusable = document.getElementById('buttons').$.button0;
+ focusable.focus();
+ assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable);
+ focusable.blur();
+ });
+
+ });
+
+ suite('restore-focus-on-close', function() {
+
+ var overlay;
+ setup(function () {
+ overlay = fixture('autofocus');
+ overlay.restoreFocusOnClose = true;
+ });
+
+ teardown(function () {
+ // No matter what, return the focus to body!
+ document.body.focus();
+ });
+
+ test('does not return focus on close by default (restore-focus-on-close=false)', function(done) {
+ overlay.restoreFocusOnClose = false;
+ var focusable = document.getElementById('focusInput');
+ focusable.focus();
+ runAfterOpen(overlay, function() {
+ runAfterClose(overlay, function() {
+ assert.notEqual(Polymer.IronOverlayManager.deepActiveElement, focusable, 'focus is not restored to focusable');
+ done();
+ });
+ });
+ });
+
+ test('overlay returns focus on close', function(done) {
+ var focusable = document.getElementById('focusInput');
+ focusable.focus();
+ runAfterOpen(overlay, function() {
+ runAfterClose(overlay, function() {
+ assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable, 'focus restored to focusable');
+ done();
+ });
+ });
+ });
+
+ test('overlay returns focus on close (ShadowDOM)', function(done) {
+ var focusable = document.getElementById('buttons').$.button0;
+ focusable.focus();
+ runAfterOpen(overlay, function() {
+ runAfterClose(overlay, function() {
+ assert.equal(Polymer.IronOverlayManager.deepActiveElement, focusable, 'focus restored to focusable');
+ done();
+ });
+ });
+ });
+
+ });
+
+ suite('overlay with backdrop', function() {
+ var overlay;
+
+ setup(function() {
+ overlay = fixture('backdrop');
+ });
+
+ test('backdrop is opened when overlay is opened', function(done) {
+ assert.isOk(overlay.backdropElement, 'backdrop is defined');
+ runAfterOpen(overlay, function() {
+ assert.isTrue(overlay.backdropElement.opened, 'backdrop is opened');
+ assert.isOk(overlay.backdropElement.parentNode, 'backdrop is inserted in the DOM');
+ done();
+ });
+ });
+
+ test('backdrop appears behind the overlay', function(done) {
+ runAfterOpen(overlay, function() {
+ styleZ = parseInt(window.getComputedStyle(overlay).zIndex, 10);
+ backdropStyleZ = parseInt(window.getComputedStyle(overlay.backdropElement).zIndex, 10);
+ assert.isTrue(styleZ > backdropStyleZ, 'overlay has higher z-index than backdrop');
+ done();
+ });
+ });
+
+ test('backdrop is removed when overlay is closed', function(done) {
+ runAfterOpen(overlay, function() {
+ runAfterClose(overlay, function() {
+ assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
+ assert.isNotOk(overlay.backdropElement.parentNode, 'backdrop is removed from the DOM');
+ assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 0, 'no backdrop elements on body');
+ done();
+ });
+ });
+ });
+
+ test('backdrop is removed when the element is removed from DOM', function(done) {
+ runAfterOpen(overlay, function() {
+ Polymer.dom(overlay).parentNode.removeChild(overlay);
+ // Ensure detached is executed.
+ Polymer.dom.flush();
+ assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
+ assert.isNotOk(overlay.backdropElement.parentNode, 'backdrop is removed from the DOM');
+ assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 0, 'no backdrop elements on body');
+ assert.isNotOk(overlay._manager.currentOverlay(), 'currentOverlay ok');
+ done();
+ });
+ });
+
+ test('manager.getBackdrops() updated on opened changes', function(done) {
+ runAfterOpen(overlay, function() {
+ assert.equal(Polymer.IronOverlayManager.getBackdrops().length, 1, 'overlay added to manager backdrops');
+ runAfterClose(overlay, function() {
+ assert.equal(Polymer.IronOverlayManager.getBackdrops().length, 0, 'overlay removed from manager backdrops');
+ done();
+ });
+ });
+ });
+
+ test('updating with-backdrop to false closes backdrop', function(done) {
+ runAfterOpen(overlay, function() {
+ overlay.withBackdrop = false;
+ assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
+ assert.isNotObject(overlay.backdropElement.parentNode, 'backdrop is removed from document');
+ done();
+ });
+ });
+
+ test('backdrop is removed when toggling overlay opened', function(done) {
+ overlay.open();
+ runAfterClose(overlay, function() {
+ assert.isFalse(overlay.backdropElement.opened, 'backdrop is closed');
+ assert.isNotOk(overlay.backdropElement.parentNode, 'backdrop is removed from document');
+ done();
+ });
+ });
+
+ test('withBackdrop = false does not prevent click outside event', function(done) {
+ overlay.withBackdrop = false;
+ runAfterOpen(overlay, function() {
+ overlay.addEventListener('iron-overlay-canceled', function(event) {
+ assert.isFalse(event.detail.defaultPrevented, 'click event not prevented');
+ done();
+ });
+ MockInteractions.tap(document.body);
+ });
+ });
+ });
+
+ suite('multiple overlays', function() {
+ var overlay1, overlay2;
+
+ setup(function() {
+ var f = fixture('multiple');
+ overlay1 = f[0];
+ overlay2 = f[1];
+ });
+
+ test('new overlays appear on top', function(done) {
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ var styleZ1 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
+ assert.isTrue(styleZ1 > styleZ, 'overlay2 has higher z-index than overlay1');
+ done();
+ });
+ });
+ });
+
+ test('ESC closes only the top overlay', function(done) {
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ MockInteractions.pressAndReleaseKeyOn(document, 27);
+ assert.isFalse(overlay2.opened, 'overlay2 was closed');
+ assert.isTrue(overlay1.opened, 'overlay1 is still opened');
+ done();
+ });
+ });
+ });
+
+ test('close an overlay in proximity to another overlay', function(done) {
+ // Open and close a separate overlay.
+ overlay1.open();
+ overlay1.close();
+
+ // Open the overlay we care about.
+ overlay2.open();
+
+ // Immediately close the first overlay.
+ // Wait for infinite recursion, otherwise we win.
+ runAfterClose(overlay2, function() {
+ done();
+ })
+ });
+
+ });
+
+ suite('Manager overlays in sync', function() {
+ var overlay1, overlay2;
+ var overlays;
+
+ setup(function() {
+ var f = fixture('multiple');
+ overlay1 = f[0];
+ overlay2 = f[1];
+ overlays = Polymer.IronOverlayManager._overlays;
+ });
+
+ test('no duplicates after attached', function(done) {
+ overlay1 = document.createElement('test-overlay');
+ runAfterOpen(overlay1, function() {
+ assert.equal(overlays.length, 1, 'correct count after open and attached');
+ document.body.removeChild(overlay1);
+ done();
+ });
+ document.body.appendChild(overlay1);
+ });
+
+ test('call open multiple times handled', function(done) {
+ overlay1.open();
+ overlay1.open();
+ runAfterOpen(overlay1, function() {
+ assert.equal(overlays.length, 1, '1 overlay after open');
+ done();
+ })
+ });
+
+ test('close handled', function(done) {
+ runAfterOpen(overlay1, function() {
+ runAfterClose(overlay1, function() {
+ assert.equal(overlays.length, 0, '0 overlays after close');
+ done();
+ });
+ });
+ });
+
+ test('open/close brings overlay on top', function(done) {
+ overlay1.open();
+ runAfterOpen(overlay2, function() {
+ assert.equal(overlays.indexOf(overlay1), 0, 'overlay1 at index 0');
+ assert.equal(overlays.indexOf(overlay2), 1, 'overlay2 at index 1');
+ overlay1.close();
+ runAfterOpen(overlay1, function() {
+ assert.equal(overlays.indexOf(overlay1), 1, 'overlay1 moved at index 1');
+ assert.isAbove(parseInt(overlay1.style.zIndex), parseInt(overlay2.style.zIndex), 'overlay1 on top of overlay2');
+ done();
+ });
+ });
+ });
+ });
+
+ suite('z-ordering', function() {
+
+ var originalMinimumZ;
+ var overlay1, overlay2;
+
+ setup(function() {
+ var f = fixture('multiple');
+ overlay1 = f[0];
+ overlay2 = f[1];
+ originalMinimumZ = Polymer.IronOverlayManager._minimumZ;
+ });
+
+ teardown(function() {
+ Polymer.IronOverlayManager._minimumZ = originalMinimumZ;
+ });
+
+ // for iframes
+ test('default z-index is greater than 100', function(done) {
+ runAfterOpen(overlay1, function() {
+ var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ assert.isTrue(styleZ > 100, 'overlay1 z-index is <= 100');
+ done();
+ });
+ });
+
+ test('ensureMinimumZ() effects z-index', function(done) {
+ Polymer.IronOverlayManager.ensureMinimumZ(1000);
+
+ runAfterOpen(overlay1, function() {
+ var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ assert.isTrue(styleZ > 1000, 'overlay1 z-index is <= 1000');
+ done();
+ });
+ });
+
+ test('ensureMinimumZ() never decreases the minimum z-index', function(done) {
+ Polymer.IronOverlayManager.ensureMinimumZ(1000);
+ Polymer.IronOverlayManager.ensureMinimumZ(500);
+
+ runAfterOpen(overlay1, function() {
+ var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ assert.isTrue(styleZ > 1000, 'overlay1 z-index is <= 1000');
+ done();
+ });
+ });
+
+ });
+
+ suite('multiple overlays with backdrop', function() {
+ var overlay1, overlay2, overlay3;
+
+ setup(function() {
+ var f = fixture('multiple');
+ overlay1 = f[0];
+ overlay2 = f[1];
+ overlay3 = f[2];
+ overlay1.withBackdrop = overlay2.withBackdrop = overlay3.withBackdrop = true;
+ });
+
+ test('multiple overlays share the same backdrop', function() {
+ assert.isTrue(overlay1.backdropElement === overlay2.backdropElement, 'overlay1 and overlay2 have the same backdrop element');
+ assert.isTrue(overlay1.backdropElement === overlay3.backdropElement, 'overlay1 and overlay3 have the same backdrop element');
+ });
+
+ test('only one iron-overlay-backdrop in the DOM', function(done) {
+ // Open them all.
+ overlay1.opened = true;
+ overlay2.opened = true;
+ runAfterOpen(overlay3, function() {
+ assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 1, 'only one backdrop element in the DOM');
+ done();
+ });
+ });
+
+ test('iron-overlay-backdrop is removed from the DOM when all overlays with backdrop are closed', function(done) {
+ // Open & close them all.
+ overlay1.opened = true;
+ overlay2.opened = true;
+ runAfterOpen(overlay3, function() {
+ overlay1.opened = overlay2.opened = false;
+ runAfterClose(overlay3, function() {
+ assert.lengthOf(document.querySelectorAll('iron-overlay-backdrop'), 0, 'backdrop element removed from the DOM');
+ done();
+ });
+ });
+ });
+
+ test('newest overlay appear on top', function(done) {
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ var style1Z = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
+ var bgStyleZ = parseInt(window.getComputedStyle(overlay1.backdropElement).zIndex, 10);
+ assert.isTrue(style1Z > styleZ, 'overlay2 has higher z-index than overlay1');
+ assert.isTrue(styleZ > bgStyleZ, 'overlay1 has higher z-index than backdrop');
+ done();
+ });
+ });
+ });
+
+ var clickEvents = ['click', 'tap'];
+ for (var i = 0; i < clickEvents.length; i++) {
+ var eventName = clickEvents[i];
+ test(eventName + ' event handled only by top overlay', function(done) {
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ var btn = Polymer.dom(overlay2).querySelector('button');
+ btn.addEventListener(eventName, overlay2.close.bind(overlay2));
+ MockInteractions.tap(btn);
+ assert.isFalse(overlay2.opened, 'overlay2 closed');
+ assert.isTrue(overlay1.opened, 'overlay1 opened');
+ overlay2.addEventListener('iron-overlay-closed', function() {
+ assert.isTrue(overlay1.opened, 'overlay1 still opened');
+ done();
+ });
+ });
+ });
+ });
+ }
+
+ test('updating with-backdrop updates z-index', function(done) {
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ overlay1.withBackdrop = false;
+ var styleZ = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ var style1Z = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
+ var bgStyleZ = parseInt(window.getComputedStyle(overlay1.backdropElement).zIndex, 10);
+ assert.isTrue(style1Z > bgStyleZ, 'overlay2 has higher z-index than backdrop');
+ assert.isTrue(styleZ < bgStyleZ, 'overlay1 has lower z-index than backdrop');
+ done();
+ });
+ });
+ });
+
+ });
+
+ suite('overlay in composed tree', function() {
+ var composed, overlay, trigger;
+ setup(function(done) {
+ composed = fixture('composed');
+ overlay = composed.$.overlay;
+ trigger = composed.$.trigger;
+ overlay.withBackdrop = true;
+ overlay.addEventListener('iron-overlay-opened', function() {
+ done();
+ });
+ // Opens the overlay.
+ MockInteractions.tap(trigger);
+ });
+
+ test('click on overlay content does not close it', function(done) {
+ // Tap on button inside overlay.
+ MockInteractions.tap(Polymer.dom(overlay).querySelector('button'));
+ Polymer.Base.async(function(){
+ assert.isTrue(overlay.opened, 'overlay still opened');
+ done();
+ }, 1);
+ });
+
+ test('with-backdrop wraps the focus within the overlay', function(done) {
+ // 1ms timeout needed by IE10 to have proper focus switching.
+ Polymer.Base.async(function(){
+ var buttons = Polymer.dom(overlay).querySelectorAll('button');
+ // Go to last element.
+ buttons[buttons.length-1].focus();
+ // Spy keydown.
+ var tabSpy = sinon.spy();
+ document.addEventListener('keydown', tabSpy);
+ // Simulate TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9);
+ assert.equal(buttons[0], Polymer.IronOverlayManager.deepActiveElement, 'focus wrapped to first focusable');
+ assert.isTrue(tabSpy.calledOnce, 'keydown spy called');
+ assert.isTrue(tabSpy.getCall(0).args[0].defaultPrevented, 'keydown default prevented');
+ // Simulate Shift+TAB.
+ MockInteractions.pressAndReleaseKeyOn(document, 9, ['shift']);
+ assert.equal(buttons[buttons.length-1], Polymer.IronOverlayManager.deepActiveElement, 'focus wrapped to last focusable');
+ assert.isTrue(tabSpy.calledTwice, 'keydown spy called again');
+ assert.isTrue(tabSpy.getCall(1).args[0].defaultPrevented, 'keydown default prevented again');
+ // Cleanup.
+ document.removeEventListener('keydown', tabSpy);
+ done();
+ }, 1);
+ });
+
+ });
+
+ suite('always-on-top', function() {
+ var overlay1, overlay2;
+
+ setup(function() {
+ var f = fixture('multiple');
+ overlay1 = f[0];
+ overlay2 = f[1];
+ overlay1.alwaysOnTop = true;
+ });
+
+ test('stays on top', function(done) {
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ var zIndex1 = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ var zIndex2 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
+ assert.isAbove(zIndex1, zIndex2, 'overlay1 on top');
+ assert.equal(Polymer.IronOverlayManager.currentOverlay(), overlay1, 'currentOverlay ok');
+ done();
+ });
+ });
+ });
+
+ test('stays on top also if another overlay is with-backdrop', function(done) {
+ overlay2.withBackdrop = true;
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ var zIndex1 = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ var zIndex2 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
+ assert.isAbove(zIndex1, zIndex2, 'overlay1 on top');
+ assert.equal(Polymer.IronOverlayManager.currentOverlay(), overlay1, 'currentOverlay ok');
+ done();
+ });
+ });
+ });
+
+ test('last overlay with always-on-top wins', function(done) {
+ overlay2.alwaysOnTop = true;
+ runAfterOpen(overlay1, function() {
+ runAfterOpen(overlay2, function() {
+ var zIndex1 = parseInt(window.getComputedStyle(overlay1).zIndex, 10);
+ var zIndex2 = parseInt(window.getComputedStyle(overlay2).zIndex, 10);
+ assert.isAbove(zIndex2, zIndex1, 'overlay2 on top');
+ assert.equal(Polymer.IronOverlayManager.currentOverlay(), overlay2, 'currentOverlay ok');
+ done();
+ });
+ });
+ });
+
+ });
+
+ suite('animations', function() {
+
+ test('overlay animations correctly triggered', function(done) {
+ var overlay = fixture('basic');
+ overlay.animated = true;
+ overlay.open();
+ overlay.addEventListener('simple-overlay-open-animation-start', function() {
+ // Since animated overlay will transition center + 300px to center,
+ // we should not find the element at the center when the open animation starts.
+ var centerElement = document.elementFromPoint(window.innerWidth/2, window.innerHeight/2);
+ assert.notEqual(centerElement, overlay, 'overlay should not be centered already');
+ done();
+ });
+ });
+
+ });
+
+ suite('a11y', function() {
+
+ test('overlay has aria-hidden=true when opened', function() {
+ var overlay = fixture('basic');
+ assert.equal(overlay.getAttribute('aria-hidden'), 'true', 'overlay has aria-hidden="true"');
+ overlay.open();
+ assert.isFalse(overlay.hasAttribute('aria-hidden'), 'overlay does not have aria-hidden attribute');
+ overlay.close();
+ assert.equal(overlay.getAttribute('aria-hidden'), 'true', 'overlay has aria-hidden="true"');
+ });
+
+ });
+ </script>
+
+ </body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons-wrapper.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons-wrapper.html
new file mode 100644
index 00000000..669b8ab5
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons-wrapper.html
@@ -0,0 +1,38 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="test-buttons.html">
+
+<dom-module id="test-buttons-wrapper">
+ <template>
+ <style>
+ :host {
+ display: block;
+ border: 1px solid gray;
+ padding: 10px;
+ }
+ </style>
+
+ <select id="select">
+ <option>1</option>
+ </select>
+ <test-buttons id="wrapped">
+ <content></content>
+ </test-buttons>
+ <div tabindex="0" id="focusableDiv">Focusable div</div>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'test-buttons-wrapper'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons.html
new file mode 100644
index 00000000..05434a01
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-buttons.html
@@ -0,0 +1,34 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+
+<dom-module id="test-buttons">
+ <template>
+ <style>
+ :host {
+ display: block;
+ border: 1px solid black;
+ padding: 10px;
+ }
+ </style>
+
+ <button id="button0">button0</button>
+ <button id="button1">button1</button>
+ <content></content>
+ <button id="button2">button2</button>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'test-buttons'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-menu-button.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-menu-button.html
new file mode 100644
index 00000000..24c78d90
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-menu-button.html
@@ -0,0 +1,38 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="test-overlay.html">
+
+<dom-module id="test-menu-button">
+
+ <template>
+ <button id="trigger" on-click="toggle">Open</button>
+ <test-overlay id="overlay">
+ Composed overlay
+ <button>button 1</button>
+ <button>button 2</button>
+ </test-overlay>
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+ Polymer({
+ is: 'test-menu-button',
+ toggle: function() {
+ this.$.overlay.toggle();
+ }
+ });
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay.html
new file mode 100644
index 00000000..6b8b32b2
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay.html
@@ -0,0 +1,101 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+
+<link rel="import" href="../iron-overlay-behavior.html">
+
+<dom-module id="test-overlay">
+ <template>
+ <style>
+ :host {
+ background: white;
+ color: black;
+ border: 1px solid black;
+ }
+
+ :host([animated]) {
+ -webkit-transition: -webkit-transform 0.3s;
+ transition: transform 0.3s;
+ -webkit-transform: translateY(300px);
+ transform: translateY(300px);
+ }
+
+ :host(.opened[animated]) {
+ -webkit-transform: translateY(0px);
+ transform: translateY(0px);
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+ (function() {
+
+ Polymer({
+
+ is: 'test-overlay',
+
+ properties: {
+ animated: {
+ type: Boolean,
+ reflectToAttribute: true
+ }
+ },
+
+ behaviors: [
+ Polymer.IronOverlayBehavior
+ ],
+
+ listeners: {
+ 'transitionend': '__onTransitionEnd'
+ },
+
+ _renderOpened: function() {
+ if (this.animated) {
+ if (this.withBackdrop) {
+ this.backdropElement.open();
+ }
+ this.classList.add('opened');
+ this.fire('simple-overlay-open-animation-start');
+ } else {
+ Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments);
+ }
+ },
+
+ _renderClosed: function() {
+ if (this.animated) {
+ if (this.withBackdrop) {
+ this.backdropElement.close();
+ }
+ this.classList.remove('opened');
+ this.fire('simple-overlay-close-animation-start');
+ } else {
+ Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments);
+ }
+ },
+
+ __onTransitionEnd: function(e) {
+ if (e && e.target === this) {
+ if (this.opened) {
+ this._finishRenderOpened();
+ } else {
+ this._finishRenderClosed();
+ }
+ }
+ },
+
+ });
+
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay2.html b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay2.html
new file mode 100644
index 00000000..23eac8e0
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-overlay-behavior/test/test-overlay2.html
@@ -0,0 +1,52 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+
+<link rel="import" href="../iron-overlay-behavior.html">
+
+<dom-module id="test-overlay2">
+ <template>
+ <style>
+ :host {
+ background: white;
+ color: black;
+ border: 1px solid black;
+ }
+ </style>
+
+ <button id="first">first</button>
+ <content></content>
+ <button id="last">last</button>
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'test-overlay2',
+
+ behaviors: [
+ Polymer.IronOverlayBehavior
+ ],
+
+ get _focusableNodes() {
+ return [this.$.first, this.$.last];
+ }
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-pages/.bower.json b/catapult/third_party/polymer/components/iron-pages/.bower.json
new file mode 100644
index 00000000..f6f4baf7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/.bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "iron-pages",
+ "version": "1.0.9",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Organizes a set of pages and shows one at a time",
+ "main": "iron-pages.html",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-pages.git"
+ },
+ "keywords": [
+ "web-components",
+ "polymer",
+ "container"
+ ],
+ "dependencies": {
+ "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0",
+ "iron-selector": "polymerelements/iron-selector#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "polymerelements/iron-demo-helpers#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-pages",
+ "_release": "1.0.9",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.9",
+ "commit": "fffb343259d2b381bebfdb13cbe08ec1d0d2b4db"
+ },
+ "_source": "https://github.com/PolymerElements/iron-pages.git",
+ "_target": "^1.0.8",
+ "_originalSource": "PolymerElements/iron-pages"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-pages/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-pages/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..8049a6c4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-pages/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-pages/.gitignore b/catapult/third_party/polymer/components/iron-pages/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-pages/.travis.yml b/catapult/third_party/polymer/components/iron-pages/.travis.yml
new file mode 100644
index 00000000..6a639deb
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ Nv52xUhhZNbvdXIs+8JV61yREtAZN5i2ZHO6uZVonGR/Y/U8SA3AcDjrPLd6VgFmasM58EpPGQGToK46r9C+h/jLWc7d/+7xn/9sphg6qQhQ5FkS/LYaTMmzCmonu79h/TqpuuYQWJqjEBM3LpoYJSMum/V34paPBU9AEK0Pd5A=
+ - secure: >-
+ fE00ZxqCvRMPXvSIghE/0wYbFORFGO4IFzaZbOxanAe7PBKDmdPBkzodo9BEgOw1cB2zw2uXMEYGf3jEZH+vens5ycngF8X/iQRCcPGpHjKIbkBtHvYQoi4ZwQf48GPtdBkMZLg7LcA4N2ScypU33X0q30hQ6BgzTduumuHlKCo=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-pages/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-pages/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-pages/README.md b/catapult/third_party/polymer/components/iron-pages/README.md
new file mode 100644
index 00000000..7a2e6b70
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/README.md
@@ -0,0 +1,42 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-pages.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-pages.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-pages)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-pages)_
+
+
+##&lt;iron-pages&gt;
+
+`iron-pages` is used to select one of its children to show. One use is to cycle through a list of
+children "pages".
+
+Example:
+
+```html
+<iron-pages selected="0">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+</iron-pages>
+
+<script>
+ document.addEventListener('click', function(e) {
+ var pages = document.querySelector('iron-pages');
+ pages.selectNext();
+ });
+</script>
+```
+
+
diff --git a/catapult/third_party/polymer/components/iron-pages/bower.json b/catapult/third_party/polymer/components/iron-pages/bower.json
new file mode 100644
index 00000000..960a199e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/bower.json
@@ -0,0 +1,34 @@
+{
+ "name": "iron-pages",
+ "version": "1.0.9",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Organizes a set of pages and shows one at a time",
+ "main": "iron-pages.html",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-pages.git"
+ },
+ "keywords": [
+ "web-components",
+ "polymer",
+ "container"
+ ],
+ "dependencies": {
+ "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0",
+ "iron-selector": "polymerelements/iron-selector#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "polymerelements/iron-demo-helpers#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.2",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-pages/demo/index.html b/catapult/third_party/polymer/components/iron-pages/demo/index.html
new file mode 100644
index 00000000..d7e7c756
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/demo/index.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-pages demo</title>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../iron-pages.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles"></style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>iron-pages shows only one of its children at a time.</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ iron-pages {
+ width: 100%;
+ height: 200px;
+ font-size: 50px;
+ color: white;
+ text-align: center;
+ }
+ iron-pages div {
+ width: 100%;
+ height: 100%;
+ padding: 80px 0;
+ }
+ iron-pages div:nth-child(1) {
+ background-color: var(--google-blue-500);
+ }
+ iron-pages div:nth-child(2) {
+ background-color: var(--google-red-500);
+ }
+ iron-pages div:nth-child(3) {
+ background-color: var(--google-green-500);
+ }
+ </style>
+ <p>Click on a page to advance to the next one.</p>
+ <iron-pages selected="0">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+ </iron-pages>
+ <script>
+ var pages = document.querySelector('iron-pages');
+ pages.addEventListener('click', function(e) {
+ pages.selectNext();
+ });
+ </script>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-pages/hero.svg b/catapult/third_party/polymer/components/iron-pages/hero.svg
new file mode 100755
index 00000000..12b9b724
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/hero.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <path d="M143.3,73.6H51.7V26.7h91.6V73.6z M53.7,71.6h87.6V28.7H53.7V71.6z"/>
+ <path d="M158.3,85.4H66.7V38.6h91.6V85.4z M68.7,83.4h87.6V40.6H68.7V83.4z"/>
+ <path d="M172,99H80.4V52.1H172V99z M82.4,97H170V54.1H82.4V97z"/>
+ <circle cx="53" cy="28" r="4"/>
+ <circle cx="171" cy="98" r="4"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/iron-pages/index.html b/catapult/third_party/polymer/components/iron-pages/index.html
new file mode 100644
index 00000000..67ae0889
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/index.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+ <title>iron-pages</title>
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-pages/iron-pages.html b/catapult/third_party/polymer/components/iron-pages/iron-pages.html
new file mode 100644
index 00000000..93257797
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/iron-pages.html
@@ -0,0 +1,88 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="../iron-selector/iron-selectable.html">
+
+<!--
+`iron-pages` is used to select one of its children to show. One use is to cycle through a list of
+children "pages".
+
+Example:
+
+ <iron-pages selected="0">
+ <div>One</div>
+ <div>Two</div>
+ <div>Three</div>
+ </iron-pages>
+
+ <script>
+ document.addEventListener('click', function(e) {
+ var pages = document.querySelector('iron-pages');
+ pages.selectNext();
+ });
+ </script>
+
+@group Iron Elements
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="iron-pages">
+
+ <template>
+ <style>
+ :host {
+ display: block;
+ }
+
+ :host > ::content > :not(.iron-selected) {
+ display: none !important;
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+ <script>
+ Polymer({
+
+ is: 'iron-pages',
+
+ behaviors: [
+ Polymer.IronResizableBehavior,
+ Polymer.IronSelectableBehavior
+ ],
+
+ properties: {
+
+ // as the selected page is the only one visible, activateEvent
+ // is both non-sensical and problematic; e.g. in cases where a user
+ // handler attempts to change the page and the activateEvent
+ // handler immediately changes it back
+ activateEvent: {
+ type: String,
+ value: null
+ }
+
+ },
+
+ observers: [
+ '_selectedPageChanged(selected)'
+ ],
+
+ _selectedPageChanged: function(selected, old) {
+ this.async(this.notifyResize);
+ }
+ });
+
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-pages/test/attr-for-selected.html b/catapult/third_party/polymer/components/iron-pages/test/attr-for-selected.html
new file mode 100644
index 00000000..954bc076
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/test/attr-for-selected.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-pages-attr-for-selected</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../iron-pages.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <iron-pages attr-for-selected="name" selected="page0">
+ <div name="page0">Page 0</div>
+ <div name="page1">Page 1</div>
+ <div name="page2">Page 2</div>
+ <div name="page3">Page 3</div>
+ </iron-pages>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+ var pages;
+
+ suite('honor the selected attribute', function() {
+ setup(function () {
+ pages = fixture('basic');
+ });
+
+ test('selected value', function() {
+ assert.equal(pages.selected, 'page0');
+ });
+
+ test('selected item', function(done) {
+ // iron-selector uses observeNodes, which is async.
+ Polymer.Base.async(function() {
+ assert.equal(pages.selectedItem, pages.items[0])
+ done();
+ }, 1);
+ });
+
+ test('selected item is display:block and all others are display:none', function() {
+ pages.items.forEach(function(p) {
+ assert.equal(getComputedStyle(p).display, p == pages.selectedItem ? 'block' : 'none');
+ });
+ });
+ });
+
+ suite('set selected attribute', function() {
+ setup(function () {
+ pages = fixture('basic');
+ pages.selected = 'page2';
+ });
+
+ test('selected value', function() {
+ assert.equal(pages.selected, 'page2');
+ });
+
+ test('selected item', function() {
+ assert.equal(pages.selectedItem, pages.items[2]);
+ });
+
+ test('selected item is display:block and all others are display:none', function() {
+ pages.items.forEach(function(p) {
+ assert.equal(getComputedStyle(p).display, p == pages.selectedItem ? 'block' : 'none');
+ });
+ });
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-pages/test/basic.html b/catapult/third_party/polymer/components/iron-pages/test/basic.html
new file mode 100644
index 00000000..f031f8e7
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/test/basic.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-pages-basic</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../iron-pages.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <iron-pages>
+ <div id="page0">Page 0</div>
+ <div id="page1">Page 1</div>
+ <div id="page2">Page 2</div>
+ <div id="page3">Page 3</div>
+ </iron-pages>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+ var pages;
+
+ suite('defaults', function() {
+ setup(function () {
+ pages = fixture('basic');
+ });
+
+ test('to nothing selected', function() {
+ assert.equal(pages.selected, undefined);
+ });
+
+ test('null activateEvent', function() {
+ // `activateEvent` is not a useful feature for iron-pages and it can interfere
+ // with ux; ensure iron-pages has cleared any default `activateEvent`
+ assert.equal(pages.activateEvent, null);
+ });
+
+ test('to iron-selected as selectedClass', function() {
+ assert.equal(pages.selectedClass, 'iron-selected');
+ });
+
+ test('as many items as children', function() {
+ assert.equal(pages.items.length, 4);
+ });
+
+ test('all pages are display:none', function() {
+ pages.items.forEach(function(p) {
+ assert.equal(getComputedStyle(p).display, 'none');
+ });
+ });
+ });
+
+ suite('set the selected attribute', function() {
+ setup(function () {
+ pages = fixture('basic');
+ pages.selected = 0;
+ });
+
+ test('selected value', function() {
+ assert.equal(pages.selected, '0');
+ });
+
+ test('selected item', function() {
+ assert.equal(pages.selectedItem, pages.items[0]);
+ });
+
+ test('selected item is display:block and all others are display:none', function() {
+ pages.items.forEach(function(p) {
+ assert.equal(getComputedStyle(p).display, p == pages.selectedItem ? 'block' : 'none');
+ });
+ });
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-pages/test/index.html b/catapult/third_party/polymer/components/iron-pages/test/index.html
new file mode 100644
index 00000000..0cc658d6
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-pages/test/index.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+
+ WCT.loadSuites([
+ 'basic.html',
+ 'attr-for-selected.html',
+ 'basic.html?dom=shadow',
+ 'attr-for-selected.html?dom=shadow'
+ ]);
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/.bower.json b/catapult/third_party/polymer/components/iron-range-behavior/.bower.json
new file mode 100644
index 00000000..c3c27add
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/.bower.json
@@ -0,0 +1,39 @@
+{
+ "name": "iron-range-behavior",
+ "version": "1.0.7",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Provides a behavior for something with a minimum and maximum value",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior"
+ ],
+ "main": "iron-range-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-range-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-input": "PolymerElements/iron-input#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-range-behavior",
+ "_release": "1.0.7",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.7",
+ "commit": "0723f9ca753f98191b54ec1cebbb94695ec7c581"
+ },
+ "_source": "https://github.com/PolymerElements/iron-range-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-range-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-range-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..02739273
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-range-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/.gitignore b/catapult/third_party/polymer/components/iron-range-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-range-behavior/.travis.yml
new file mode 100644
index 00000000..de1ba8fd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ ioGoTHzroJHO2O9B8y/xB7dsnWY+dArvRF+Dem/0Z8yTJZ/QFCeqFctQXNdj7HIm/y7ZxetVFBnxr+0HbF62qdmVkvfBzD/TaQPeXOHSyrSbR2aAQLeSwRbcUyOmmtk/q0frf1o81i3inoLjrGKieCZq7U8VF/6YyfRDhouU5/Q=
+ - secure: >-
+ LxoITNXWo1Vh2dOlg0I/KI/wuEaiMpNPRcg8MN6TOwy6UIKPwlpA69YZ4eLNDnuCjndQHL7Cl/TA5njubss4UeXG+L3oQMGNBEcRSHGZGxEyTZpofJp5FFK3Ja9CpaBMB7IsWAwuGPJZXM/6aoyAVipJ1et2Gbbasd5EbQYjRwc=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-range-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/README.md b/catapult/third_party/polymer/components/iron-range-behavior/README.md
new file mode 100644
index 00000000..99202696
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/README.md
@@ -0,0 +1,24 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-range-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-range-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-range-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-range-behavior)_
+
+
+##Polymer.IronRangeBehavior
+
+`iron-range-behavior` provides the behavior for something with a minimum to maximum range.
+
+
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/bower.json b/catapult/third_party/polymer/components/iron-range-behavior/bower.json
new file mode 100644
index 00000000..b4a9b77f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/bower.json
@@ -0,0 +1,29 @@
+{
+ "name": "iron-range-behavior",
+ "version": "1.0.7",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Provides a behavior for something with a minimum and maximum value",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "behavior"
+ ],
+ "main": "iron-range-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-range-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-input": "PolymerElements/iron-input#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-range-behavior/demo/index.html
new file mode 100644
index 00000000..a0afe84d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/demo/index.html
@@ -0,0 +1,80 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<!doctype html>
+<html>
+ <head>
+ <title>iron-range-behavior demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../iron-range-behavior.html">
+ <link rel="import" href="../../iron-input/iron-input.html">
+
+ <style>
+
+ body {
+ font-family: sans-serif;
+ }
+
+ </style>
+ </head>
+
+ <body unresolved>
+
+ <dom-module id="x-progressbar">
+ <template>
+ <style>
+ :host {
+ display: block;
+ height: 40px;
+ background-color: #555;
+ border-radius: 4px;
+ padding: 8px;
+ box-shadow: inset 0px 2px 5px rgba(0, 0, 0, 0.5);
+ }
+
+ .progress {
+ background-color: #999;
+ height: 100%;
+ border-radius: 4px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5);
+ }
+
+ .progress-value {
+ padding: 0 8px;
+ font-size: 18px;
+ color: #fff;
+ }
+ </style>
+
+ <div class="progress" horizontal center layout style$="{{_computeStyle(ratio)}}">
+ <div class="progress-value"><span>{{ratio}}</span>%</div>
+ </div>
+ </template>
+ </dom-module>
+
+ <script>
+ HTMLImports.whenReady(function() {
+ Polymer({
+ is: 'x-progressbar',
+
+ behaviors: [Polymer.IronRangeBehavior],
+
+ _computeStyle: function(ratio) {
+ return 'width: ' + ratio + '%;';
+ }
+ });
+ });
+ </script>
+
+ <x-progressbar min="0" max="200" value="120"></x-progressbar>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/index.html b/catapult/third_party/polymer/components/iron-range-behavior/index.html
new file mode 100644
index 00000000..cc777887
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/iron-range-behavior.html b/catapult/third_party/polymer/components/iron-range-behavior/iron-range-behavior.html
new file mode 100644
index 00000000..96f3c15b
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/iron-range-behavior.html
@@ -0,0 +1,121 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+
+ /**
+ * `iron-range-behavior` provides the behavior for something with a minimum to maximum range.
+ *
+ * @demo demo/index.html
+ * @polymerBehavior
+ */
+ Polymer.IronRangeBehavior = {
+
+ properties: {
+
+ /**
+ * The number that represents the current value.
+ */
+ value: {
+ type: Number,
+ value: 0,
+ notify: true,
+ reflectToAttribute: true
+ },
+
+ /**
+ * The number that indicates the minimum value of the range.
+ */
+ min: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
+ * The number that indicates the maximum value of the range.
+ */
+ max: {
+ type: Number,
+ value: 100,
+ notify: true
+ },
+
+ /**
+ * Specifies the value granularity of the range's value.
+ */
+ step: {
+ type: Number,
+ value: 1,
+ notify: true
+ },
+
+ /**
+ * Returns the ratio of the value.
+ */
+ ratio: {
+ type: Number,
+ value: 0,
+ readOnly: true,
+ notify: true
+ },
+ },
+
+ observers: [
+ '_update(value, min, max, step)'
+ ],
+
+ _calcRatio: function(value) {
+ return (this._clampValue(value) - this.min) / (this.max - this.min);
+ },
+
+ _clampValue: function(value) {
+ return Math.min(this.max, Math.max(this.min, this._calcStep(value)));
+ },
+
+ _calcStep: function(value) {
+ // polymer/issues/2493
+ value = parseFloat(value);
+
+ if (!this.step) {
+ return value;
+ }
+
+ var numSteps = Math.round((value - this.min) / this.step);
+ if (this.step < 1) {
+ /**
+ * For small values of this.step, if we calculate the step using
+ * `Math.round(value / step) * step` we may hit a precision point issue
+ * eg. 0.1 * 0.2 = 0.020000000000000004
+ * http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
+ *
+ * as a work around we can divide by the reciprocal of `step`
+ */
+ return numSteps / (1 / this.step) + this.min;
+ } else {
+ return numSteps * this.step + this.min;
+ }
+ },
+
+ _validateValue: function() {
+ var v = this._clampValue(this.value);
+ this.value = this.oldValue = isNaN(v) ? this.oldValue : v;
+ return this.value !== v;
+ },
+
+ _update: function() {
+ this._validateValue();
+ this._setRatio(this._calcRatio(this.value) * 100);
+ }
+
+};
+</script>
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/test/basic.html b/catapult/third_party/polymer/components/iron-range-behavior/test/basic.html
new file mode 100644
index 00000000..9a7e41dc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/test/basic.html
@@ -0,0 +1,180 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>iron-range-behavior</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="x-progressbar.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+</head>
+<body>
+
+ <test-fixture id="trivialRange">
+ <template>
+ <x-progressbar></x-progressbar>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('<x-progressbar>', function() {
+ var range;
+
+ setup(function() {
+ range = fixture('trivialRange');
+ });
+
+ test('check default', function() {
+ assert.equal(range.min, 0);
+ assert.equal(range.max, 100);
+ assert.equal(range.value, 0);
+ });
+
+ test('set value', function(done) {
+ range.value = 50;
+ flush(function() {
+ assert.equal(range.value, 50);
+ // test clamp value
+ range.value = 60.1;
+ flush(function() {
+ assert.equal(range.value, 60);
+ done();
+ });
+ });
+ });
+
+ test('set max', function(done) {
+ range.max = 10;
+ range.value = 11;
+ flush(function() {
+ assert.equal(range.value, range.max);
+ done();
+ });
+ });
+
+ test('test ratio', function(done) {
+ range.max = 10;
+ range.value = 5;
+ flush(function() {
+ assert.equal(range.ratio, 50);
+ done();
+ });
+ });
+
+ test('set min', function(done) {
+ range.min = 10
+ range.max = 50;
+ range.value = 30;
+ flush(function() {
+ assert.equal(range.ratio, 50);
+ range.value = 0;
+ flush(function() {
+ assert.equal(range.value, range.min);
+ done();
+ });
+ });
+ });
+
+ test('set step', function(done) {
+ range.min = 0;
+ range.max = 10;
+ range.value = 5.1;
+ flush(function() {
+ assert.equal(range.value, 5);
+ range.step = 0.1;
+ range.value = 5.1;
+ flush(function() {
+ assert.equal(range.value, 5.1);
+ done();
+ });
+ });
+ });
+
+ test('set large step', function(done) {
+ // PolymerElements/paper-slider#135
+ range.min = 0;
+ range.max = 2625;
+ range.step = 875;
+ range.value = 875;
+ flush(function() {
+ assert.equal(range.value, 875);
+ done();
+ });
+ });
+
+ test('set step with min', function(done) {
+ range.min = -0.9;
+ range.max = 1.1;
+ range.step = 0.5;
+ range.value = -0.5;
+ flush(function() {
+ assert.equal(range.value, -0.4);
+ range.value = 0.7;
+ flush(function() {
+ assert.equal(range.value, 0.6);
+ done();
+ });
+ });
+ });
+
+ test('odd values', function(done) {
+ range.min = 1;
+ range.max = 7;
+ range.step = 2;
+ range.value = 3;
+
+ flush(function() {
+ assert.equal(range.value, 3);
+
+ range.value += range.step;
+ assert.equal(range.value, 5);
+
+ range.value += range.step;
+ assert.equal(range.value, 7);
+ done();
+ });
+ });
+
+ test('negative values should round up', function(done) {
+ range.min = -10;
+ range.max = 10;
+ range.step = 0.1;
+ range.value = -8.4252;
+
+ flush(function() {
+ assert.equal(range.value, -8.4);
+ done();
+ });
+ });
+
+ test('positive values should round up', function(done) {
+ range.min = 10;
+ range.max = 100;
+ range.step = 0.25;
+ range.value = 19.34567;
+
+ flush(function() {
+ assert.equal(range.value, 19.25);
+ done();
+ });
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/test/index.html b/catapult/third_party/polymer/components/iron-range-behavior/test/index.html
new file mode 100644
index 00000000..b66f1def
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-range-behavior/test/x-progressbar.html b/catapult/third_party/polymer/components/iron-range-behavior/test/x-progressbar.html
new file mode 100644
index 00000000..2f0db85f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-range-behavior/test/x-progressbar.html
@@ -0,0 +1,19 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-range-behavior.html">
+
+<script>
+ Polymer({
+ is: 'x-progressbar',
+
+ behaviors: [Polymer.IronRangeBehavior]
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/.bower.json b/catapult/third_party/polymer/components/iron-resizable-behavior/.bower.json
new file mode 100644
index 00000000..af396237
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/.bower.json
@@ -0,0 +1,41 @@
+{
+ "name": "iron-resizable-behavior",
+ "version": "1.0.6",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Coordinates the flow of resizeable elements",
+ "private": true,
+ "main": "iron-resizable-behavior.html",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "iron",
+ "behavior"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-resizable-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/iron-resizable-behavior",
+ "_release": "1.0.6",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.6",
+ "commit": "13969b17cc06675e5887f6957b202cda7bfb9524"
+ },
+ "_source": "https://github.com/PolymerElements/iron-resizable-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-resizable-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-resizable-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..b890b594
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-resizable-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/.gitignore b/catapult/third_party/polymer/components/iron-resizable-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-resizable-behavior/.travis.yml
new file mode 100644
index 00000000..3e4f75f9
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ Bsp8pa19FCij3Eee0P5eOE8ZrbylrYL99R9EIxH6rcDH+rxMhK0BIfSiXTPSFRk2iqJp++RIZN8kUu5GddetiXreRZDtnhXBA2QoYteIAS8Hzy9u2EggqI0UdGqbCiFHDVD/rEQyhSPiIfWHGCg/Moeo9J2k/eC2w6I8I9C15Zw=
+ - secure: >-
+ j9xIW/banUkkNaLrWVGcTsasAp64zEmZJQO+eY8ExINx3uLbDh5h6Foc7xWhJ4Yu7aey3+nH5BWKYimtPTYoXUD2nIWU31yBNbqU/+G6vIppDbgPHMRcVnRiO7YZ0yW1JunvKs+1lOPhOw4ibztJjGkQ2ZK9q6ZSJvQKRbo6Au0=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-resizable-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/README.md b/catapult/third_party/polymer/components/iron-resizable-behavior/README.md
new file mode 100644
index 00000000..2f37628c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/README.md
@@ -0,0 +1,36 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-resizable-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-resizable-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-resizable-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-resizable-behavior)_
+
+
+##Polymer.IronResizableBehavior
+
+`IronResizableBehavior` is a behavior that can be used in Polymer elements to
+coordinate the flow of resize events between "resizers" (elements that control the
+size or hidden state of their children) and "resizables" (elements that need to be
+notified when they are resized or un-hidden by their parents in order to take
+action on their new measurements).
+
+Elements that perform measurement should add the `IronResizableBehavior` behavior to
+their element definition and listen for the `iron-resize` event on themselves.
+This event will be fired when they become showing after having been hidden,
+when they are resized explicitly by another resizable, or when the window has been
+resized.
+
+Note, the `iron-resize` event is non-bubbling.
+
+
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/bower.json b/catapult/third_party/polymer/components/iron-resizable-behavior/bower.json
new file mode 100644
index 00000000..63b84a7e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "iron-resizable-behavior",
+ "version": "1.0.6",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Coordinates the flow of resizeable elements",
+ "private": true,
+ "main": "iron-resizable-behavior.html",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "iron",
+ "behavior"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-resizable-behavior.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-resizable-behavior/demo/index.html
new file mode 100644
index 00000000..2896c50d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/demo/index.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-resizable-behavior demo</title>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="src/x-app.html">
+
+</head>
+<body>
+
+ <x-app></x-app>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/demo/src/x-app.html b/catapult/third_party/polymer/components/iron-resizable-behavior/demo/src/x-app.html
new file mode 100644
index 00000000..c4bfa67f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/demo/src/x-app.html
@@ -0,0 +1,105 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../iron-resizable-behavior.html">
+
+<dom-module id="x-puck">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ border: 3px solid lightblue;
+ }
+ </style>
+
+ <b>I'm a resize-aware, thirdifying puck at (<span>{{x}}</span> x <span>{{y}}</span>).</b>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'x-puck',
+
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ],
+
+ properties: {
+ x: {
+ type: Number,
+ value: 0
+ },
+
+ y: {
+ type: Number,
+ value: 0
+ }
+ },
+
+ listeners: {
+ 'iron-resize': '_onIronResize'
+ },
+
+ attached: function() {
+ this.async(this.notifyResize, 1);
+ },
+
+ get parent() {
+ if (this.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+ return this.parentNode.host;
+ }
+
+ return this.parentNode;
+ },
+
+ _onIronResize: function() {
+ var x = this.x = Math.floor(this.parent.offsetWidth / 3);
+ var y = this.y = Math.floor(this.parent.offsetHeight / 3);
+
+ this.translate3d(x + 'px', y + 'px', 0);
+ }
+ });
+
+</script>
+
+<dom-module id="x-app">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+ </style>
+ <x-puck></x-puck>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'x-app',
+
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ]
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/index.html b/catapult/third_party/polymer/components/iron-resizable-behavior/index.html
new file mode 100644
index 00000000..b9b88095
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/index.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-resizable-behavior</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/iron-resizable-behavior.html b/catapult/third_party/polymer/components/iron-resizable-behavior/iron-resizable-behavior.html
new file mode 100644
index 00000000..42b65562
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/iron-resizable-behavior.html
@@ -0,0 +1,195 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+ /**
+ * `IronResizableBehavior` is a behavior that can be used in Polymer elements to
+ * coordinate the flow of resize events between "resizers" (elements that control the
+ * size or hidden state of their children) and "resizables" (elements that need to be
+ * notified when they are resized or un-hidden by their parents in order to take
+ * action on their new measurements).
+ *
+ * Elements that perform measurement should add the `IronResizableBehavior` behavior to
+ * their element definition and listen for the `iron-resize` event on themselves.
+ * This event will be fired when they become showing after having been hidden,
+ * when they are resized explicitly by another resizable, or when the window has been
+ * resized.
+ *
+ * Note, the `iron-resize` event is non-bubbling.
+ *
+ * @polymerBehavior Polymer.IronResizableBehavior
+ * @demo demo/index.html
+ **/
+ Polymer.IronResizableBehavior = {
+ properties: {
+ /**
+ * The closest ancestor element that implements `IronResizableBehavior`.
+ */
+ _parentResizable: {
+ type: Object,
+ observer: '_parentResizableChanged'
+ },
+
+ /**
+ * True if this element is currently notifying its descendant elements of
+ * resize.
+ */
+ _notifyingDescendant: {
+ type: Boolean,
+ value: false
+ }
+ },
+
+ listeners: {
+ 'iron-request-resize-notifications': '_onIronRequestResizeNotifications'
+ },
+
+ created: function() {
+ // We don't really need property effects on these, and also we want them
+ // to be created before the `_parentResizable` observer fires:
+ this._interestedResizables = [];
+ this._boundNotifyResize = this.notifyResize.bind(this);
+ },
+
+ attached: function() {
+ this.fire('iron-request-resize-notifications', null, {
+ node: this,
+ bubbles: true,
+ cancelable: true
+ });
+
+ if (!this._parentResizable) {
+ window.addEventListener('resize', this._boundNotifyResize);
+ this.notifyResize();
+ }
+ },
+
+ detached: function() {
+ if (this._parentResizable) {
+ this._parentResizable.stopResizeNotificationsFor(this);
+ } else {
+ window.removeEventListener('resize', this._boundNotifyResize);
+ }
+
+ this._parentResizable = null;
+ },
+
+ /**
+ * Can be called to manually notify a resizable and its descendant
+ * resizables of a resize change.
+ */
+ notifyResize: function() {
+ if (!this.isAttached) {
+ return;
+ }
+
+ this._interestedResizables.forEach(function(resizable) {
+ if (this.resizerShouldNotify(resizable)) {
+ this._notifyDescendant(resizable);
+ }
+ }, this);
+
+ this._fireResize();
+ },
+
+ /**
+ * Used to assign the closest resizable ancestor to this resizable
+ * if the ancestor detects a request for notifications.
+ */
+ assignParentResizable: function(parentResizable) {
+ this._parentResizable = parentResizable;
+ },
+
+ /**
+ * Used to remove a resizable descendant from the list of descendants
+ * that should be notified of a resize change.
+ */
+ stopResizeNotificationsFor: function(target) {
+ var index = this._interestedResizables.indexOf(target);
+
+ if (index > -1) {
+ this._interestedResizables.splice(index, 1);
+ this.unlisten(target, 'iron-resize', '_onDescendantIronResize');
+ }
+ },
+
+ /**
+ * This method can be overridden to filter nested elements that should or
+ * should not be notified by the current element. Return true if an element
+ * should be notified, or false if it should not be notified.
+ *
+ * @param {HTMLElement} element A candidate descendant element that
+ * implements `IronResizableBehavior`.
+ * @return {boolean} True if the `element` should be notified of resize.
+ */
+ resizerShouldNotify: function(element) { return true; },
+
+ _onDescendantIronResize: function(event) {
+ if (this._notifyingDescendant) {
+ event.stopPropagation();
+ return;
+ }
+
+ // NOTE(cdata): In ShadowDOM, event retargeting makes echoing of the
+ // otherwise non-bubbling event "just work." We do it manually here for
+ // the case where Polymer is not using shadow roots for whatever reason:
+ if (!Polymer.Settings.useShadow) {
+ this._fireResize();
+ }
+ },
+
+ _fireResize: function() {
+ this.fire('iron-resize', null, {
+ node: this,
+ bubbles: false
+ });
+ },
+
+ _onIronRequestResizeNotifications: function(event) {
+ var target = event.path ? event.path[0] : event.target;
+
+ if (target === this) {
+ return;
+ }
+
+ if (this._interestedResizables.indexOf(target) === -1) {
+ this._interestedResizables.push(target);
+ this.listen(target, 'iron-resize', '_onDescendantIronResize');
+ }
+
+ target.assignParentResizable(this);
+ this._notifyDescendant(target);
+
+ event.stopPropagation();
+ },
+
+ _parentResizableChanged: function(parentResizable) {
+ if (parentResizable) {
+ window.removeEventListener('resize', this._boundNotifyResize);
+ }
+ },
+
+ _notifyDescendant: function(descendant) {
+ // NOTE(cdata): In IE10, attached is fired on children first, so it's
+ // important not to notify them if the parent is not attached yet (or
+ // else they will get redundantly notified when the parent attaches).
+ if (!this.isAttached) {
+ return;
+ }
+
+ this._notifyingDescendant = true;
+ descendant.notifyResize();
+ this._notifyingDescendant = false;
+ }
+ };
+</script>
+
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/test/basic.html b/catapult/third_party/polymer/components/iron-resizable-behavior/test/basic.html
new file mode 100644
index 00000000..d3183c02
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/test/basic.html
@@ -0,0 +1,223 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-resizable-behavior tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-resizable-behavior.html">
+ <link rel="import" href="test-elements.html">
+
+</head>
+<body>
+
+<!--
+
+Notes on Polyfill compatibility in tests:
+- Test elements loaded via imports, to ensure load order correctness
+ w.r.t. Polymer.mixin being availbale
+- Resize notifications and asserts are done asynchronously, since
+ there are timing differences w.r.t. when detached callbacks occur
+
+-->
+
+ <test-fixture id="TestElement">
+ <template>
+ <test-element></test-element>
+ </template>
+ </test-fixture>
+
+
+ <script>
+
+ suite('iron-resizable-behavior', function() {
+ function ListenForResize(el, expectsResize) {
+ var listener = function(event) {
+ var target = event.path ? event.path[0] : event.target;
+ pendingNotifications--;
+ };
+
+ if (expectsResize !== false) {
+ pendingNotifications++;
+ }
+
+ el.addEventListener('iron-resize', listener);
+
+ return {
+ el: el,
+ remove: function() {
+ el.removeEventListener('iron-resize', listener);
+ }
+ };
+ }
+
+ function RemoveListeners(listeners) {
+ listeners.forEach(function(listener) {
+ listener.remove();
+ });
+ }
+
+ var pendingNotifications;
+ var testEl;
+
+ setup(function() {
+ pendingNotifications = 0;
+ testEl = fixture('TestElement');
+ });
+
+ suite('x-resizer-parent', function() {
+
+ test('notify resizables from window', function() {
+ var listeners = [
+ ListenForResize(testEl.$.parent),
+ ListenForResize(testEl.$.child1a),
+ ListenForResize(testEl.$.child1b),
+ ListenForResize(testEl.$.shadow1c.$.resizable),
+ ListenForResize(testEl.$.shadow1d.$.resizable)
+ ];
+
+ window.dispatchEvent(new CustomEvent('resize', { bubbles: false }));
+ expect(pendingNotifications).to.be.eql(0);
+
+ RemoveListeners(listeners);
+ });
+
+ test('notify resizables from parent', function() {
+ var listeners = [
+ ListenForResize(testEl.$.parent),
+ ListenForResize(testEl.$.child1a),
+ ListenForResize(testEl.$.child1b),
+ ListenForResize(testEl.$.shadow1c.$.resizable),
+ ListenForResize(testEl.$.shadow1d.$.resizable)
+ ];
+
+ testEl.$.parent.notifyResize();
+ expect(pendingNotifications).to.be.eql(0);
+ RemoveListeners(listeners);
+ });
+
+ test('detach resizables then notify parent', function() {
+ sinon.spy(testEl.$.child1a, 'notifyResize');
+ sinon.spy(testEl.$.shadow1c.$.resizable, 'notifyResize');
+ sinon.spy(testEl.$.child1b, 'notifyResize');
+ sinon.spy(testEl.$.shadow1d.$.resizable, 'notifyResize');
+
+ var firstElementToRemove = testEl.$.child1a;
+ var firstElementParent = Polymer.dom(firstElementToRemove).parentNode;
+ var secondElementToRemove = testEl.$.shadow1c.$.resizable;
+ var secondElementParent = Polymer.dom(secondElementToRemove).parentNode;
+
+ Polymer.dom(firstElementParent).removeChild(firstElementToRemove);
+ Polymer.dom(secondElementParent).removeChild(secondElementToRemove);
+
+ Polymer.dom.flush();
+
+ testEl.$.parent.notifyResize();
+
+ expect(testEl.$.child1a.notifyResize.callCount).to.be.equal(0);
+ expect(testEl.$.shadow1c.$.resizable.notifyResize.callCount).to.be.equal(0);
+ expect(testEl.$.child1b.notifyResize.callCount).to.be.equal(1);
+ expect(testEl.$.shadow1d.$.resizable.notifyResize.callCount).to.be.equal(1);
+ });
+
+ test('detach parent then notify window', function(done) {
+ var listeners = [
+ ListenForResize(testEl.$.parent, false),
+ ListenForResize(testEl.$.child1a, false),
+ ListenForResize(testEl.$.child1b, false),
+ ListenForResize(testEl.$.shadow1c.$.resizable, false),
+ ListenForResize(testEl.$.shadow1d.$.resizable, false)
+ ];
+
+ var el = Polymer.dom(testEl.root).querySelector('#parent');
+
+ el.parentNode.removeChild(el);
+
+ setTimeout(function() {
+ try {
+ window.dispatchEvent(new CustomEvent('resize', { bubbles: false }));
+ expect(pendingNotifications).to.be.eql(0);
+ RemoveListeners(listeners);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }, 0);
+ });
+
+ });
+
+ suite('x-resizer-parent-filtered', function() {
+
+ test('notify resizables from window', function() {
+ var listeners = [
+ ListenForResize(testEl.$.parentFiltered),
+ ListenForResize(testEl.$.child2a),
+ ListenForResize(testEl.$.child2b, false),
+ ListenForResize(testEl.$.shadow2c.$.resizable, false),
+ ListenForResize(testEl.$.shadow2d.$.resizable, false)
+ ];
+
+ testEl.$.parentFiltered.active = testEl.$.child2a;
+
+ window.dispatchEvent(new CustomEvent('resize', { bubbles: false }));
+ expect(pendingNotifications).to.be.eql(0);
+ RemoveListeners(listeners);
+ });
+
+ test('notify resizables from parent', function() {
+ var listeners = [
+ ListenForResize(testEl.$.parentFiltered),
+ ListenForResize(testEl.$.child2a),
+ ListenForResize(testEl.$.child2b, false),
+ ListenForResize(testEl.$.shadow2c.$.resizable, false),
+ ListenForResize(testEl.$.shadow2d.$.resizable, false)
+ ];
+
+ testEl.$.parentFiltered.active = testEl.$.child2a;
+
+ testEl.$.parentFiltered.notifyResize();
+ expect(pendingNotifications).to.be.eql(0);
+ RemoveListeners(listeners);
+ });
+
+ test('detach resizables then notify parent', function() {
+ var listeners = [
+ ListenForResize(testEl.$.parentFiltered),
+ ListenForResize(testEl.$.child2a, false),
+ ListenForResize(testEl.$.child2b, false),
+ ListenForResize(testEl.$.shadow2c.$.resizable, false),
+ ListenForResize(testEl.$.shadow2d.$.resizable)
+ ];
+
+ var el = Polymer.dom(testEl.root).querySelector('#child2a');
+ el.parentNode.removeChild(el);
+ el = Polymer.dom(testEl.root).querySelector('#shadow2c');
+ el.parentNode.removeChild(el);
+
+ testEl.$.parentFiltered.active = testEl.$.shadow2d.$.resizable;
+
+ testEl.$.parentFiltered.notifyResize();
+ expect(pendingNotifications).to.be.eql(0);
+ RemoveListeners(listeners);
+ });
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/test/index.html b/catapult/third_party/polymer/components/iron-resizable-behavior/test/index.html
new file mode 100644
index 00000000..fa83bf6e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/test/index.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+</head>
+<body>
+
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'iron-resizable-behavior.html',
+ 'basic.html?dom=shadow',
+ 'iron-resizable-behavior.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/test/iron-resizable-behavior.html b/catapult/third_party/polymer/components/iron-resizable-behavior/test/iron-resizable-behavior.html
new file mode 100644
index 00000000..91490046
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/test/iron-resizable-behavior.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-resizable-behavior tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-resizable-behavior.html">
+ <link rel="import" href="test-elements.html">
+
+</head>
+<body>
+
+ <test-fixture id="ResizableAndShadowBoundaries">
+ <template>
+ <x-light-resizable></x-light-resizable>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('iron-resizable-behavior', function() {
+ var resizable;
+
+ suite('events across shadow boundaries', function() {
+ setup(function() {
+ resizable = fixture('ResizableAndShadowBoundaries');
+ });
+
+ suite('ancestor\'s iron-resize', function() {
+ test('only fires once for a notifying shadow descendent', function() {
+ resizable.$.childResizable1.notifyResize();
+
+ expect(resizable.ironResizeCount).to.be.equal(2);
+ });
+
+ test('only fires once when notifying descendent observables', function() {
+ resizable.notifyResize();
+
+ expect(resizable.ironResizeCount).to.be.equal(2);
+ });
+ });
+
+ suite('descendant\'s iron-resize', function() {
+ test('only fires once for a notifying shadow root', function() {
+ resizable.notifyResize();
+
+ expect(resizable.$.childResizable1.ironResizeCount).to.be.equal(2);
+ expect(resizable.$.childResizable2.ironResizeCount).to.be.equal(2);
+ });
+
+ test('only fires once for a notifying descendent observable', function() {
+ resizable.$.childResizable1.notifyResize();
+
+ expect(resizable.$.childResizable1.ironResizeCount).to.be.equal(2);
+ });
+ });
+
+ suite('window\'s resize', function() {
+ test('causes all iron-resize events to fire once', function() {
+ window.dispatchEvent(new CustomEvent('resize'));
+ Polymer.dom.flush();
+ expect(resizable.ironResizeCount).to.be.equal(2);
+ expect(resizable.$.childResizable1.ironResizeCount).to.be.equal(2);
+ expect(resizable.$.childResizable2.ironResizeCount).to.be.equal(2);
+ });
+ });
+ });
+
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-resizable-behavior/test/test-elements.html b/catapult/third_party/polymer/components/iron-resizable-behavior/test/test-elements.html
new file mode 100644
index 00000000..1ab8a9dd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-resizable-behavior/test/test-elements.html
@@ -0,0 +1,194 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../iron-resizable-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'x-resizer-parent',
+
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ],
+
+ listeners: {
+ 'core-resize': 'resizeHandler'
+ },
+
+ resizeHandler: function() {
+ }
+
+ });
+
+</script>
+
+<script>
+
+ Polymer({
+
+ is: 'x-resizer-parent-filtered',
+
+ active: null,
+
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ],
+
+ listeners: {
+ 'core-resize': 'resizeHandler'
+ },
+
+ resizeHandler: function() {
+ },
+
+ resizerShouldNotify: function(el) {
+ return (el == this.active);
+ }
+
+ });
+
+</script>
+
+<script>
+
+ Polymer({
+
+ is: 'x-resizable',
+
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ],
+
+ listeners: {
+ 'core-resize': 'resizeHandler'
+ },
+
+ resizeHandler: function() {
+ }
+
+ });
+
+</script>
+
+<dom-module id="x-resizable-in-shadow">
+
+ <template>
+
+ <div>
+ <x-resizable id="resizable"></x-resizable>
+ </div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'x-resizable-in-shadow'
+
+ });
+
+</script>
+
+<dom-module id='test-element'>
+
+ <template>
+
+ <!-- Normal resizable parent with child resizables -->
+ <x-resizer-parent id="parent">
+ <x-resizable id="child1a"></x-resizable>
+ <div>
+ <x-resizable id="child1b"></x-resizable>
+ </div>
+ <x-resizable-in-shadow id="shadow1c"></x-resizable-in-shadow>
+ <div>
+ <x-resizable-in-shadow id="shadow1d"></x-resizable-in-shadow>
+ </div>
+ </x-resizer-parent>
+
+ <!-- Resizable parent using resizerShouldNotify, with child resizables -->
+ <x-resizer-parent-filtered id="parentFiltered">
+ <x-resizable id="child2a"></x-resizable>
+ <div>
+ <x-resizable id="child2b"></x-resizable>
+ </div>
+ <x-resizable-in-shadow id="shadow2c"></x-resizable-in-shadow>
+ <div>
+ <x-resizable-in-shadow id="shadow2d"></x-resizable-in-shadow>
+ </div>
+ </x-resizer-parent-filtered>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'test-element'
+
+ });
+
+</script>
+<script>
+ /** @polymerBehavior */
+ Polymer.ObserveIronResizeBehavior = {
+ properties: {
+ ironResizeCount: {
+ type: Number,
+ value: 0
+ }
+ },
+
+ listeners: {
+ 'iron-resize': '_incrementIronResizeCount'
+ },
+
+ _incrementIronResizeCount: function() {
+ this.ironResizeCount++;
+ }
+ };
+</script>
+<dom-module id="x-shadow-resizable">
+ <template>
+ <div></div>
+ </template>
+</dom-module>
+<script>
+ Polymer({
+ is: 'x-shadow-resizable',
+
+ behaviors: [
+ Polymer.IronResizableBehavior,
+ Polymer.ObserveIronResizeBehavior
+ ]
+ });
+</script>
+
+<dom-module id="x-light-resizable">
+ <template>
+ <x-shadow-resizable id="childResizable1"></x-shadow-resizable>
+ <x-shadow-resizable id="childResizable2"></x-shadow-resizable>
+ </template>
+</dom-module>
+<script>
+ Polymer({
+ is: 'x-light-resizable',
+
+ behaviors: [
+ Polymer.IronResizableBehavior,
+ Polymer.ObserveIronResizeBehavior
+ ]
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/iron-selector/.bower.json b/catapult/third_party/polymer/components/iron-selector/.bower.json
new file mode 100644
index 00000000..44446e67
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/.bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "iron-selector",
+ "version": "1.5.3",
+ "description": "Manages a set of elements that can be selected",
+ "private": true,
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "main": [
+ "iron-selector.html",
+ "iron-multi-selectable.html",
+ "iron-selectable.html",
+ "iron-selection.html"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "selector"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-selector.git"
+ },
+ "homepage": "https://github.com/PolymerElements/iron-selector",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.2.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.4",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.5.3",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.3",
+ "commit": "6486761a670829e2c34625abed0b773b6d9230d6"
+ },
+ "_source": "https://github.com/PolymerElements/iron-selector.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-selector"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-selector/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-selector/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..07a3448a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-selector/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-selector/.gitignore b/catapult/third_party/polymer/components/iron-selector/.gitignore
new file mode 100644
index 00000000..b13058c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/.gitignore
@@ -0,0 +1,2 @@
+bower_components
+.DS_Store
diff --git a/catapult/third_party/polymer/components/iron-selector/.travis.yml b/catapult/third_party/polymer/components/iron-selector/.travis.yml
new file mode 100644
index 00000000..aab07587
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ SFfLw5U6C/7UFRyt/ho5AXNnf8E1d7bY6V9lPT2UnHjkNl4NldWyJEkBeaFTERChKmrYBNk33g748OGohL8snCRXCPxM9A/OKRWEGSFIvPmgLs8mkpbNodUAcpL6dbDLlZgcVDHXCn1uvVQgOgzg64FWW5ozphxD4u/nl5nA7aY=
+ - secure: >-
+ a6YFcuIvE3PJ9C0F7cimJ/R8EGlKy2rlg7Re2ezpL85f4++cYnYBUrNnbwj+BNp5F9IXZxvD6T+kNr+1BJtqILFf54W5baBgB5PVCSt7dry+lrcRUf5yTIRl0yJ+73UJrEWR2UOXaXlGzbQ56k2TWAbnzVbq2jik1vdgSnxbjLE=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-selector/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-selector/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-selector/README.md b/catapult/third_party/polymer/components/iron-selector/README.md
new file mode 100755
index 00000000..9d2ef310
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/README.md
@@ -0,0 +1,91 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-multi-selectable.html iron-selectable.html iron-selector.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-selector.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-selector)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-selector)_
+
+
+##&lt;iron-selector&gt;
+
+ `iron-selector` is an element which can be used to manage a list of elements
+ that can be selected. Tapping on the item will make the item selected. The `selected` indicates
+ which item is being selected. The default is to use the index of the item.
+
+ Example:
+
+```html
+ <iron-selector selected="0">
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ </iron-selector>
+```
+
+ If you want to use the attribute value of an element for `selected` instead of the index,
+ set `attrForSelected` to the name of the attribute. For example, if you want to select item by
+ `name`, set `attrForSelected` to `name`.
+
+ Example:
+
+```html
+ <iron-selector attr-for-selected="name" selected="foo">
+ <div name="foo">Foo</div>
+ <div name="bar">Bar</div>
+ <div name="zot">Zot</div>
+ </iron-selector>
+```
+
+ You can specify a default fallback with `fallbackSelection` in case the `selected` attribute does
+ not match the `attrForSelected` attribute of any elements.
+
+ Example:
+
+```html
+ <iron-selector attr-for-selected="name" selected="non-existing"
+ fallback-selection="default">
+ <div name="foo">Foo</div>
+ <div name="bar">Bar</div>
+ <div name="default">Default</div>
+ </iron-selector>
+```
+
+ Note: When the selector is multi, the selection will set to `fallbackSelection` iff
+ the number of matching elements is zero.
+
+ `iron-selector` is not styled. Use the `iron-selected` CSS class to style the selected element.
+
+ Example:
+
+```html
+ <style>
+ .iron-selected {
+ background: #eee;
+ }
+ </style>
+
+ ...
+
+ <iron-selector selected="0">
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ </iron-selector>
+```
+
+
+
+<!-- No docs for Polymer.IronMultiSelectableBehavior found. -->
+
+<!-- No docs for Polymer.IronSelectableBehavior found. -->
diff --git a/catapult/third_party/polymer/components/iron-selector/bower.json b/catapult/third_party/polymer/components/iron-selector/bower.json
new file mode 100755
index 00000000..36295dc2
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/bower.json
@@ -0,0 +1,38 @@
+{
+ "name": "iron-selector",
+ "version": "1.5.3",
+ "description": "Manages a set of elements that can be selected",
+ "private": true,
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "main": [
+ "iron-selector.html",
+ "iron-multi-selectable.html",
+ "iron-selectable.html",
+ "iron-selection.html"
+ ],
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "selector"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-selector.git"
+ },
+ "homepage": "https://github.com/PolymerElements/iron-selector",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.2.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.4",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-selector/demo/index.html b/catapult/third_party/polymer/components/iron-selector/demo/index.html
new file mode 100644
index 00000000..66724a19
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/demo/index.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>iron-selector</title>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../iron-selector.html">
+
+ <style is="custom-style">
+
+ iron-selector > * {
+ padding: 8px;
+ }
+
+ .horizontal-section {
+ padding: 0;
+ }
+
+ .iron-selected {
+ background-color: var(--google-blue-500);
+ color: white;
+ }
+
+ </style>
+
+ </head>
+ <body unresolved>
+
+ <div class="horizontal center-justified layout">
+ <div>
+ <h3>Basic</h3>
+ <div class="horizontal-section">
+ <iron-selector selected="0">
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ <div>Item 4</div>
+ </iron-selector>
+ </div>
+ </div>
+
+ <div>
+ <h3>Multi-select</h3>
+ <div class="horizontal-section">
+ <iron-selector multi selected-values='[0,2]'>
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ <div>Item 4</div>
+ </iron-selector>
+ </div>
+ </div>
+
+ <div>
+ <h3>Example with attr-for-selected</h3>
+ <div class="horizontal-section">
+ <iron-selector selected="foo" attr-for-selected="name">
+ <div name="foo">Foo</div>
+ <div name="bar">Bar</div>
+ <div name="baz">Baz</div>
+ <div name="qux">Qux</div>
+ <div name="quux">Quux</div>
+ </iron-selector>
+ </div>
+ </div>
+
+ <div>
+ <h3>Example with fallback-selection</h3>
+ <div class="horizontal-section">
+ <iron-selector selected="non-existing" attr-for-selected="name" fallback-selection="default">
+ <div name="foo">Foo</div>
+ <div name="bar">Bar</div>
+ <div name="baz">Baz</div>
+ <div name="qux">Qux</div>
+ <div name="quux">Quux</div>
+ <div name="default">Default</div>
+ </iron-selector>
+ </div>
+ </div>
+ </div>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/index.html b/catapult/third_party/polymer/components/iron-selector/index.html
new file mode 100755
index 00000000..a27840bc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/iron-multi-selectable.html b/catapult/third_party/polymer/components/iron-selector/iron-multi-selectable.html
new file mode 100644
index 00000000..9993151d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/iron-multi-selectable.html
@@ -0,0 +1,154 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="iron-selectable.html">
+
+<script>
+ /** @polymerBehavior Polymer.IronMultiSelectableBehavior */
+ Polymer.IronMultiSelectableBehaviorImpl = {
+ properties: {
+
+ /**
+ * If true, multiple selections are allowed.
+ */
+ multi: {
+ type: Boolean,
+ value: false,
+ observer: 'multiChanged'
+ },
+
+ /**
+ * Gets or sets the selected elements. This is used instead of `selected` when `multi`
+ * is true.
+ */
+ selectedValues: {
+ type: Array,
+ notify: true
+ },
+
+ /**
+ * Returns an array of currently selected items.
+ */
+ selectedItems: {
+ type: Array,
+ readOnly: true,
+ notify: true
+ },
+
+ },
+
+ observers: [
+ '_updateSelected(selectedValues.splices)'
+ ],
+
+ /**
+ * Selects the given value. If the `multi` property is true, then the selected state of the
+ * `value` will be toggled; otherwise the `value` will be selected.
+ *
+ * @method select
+ * @param {string|number} value the value to select.
+ */
+ select: function(value) {
+ if (this.multi) {
+ if (this.selectedValues) {
+ this._toggleSelected(value);
+ } else {
+ this.selectedValues = [value];
+ }
+ } else {
+ this.selected = value;
+ }
+ },
+
+ multiChanged: function(multi) {
+ this._selection.multi = multi;
+ },
+
+ get _shouldUpdateSelection() {
+ return this.selected != null ||
+ (this.selectedValues != null && this.selectedValues.length);
+ },
+
+ _updateAttrForSelected: function() {
+ if (!this.multi) {
+ Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this);
+ } else if (this._shouldUpdateSelection) {
+ this.selectedValues = this.selectedItems.map(function(selectedItem) {
+ return this._indexToValue(this.indexOf(selectedItem));
+ }, this).filter(function(unfilteredValue) {
+ return unfilteredValue != null;
+ }, this);
+ }
+ },
+
+ _updateSelected: function() {
+ if (this.multi) {
+ this._selectMulti(this.selectedValues);
+ } else {
+ this._selectSelected(this.selected);
+ }
+ },
+
+ _selectMulti: function(values) {
+ if (values) {
+ var selectedItems = this._valuesToItems(values);
+ // clear all but the current selected items
+ this._selection.clear(selectedItems);
+ // select only those not selected yet
+ for (var i = 0; i < selectedItems.length; i++) {
+ this._selection.setItemSelected(selectedItems[i], true);
+ }
+ // Check for items, since this array is populated only when attached
+ if (this.fallbackSelection && this.items.length && !this._selection.get().length) {
+ var fallback = this._valueToItem(this.fallbackSelection);
+ if (fallback) {
+ this.selectedValues = [this.fallbackSelection];
+ }
+ }
+ } else {
+ this._selection.clear();
+ }
+ },
+
+ _selectionChange: function() {
+ var s = this._selection.get();
+ if (this.multi) {
+ this._setSelectedItems(s);
+ } else {
+ this._setSelectedItems([s]);
+ this._setSelectedItem(s);
+ }
+ },
+
+ _toggleSelected: function(value) {
+ var i = this.selectedValues.indexOf(value);
+ var unselected = i < 0;
+ if (unselected) {
+ this.push('selectedValues',value);
+ } else {
+ this.splice('selectedValues',i,1);
+ }
+ },
+
+ _valuesToItems: function(values) {
+ return (values == null) ? null : values.map(function(value) {
+ return this._valueToItem(value);
+ }, this);
+ }
+ };
+
+ /** @polymerBehavior */
+ Polymer.IronMultiSelectableBehavior = [
+ Polymer.IronSelectableBehavior,
+ Polymer.IronMultiSelectableBehaviorImpl
+ ];
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-selector/iron-selectable.html b/catapult/third_party/polymer/components/iron-selector/iron-selectable.html
new file mode 100644
index 00000000..25cfe91e
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/iron-selectable.html
@@ -0,0 +1,388 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="iron-selection.html">
+
+<script>
+
+ /** @polymerBehavior */
+ Polymer.IronSelectableBehavior = {
+
+ /**
+ * Fired when iron-selector is activated (selected or deselected).
+ * It is fired before the selected items are changed.
+ * Cancel the event to abort selection.
+ *
+ * @event iron-activate
+ */
+
+ /**
+ * Fired when an item is selected
+ *
+ * @event iron-select
+ */
+
+ /**
+ * Fired when an item is deselected
+ *
+ * @event iron-deselect
+ */
+
+ /**
+ * Fired when the list of selectable items changes (e.g., items are
+ * added or removed). The detail of the event is a mutation record that
+ * describes what changed.
+ *
+ * @event iron-items-changed
+ */
+
+ properties: {
+
+ /**
+ * If you want to use an attribute value or property of an element for
+ * `selected` instead of the index, set this to the name of the attribute
+ * or property. Hyphenated values are converted to camel case when used to
+ * look up the property of a selectable element. Camel cased values are
+ * *not* converted to hyphenated values for attribute lookup. It's
+ * recommended that you provide the hyphenated form of the name so that
+ * selection works in both cases. (Use `attr-or-property-name` instead of
+ * `attrOrPropertyName`.)
+ */
+ attrForSelected: {
+ type: String,
+ value: null
+ },
+
+ /**
+ * Gets or sets the selected element. The default is to use the index of the item.
+ * @type {string|number}
+ */
+ selected: {
+ type: String,
+ notify: true
+ },
+
+ /**
+ * Returns the currently selected item.
+ *
+ * @type {?Object}
+ */
+ selectedItem: {
+ type: Object,
+ readOnly: true,
+ notify: true
+ },
+
+ /**
+ * The event that fires from items when they are selected. Selectable
+ * will listen for this event from items and update the selection state.
+ * Set to empty string to listen to no events.
+ */
+ activateEvent: {
+ type: String,
+ value: 'tap',
+ observer: '_activateEventChanged'
+ },
+
+ /**
+ * This is a CSS selector string. If this is set, only items that match the CSS selector
+ * are selectable.
+ */
+ selectable: String,
+
+ /**
+ * The class to set on elements when selected.
+ */
+ selectedClass: {
+ type: String,
+ value: 'iron-selected'
+ },
+
+ /**
+ * The attribute to set on elements when selected.
+ */
+ selectedAttribute: {
+ type: String,
+ value: null
+ },
+
+ /**
+ * Default fallback if the selection based on selected with `attrForSelected`
+ * is not found.
+ */
+ fallbackSelection: {
+ type: String,
+ value: null
+ },
+
+ /**
+ * The list of items from which a selection can be made.
+ */
+ items: {
+ type: Array,
+ readOnly: true,
+ notify: true,
+ value: function() {
+ return [];
+ }
+ },
+
+ /**
+ * The set of excluded elements where the key is the `localName`
+ * of the element that will be ignored from the item list.
+ *
+ * @default {template: 1}
+ */
+ _excludedLocalNames: {
+ type: Object,
+ value: function() {
+ return {
+ 'template': 1
+ };
+ }
+ }
+ },
+
+ observers: [
+ '_updateAttrForSelected(attrForSelected)',
+ '_updateSelected(selected)',
+ '_checkFallback(fallbackSelection)'
+ ],
+
+ created: function() {
+ this._bindFilterItem = this._filterItem.bind(this);
+ this._selection = new Polymer.IronSelection(this._applySelection.bind(this));
+ },
+
+ attached: function() {
+ this._observer = this._observeItems(this);
+ this._updateItems();
+ if (!this._shouldUpdateSelection) {
+ this._updateSelected();
+ }
+ this._addListener(this.activateEvent);
+ },
+
+ detached: function() {
+ if (this._observer) {
+ Polymer.dom(this).unobserveNodes(this._observer);
+ }
+ this._removeListener(this.activateEvent);
+ },
+
+ /**
+ * Returns the index of the given item.
+ *
+ * @method indexOf
+ * @param {Object} item
+ * @returns Returns the index of the item
+ */
+ indexOf: function(item) {
+ return this.items.indexOf(item);
+ },
+
+ /**
+ * Selects the given value.
+ *
+ * @method select
+ * @param {string|number} value the value to select.
+ */
+ select: function(value) {
+ this.selected = value;
+ },
+
+ /**
+ * Selects the previous item.
+ *
+ * @method selectPrevious
+ */
+ selectPrevious: function() {
+ var length = this.items.length;
+ var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length;
+ this.selected = this._indexToValue(index);
+ },
+
+ /**
+ * Selects the next item.
+ *
+ * @method selectNext
+ */
+ selectNext: function() {
+ var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length;
+ this.selected = this._indexToValue(index);
+ },
+
+ /**
+ * Selects the item at the given index.
+ *
+ * @method selectIndex
+ */
+ selectIndex: function(index) {
+ this.select(this._indexToValue(index));
+ },
+
+ /**
+ * Force a synchronous update of the `items` property.
+ *
+ * NOTE: Consider listening for the `iron-items-changed` event to respond to
+ * updates to the set of selectable items after updates to the DOM list and
+ * selection state have been made.
+ *
+ * WARNING: If you are using this method, you should probably consider an
+ * alternate approach. Synchronously querying for items is potentially
+ * slow for many use cases. The `items` property will update asynchronously
+ * on its own to reflect selectable items in the DOM.
+ */
+ forceSynchronousItemUpdate: function() {
+ this._updateItems();
+ },
+
+ get _shouldUpdateSelection() {
+ return this.selected != null;
+ },
+
+ _checkFallback: function() {
+ if (this._shouldUpdateSelection) {
+ this._updateSelected();
+ }
+ },
+
+ _addListener: function(eventName) {
+ this.listen(this, eventName, '_activateHandler');
+ },
+
+ _removeListener: function(eventName) {
+ this.unlisten(this, eventName, '_activateHandler');
+ },
+
+ _activateEventChanged: function(eventName, old) {
+ this._removeListener(old);
+ this._addListener(eventName);
+ },
+
+ _updateItems: function() {
+ var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*');
+ nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);
+ this._setItems(nodes);
+ },
+
+ _updateAttrForSelected: function() {
+ if (this._shouldUpdateSelection) {
+ this.selected = this._indexToValue(this.indexOf(this.selectedItem));
+ }
+ },
+
+ _updateSelected: function() {
+ this._selectSelected(this.selected);
+ },
+
+ _selectSelected: function(selected) {
+ this._selection.select(this._valueToItem(this.selected));
+ // Check for items, since this array is populated only when attached
+ // Since Number(0) is falsy, explicitly check for undefined
+ if (this.fallbackSelection && this.items.length && (this._selection.get() === undefined)) {
+ this.selected = this.fallbackSelection;
+ }
+ },
+
+ _filterItem: function(node) {
+ return !this._excludedLocalNames[node.localName];
+ },
+
+ _valueToItem: function(value) {
+ return (value == null) ? null : this.items[this._valueToIndex(value)];
+ },
+
+ _valueToIndex: function(value) {
+ if (this.attrForSelected) {
+ for (var i = 0, item; item = this.items[i]; i++) {
+ if (this._valueForItem(item) == value) {
+ return i;
+ }
+ }
+ } else {
+ return Number(value);
+ }
+ },
+
+ _indexToValue: function(index) {
+ if (this.attrForSelected) {
+ var item = this.items[index];
+ if (item) {
+ return this._valueForItem(item);
+ }
+ } else {
+ return index;
+ }
+ },
+
+ _valueForItem: function(item) {
+ var propValue = item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)];
+ return propValue != undefined ? propValue : item.getAttribute(this.attrForSelected);
+ },
+
+ _applySelection: function(item, isSelected) {
+ if (this.selectedClass) {
+ this.toggleClass(this.selectedClass, isSelected, item);
+ }
+ if (this.selectedAttribute) {
+ this.toggleAttribute(this.selectedAttribute, isSelected, item);
+ }
+ this._selectionChange();
+ this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item});
+ },
+
+ _selectionChange: function() {
+ this._setSelectedItem(this._selection.get());
+ },
+
+ // observe items change under the given node.
+ _observeItems: function(node) {
+ return Polymer.dom(node).observeNodes(function(mutation) {
+ this._updateItems();
+
+ if (this._shouldUpdateSelection) {
+ this._updateSelected();
+ }
+
+ // Let other interested parties know about the change so that
+ // we don't have to recreate mutation observers everywhere.
+ this.fire('iron-items-changed', mutation, {
+ bubbles: false,
+ cancelable: false
+ });
+ });
+ },
+
+ _activateHandler: function(e) {
+ var t = e.target;
+ var items = this.items;
+ while (t && t != this) {
+ var i = items.indexOf(t);
+ if (i >= 0) {
+ var value = this._indexToValue(i);
+ this._itemActivate(value, t);
+ return;
+ }
+ t = t.parentNode;
+ }
+ },
+
+ _itemActivate: function(value, item) {
+ if (!this.fire('iron-activate',
+ {selected: value, item: item}, {cancelable: true}).defaultPrevented) {
+ this.select(value);
+ }
+ }
+
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-selector/iron-selection.html b/catapult/third_party/polymer/components/iron-selector/iron-selection.html
new file mode 100644
index 00000000..408ccae9
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/iron-selection.html
@@ -0,0 +1,119 @@
+
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+
+ /**
+ * @param {!Function} selectCallback
+ * @constructor
+ */
+ Polymer.IronSelection = function(selectCallback) {
+ this.selection = [];
+ this.selectCallback = selectCallback;
+ };
+
+ Polymer.IronSelection.prototype = {
+
+ /**
+ * Retrieves the selected item(s).
+ *
+ * @method get
+ * @returns Returns the selected item(s). If the multi property is true,
+ * `get` will return an array, otherwise it will return
+ * the selected item or undefined if there is no selection.
+ */
+ get: function() {
+ return this.multi ? this.selection.slice() : this.selection[0];
+ },
+
+ /**
+ * Clears all the selection except the ones indicated.
+ *
+ * @method clear
+ * @param {Array} excludes items to be excluded.
+ */
+ clear: function(excludes) {
+ this.selection.slice().forEach(function(item) {
+ if (!excludes || excludes.indexOf(item) < 0) {
+ this.setItemSelected(item, false);
+ }
+ }, this);
+ },
+
+ /**
+ * Indicates if a given item is selected.
+ *
+ * @method isSelected
+ * @param {*} item The item whose selection state should be checked.
+ * @returns Returns true if `item` is selected.
+ */
+ isSelected: function(item) {
+ return this.selection.indexOf(item) >= 0;
+ },
+
+ /**
+ * Sets the selection state for a given item to either selected or deselected.
+ *
+ * @method setItemSelected
+ * @param {*} item The item to select.
+ * @param {boolean} isSelected True for selected, false for deselected.
+ */
+ setItemSelected: function(item, isSelected) {
+ if (item != null) {
+ if (isSelected !== this.isSelected(item)) {
+ // proceed to update selection only if requested state differs from current
+ if (isSelected) {
+ this.selection.push(item);
+ } else {
+ var i = this.selection.indexOf(item);
+ if (i >= 0) {
+ this.selection.splice(i, 1);
+ }
+ }
+ if (this.selectCallback) {
+ this.selectCallback(item, isSelected);
+ }
+ }
+ }
+ },
+
+ /**
+ * Sets the selection state for a given item. If the `multi` property
+ * is true, then the selected state of `item` will be toggled; otherwise
+ * the `item` will be selected.
+ *
+ * @method select
+ * @param {*} item The item to select.
+ */
+ select: function(item) {
+ if (this.multi) {
+ this.toggle(item);
+ } else if (this.get() !== item) {
+ this.setItemSelected(this.get(), false);
+ this.setItemSelected(item, true);
+ }
+ },
+
+ /**
+ * Toggles the selection state for `item`.
+ *
+ * @method toggle
+ * @param {*} item The item to toggle.
+ */
+ toggle: function(item) {
+ this.setItemSelected(item, !this.isSelected(item));
+ }
+
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-selector/iron-selector.html b/catapult/third_party/polymer/components/iron-selector/iron-selector.html
new file mode 100644
index 00000000..0ecc9fbc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/iron-selector.html
@@ -0,0 +1,87 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="iron-multi-selectable.html">
+
+<script>
+ /**
+ `iron-selector` is an element which can be used to manage a list of elements
+ that can be selected. Tapping on the item will make the item selected. The `selected` indicates
+ which item is being selected. The default is to use the index of the item.
+
+ Example:
+
+ <iron-selector selected="0">
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ </iron-selector>
+
+ If you want to use the attribute value of an element for `selected` instead of the index,
+ set `attrForSelected` to the name of the attribute. For example, if you want to select item by
+ `name`, set `attrForSelected` to `name`.
+
+ Example:
+
+ <iron-selector attr-for-selected="name" selected="foo">
+ <div name="foo">Foo</div>
+ <div name="bar">Bar</div>
+ <div name="zot">Zot</div>
+ </iron-selector>
+
+ You can specify a default fallback with `fallbackSelection` in case the `selected` attribute does
+ not match the `attrForSelected` attribute of any elements.
+
+ Example:
+
+ <iron-selector attr-for-selected="name" selected="non-existing"
+ fallback-selection="default">
+ <div name="foo">Foo</div>
+ <div name="bar">Bar</div>
+ <div name="default">Default</div>
+ </iron-selector>
+
+ Note: When the selector is multi, the selection will set to `fallbackSelection` iff
+ the number of matching elements is zero.
+
+ `iron-selector` is not styled. Use the `iron-selected` CSS class to style the selected element.
+
+ Example:
+
+ <style>
+ .iron-selected {
+ background: #eee;
+ }
+ </style>
+
+ ...
+
+ <iron-selector selected="0">
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ </iron-selector>
+
+ @demo demo/index.html
+ */
+
+ Polymer({
+
+ is: 'iron-selector',
+
+ behaviors: [
+ Polymer.IronMultiSelectableBehavior
+ ]
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/activate-event.html b/catapult/third_party/polymer/components/iron-selector/test/activate-event.html
new file mode 100644
index 00000000..40aa4fdd
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/activate-event.html
@@ -0,0 +1,150 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-activate-event</title>
+ <meta charset="UTF-8">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-selector.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+ </style>
+
+</head>
+<body>
+
+ <test-fixture id="test">
+ <template>
+ <iron-selector id="selector" selected="0">
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ <div>Item 4</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('activate event', function() {
+
+ var s;
+
+ setup(function () {
+ s = fixture('test');
+ });
+
+ test('activates on tap', function() {
+ assert.equal(s.selected, '0');
+
+ // select Item 1
+ s.children[1].dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ assert.equal(s.selected, '1');
+ });
+
+ test('activates on tap and fires iron-activate', function(done) {
+ assert.equal(s.selected, '0');
+
+ // attach iron-activate listener
+ s.addEventListener("iron-activate", function(event) {
+ assert.equal(event.detail.selected, '1');
+ assert.equal(event.detail.item, s.children[1]);
+ done();
+ });
+
+ // select Item 1
+ s.children[1].dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ });
+
+ test('tap on already selected and fires iron-activate', function(done) {
+ assert.equal(s.selected, '0');
+
+ // attach iron-activate listener
+ s.addEventListener("iron-activate", function(event) {
+ assert.equal(event.detail.selected, '0');
+ assert.equal(event.detail.item, s.children[0]);
+ done();
+ });
+
+ // select Item 0
+ s.children[0].dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ });
+
+ test('activates on mousedown', function() {
+ // set activateEvent to mousedown
+ s.activateEvent = 'mousedown';
+ // select Item 2
+ s.children[2].dispatchEvent(new CustomEvent('mousedown', {bubbles: true}));
+ assert.equal(s.selected, '2');
+ });
+
+ test('activates on mousedown and fires iron-activate', function(done) {
+ // attach iron-activate listener
+ s.addEventListener("iron-activate", function(event) {
+ assert.equal(event.detail.selected, '2');
+ assert.equal(event.detail.item, s.children[2]);
+ done();
+ });
+
+ // set activateEvent to mousedown
+ s.activateEvent = 'mousedown';
+ // select Item 2
+ s.children[2].dispatchEvent(new CustomEvent('mousedown', {bubbles: true}));
+ });
+
+ test('no activation', function() {
+ assert.equal(s.selected, '0');
+ // set activateEvent to null
+ s.activateEvent = null;
+ // select Item 2
+ s.children[2].dispatchEvent(new CustomEvent('mousedown', {bubbles: true}));
+ assert.equal(s.selected, '0');
+ });
+
+ test('activates on tap and preventDefault', function() {
+ // attach iron-activate listener
+ s.addEventListener("iron-activate", function(event) {
+ event.preventDefault();
+ });
+ // select Item 2
+ s.children[2].dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ // shouldn't got selected since we preventDefault in iron-activate
+ assert.equal(s.selected, '0');
+ });
+
+ test('activates after detach and re-attach', function() {
+ // Detach and re-attach
+ var parent = s.parentNode;
+ parent.removeChild(s);
+ parent.appendChild(s);
+
+ // select Item 2
+ s.children[2].dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ assert.equal(s.selected, '2');
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/attr-for-selected-elements.html b/catapult/third_party/polymer/components/iron-selector/test/attr-for-selected-elements.html
new file mode 100644
index 00000000..d9e4c4fc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/attr-for-selected-elements.html
@@ -0,0 +1,30 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+
+<dom-module id="attr-reflector">
+ <template>
+ <div>{{someAttr}}</div>
+ </template>
+ <script>
+ Polymer({
+ is: 'attr-reflector',
+
+ properties: {
+ someAttr: {
+ type: String,
+ value: "",
+ reflectToAttribute: true
+ }
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/attr-for-selected.html b/catapult/third_party/polymer/components/iron-selector/test/attr-for-selected.html
new file mode 100644
index 00000000..cc0dd148
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/attr-for-selected.html
@@ -0,0 +1,229 @@
+<!doctype html>
+<!--
+@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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector attr-for-selected</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+
+ <link rel="import" href="../iron-selector.html">
+ <link rel="import" href="attr-for-selected-elements.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+
+ .my-selected {
+ background: red;
+ }
+ </style>
+
+</head>
+<body>
+
+ <test-fixture id="inlineAttributes">
+ <template>
+ <iron-selector attr-for-selected="some-attr">
+ <div some-attr="value0">Item 0</div>
+ <div some-attr="value1">Item 1</div>
+ <div some-attr="value2">Item 2</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="reflectedProperties">
+ <template>
+ <iron-selector attr-for-selected="some-attr">
+ <attr-reflector>Item 0</attr-reflector>
+ <attr-reflector>Item 1</attr-reflector>
+ <attr-reflector>Item 2</attr-reflector>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="mixedPropertiesAndAttributes">
+ <template>
+ <iron-selector attr-for-selected="some-attr">
+ <attr-reflector>Item 0</attr-reflector>
+ <attr-reflector>Item 1</attr-reflector>
+ <div some-attr="value2">Item 2</div>
+ <div some-attr="value3">Item 3</div>
+ <attr-reflector>Item 4</attr-reflector>
+ <div some-attr="value5">Item 5</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="defaultAttribute">
+ <template>
+ <iron-selector attr-for-selected="some-attr" fallback-selection="default">
+ <div some-attr="value0">Item 0</div>
+ <div some-attr="value1">Item 1</div>
+ <div some-attr="default">Item 2</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('inline attributes', function() {
+ var selector;
+ var items;
+
+ setup(function () {
+ selector = fixture('inlineAttributes');
+ items = Array.prototype.slice.apply(selector.querySelectorAll('div[some-attr]'));
+ });
+
+ test('selecting value programatically selects correct item', function() {
+ selector.select('value1');
+ assert.equal(selector.selectedItem, items[1]);
+ });
+
+ test('selecting item sets the correct selected value', function(done) {
+ MockInteractions.downAndUp(items[2], function() {
+ assert.equal(selector.selected, 'value2');
+ done();
+ });
+ });
+ });
+
+ suite('reflected properties as attributes', function() {
+ var selector;
+ var items;
+
+ setup(function () {
+ selector = fixture('reflectedProperties');
+ items = Array.prototype.slice.apply(selector.querySelectorAll('attr-reflector'));
+ for (var i = 0; i < items.length; i++) {
+ items[i].someAttr = "value" + i;
+ }
+ });
+
+ test('selecting value programatically selects correct item', function() {
+ selector.select('value1');
+ assert.equal(selector.selectedItem, items[1]);
+ });
+
+ test('selecting item sets the correct selected value', function(done) {
+ MockInteractions.downAndUp(items[2], function() {
+ assert.equal(selector.selected, 'value2');
+ done();
+ });
+ });
+ });
+
+ suite('mixed properties and inline attributes', function() {
+ var selector;
+ var items;
+
+ setup(function () {
+ selector = fixture('mixedPropertiesAndAttributes');
+ items = Array.prototype.slice.apply(selector.querySelectorAll('attr-reflector, div[some-attr]'));
+ for (var i = 0; i < items.length; i++) {
+ items[i].someAttr = "value" + i;
+ }
+ });
+
+ test('selecting value programatically selects correct item', function() {
+ for (var i = 0; i < items.length; i++) {
+ selector.select('value' + i);
+ assert.equal(selector.selectedItem, items[i]);
+ }
+ });
+
+ test('selecting item sets the correct selected value', function(done) {
+ var i = 0;
+
+ function testSelectItem(i) {
+ if (i >= items.length) {
+ done();
+ return;
+ }
+
+ MockInteractions.downAndUp(items[i], function() {
+ assert.equal(selector.selected, 'value' + i);
+
+ testSelectItem(i + 1);
+ });
+ }
+
+ testSelectItem(i);
+ });
+ });
+
+ suite('default attribute', function() {
+ var selector;
+ var items;
+
+ setup(function () {
+ selector = fixture('defaultAttribute');
+ items = Array.prototype.slice.apply(selector.querySelectorAll('div[some-attr]'));
+ });
+
+ test('setting non-existing value sets default', function() {
+ selector.select('non-existing-value');
+ assert.equal(selector.selected, 'default');
+ assert.equal(selector.selectedItem, items[2]);
+ });
+
+ test('setting non-existing value sets default', function() {
+ selector.multi = true;
+ selector.select(['non-existing-value']);
+ assert.deepEqual(selector.selectedValues, ['default']);
+ assert.deepEqual(selector.selectedItems, [items[2]]);
+ });
+
+ test('default not used when there was at least one match', function() {
+ selector.multi = true;
+ selector.selectedValues = ['non-existing-value', 'value0'];
+ assert.deepEqual(selector.selectedValues, ['non-existing-value', 'value0']);
+ assert.deepEqual(selector.selectedItems, [items[0]]);
+ });
+
+ test('default element not found does not result in infinite loop', function() {
+ selector.fallbackSelection = 'non-existing-fallback';
+ selector.select('non-existing-value');
+ assert.equal(selector.selectedItem, undefined);
+ selector.multi = true;
+ selector.selectedValues = ['non-existing-value'];
+ assert.deepEqual(selector.selectedItems, [undefined]);
+ selector.fallbackSelection = 'default';
+ assert.deepEqual(selector.selectedItems, [items[2]]);
+ });
+
+ test('selection is updated after fallback is set', function() {
+ selector.fallbackSolution = undefined;
+ selector.select('non-existing-value');
+ selector.fallbackSelection = 'default';
+ assert.equal(selector.selectedItem, items[2]);
+ });
+
+ test('multi-selection is updated after fallback is set', function() {
+ selector.fallbackSolution = undefined;
+ selector.selectedValues = ['non-existing-value'];
+ selector.fallbackSolution = 'default';
+ assert.equal(selector.selectedItem, items[2]);
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/basic.html b/catapult/third_party/polymer/components/iron-selector/test/basic.html
new file mode 100644
index 00000000..08866bb3
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/basic.html
@@ -0,0 +1,263 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-basic</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-selector.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+
+ .my-selected {
+ background: red;
+ }
+ </style>
+
+</head>
+<body>
+
+ <test-fixture id="defaults">
+ <template>
+ <iron-selector>
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ <div>Item 4</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <br><br>
+
+ <test-fixture id="basic">
+ <template>
+ <iron-selector selected="item2" attr-for-selected="id">
+ <div id="item0">Item 0</div>
+ <div id="item1">Item 1</div>
+ <div id="item2">Item 2</div>
+ <div id="item3">Item 3</div>
+ <div id="item4">Item 4</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('defaults', function() {
+
+ var s1;
+
+ setup(function () {
+ s1 = fixture('defaults');
+ });
+
+ test('to nothing selected', function() {
+ assert.equal(s1.selected, null);
+ });
+
+ test('to iron-selected as selectedClass', function() {
+ assert.equal(s1.selectedClass, 'iron-selected');
+ });
+
+ test('to false as multi', function() {
+ assert.isFalse(s1.multi);
+ });
+
+ test('to tap as activateEvent', function() {
+ assert.equal(s1.activateEvent, 'tap');
+ });
+
+ test('to nothing as attrForSelected', function() {
+ assert.equal(s1.attrForSelected, null);
+ });
+
+ test('as many items as children', function() {
+ assert.equal(s1.items.length, s1.querySelectorAll('div').length);
+ });
+ });
+
+ suite('basic', function() {
+
+ var s2;
+
+ setup(function () {
+ s2 = fixture('basic');
+ });
+
+ test('honors the attrForSelected attribute', function(done) {
+ Polymer.Base.async(function() {
+ assert.equal(s2.attrForSelected, 'id');
+ assert.equal(s2.selected, 'item2');
+ assert.equal(s2.selectedItem, document.querySelector('#item2'));
+ done();
+ });
+ });
+
+ test('allows assignment to selected', function() {
+ // set selected
+ s2.selected = 'item4';
+ // check selected class
+ assert.isTrue(s2.children[4].classList.contains('iron-selected'));
+ // check item
+ assert.equal(s2.selectedItem, s2.children[4]);
+ });
+
+ test('fire iron-select when selected is set', function() {
+ // setup listener for iron-select event
+ var selectedEventCounter = 0;
+ s2.addEventListener('iron-select', function(e) {
+ selectedEventCounter++;
+ });
+ // set selected
+ s2.selected = 'item4';
+ // check iron-select event
+ assert.equal(selectedEventCounter, 1);
+ });
+
+ test('set selected to old value', function() {
+ // setup listener for iron-select event
+ var selectedEventCounter = 0;
+ s2.addEventListener('iron-select', function(e) {
+ selectedEventCounter++;
+ });
+ // selecting the same value shouldn't fire iron-select
+ s2.selected = 'item2';
+ assert.equal(selectedEventCounter, 0);
+ });
+
+ test('force synchronous item update', function() {
+ expect(s2.items.length).to.be.equal(5);
+ Polymer.dom(s2).appendChild(document.createElement('div'));
+ expect(s2.items.length).to.be.equal(5);
+ s2.forceSynchronousItemUpdate();
+ expect(s2.items.length).to.be.equal(6);
+ });
+
+ suite('`select()` and `selectIndex()`', function() {
+ test('`select()` selects an item with the given value', function() {
+ s2.select('item1');
+ assert.equal(s2.selected, 'item1');
+
+ s2.select('item3');
+ assert.equal(s2.selected, 'item3');
+
+ s2.select('item2');
+ assert.equal(s2.selected, 'item2');
+ });
+
+ test('`selectIndex()` selects an item with the given index', function() {
+ assert.equal(s2.selectedItem, undefined);
+
+ s2.selectIndex(1);
+ assert.equal(s2.selected, 'item1');
+ assert.equal(s2.selectedItem, s2.items[1]);
+
+ s2.selectIndex(3);
+ assert.equal(s2.selected, 'item3');
+ assert.equal(s2.selectedItem, s2.items[3]);
+
+ s2.selectIndex(4);
+ assert.equal(s2.selected, 'item4');
+ assert.equal(s2.selectedItem, s2.items[4]);
+ });
+ });
+
+ suite('items changing', function() {
+ var s1;
+
+ setup(function() {
+ s1 = fixture('defaults');
+ });
+
+ test('cause iron-items-changed to fire', function(done) {
+ var newItem = document.createElement('div');
+ var changeCount = 0;
+
+ newItem.id = 'item999';
+
+ s2.addEventListener('iron-items-changed', function(event) {
+ changeCount++;
+ var mutation = event.detail;
+ assert.notEqual(mutation, undefined);
+ assert.notEqual(mutation.addedNodes, undefined);
+ assert.notEqual(mutation.removedNodes, undefined);
+ });
+
+ Polymer.dom(s2).appendChild(newItem);
+
+ Polymer.Base.async(function() {
+ Polymer.dom(s2).removeChild(newItem);
+
+ Polymer.Base.async(function() {
+ expect(changeCount).to.be.equal(2);
+ done();
+ }, 1);
+ }, 1);
+ });
+
+ test('updates selected item', function(done) {
+ s1.addEventListener('iron-items-changed', function firstListener() {
+ s1.removeEventListener('iron-items-changed', firstListener);
+ var firstElementChild = Polymer.dom(s1).firstElementChild;
+ expect(firstElementChild).to.be.equal(s1.selectedItem);
+ expect(firstElementChild.classList.contains('iron-selected'))
+ .to.be.eql(true);
+ Polymer.dom(s1).removeChild(s1.selectedItem);
+
+ s1.addEventListener('iron-items-changed', function() {
+ firstElementChild = Polymer.dom(s1).firstElementChild;
+ expect(firstElementChild).to.be.equal(s1.selectedItem);
+ expect(firstElementChild.classList.contains('iron-selected'))
+ .to.be.eql(true);
+ done();
+ });
+ });
+ s1.selected = 0;
+ });
+ });
+
+ suite('dynamic selector', function() {
+ test('selects dynamically added child automatically', function(done) {
+ var selector = document.createElement('iron-selector');
+ var child = document.createElement('div');
+
+ selector.selected = '0';
+ child.textContent = 'Item 0';
+
+ Polymer.dom(selector).appendChild(child);
+ document.body.appendChild(selector);
+
+ Polymer.Base.async(function() {
+ assert.equal(child.className, 'iron-selected');
+ document.body.removeChild(selector);
+ done();
+ }, 1);
+ });
+ });
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/content-element.html b/catapult/third_party/polymer/components/iron-selector/test/content-element.html
new file mode 100644
index 00000000..7d1d6040
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/content-element.html
@@ -0,0 +1,44 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-selector.html">
+
+<dom-module id="test-content-element">
+
+ <template>
+
+ <iron-selector id="selector" selected="{{selected}}" selectable="[[selectable]]" attr-for-selected="id">
+ <content></content>
+ </iron-selector>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'test-content-element',
+
+ properties: {
+
+ selectable: String,
+
+ selected: {
+ type: String,
+ notify: true
+ }
+
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/content.html b/catapult/third_party/polymer/components/iron-selector/test/content.html
new file mode 100644
index 00000000..af27cf80
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/content.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-content</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="content-element.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+ </style>
+
+</head>
+<body>
+
+ <test-content-element id="selector1" selected="item0">
+ <div id="item0">item0</div>
+ <div id="item1">item1</div>
+ <div id="item2">item2</div>
+ <div id="item3">item3</div>
+ </test-content-element>
+
+ <test-content-element id="selector2" selected="item0" selectable="item">
+ <item id="item0">item0</item>
+ <hr>
+ <item id="item1">item1</item>
+ <item id="item2">item2</item>
+ <hr>
+ <item id="item3">item3</item>
+ </test-content-element>
+
+ <test-content-element id="selector3" selected="item0">
+ <template is="dom-repeat" id="t">
+ <div id$="[[item.name]]">[[item.name]]</div>
+ </template>
+ </test-content-element>
+
+ <script>
+
+ var s1 = document.querySelector('#selector1');
+ var s2 = document.querySelector('#selector2');
+ var s3 = document.querySelector('#selector3');
+
+ var t = document.querySelector('#t');
+
+ suite('content', function() {
+
+ test('attribute selected', function() {
+ // check selected class
+ assert.isTrue(s1.querySelector('#item0').classList.contains('iron-selected'));
+ });
+
+ test('set selected', function() {
+ // set selected
+ s1.selected = 'item1';
+ // check selected class
+ assert.isTrue(s1.querySelector('#item1').classList.contains('iron-selected'));
+ });
+
+ test('get items', function() {
+ assert.equal(s1.$.selector.items.length, 4);
+ });
+
+ test('activate event', function() {
+ var item = s1.querySelector('#item2');
+ item.dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ // check selected class
+ assert.isTrue(item.classList.contains('iron-selected'));
+ });
+
+ test('add item dynamically', function() {
+ var item = document.createElement('div');
+ item.id = 'item4';
+ item.textContent = 'item4';
+ Polymer.dom(s1).appendChild(item);
+ Polymer.dom.flush();
+ // set selected
+ s1.selected = 'item4';
+ // check items length
+ assert.equal(s1.$.selector.items.length, 5);
+ // check selected class
+ assert.isTrue(s1.querySelector('#item4').classList.contains('iron-selected'));
+ });
+
+ });
+
+ suite('content with selectable', function() {
+
+ test('attribute selected', function() {
+ // check selected class
+ assert.isTrue(s2.querySelector('#item0').classList.contains('iron-selected'));
+ });
+
+ test('set selected', function() {
+ // set selected
+ s2.selected = 'item1';
+ // check selected class
+ assert.isTrue(s2.querySelector('#item1').classList.contains('iron-selected'));
+ });
+
+ test('get items', function() {
+ assert.equal(s2.$.selector.items.length, 4);
+ });
+
+ test('activate event', function() {
+ var item = s2.querySelector('#item2');
+ item.dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ // check selected class
+ assert.isTrue(item.classList.contains('iron-selected'));
+ });
+
+ test('add item dynamically', function() {
+ var item = document.createElement('item');
+ item.id = 'item4';
+ item.textContent = 'item4';
+ Polymer.dom(s2).appendChild(item);
+ Polymer.dom.flush();
+ // set selected
+ s2.selected = 'item4';
+ // check items length
+ assert.equal(s2.$.selector.items.length, 5);
+ // check selected class
+ assert.isTrue(s2.querySelector('#item4').classList.contains('iron-selected'));
+ });
+
+ });
+
+ suite('content with dom-repeat', function() {
+
+ test('supports repeated children', function(done) {
+ t.items = [{name:'item0'}, {name: 'item1'}, {name: 'item2'}, {name: 'item3'}];
+ setTimeout(function() {
+ // check selected
+ assert.equal(s3.selected, 'item0');
+ // check selected class
+ assert.isTrue(s3.querySelector('#item0').classList.contains('iron-selected'));
+ // set selected
+ s3.selected = 'item2';
+ // check selected class
+ assert.isTrue(s3.querySelector('#item2').classList.contains('iron-selected'));
+ done();
+ });
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/excluded-local-names.html b/catapult/third_party/polymer/components/iron-selector/test/excluded-local-names.html
new file mode 100644
index 00000000..4b8d4257
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/excluded-local-names.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-selected-attribute</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-selector.html">
+</head>
+<body>
+
+ <test-fixture id="test1">
+ <template>
+ <iron-selector>
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <span>Item 3</span>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="test2">
+ <template>
+ <iron-selector>
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <p>Item 3</p>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('excluded local names', function() {
+
+ var test1, test2;
+
+ setup(function () {
+ test1 = fixture('test1');
+ test2 = fixture('test2');
+ });
+
+ test('default `_excludedLocalNames`', function() {
+ assert.isTrue('template' in test1._excludedLocalNames);
+ assert.isTrue('template' in test2._excludedLocalNames);
+ });
+
+ test('custom `_excludedLocalNames`', function() {
+ test1._excludedLocalNames.foo = 1;
+
+ assert.isTrue('foo' in test1._excludedLocalNames);
+ assert.isFalse('foo' in test2._excludedLocalNames);
+ });
+
+
+ test('items', function(done) {
+ test1._excludedLocalNames.span = 1;
+ test2._excludedLocalNames.div = 1;
+ test1._updateItems();
+ test2._updateItems();
+
+ Polymer.Base.async(function() {
+ var NOT_FOUND = -1;
+ var items1 = test1.items.map(function(el) { return el.localName; });
+ var items2 = test2.items.map(function(el) { return el.localName; });
+
+ assert.equal(items1.indexOf('span'), NOT_FOUND);
+ assert.equal(items2.indexOf('div'), NOT_FOUND);
+ done();
+ });
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/index.html b/catapult/third_party/polymer/components/iron-selector/test/index.html
new file mode 100644
index 00000000..3cfe559a
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+</head>
+<body>
+
+ <script>
+ WCT.loadSuites([
+ 'activate-event.html',
+ 'attr-for-selected.html',
+ 'basic.html',
+ 'multi.html',
+ 'next-previous.html',
+ 'numeric-ids.html',
+ 'selected-attribute.html',
+ 'template-repeat.html',
+ 'content.html',
+ 'excluded-local-names.html',
+ 'activate-event.html?dom=shadow',
+ 'basic.html?dom=shadow',
+ 'multi.html?dom=shadow',
+ 'next-previous.html?dom=shadow',
+ 'selected-attribute.html?dom=shadow',
+ 'template-repeat.html?dom=shadow',
+ 'content.html?dom=shadow',
+ 'excluded-local-names.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/multi.html b/catapult/third_party/polymer/components/iron-selector/test/multi.html
new file mode 100644
index 00000000..39b70b94
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/multi.html
@@ -0,0 +1,375 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-multi</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+ <link rel="import" href="../iron-selector.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+ </style>
+
+</head>
+<body>
+
+ <test-fixture id="test">
+ <template>
+ <iron-selector multi>
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ <div>Item 4</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="valueById">
+ <template>
+ <iron-selector multi attr-for-selected="id">
+ <div id="item0">Item 0</div>
+ <div id="item1">Item 1</div>
+ <div id="item2">Item 2</div>
+ <div id="item3">Item 3</div>
+ <div id="item4">Item 4</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <!--
+ NOTE(cdata): Enable test-fixture when polymer/polymer#2495 is resolved
+ -->
+ <!--<test-fixture id="repeatedItems">
+ <template>-->
+ <iron-selector multi id="repeatedItems">
+ <template is="dom-repeat" items='["foo", "bar", "baz"]'>
+ <div>[[item]]</div>
+ </template>
+ <div>vim</div>
+ </iron-selector>
+ <!--</template>
+ </test-fixture>-->
+
+ <script>
+
+ suite('multi', function() {
+
+ var s;
+
+ setup(function () {
+ s = fixture('test');
+ t = Polymer.dom(s).querySelector('[is="dom-repeat"]');
+ });
+
+ test('honors the multi attribute', function() {
+ assert.isTrue(s.multi);
+ });
+
+ test('has sane defaults', function() {
+ assert.equal(s.selectedValues, undefined);
+ assert.equal(s.selectedClass, 'iron-selected');
+ assert.equal(s.items.length, 5);
+ });
+
+ test('set multi-selection via selected property', function() {
+ // set selectedValues
+ s.selectedValues = [0, 2];
+ // check selected class
+ assert.isTrue(s.children[0].classList.contains('iron-selected'));
+ assert.isTrue(s.children[2].classList.contains('iron-selected'));
+ // check selectedItems
+ assert.equal(s.selectedItems.length, 2);
+ assert.equal(s.selectedItems[0], s.children[0]);
+ assert.equal(s.selectedItems[1], s.children[2]);
+ });
+
+ test('set multi-selection via tap', function() {
+ // set selectedValues
+ MockInteractions.tap(s.children[0]);
+ MockInteractions.tap(s.children[2]);
+ // check selected class
+ assert.isTrue(s.children[0].classList.contains('iron-selected'));
+ assert.isTrue(s.children[2].classList.contains('iron-selected'));
+ // check selectedItems
+ assert.equal(s.selectedItems.length, 2);
+ assert.equal(s.selectedItems[0], s.children[0]);
+ assert.equal(s.selectedItems[1], s.children[2]);
+ });
+
+ test('fire iron-select/deselect events when selectedValues changes', function() {
+ // setup listener for iron-select/deselect events
+ var items = [s.children[0], s.children[1], s.children[2]],
+ selectEventCounters = [0, 0, 0],
+ deselectEventCounters = [0, 0, 0];
+
+ s.addEventListener('iron-select', function(e) {
+ selectEventCounters[items.indexOf(e.detail.item)]++;
+ });
+ s.addEventListener('iron-deselect', function(e) {
+ deselectEventCounters[items.indexOf(e.detail.item)]++;
+ });
+
+ // programatically select values 0 and 1 (both fire select)
+ s.selectedValues = [0, 1];
+
+ // programatically select values 1 and 2 (2 fires select, 0 fires deselect)
+ s.selectedValues = [1, 2];
+
+ // programatically deselect all values (1 and 2 fire deselect)
+ s.selectedValues = [];
+
+ // check events
+ assert.equal(selectEventCounters[0], 1);
+ assert.equal(deselectEventCounters[0], 1);
+ assert.equal(selectEventCounters[1], 1);
+ assert.equal(deselectEventCounters[1], 1);
+ assert.equal(selectEventCounters[2], 1);
+ assert.equal(deselectEventCounters[2], 1);
+ });
+
+ test('fire iron-select/deselect events when selectedValues is modified', function() {
+ // setup listener for iron-select/deselect events
+ var items = [s.children[0], s.children[1], s.children[2]],
+ selectEventCounters = [0, 0, 0],
+ deselectEventCounters = [0, 0, 0];
+
+ s.addEventListener('iron-select', function(e) {
+ selectEventCounters[items.indexOf(e.detail.item)]++;
+ });
+ s.addEventListener('iron-deselect', function(e) {
+ deselectEventCounters[items.indexOf(e.detail.item)]++;
+ });
+
+ s.selectedValues = []
+
+ // programatically select value 0
+ s.push('selectedValues', 0, 1);
+
+ // programatically deselect value 0
+ s.shift('selectedValues');
+
+ // programatically select value 2
+ s.push('selectedValues', 2);
+
+ // programatically deselect value 1
+ s.shift('selectedValues');
+
+ assert.equal(selectEventCounters[0], 1);
+ assert.equal(deselectEventCounters[0], 1);
+ assert.equal(selectEventCounters[1], 1);
+ assert.equal(deselectEventCounters[1], 1);
+ assert.equal(selectEventCounters[2], 1);
+ assert.equal(deselectEventCounters[2], 0);
+ });
+
+ test('fire iron-select/deselect events when toggling items', function() {
+ // setup listener for iron-select/deselect events
+ var items = [s.children[0], s.children[1], s.children[2]],
+ selectEventCounters = [0, 0, 0],
+ deselectEventCounters = [0, 0, 0];
+
+ s.addEventListener('iron-select', function(e) {
+ selectEventCounters[items.indexOf(e.detail.item)]++;
+ });
+ s.addEventListener('iron-deselect', function(e) {
+ deselectEventCounters[items.indexOf(e.detail.item)]++;
+ });
+
+ // tap to select items 0 and 1 (both fire select)
+ MockInteractions.tap(items[0]);
+ MockInteractions.tap(items[1]);
+
+ // programatically select values 1 and 2 (2 fires select, 0 fires deselect)
+ s.selectedValues = [1, 2];
+
+ // tap to deselect items 1 and 2 (both fire deselect)
+ MockInteractions.tap(items[1]);
+ MockInteractions.tap(items[2]);
+
+ // check events
+ assert.equal(selectEventCounters[0], 1);
+ assert.equal(deselectEventCounters[0], 1);
+ assert.equal(selectEventCounters[1], 1);
+ assert.equal(deselectEventCounters[1], 1);
+ assert.equal(selectEventCounters[2], 1);
+ assert.equal(deselectEventCounters[2], 1);
+ });
+
+ test('toggle iron-selected class when toggling items selection', function() {
+ // setup listener for iron-item-select/deselect events
+ var item0 = s.children[0], item1 = s.children[1];
+
+ assert.isFalse(item0.classList.contains('iron-selected'));
+ assert.isFalse(item1.classList.contains('iron-selected'));
+
+ // tap to select item 0 (add iron-selected class)
+ MockInteractions.tap(item0);
+
+ assert.isTrue(item0.classList.contains('iron-selected'));
+ assert.isFalse(item1.classList.contains('iron-selected'));
+
+ // tap to select item 1 (add iron-selected class)
+ MockInteractions.tap(item1);
+
+ assert.isTrue(item0.classList.contains('iron-selected'));
+ assert.isTrue(item1.classList.contains('iron-selected'));
+
+ // tap to deselect item 1 (remove iron-selected class)
+ MockInteractions.tap(item1);
+
+ assert.isTrue(item0.classList.contains('iron-selected'));
+ assert.isFalse(item1.classList.contains('iron-selected'));
+
+ // programatically select both values (1 add iron-selected class)
+ s.selectedValues = [0, 1];
+
+ assert.isTrue(item0.classList.contains('iron-selected'));
+ assert.isTrue(item1.classList.contains('iron-selected'));
+
+ // programatically deselect all values (both removes iron-selected class)
+ s.selectedValues = [];
+
+ assert.isFalse(item0.classList.contains('iron-selected'));
+ assert.isFalse(item1.classList.contains('iron-selected'));
+ });
+
+ test('fires selected-values-changed when selection changes', function() {
+ var selectedValuesChangedEventCounter = 0;
+
+ s.addEventListener('selected-values-changed', function(e) {
+ selectedValuesChangedEventCounter++;
+ });
+
+ MockInteractions.tap(Polymer.dom(s).children[0]);
+ MockInteractions.tap(Polymer.dom(s).children[0]);
+ MockInteractions.tap(Polymer.dom(s).children[0]);
+
+ expect(selectedValuesChangedEventCounter);
+ });
+
+ test('selects from items created by dom-repeat', function(done) {
+ var selectEventCounter = 0;
+ var firstChild;
+
+ s = document.querySelector('#repeatedItems');
+ s.addEventListener('iron-select', function(e) {
+ selectEventCounter++;
+ });
+
+ // NOTE(cdata): I guess `dom-repeat` doesn't stamp synchronously..
+ Polymer.Base.async(function() {
+ firstChild = Polymer.dom(s).querySelector('div');
+ MockInteractions.tap(firstChild);
+
+ assert.equal(s.selectedItems[0].textContent, 'foo');
+ done();
+ });
+ });
+
+ test('updates selection when dom changes', function(done) {
+ var selectEventCounter = 0;
+
+ s = fixture('test');
+
+ Polymer.Base.async(function() {
+ var firstChild = Polymer.dom(s).querySelector(':first-child');
+ var lastChild = Polymer.dom(s).querySelector(':last-child');
+
+ MockInteractions.tap(firstChild);
+ MockInteractions.tap(lastChild);
+
+ expect(s.selectedItems.length).to.be.equal(2);
+
+ Polymer.dom(s).removeChild(lastChild);
+
+ Polymer.dom.flush();
+
+ expect(s.selectedItems.length).to.be.equal(1);
+ expect(s.selectedItems[0]).to.be.equal(firstChild);
+
+ done();
+ });
+
+ });
+
+ suite('`select()` and `selectIndex()`', function() {
+ var selector;
+
+ setup(function() {
+ selector = fixture('valueById');
+ });
+
+ test('`select()` selects an item with the given value', function() {
+ selector.select('item1');
+ assert.equal(selector.selectedValues.length, 1);
+ assert.equal(selector.selectedValues.indexOf('item1'), 0);
+
+ selector.select('item3');
+ assert.equal(selector.selectedValues.length, 2);
+ assert.isTrue(selector.selectedValues.indexOf('item3') >= 0);
+
+ selector.select('item2');
+ assert.equal(selector.selectedValues.length, 3);
+ assert.isTrue(selector.selectedValues.indexOf('item2') >= 0);
+ });
+
+ test('`selectIndex()` selects an item with the given index', function() {
+ selector.selectIndex(1);
+ assert.equal(selector.selectedValues.length, 1);
+ assert.isTrue(selector.selectedValues.indexOf('item1') >= 0);
+ assert.equal(selector.selectedItems.length, 1);
+ assert.isTrue(selector.selectedItems.indexOf(selector.items[1]) >= 0);
+
+ selector.selectIndex(3);
+ assert.equal(selector.selectedValues.length, 2);
+ assert.isTrue(selector.selectedValues.indexOf('item3') >= 0);
+ assert.equal(selector.selectedItems.length, 2);
+ assert.isTrue(selector.selectedItems.indexOf(selector.items[3]) >= 0);
+
+ selector.selectIndex(0);
+ assert.equal(selector.selectedValues.length, 3);
+ assert.isTrue(selector.selectedValues.indexOf('item0') >= 0);
+ assert.equal(selector.selectedItems.length, 3);
+ assert.isTrue(selector.selectedItems.indexOf(selector.items[0]) >= 0);
+ });
+ });
+
+ /* test('toggle multi from true to false', function() {
+ // set selected
+ s.selected = [0, 2];
+ var first = s.selected[0];
+ // set mutli to false, so to make it single-selection
+ s.multi = false;
+ // selected should not be an array
+ assert.isNotArray(s.selected);
+ // selected should be the first value from the old array
+ assert.equal(s.selected, first);
+ }); */
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/next-previous.html b/catapult/third_party/polymer/components/iron-selector/test/next-previous.html
new file mode 100644
index 00000000..e161c1b1
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/next-previous.html
@@ -0,0 +1,135 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-next-previous</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-selector.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+ </style>
+
+</head>
+<body>
+
+ <test-fixture id="test1">
+ <template>
+ <iron-selector selected="0">
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="test2">
+ <template>
+ <iron-selector selected="foo" attr-for-selected="name">
+ <div name="foo">Item Foo</div>
+ <div name="bar">Item Bar</div>
+ <div name="zot">Item Zot</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ var s;
+
+ function assertAndSelect(method, expectedIndex) {
+ assert.equal(s.selected, expectedIndex);
+ s[method]();
+ }
+
+ suite('next/previous', function() {
+
+ setup(function () {
+ s = fixture('test1');
+ });
+
+ test('selectNext', function() {
+ assert.equal(s.selected, 0);
+ assertAndSelect('selectNext', 0);
+ assertAndSelect('selectNext', 1);
+ assertAndSelect('selectNext', 2);
+ assert.equal(s.selected, 0);
+ });
+
+ test('selectPrevious', function() {
+ assert.equal(s.selected, 0);
+ assertAndSelect('selectPrevious', 0);
+ assertAndSelect('selectPrevious', 2);
+ assertAndSelect('selectPrevious', 1);
+ assert.equal(s.selected, 0);
+ });
+
+ test('selectNext/Previous', function() {
+ assert.equal(s.selected, 0);
+ assertAndSelect('selectNext', 0);
+ assertAndSelect('selectNext', 1);
+ assertAndSelect('selectPrevious', 2);
+ assertAndSelect('selectNext', 1);
+ assertAndSelect('selectPrevious', 2);
+ assert.equal(s.selected, 1);
+ });
+
+ });
+
+ suite('next/previous attrForSelected', function() {
+
+ setup(function () {
+ s = fixture('test2');
+ });
+
+ test('selectNext', function() {
+ assert.equal(s.selected, 'foo');
+ assertAndSelect('selectNext', 'foo');
+ assertAndSelect('selectNext', 'bar');
+ assertAndSelect('selectNext', 'zot');
+ assert.equal(s.selected, 'foo');
+ });
+
+ test('selectPrevious', function() {
+ assert.equal(s.selected, 'foo');
+ assertAndSelect('selectPrevious', 'foo');
+ assertAndSelect('selectPrevious', 'zot');
+ assertAndSelect('selectPrevious', 'bar');
+ assert.equal(s.selected, 'foo');
+ });
+
+ test('selectNext/Previous', function() {
+ assert.equal(s.selected, 'foo');
+ assertAndSelect('selectNext', 'foo');
+ assertAndSelect('selectNext', 'bar');
+ assertAndSelect('selectPrevious', 'zot');
+ assertAndSelect('selectNext', 'bar');
+ assertAndSelect('selectPrevious', 'zot');
+ assert.equal(s.selected, 'bar');
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/numeric-ids.html b/catapult/third_party/polymer/components/iron-selector/test/numeric-ids.html
new file mode 100644
index 00000000..f8b58242
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/numeric-ids.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<!--
+@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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-numeric-ids</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../iron-selector.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+ </style>
+
+</head>
+<body>
+
+ <template is="dom-bind">
+ <iron-selector id="selector" attr-for-selected="name">
+ <template id="t" is="dom-repeat">
+ <div name="[[item.id]]">{{item.name}}</div>
+ </template>
+ </iron-selector>
+ </template>
+
+ <script>
+
+ suite('select by a numeric property', function() {
+
+ var scope, s, t;
+
+ setup(function() {
+ scope = document.querySelector('template[is="dom-bind"]');
+ s = scope.$.selector;
+ t = scope.$.t;
+ t.items = [{ id: 0, name:'item0'}, {id: 1, name: 'item1'}, {id: 2, name: 'item2'}];
+ });
+
+ teardown(function() {
+ t.items = [];
+ });
+
+ test('select a value of zero', function() {
+ t.render();
+ s.selected = 1;
+ assert.equal(s.selected, '1');
+
+ // select item with a name value of 0
+ s.children[0].dispatchEvent(new CustomEvent('tap', {bubbles: true}));
+ assert.equal(s.selected, 0);
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/selected-attribute.html b/catapult/third_party/polymer/components/iron-selector/test/selected-attribute.html
new file mode 100644
index 00000000..35e7cece
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/selected-attribute.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-selected-attribute</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../iron-selector.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+ </style>
+
+</head>
+<body>
+
+ <test-fixture id="test">
+ <template>
+ <iron-selector id="selector">
+ <div>Item 0</div>
+ <div>Item 1</div>
+ <div>Item 2</div>
+ <div>Item 3</div>
+ <div>Item 4</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="test-attr-change">
+ <template>
+ <iron-selector id="selector" attr-for-selected="data-x" selected="x-1">
+ <div data-x="x-1" data-y="y-1">1</div>
+ <div data-x="x-2" data-y="y-2">2</div>
+ <div data-x="x-3" data-y="y-3">3</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="test-attr-change-multi">
+ <template>
+ <iron-selector multi id="selector"
+ attr-for-selected="data-x"
+ selected-values='["x-1","x-2"]'>
+ <div data-x="x-1" data-y="y-1">1</div>
+ <div data-x="x-2" data-y="y-2">2</div>
+ <div data-x="x-3" data-y="y-3">3</div>
+ </iron-selector>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('selected attributes', function() {
+
+ var s;
+
+ setup(function () {
+ s = fixture('test');
+ });
+
+ test('custom selectedAttribute', function() {
+ // set selectedAttribute
+ s.selectedAttribute = 'myattr';
+ // check selected attribute (should not be there)
+ assert.isFalse(s.children[4].hasAttribute('myattr'));
+ // set selected
+ s.selected = 4;
+ // now selected attribute should be there
+ assert.isTrue(s.children[4].hasAttribute('myattr'));
+ });
+
+ });
+
+ suite('changing attrForSelected', function() {
+
+ var s;
+
+ setup(function () {
+ s = fixture('test-attr-change');
+ });
+
+ test('changing selectedAttribute', function() {
+ Polymer.dom.flush();
+ s.attrForSelected = 'data-y';
+ assert.equal(s.selected, 'y-1');
+ });
+
+ });
+
+ suite('changing attrForSelected in multi', function() {
+
+ var s;
+
+ setup(function () {
+ s = fixture('test-attr-change-multi');
+ });
+
+ test('changing selectedAttribute', function() {
+ Polymer.dom.flush();
+ s.attrForSelected = 'data-y';
+ assert.equal(s.selectedValues.length, 2);
+ assert.equal(s.selectedValues[0],'y-1');
+ assert.equal(s.selectedValues[1],'y-2');
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-selector/test/template-repeat.html b/catapult/third_party/polymer/components/iron-selector/test/template-repeat.html
new file mode 100644
index 00000000..b1ab6a6f
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-selector/test/template-repeat.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <title>iron-selector-template-repeat</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../iron-selector.html">
+
+ <style>
+ .iron-selected {
+ background: #ccc;
+ }
+ </style>
+
+</head>
+<body>
+
+ <template is="dom-bind">
+ <iron-selector id="selector" selected="1">
+ <template id="t" is="dom-repeat">
+ <div id$="[[item.name]]">{{item.name}}</div>
+ </template>
+ </iron-selector>
+ </template>
+
+ <script>
+
+ suite('dom-repeat', function() {
+
+ var scope, s, t;
+
+ setup(function() {
+ scope = document.querySelector('template[is="dom-bind"]');
+ s = scope.$.selector;
+ t = scope.$.t;
+ t.items = [{name:'item0'}, {name: 'item1'}, {name: 'item2'}, {name: 'item3'}];
+ });
+
+ teardown(function() {
+ t.items = [];
+ });
+
+ test('supports repeated items', function() {
+ t.render();
+ // check items
+ assert.equal(s.items.length, 4);
+ // check selected
+ assert.equal(s.selected, 1);
+ // check selected item
+ var item = s.selectedItem;
+ assert.equal(s.items[1], item);
+ // check selected class
+ assert.isTrue(item.classList.contains('iron-selected'));
+ });
+
+ test('update items', function() {
+ t.render();
+ // check items
+ assert.equal(s.items.length, 4);
+ // check selected
+ assert.equal(s.selected, 1);
+ // update items
+ t.items = [{name:'foo'}, {name: 'bar'}];
+ t.render();
+ // check items
+ assert.equal(s.items.length, 2);
+ // check selected (should still honor the selected)
+ assert.equal(s.selected, 1);
+ // check selected class
+ assert.isTrue(s.querySelector('#bar').classList.contains('iron-selected'));
+ });
+
+ test('set selected to something else', function() {
+ t.render();
+ // set selected to something else
+ s.selected = 3;
+ // check selected item
+ var item = s.selectedItem;
+ assert.equal(s.items[3], item);
+ // check selected class
+ assert.isTrue(item.classList.contains('iron-selected'));
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/.bower.json b/catapult/third_party/polymer/components/iron-validatable-behavior/.bower.json
new file mode 100644
index 00000000..22ad7983
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/.bower.json
@@ -0,0 +1,42 @@
+{
+ "name": "iron-validatable-behavior",
+ "version": "1.1.2",
+ "description": "Provides a behavior for an element that validates user input",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "iron",
+ "behavior"
+ ],
+ "main": "iron-validatable-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-validatable-behavior.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-validatable-behavior",
+ "ignore": [],
+ "dependencies": {
+ "iron-meta": "PolymerElements/iron-meta#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "PolymerElements/paper-styles#^1.0.4",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.1.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.2",
+ "commit": "f5d85f8dea62d13176b14a4a25c206feb18432c5"
+ },
+ "_source": "https://github.com/PolymerElements/iron-validatable-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/iron-validatable-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/iron-validatable-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..a69a2fdc
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/iron-validatable-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/.gitignore b/catapult/third_party/polymer/components/iron-validatable-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/.travis.yml b/catapult/third_party/polymer/components/iron-validatable-behavior/.travis.yml
new file mode 100644
index 00000000..395e321c
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ kwLs1FMf/hBnbxItbOJzbIdGtPDK2R2dngleJPgXLW9ExzpGHPgOmWsvxWL2BzlxlzZgzwpmUhnnJdxHlo8AZtQl1uq6NWl/e3oj9oeenFugF60eIcd01ak4QmhBsk3/20O426QQsz2WckGZRGcudBz5Zj2it3t5cbH3pAi90jo=
+ - secure: >-
+ hGl3bsjz9dbKTA/bAgfdrFMlT6VawxFljIPqX6zJD556jhpYxtkno278eIW8uLlIGC5nXRTWXQzWrRhN8i9osnqw1RFhW8C9lH+TLHEkYMvDnEOp9sZ8Amv6j8FdNibcDP8IPTo2PmIRwBK1nFp0VqhcQu/Eq7WUFolSns9E27k=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/iron-validatable-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/README.md b/catapult/third_party/polymer/components/iron-validatable-behavior/README.md
new file mode 100644
index 00000000..6fff76e4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/README.md
@@ -0,0 +1,42 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+iron-validatable-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/iron-validatable-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/iron-validatable-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/iron-validatable-behavior)_
+
+
+##Polymer.IronValidatableBehavior
+
+`Use Polymer.IronValidatableBehavior` to implement an element that validates user input.
+Use the related `Polymer.IronValidatorBehavior` to add custom validation logic to an iron-input.
+
+By default, an `<iron-form>` element validates its fields when the user presses the submit button.
+To validate a form imperatively, call the form's `validate()` method, which in turn will
+call `validate()` on all its children. By using `Polymer.IronValidatableBehavior`, your
+custom element will get a public `validate()`, which
+will return the validity of the element, and a corresponding `invalid` attribute,
+which can be used for styling.
+
+To implement the custom validation logic of your element, you must override
+the protected `_getValidity()` method of this behaviour, rather than `validate()`.
+See [this](https://github.com/PolymerElements/iron-form/blob/master/demo/simple-element.html)
+for an example.
+
+### Accessibility
+
+Changing the `invalid` property, either manually or by calling `validate()` will update the
+`aria-invalid` attribute.
+
+
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/bower.json b/catapult/third_party/polymer/components/iron-validatable-behavior/bower.json
new file mode 100644
index 00000000..36f20c10
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "iron-validatable-behavior",
+ "version": "1.1.2",
+ "description": "Provides a behavior for an element that validates user input",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "iron",
+ "behavior"
+ ],
+ "main": "iron-validatable-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/iron-validatable-behavior.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/iron-validatable-behavior",
+ "ignore": [],
+ "dependencies": {
+ "iron-meta": "PolymerElements/iron-meta#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "paper-styles": "PolymerElements/paper-styles#^1.0.4",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/demo/cats-only.html b/catapult/third_party/polymer/components/iron-validatable-behavior/demo/cats-only.html
new file mode 100644
index 00000000..83ef9ba6
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/demo/cats-only.html
@@ -0,0 +1,46 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-validator-behavior/iron-validator-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'cats-only',
+
+ behaviors: [
+ Polymer.IronValidatorBehavior
+ ],
+
+ validateObject: function(obj) {
+ var valid = true;
+ for (key in obj) {
+ if (obj[key] !== 'cats') {
+ valid = false;
+ break;
+ }
+ }
+ return valid;
+ },
+
+ validate: function(values) {
+ if (typeof values === 'object') {
+ return this.validateObject(values);
+ } else {
+ var value = Array.isArray(values) ? values.join('') : values;
+ return value.match(/^(c|ca|cat|cats)?$/) !== null;
+ }
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/demo/index.html b/catapult/third_party/polymer/components/iron-validatable-behavior/demo/index.html
new file mode 100644
index 00000000..84b96a8d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/demo/index.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>iron-validatable-behavior demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="cats-only.html">
+ <link rel="import" href="validatable-input.html">
+
+ <style is="custom-style">
+
+ .valid {
+ color: var(--google-green-500);
+ }
+
+ .invalid {
+ color: var(--google-red-500);
+ }
+
+ </style>
+
+</head>
+<body>
+ <div class="vertical-section vertical-section-container centered">
+ <h1>&lt;iron-validatable-behavior&gt;</h1>
+
+ <template is="dom-bind">
+
+ <cats-only></cats-only>
+
+ <section>
+
+ <p>
+ only type <code>cats</code>:
+
+ <input is="validatable-input" invalid="{{invalid}}" validator="cats-only">
+
+ <span class="valid" hidden$="[[invalid]]">valid</span>
+ <span class="invalid" hidden$="[[!invalid]]">invalid</span>
+ </p>
+
+ </section>
+
+ </template>
+
+ </div>
+
+ <script>
+
+ document.querySelector('template[is="dom-bind"]').invalid = false;
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/demo/validatable-input.html b/catapult/third_party/polymer/components/iron-validatable-behavior/demo/validatable-input.html
new file mode 100644
index 00000000..19cf4775
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/demo/validatable-input.html
@@ -0,0 +1,46 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../iron-validatable-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'validatable-input',
+
+ extends: 'input',
+
+ properties: {
+
+ invalid: {
+ notify: true,
+ type: Boolean,
+ value: false
+ }
+
+ },
+
+ behaviors: [
+ Polymer.IronValidatableBehavior
+ ],
+
+ listeners: {
+ 'input': '_onInput'
+ },
+
+ _onInput: function(event) {
+ this.invalid = !this.validate(event.target.value);
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/index.html b/catapult/third_party/polymer/components/iron-validatable-behavior/index.html
new file mode 100644
index 00000000..cfaa5b17
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>iron-validatable-behavior</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/iron-validatable-behavior.html b/catapult/third_party/polymer/components/iron-validatable-behavior/iron-validatable-behavior.html
new file mode 100644
index 00000000..803731be
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/iron-validatable-behavior.html
@@ -0,0 +1,149 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-meta/iron-meta.html">
+
+<script>
+ /**
+ * Singleton IronMeta instance.
+ */
+ Polymer.IronValidatableBehaviorMeta = null;
+
+ /**
+ * `Use Polymer.IronValidatableBehavior` to implement an element that validates user input.
+ * Use the related `Polymer.IronValidatorBehavior` to add custom validation logic to an iron-input.
+ *
+ * By default, an `<iron-form>` element validates its fields when the user presses the submit button.
+ * To validate a form imperatively, call the form's `validate()` method, which in turn will
+ * call `validate()` on all its children. By using `Polymer.IronValidatableBehavior`, your
+ * custom element will get a public `validate()`, which
+ * will return the validity of the element, and a corresponding `invalid` attribute,
+ * which can be used for styling.
+ *
+ * To implement the custom validation logic of your element, you must override
+ * the protected `_getValidity()` method of this behaviour, rather than `validate()`.
+ * See [this](https://github.com/PolymerElements/iron-form/blob/master/demo/simple-element.html)
+ * for an example.
+ *
+ * ### Accessibility
+ *
+ * Changing the `invalid` property, either manually or by calling `validate()` will update the
+ * `aria-invalid` attribute.
+ *
+ * @demo demo/index.html
+ * @polymerBehavior
+ */
+ Polymer.IronValidatableBehavior = {
+
+ properties: {
+
+ /**
+ * Name of the validator to use.
+ */
+ validator: {
+ type: String
+ },
+
+ /**
+ * True if the last call to `validate` is invalid.
+ */
+ invalid: {
+ notify: true,
+ reflectToAttribute: true,
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * This property is deprecated and should not be used. Use the global
+ * validator meta singleton, `Polymer.IronValidatableBehaviorMeta` instead.
+ */
+ _validatorMeta: {
+ type: Object
+ },
+
+ /**
+ * Namespace for this validator. This property is deprecated and should
+ * not be used. For all intents and purposes, please consider it a
+ * read-only, config-time property.
+ */
+ validatorType: {
+ type: String,
+ value: 'validator'
+ },
+
+ _validator: {
+ type: Object,
+ computed: '__computeValidator(validator)'
+ }
+ },
+
+ observers: [
+ '_invalidChanged(invalid)'
+ ],
+
+ registered: function() {
+ Polymer.IronValidatableBehaviorMeta = new Polymer.IronMeta({type: 'validator'});
+ },
+
+ _invalidChanged: function() {
+ if (this.invalid) {
+ this.setAttribute('aria-invalid', 'true');
+ } else {
+ this.removeAttribute('aria-invalid');
+ }
+ },
+
+ /**
+ * @return {boolean} True if the validator `validator` exists.
+ */
+ hasValidator: function() {
+ return this._validator != null;
+ },
+
+ /**
+ * Returns true if the `value` is valid, and updates `invalid`. If you want
+ * your element to have custom validation logic, do not override this method;
+ * override `_getValidity(value)` instead.
+
+ * @param {Object} value The value to be validated. By default, it is passed
+ * to the validator's `validate()` function, if a validator is set.
+ * @return {boolean} True if `value` is valid.
+ */
+ validate: function(value) {
+ this.invalid = !this._getValidity(value);
+ return !this.invalid;
+ },
+
+ /**
+ * Returns true if `value` is valid. By default, it is passed
+ * to the validator's `validate()` function, if a validator is set. You
+ * should override this method if you want to implement custom validity
+ * logic for your element.
+ *
+ * @param {Object} value The value to be validated.
+ * @return {boolean} True if `value` is valid.
+ */
+
+ _getValidity: function(value) {
+ if (this.hasValidator()) {
+ return this._validator.validate(value);
+ }
+ return true;
+ },
+
+ __computeValidator: function() {
+ return Polymer.IronValidatableBehaviorMeta &&
+ Polymer.IronValidatableBehaviorMeta.byKey(this.validator);
+ }
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/test/cats-only.html b/catapult/third_party/polymer/components/iron-validatable-behavior/test/cats-only.html
new file mode 100644
index 00000000..f1669936
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/test/cats-only.html
@@ -0,0 +1,30 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-validator-behavior/iron-validator-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'cats-only',
+
+ behaviors: [
+ Polymer.IronValidatorBehavior
+ ],
+
+ validate: function(value) {
+ return value === 'cats';
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/test/dogs-only.html b/catapult/third_party/polymer/components/iron-validatable-behavior/test/dogs-only.html
new file mode 100644
index 00000000..1b462a41
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/test/dogs-only.html
@@ -0,0 +1,30 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-validator-behavior/iron-validator-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'dogs-only',
+
+ behaviors: [
+ Polymer.IronValidatorBehavior
+ ],
+
+ validate: function(value) {
+ return value === 'dogs';
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/test/index.html b/catapult/third_party/polymer/components/iron-validatable-behavior/test/index.html
new file mode 100644
index 00000000..605c95ad
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/test/index.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>iron-validatable-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ /* no tests */
+ WCT.loadSuites([
+ 'iron-validatable-behavior.html',
+ 'iron-validatable-behavior.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/test/iron-validatable-behavior.html b/catapult/third_party/polymer/components/iron-validatable-behavior/test/iron-validatable-behavior.html
new file mode 100644
index 00000000..a8040ef4
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/test/iron-validatable-behavior.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>iron-validatable-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="test-validatable.html">
+ <link rel="import" href="cats-only.html">
+ <link rel="import" href="dogs-only.html">
+
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <test-validatable></test-validatable>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="validators">
+ <template>
+ <cats-only></cats-only>
+ <dogs-only></dogs-only>
+ <test-validatable></test-validatable>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('setting `invalid` sets `aria-invalid=true`', function() {
+ var node = fixture('basic');
+ node.invalid = true;
+ assert.equal(node.getAttribute('aria-invalid'), 'true', 'aria-invalid is set');
+ node.invalid = false;
+ assert.isFalse(node.hasAttribute('aria-invalid'), 'aria-invalid is unset');
+ });
+
+ test('validate() is true if a validator isn\'t set', function() {
+ var node = fixture('basic');
+ var valid = node.validate();
+ assert.isTrue(valid);
+ });
+
+ test('changing the validator works', function() {
+ var node = fixture('validators');
+ var input = node[2];
+
+ // Initially there's no validator, so everything is valid.
+ assert.isTrue(input.validate(''));
+ assert.isTrue(input.validate('cats'));
+
+ // Only valid if the value is 'cats'.
+ input.validator = 'cats-only';
+ assert.isFalse(input.validate('ca'));
+ assert.isTrue(input.validate('cats'));
+
+ // Only valid if the value is 'dogs'.
+ input.validator = 'dogs-only';
+ assert.isFalse(input.validate('cats'));
+ assert.isTrue(input.validate('dogs'));
+ });
+
+ });
+
+ </script>
+
+</body>
diff --git a/catapult/third_party/polymer/components/iron-validatable-behavior/test/test-validatable.html b/catapult/third_party/polymer/components/iron-validatable-behavior/test/test-validatable.html
new file mode 100644
index 00000000..c4af269d
--- /dev/null
+++ b/catapult/third_party/polymer/components/iron-validatable-behavior/test/test-validatable.html
@@ -0,0 +1,29 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../iron-validatable-behavior.html">
+
+<dom-module id="test-validatable">
+ <template>
+ <content></content>
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'test-validatable',
+
+ behaviors: [
+ Polymer.IronValidatableBehavior
+ ]
+
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/.bower.json b/catapult/third_party/polymer/components/neon-animation/.bower.json
new file mode 100644
index 00000000..3eeee660
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/.bower.json
@@ -0,0 +1,61 @@
+{
+ "name": "neon-animation",
+ "description": "A system for animating Polymer-based web components",
+ "version": "1.2.5",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "web-animations"
+ ],
+ "main": [
+ "neon-animated-pages.html",
+ "neon-animatable-behavior.html",
+ "neon-animation-behavior.html",
+ "neon-animation-runner-behavior.html",
+ "neon-shared-element-animatable-behavior.html",
+ "neon-shared-element-animation-behavior.html",
+ "neon-animatable.html",
+ "neon-animations.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/neon-animation"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/neon-animation",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-meta": "PolymerElements/iron-meta#^1.0.0",
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "iron-selector": "PolymerElements/iron-selector#^1.0.0",
+ "web-animations-js": "web-animations/web-animations-js#^2.2.0"
+ },
+ "devDependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-toolbar": "PolymerElements/paper-toolbar#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0"
+ },
+ "_release": "1.2.5",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.5",
+ "commit": "9af00bc65118e0806cdfa5a5e1ea5fb155147016"
+ },
+ "_source": "https://github.com/PolymerElements/neon-animation.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/neon-animation"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/neon-animation/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/neon-animation/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..08943a1e
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/neon-animation/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/neon-animation/.gitignore b/catapult/third_party/polymer/components/neon-animation/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/neon-animation/.travis.yml b/catapult/third_party/polymer/components/neon-animation/.travis.yml
new file mode 100644
index 00000000..52ede66d
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+env:
+ global:
+ - secure: >-
+ coZBjQTrTsejOS2utVzo0hhyYRF40H2KoN30G44tRxVKV/0orSm1HJbvG2YDb/tmAFtEObJmNlYonu1I3G9RVncn9y69nRf8BjLHrpOIhNvesug7kPUuprj5KRoLo9O0UyIYmUh6i4YGt1aAeR/Wq/XVifSNIXnAVguzntbSmqvWJdqj8+FHmNzyo5269+tnOzTruXByrUcSZnvNgHjCYFZOY00a29cyK6fTdd0Qbzi9cvhEDfx1j6uXzYah7bdA8caunW1HvxwI+jiV8yEVySB7eku+SHA+8JJUIIZEL1iCASAqqVbTUoOxtfgbTXQRJdVAuDNj1J+AqFVtuJpJl4xvX97dF9FVQvgyLW9fpeb7rojdU0TSazBpVi7LLvocQjofp8bbAtbJ8Z0BN+/3DgM33uDTF8DeBGj2WmR+49F2oPnYh7FwjhbiOVqkOKyolqrRpK0PYczVdHusR6+cONkGcLzO9G5nakWzE9diJSW+nw9qD3bVL99ZzHK+6U7+R75KJdxj0V7VA1lkvWRlchxXrpsEoM0AQEUf97LsvkTpXPQ4Wf9XkCxFRAicSTeDijLCPy3ti0L6D6x9sBZwV5d3ODnkdxCAy2aY7g2YEWuuBDZC+W1ggQ3Xe705GwbBNk22HikA6XOVX9lb3xm7SnenfDGkFhNDzRijoGg96jU=
+ - secure: >-
+ hJ6VMaJqDh5HPdeERIqoOcdWZJTAOTeS/Lr6FLCWEOOvYPxwRi/L1pKbVuYf2ugeS2Z0TEvAS7RdfjRuC+NGFhsmpnpBlxAyJhVKcXrhHKVtoFK04xaCD2wDMkPaZiDZQRGF/H6/PyuoB4Tqss3aN/DC9eSMwY9HFfoeYRIwiJeb8Kq09pBtvoxU151VQt+jlmyCVw7rhNDTAazaCGKicgvbAtxSJ6xNswNfqOrtCYB+WW5CpUE+qqTjjbistSQJu3hjk2UwFMLJZXtJWdkcD88vNdHy3mtNZQNtE8x+ckNVqgJ98DyAN7HLUU0GvzTOiOGFfwr+bpl6Td8klR2YoQOW4ypluAetcDN9X7tKOxDQaaI8AyPLXPWi9JcESEGAeLzpfkWZ6clrQmqfLdVDsqYKPldF9m7Ozjdhq40r3qFdQAdEI29dBOKOalaZXgWnvU2wu5FBtWnBMSk89zOYC5wGR0E+wOMMMRu90LuMQY40Myhqso5iUFL5qhrn41I8ZfFi0EyOgEeKzYH3zrdo7/msEkCXxI0SKfG1CGSQSOT2luO6Xu6xIuf7qoWYKJiJQ40ObsRKn0cA4GQpNZe/dgKMRH2zzfKW6d2BSAsZGTDJG14XcDi9d/zz7djgboMjc/A6sH8L3c8BkbUa4wuyJRNu7VBZQHq5yrnHpxC6SbA=
+dist: trusty
diff --git a/catapult/third_party/polymer/components/neon-animation/CONTRIBUTING.md b/catapult/third_party/polymer/components/neon-animation/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/neon-animation/README.md b/catapult/third_party/polymer/components/neon-animation/README.md
new file mode 100644
index 00000000..29c07539
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/README.md
@@ -0,0 +1,306 @@
+# neon-animation
+
+`neon-animation` is a suite of elements and behaviors to implement pluggable animated transitions for Polymer Elements using [Web Animations](https://w3c.github.io/web-animations/).
+
+*Warning: The API may change.*
+
+* [A basic animatable element](#basic)
+* [Animation configuration](#configuration)
+ * [Animation types](#configuration-types)
+ * [Configuration properties](#configuration-properties)
+ * [Using multiple animations](#configuration-multiple)
+ * [Running animations encapsulated in children nodes](#configuration-encapsulation)
+* [Page transitions](#page-transitions)
+ * [Shared element animations](#shared-element)
+ * [Declarative page transitions](#declarative-page)
+* [Included animations](#animations)
+* [Demos](#demos)
+
+<a name="basic"></a>
+## A basic animatable element
+
+Elements that can be animated should implement the `Polymer.NeonAnimatableBehavior` behavior, or `Polymer.NeonAnimationRunnerBehavior` if they're also responsible for running an animation.
+
+```js
+Polymer({
+ is: 'my-animatable',
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+ properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ // provided by neon-animation/animations/scale-down-animation.html
+ name: 'scale-down-animation',
+ node: this
+ }
+ }
+ }
+ },
+ listeners: {
+ // this event is fired when the animation finishes
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+ animate: function() {
+ // run scale-down-animation
+ this.playAnimation();
+ },
+ _onNeonAnimationFinish: function() {
+ console.log('animation done!');
+ }
+});
+```
+
+[Live demo](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/doc/basic.html)
+
+<a name="configuration"></a>
+## Animation configuration
+
+<a name="configuration-types"></a>
+### Animation types
+
+An element might run different animations, for example it might do something different when it enters the view and when it exits from view. You can set the `animationConfig` property to a map from an animation type to configuration.
+
+```js
+Polymer({
+ is: 'my-dialog',
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+ properties: {
+ opened: {
+ type: Boolean
+ },
+ animationConfig: {
+ value: function() {
+ return {
+ 'entry': {
+ // provided by neon-animation/animations/scale-up-animation.html
+ name: 'scale-up-animation',
+ node: this
+ },
+ 'exit': {
+ // provided by neon-animation/animations/fade-out-animation.html
+ name: 'fade-out-animation',
+ node: this
+ }
+ }
+ }
+ }
+ },
+ listeners: {
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+ show: function() {
+ this.opened = true;
+ this.style.display = 'inline-block';
+ // run scale-up-animation
+ this.playAnimation('entry');
+ },
+ hide: function() {
+ this.opened = false;
+ // run fade-out-animation
+ this.playAnimation('exit');
+ },
+ _onNeonAnimationFinish: function() {
+ if (!this.opened) {
+ this.style.display = 'none';
+ }
+ }
+});
+```
+
+[Live demo](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/doc/types.html)
+
+You can also use the convenience properties `entryAnimation` and `exitAnimation` to set `entry` and `exit` animations:
+
+```js
+properties: {
+ entryAnimation: {
+ value: 'scale-up-animation'
+ },
+ exitAnimation: {
+ value: 'fade-out-animation'
+ }
+}
+```
+
+<a name="configuration-properties"></a>
+### Configuration properties
+
+You can pass additional parameters to configure an animation in the animation configuration object.
+All animations should accept the following properties:
+
+ * `name`: The name of an animation, ie. an element implementing `Polymer.NeonAnimationBehavior`.
+ * `node`: The target node to apply the animation to. Defaults to `this`.
+ * `timing`: Timing properties to use in this animation. They match the [Web Animations Animation Effect Timing interface](https://w3c.github.io/web-animations/#the-animationeffecttiming-interface). The
+ properties include the following:
+ * `duration`: The duration of the animation in milliseconds.
+ * `delay`: The delay before the start of the animation in milliseconds.
+ * `easing`: A timing function for the animation. Matches the CSS timing function values.
+
+Animations may define additional configuration properties and they are listed in their documentation.
+
+<a name="configuration-multiple"></a>
+### Using multiple animations
+
+Set the animation configuration to an array to combine animations, like this:
+
+```js
+animationConfig: {
+ value: function() {
+ return {
+ // fade-in-animation is run with a 50ms delay from slide-down-animation
+ 'entry': [{
+ name: 'slide-down-animation',
+ node: this
+ }, {
+ name: 'fade-in-animation',
+ node: this,
+ timing: {delay: 50}
+ }]
+ }
+ }
+}
+```
+
+<a name="configuration-encapsulation"></a>
+### Running animations encapsulated in children nodes
+
+You can include animations in the configuration that are encapsulated in a child element that implement `Polymer.NeonAnimatableBehavior` with the `animatable` property.
+
+```js
+animationConfig: {
+ value: function() {
+ return {
+ // run fade-in-animation on this, and the entry animation on this.$.myAnimatable
+ 'entry': [
+ {name: 'fade-in-animation', node: this},
+ {animatable: this.$.myAnimatable, type: 'entry'}
+ ]
+ }
+ }
+}
+```
+
+<a name="page-transitions"></a>
+## Page transitions
+
+*The artist formerly known as `<core-animated-pages>`*
+
+The `neon-animated-pages` element manages a set of pages to switch between, and runs animations between the page transitions. It implements the `Polymer.IronSelectableBehavior` behavior. Each child node should implement `Polymer.NeonAnimatableBehavior` and define the `entry` and `exit` animations. During a page transition, the `entry` animation is run on the new page and the `exit` animation is run on the old page.
+
+<a name="shared-element"></a>
+### Shared element animations
+
+Shared element animations work on multiple nodes. For example, a "hero" animation is used during a page transition to make two elements from separate pages appear to animate as a single element. Shared element animation configurations have an `id` property that identify they belong in the same animation. Elements containing shared elements also have a `sharedElements` property defines a map from `id` to element, the element involved with the animation.
+
+In the incoming page:
+
+```js
+properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ // the incoming page defines the 'entry' animation
+ 'entry': {
+ name: 'hero-animation',
+ id: 'hero',
+ toPage: this
+ }
+ }
+ }
+ },
+ sharedElements: {
+ value: function() {
+ return {
+ 'hero': this.$.hero
+ }
+ }
+ }
+}
+```
+
+In the outgoing page:
+
+```js
+properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ // the outgoing page defines the 'exit' animation
+ 'exit': {
+ name: 'hero-animation',
+ id: 'hero',
+ fromPage: this
+ }
+ }
+ }
+ },
+ sharedElements: {
+ value: function() {
+ return {
+ 'hero': this.$.otherHero
+ }
+ }
+ }
+}
+```
+
+<a name="declarative-page"></a>
+### Declarative page transitions
+
+For convenience, if you define the `entry-animation` and `exit-animation` attributes on `<neon-animated-pages>`, those animations will apply for all page transitions.
+
+For example:
+
+```js
+<neon-animated-pages id="pages" class="flex" selected="[[selected]]" entry-animation="slide-from-right-animation" exit-animation="slide-left-animation">
+ <neon-animatable>1</neon-animatable>
+ <neon-animatable>2</neon-animatable>
+ <neon-animatable>3</neon-animatable>
+ <neon-animatable>4</neon-animatable>
+ <neon-animatable>5</neon-animatable>
+</neon-animated-pages>
+```
+
+The new page will slide in from the right, and the old page slide away to the left.
+
+<a name="animations"></a>
+## Included animations
+
+Single element animations:
+
+ * `fade-in-animation` Animates opacity from `0` to `1`;
+ * `fade-out-animation` Animates opacity from `1` to `0`;
+ * `scale-down-animation` Animates transform from `scale(1)` to `scale(0)`;
+ * `scale-up-animation` Animates transform from `scale(0)` to `scale(1)`;
+ * `slide-down-animation` Animates transform from `none` to `translateY(100%)`;
+ * `slide-up-animation` Animates transform from `none` to `translateY(-100%)`;
+ * `slide-from-top-animation` Animates transform from `translateY(-100%)` to `none`;
+ * `slide-from-bottom-animation` Animates transform from `translateY(100%)` to `none`;
+ * `slide-left-animation` Animates transform from `none` to `translateX(-100%)`;
+ * `slide-right-animation` Animates transform from `none` to `translateX(100%)`;
+ * `slide-from-left-animation` Animates transform from `translateX(-100%)` to `none`;
+ * `slide-from-right-animation` Animates transform from `translateX(100%)` to `none`;
+ * `transform-animation` Animates a custom transform.
+
+Note that there is a restriction that only one transform animation can be applied on the same element at a time. Use the custom `transform-animation` to combine transform properties.
+
+Shared element animations
+
+ * `hero-animation` Animates an element such that it looks like it scales and transforms from another element.
+ * `ripple-animation` Animates an element to full screen such that it looks like it ripples from another element.
+
+Group animations
+ * `cascaded-animation` Applys an animation to an array of elements with a delay between each.
+
+<a name="demos"></a>
+## Demos
+
+ * [Grid to full screen](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/grid/index.html)
+ * [Animation on load](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/load/index.html)
+ * [List item to detail](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/list/index.html) (For narrow width)
+ * [Dots to squares](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/tiles/index.html)
+ * [Declarative](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/declarative/index.html)
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/cascaded-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/cascaded-animation.html
new file mode 100644
index 00000000..53997533
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/cascaded-animation.html
@@ -0,0 +1,95 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<cascaded-animation>` applies an animation on an array of elements with a delay between each.
+the delay defaults to 50ms.
+
+Configuration:
+```
+{
+ name: 'cascaded-animation',
+ animation: <animation-name>,
+ nodes: <array-of-nodes>,
+ nodeDelay: <node-delay-in-ms>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'cascaded-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ /**
+ * @param {{
+ * animation: string,
+ * nodes: !Array<!Element>,
+ * nodeDelay: (number|undefined),
+ * timing: (Object|undefined)
+ * }} config
+ */
+ configure: function(config) {
+ this._animations = [];
+ var nodes = config.nodes;
+ var effects = [];
+ var nodeDelay = config.nodeDelay || 50;
+
+ config.timing = config.timing || {};
+ config.timing.delay = config.timing.delay || 0;
+
+ var oldDelay = config.timing.delay;
+ var abortedConfigure;
+ for (var node, index = 0; node = nodes[index]; index++) {
+ config.timing.delay += nodeDelay;
+ config.node = node;
+
+ var animation = document.createElement(config.animation);
+ if (animation.isNeonAnimation) {
+ var effect = animation.configure(config);
+
+ this._animations.push(animation);
+ effects.push(effect);
+ } else {
+ console.warn(this.is + ':', config.animation, 'not found!');
+ abortedConfigure = true;
+ break;
+ }
+ }
+ config.timing.delay = oldDelay;
+ config.node = null;
+ // if a bad animation was configured, abort config.
+ if (abortedConfigure) {
+ return;
+ }
+
+ this._effect = new GroupEffect(effects);
+ return this._effect;
+ },
+
+ complete: function() {
+ for (var animation, index = 0; animation = this._animations[index]; index++) {
+ animation.complete(animation.config);
+ }
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/fade-in-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/fade-in-animation.html
new file mode 100644
index 00000000..cdb74e30
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/fade-in-animation.html
@@ -0,0 +1,49 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<fade-in-animation>` animates the opacity of an element from 0 to 1.
+
+Configuration:
+```
+{
+ name: 'fade-in-animation',
+ node: <node>
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'fade-in-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+ this._effect = new KeyframeEffect(node, [
+ {'opacity': '0'},
+ {'opacity': '1'}
+ ], this.timingFromConfig(config));
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/fade-out-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/fade-out-animation.html
new file mode 100644
index 00000000..82cc3997
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/fade-out-animation.html
@@ -0,0 +1,49 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<fade-out-animation>` animates the opacity of an element from 1 to 0.
+
+Configuration:
+```
+{
+ name: 'fade-out-animation',
+ node: <node>
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'fade-out-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+ this._effect = new KeyframeEffect(node, [
+ {'opacity': '1'},
+ {'opacity': '0'}
+ ], this.timingFromConfig(config));
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/hero-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/hero-animation.html
new file mode 100644
index 00000000..176bee78
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/hero-animation.html
@@ -0,0 +1,83 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-shared-element-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<hero-animation>` is a shared element animation that scales and transform an element such that it
+appears to be shared between two pages. Use this in `<neon-animated-pages>`. The source page
+should use this animation in an 'exit' animation and set the `fromPage` configuration property to
+itself, and the destination page should use this animation in an `entry` animation and set the
+`toPage` configuration property to itself. They should also define the hero elements in the
+`sharedElements` property (not a configuration property, see
+`Polymer.NeonSharedElementAnimatableBehavior`).
+
+Configuration:
+```
+{
+ name: 'hero-animation',
+ id: <shared-element-id>,
+ timing: <animation-timing>,
+ toPage: <node>, /* define for the destination page */
+ fromPage: <node>, /* define for the source page */
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'hero-animation',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var shared = this.findSharedElements(config);
+ if (!shared) {
+ return;
+ }
+
+ var fromRect = shared.from.getBoundingClientRect();
+ var toRect = shared.to.getBoundingClientRect();
+
+ var deltaLeft = fromRect.left - toRect.left;
+ var deltaTop = fromRect.top - toRect.top;
+ var deltaWidth = fromRect.width / toRect.width;
+ var deltaHeight = fromRect.height / toRect.height;
+
+ this._effect = new KeyframeEffect(shared.to, [
+ {'transform': 'translate(' + deltaLeft + 'px,' + deltaTop + 'px) scale(' + deltaWidth + ',' + deltaHeight + ')'},
+ {'transform': 'none'}
+ ], this.timingFromConfig(config));
+
+ this.setPrefixedProperty(shared.to, 'transformOrigin', '0 0');
+ shared.to.style.zIndex = 10000;
+ shared.from.style.visibility = 'hidden';
+
+ return this._effect;
+ },
+
+ complete: function(config) {
+ var shared = this.findSharedElements(config);
+ if (!shared) {
+ return null;
+ }
+ shared.to.style.zIndex = '';
+ shared.from.style.visibility = '';
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/opaque-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/opaque-animation.html
new file mode 100644
index 00000000..e3a00725
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/opaque-animation.html
@@ -0,0 +1,46 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<opaque-animation>` makes an element `opacity:1` for the duration of the animation. Used to prevent
+webkit/safari from drawing a frame before an animation for elements that animate from display:none.
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'opaque-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+ this._effect = new KeyframeEffect(node, [
+ {'opacity': '1'},
+ {'opacity': '1'}
+ ], this.timingFromConfig(config));
+ node.style.opacity = '0';
+ return this._effect;
+ },
+
+ complete: function(config) {
+ config.node.style.opacity = '';
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/reverse-ripple-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/reverse-ripple-animation.html
new file mode 100644
index 00000000..f1f9de96
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/reverse-ripple-animation.html
@@ -0,0 +1,87 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-shared-element-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<reverse-ripple-animation>` scales and transform an element such that it appears to ripple down from this element, to either
+a shared element, or a screen position.
+
+If using as a shared element animation in `<neon-animated-pages>`, use this animation in an `exit`
+animation in the source page and in an `entry` animation in the destination page. Also, define the
+reverse-ripple elements in the `sharedElements` property (not a configuration property, see
+`Polymer.NeonSharedElementAnimatableBehavior`).
+If using a screen position, define the `gesture` property.
+Configuration:
+```
+{
+ name: 'reverse-ripple-animation`.
+ id: <shared-element-id>, /* set this or gesture */
+ gesture: {x: <page-x>, y: <page-y>}, /* set this or id */
+ timing: <animation-timing>,
+ toPage: <node>, /* define for the destination page */
+ fromPage: <node>, /* define for the source page */
+}
+```
+-->
+
+<script>
+ Polymer({
+ is: 'reverse-ripple-animation',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var shared = this.findSharedElements(config);
+ if (!shared) {
+ return null;
+ }
+
+ var translateX, translateY;
+ var fromRect = shared.from.getBoundingClientRect();
+ if (config.gesture) {
+ translateX = config.gesture.x - (fromRect.left + (fromRect.width / 2));
+ translateY = config.gesture.y - (fromRect.top + (fromRect.height / 2));
+ } else {
+ var toRect = shared.to.getBoundingClientRect();
+ translateX = (toRect.left + (toRect.width / 2)) - (fromRect.left + (fromRect.width / 2));
+ translateY = (toRect.top + (toRect.height / 2)) - (fromRect.top + (fromRect.height / 2));
+ }
+ var translate = 'translate(' + translateX + 'px,' + translateY + 'px)';
+
+ var size = Math.max(fromRect.width + Math.abs(translateX) * 2, fromRect.height + Math.abs(translateY) * 2);
+ var diameter = Math.sqrt(2 * size * size);
+ var scaleX = diameter / fromRect.width;
+ var scaleY = diameter / fromRect.height;
+ var scale = 'scale(' + scaleX + ',' + scaleY + ')';
+
+ this._effect = new KeyframeEffect(shared.from, [
+ {'transform': translate + ' ' + scale},
+ {'transform': translate + ' scale(0)'}
+ ], this.timingFromConfig(config));
+
+ this.setPrefixedProperty(shared.from, 'transformOrigin', '50% 50%');
+ shared.from.style.borderRadius = '50%';
+
+ return this._effect;
+ },
+
+ complete: function() {
+ if (this.sharedElements) {
+ this.setPrefixedProperty(this.sharedElements.from, 'transformOrigin', '');
+ this.sharedElements.from.style.borderRadius = '';
+ }
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/ripple-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/ripple-animation.html
new file mode 100644
index 00000000..d97186c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/ripple-animation.html
@@ -0,0 +1,93 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-shared-element-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<ripple-animation>` scales and transform an element such that it appears to ripple from either
+a shared element, or from a screen position, to full screen.
+
+If using as a shared element animation in `<neon-animated-pages>`, use this animation in an `exit`
+animation in the source page and in an `entry` animation in the destination page. Also, define the
+hero elements in the `sharedElements` property (not a configuration property, see
+`Polymer.NeonSharedElementAnimatableBehavior`).
+
+If using a screen position, define the `gesture` property.
+
+Configuration:
+```
+{
+ name: 'ripple-animation`.
+ id: <shared-element-id>, /* set this or gesture */
+ gesture: {x: <page-x>, y: <page-y>}, /* set this or id */
+ timing: <animation-timing>,
+ toPage: <node>, /* define for the destination page */
+ fromPage: <node>, /* define for the source page */
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'ripple-animation',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var shared = this.findSharedElements(config);
+ if (!shared) {
+ return null;
+ }
+
+ var translateX, translateY;
+ var toRect = shared.to.getBoundingClientRect();
+ if (config.gesture) {
+ translateX = config.gesture.x - (toRect.left + (toRect.width / 2));
+ translateY = config.gesture.y - (toRect.top + (toRect.height / 2));
+ } else {
+ var fromRect = shared.from.getBoundingClientRect();
+ translateX = (fromRect.left + (fromRect.width / 2)) - (toRect.left + (toRect.width / 2));
+ translateY = (fromRect.top + (fromRect.height / 2)) - (toRect.top + (toRect.height / 2));
+ }
+ var translate = 'translate(' + translateX + 'px,' + translateY + 'px)';
+
+ var size = Math.max(toRect.width + Math.abs(translateX) * 2, toRect.height + Math.abs(translateY) * 2);
+ var diameter = Math.sqrt(2 * size * size);
+ var scaleX = diameter / toRect.width;
+ var scaleY = diameter / toRect.height;
+ var scale = 'scale(' + scaleX + ',' + scaleY + ')';
+
+ this._effect = new KeyframeEffect(shared.to, [
+ {'transform': translate + ' scale(0)'},
+ {'transform': translate + ' ' + scale}
+ ], this.timingFromConfig(config));
+
+ this.setPrefixedProperty(shared.to, 'transformOrigin', '50% 50%');
+ shared.to.style.borderRadius = '50%';
+
+ return this._effect;
+ },
+
+ complete: function() {
+ if (this.sharedElements) {
+ this.setPrefixedProperty(this.sharedElements.to, 'transformOrigin', '');
+ this.sharedElements.to.style.borderRadius = '';
+ }
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/scale-down-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/scale-down-animation.html
new file mode 100644
index 00000000..1dcb1713
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/scale-down-animation.html
@@ -0,0 +1,65 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<scale-down-animation>` animates the scale transform of an element from 1 to 0. By default it
+scales in both the x and y axes.
+
+Configuration:
+```
+{
+ name: 'scale-down-animation',
+ node: <node>,
+ axis: 'x' | 'y' | '',
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'scale-down-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ var scaleProperty = 'scale(0, 0)';
+ if (config.axis === 'x') {
+ scaleProperty = 'scale(0, 1)';
+ } else if (config.axis === 'y') {
+ scaleProperty = 'scale(1, 0)';
+ }
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'scale(1,1)'},
+ {'transform': scaleProperty}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/scale-up-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/scale-up-animation.html
new file mode 100644
index 00000000..42ec3f32
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/scale-up-animation.html
@@ -0,0 +1,65 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<scale-up-animation>` animates the scale transform of an element from 0 to 1. By default it
+scales in both the x and y axes.
+
+Configuration:
+```
+{
+ name: 'scale-up-animation',
+ node: <node>,
+ axis: 'x' | 'y' | '',
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'scale-up-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ var scaleProperty = 'scale(0)';
+ if (config.axis === 'x') {
+ scaleProperty = 'scale(0, 1)';
+ } else if (config.axis === 'y') {
+ scaleProperty = 'scale(1, 0)';
+ }
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': scaleProperty},
+ {'transform': 'scale(1, 1)'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-down-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-down-animation.html
new file mode 100644
index 00000000..9db28dd1
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-down-animation.html
@@ -0,0 +1,59 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-down-animation>` animates the transform of an element from `none` `translateY(100%)`.
+The `transformOrigin` defaults to `50% 0`.
+
+Configuration:
+```
+{
+ name: 'slide-down-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-down-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'translateY(0%)'},
+ {'transform': 'translateY(100%)'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '50% 0');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-from-bottom-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-bottom-animation.html
new file mode 100644
index 00000000..d1156e93
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-bottom-animation.html
@@ -0,0 +1,59 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-from-bottom-animation>` animates the transform of an element from `none` to `translateY(100%)`.
+The `transformOrigin` defaults to `50% 0`.
+
+Configuration:
+```
+{
+ name: 'slide-from-bottom-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-from-bottom-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'translateY(100%)'},
+ {'transform': 'translateY(0)'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '50% 0');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-from-left-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-left-animation.html
new file mode 100644
index 00000000..5386c9b0
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-left-animation.html
@@ -0,0 +1,60 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-from-left-animation>` animates the transform of an element from
+`translateX(-100%)` to `none`.
+The `transformOrigin` defaults to `0 50%`.
+
+Configuration:
+```
+{
+ name: 'slide-from-left-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-from-left-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'translateX(-100%)'},
+ {'transform': 'none'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-from-right-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-right-animation.html
new file mode 100644
index 00000000..2afb591f
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-right-animation.html
@@ -0,0 +1,60 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-from-right-animation>` animates the transform of an element from
+`translateX(100%)` to `none`.
+The `transformOrigin` defaults to `0 50%`.
+
+Configuration:
+```
+{
+ name: 'slide-from-right-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-from-right-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'translateX(100%)'},
+ {'transform': 'none'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-from-top-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-top-animation.html
new file mode 100644
index 00000000..c0a48811
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-from-top-animation.html
@@ -0,0 +1,59 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-from-top-animation>` animates the transform of an element from `translateY(-100%)` to
+`none`. The `transformOrigin` defaults to `50% 0`.
+
+Configuration:
+```
+{
+ name: 'slide-from-top-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-from-top-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'translateY(-100%)'},
+ {'transform': 'translateY(0%)'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '50% 0');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-left-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-left-animation.html
new file mode 100644
index 00000000..da80a6bd
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-left-animation.html
@@ -0,0 +1,59 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-left-animation>` animates the transform of an element from `none` to `translateX(-100%)`.
+The `transformOrigin` defaults to `0 50%`.
+
+Configuration:
+```
+{
+ name: 'slide-left-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-left-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'none'},
+ {'transform': 'translateX(-100%)'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-right-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-right-animation.html
new file mode 100644
index 00000000..99b5794e
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-right-animation.html
@@ -0,0 +1,59 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-right-animation>` animates the transform of an element from `none` to `translateX(100%)`.
+The `transformOrigin` defaults to `0 50%`.
+
+Configuration:
+```
+{
+ name: 'slide-right-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-right-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'none'},
+ {'transform': 'translateX(100%)'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/slide-up-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/slide-up-animation.html
new file mode 100644
index 00000000..6464e78e
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/slide-up-animation.html
@@ -0,0 +1,59 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<slide-up-animation>` animates the transform of an element from `translateY(0)` to
+`translateY(-100%)`. The `transformOrigin` defaults to `50% 0`.
+
+Configuration:
+```
+{
+ name: 'slide-up-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'slide-up-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': 'translate(0)'},
+ {'transform': 'translateY(-100%)'}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ } else {
+ this.setPrefixedProperty(node, 'transformOrigin', '50% 0');
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/animations/transform-animation.html b/catapult/third_party/polymer/components/neon-animation/animations/transform-animation.html
new file mode 100644
index 00000000..2e504442
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/animations/transform-animation.html
@@ -0,0 +1,70 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-animation-behavior.html">
+<link rel="import" href="../web-animations.html">
+
+<!--
+`<transform-animation>` animates a custom transform on an element. Use this to animate multiple
+transform properties, or to apply a custom transform value.
+
+Configuration:
+```
+{
+ name: 'transform-animation',
+ node: <node>,
+ transformOrigin: <transform-origin>,
+ transformFrom: <transform-from-string>,
+ transformTo: <transform-to-string>,
+ timing: <animation-timing>
+}
+```
+-->
+
+<script>
+
+ Polymer({
+
+ is: 'transform-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ /**
+ * @param {{
+ * node: !Element,
+ * transformOrigin: (string|undefined),
+ * transformFrom: (string|undefined),
+ * transformTo: (string|undefined),
+ * timing: (Object|undefined)
+ * }} config
+ */
+ configure: function(config) {
+ var node = config.node;
+ var transformFrom = config.transformFrom || 'none';
+ var transformTo = config.transformTo || 'none';
+
+ this._effect = new KeyframeEffect(node, [
+ {'transform': transformFrom},
+ {'transform': transformTo}
+ ], this.timingFromConfig(config));
+
+ if (config.transformOrigin) {
+ this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
+ }
+
+ return this._effect;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/bower.json b/catapult/third_party/polymer/components/neon-animation/bower.json
new file mode 100644
index 00000000..c3e121e9
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/bower.json
@@ -0,0 +1,52 @@
+{
+ "name": "neon-animation",
+ "description": "A system for animating Polymer-based web components",
+ "version": "1.2.5",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "web-animations"
+ ],
+ "main": [
+ "neon-animated-pages.html",
+ "neon-animatable-behavior.html",
+ "neon-animation-behavior.html",
+ "neon-animation-runner-behavior.html",
+ "neon-shared-element-animatable-behavior.html",
+ "neon-shared-element-animation-behavior.html",
+ "neon-animatable.html",
+ "neon-animations.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/neon-animation"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/neon-animation",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-meta": "PolymerElements/iron-meta#^1.0.0",
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "iron-selector": "PolymerElements/iron-selector#^1.0.0",
+ "web-animations-js": "web-animations/web-animations-js#^2.2.0"
+ },
+ "devDependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-toolbar": "PolymerElements/paper-toolbar#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/card/index.html b/catapult/third_party/polymer/components/neon-animation/demo/card/index.html
new file mode 100644
index 00000000..44993465
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/card/index.html
@@ -0,0 +1,166 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: card</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../neon-animated-pages.html">
+ <link rel="import" href="../../neon-animations.html">
+ <link rel="import" href="../../../paper-styles/typography.html">
+ <link rel="import" href="x-card.html">
+ <link rel="import" href="x-cards-list.html">
+
+ <style is="custom-style">
+
+ body {
+ padding: 15px;
+ @apply(--layout-fullbleed);
+ @apply(--paper-font-common-base);
+ }
+
+ neon-animated-pages {
+ height: 100%;
+ }
+
+ .large {
+ width: 100%
+ }
+
+ .button {
+ text-align: center;
+ width: 120px;
+ height: 32px;
+ line-height: 32px;
+ border-radius: 2px;
+ font-size: 0.9em;
+ background-color: #fff;
+ color: #646464;
+ }
+
+ .button.blue {
+ background-color: #4285f4;
+ color: #fff;
+ }
+
+ .button.raised {
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
+ }
+
+ .button.back {
+ position: fixed;
+ top: 30px;
+ left: 30px;
+ }
+
+ .card-contents {
+ @apply(--layout-vertical);
+ @apply(--layout-center-center);
+ @apply(--layout-fit);
+ }
+
+ .button-container {
+ @apply(--layout-flex);
+ @apply(--layout-horizontal);
+ @apply(--layout-around-justified);
+ }
+ </style>
+
+ </head>
+ <body>
+
+ <template is="dom-bind">
+ <neon-animated-pages id="pages" selected="0">
+ <x-cards-list id="list">
+ <div class="card-contents">
+ <h2>Choose a subject</h2>
+ <div class="button-container large">
+ <div class="blue raised button" on-click="_onPolymerClick">
+ POLYMER
+ </div>
+ <div class="blue raised button" on-click="_onAngularClick">
+ ANGULAR
+ </div>
+ </div>
+ </div>
+ </x-cards-list>
+
+ <x-card>
+ <div class="card-contents">
+ <div class="raised back button" on-click="_onBackClick">
+ BACK
+ </div>
+ <h2>Polymer</h2>
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </p>
+ </div>
+ </x-card>
+
+ <x-card>
+ <div class="card-contents">
+ <div class="raised back button" on-click="_onBackClick">
+ BACK
+ </div>
+ <h2>Angular</h2>
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </p>
+ </div>
+ </x-card>
+
+ </neon-animated-pages>
+ </template>
+
+ <script>
+
+ var scope = document.querySelector('template[is="dom-bind"]');
+
+ scope._onPolymerClick = function(event) {
+ this.$.list.sharedElements = {
+ 'ripple': event.target,
+ 'reverse-ripple': event.target
+ };
+ this.$.pages.selected = 1;
+ };
+
+ scope._onAngularClick = function(event) {
+ this.$.list.sharedElements = {
+ 'ripple': event.target,
+ 'reverse-ripple': event.target
+ };
+ this.$.pages.selected = 2;
+ };
+
+ scope._onBackClick = function(event) {
+ this.$.pages.selected = 0;
+ };
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/card/x-card.html b/catapult/third_party/polymer/components/neon-animation/demo/card/x-card.html
new file mode 100644
index 00000000..92885d29
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/card/x-card.html
@@ -0,0 +1,94 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+
+<dom-module id="x-card">
+ <template>
+ <style>
+ :host {
+ display: block;
+ overflow: hidden;
+ }
+ #placeholder {
+ opacity: 0;
+ background-color: grey;
+ @apply(--layout-fit);
+ }
+ </style>
+
+ <div id="placeholder"></div>
+ <div id="container">
+ <content select="div"></content>
+ </div>
+
+ </template>
+</dom-module>
+
+<script>
+(function() {
+ Polymer({
+ is: 'x-card',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'ripple-animation',
+ id: 'ripple',
+ toPage: this
+ }, {
+ name: 'fade-out-animation',
+ node: this.$.placeholder,
+ timing: {
+ delay: 250
+ }
+ }, {
+ name: 'fade-in-animation',
+ node: this.$.container,
+ timing: {
+ delay: 50
+ }
+ }],
+
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this.$.container,
+ timing: {
+ duration: 0
+ }
+ }, {
+ name: 'reverse-ripple-animation',
+ id: 'reverse-ripple',
+ fromPage: this
+ }]
+ };
+ }
+ },
+
+ sharedElements: {
+ value: function() {
+ return {
+ 'ripple': this.$.placeholder,
+ 'reverse-ripple': this.$.placeholder
+ };
+ }
+ }
+ }
+ });
+})();
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/card/x-cards-list.html b/catapult/third_party/polymer/components/neon-animation/demo/card/x-cards-list.html
new file mode 100644
index 00000000..b817698f
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/card/x-cards-list.html
@@ -0,0 +1,75 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+
+<dom-module id="x-cards-list">
+ <template>
+ <style>
+ :host {
+ display: block;
+ overflow: hidden;
+ }
+
+ #placeholder {
+ opacity: 0;
+ background-color: grey;
+ @apply(--layout-fit);
+ }
+ </style>
+
+ <div id="placeholder"></div>
+ <div id="container">
+ <content select="div"></content>
+ </div>
+
+ </template>
+</dom-module>
+
+<script>
+(function() {
+ Polymer({
+ is: 'x-cards-list',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'reverse-ripple-animation',
+ id: 'reverse-ripple',
+ toPage: this
+ }],
+
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this.$.container,
+ timing: {
+ delay: 150,
+ duration: 0
+ }
+ }, {
+ name: 'ripple-animation',
+ id: 'ripple',
+ fromPage: this
+ }]
+ };
+ }
+ }
+ }
+ });
+})();
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/declarative/index.html b/catapult/third_party/polymer/components/neon-animation/demo/declarative/index.html
new file mode 100644
index 00000000..26ba53ff
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/declarative/index.html
@@ -0,0 +1,132 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: declarative</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../../paper-styles/typography.html">
+ <link rel="import" href="../../../paper-styles/color.html">
+ <link rel="import" href="../../neon-animated-pages.html">
+ <link rel="import" href="../../neon-animatable.html">
+ <link rel="import" href="../../neon-animations.html">
+
+ <style is="custom-style">
+ body {
+ overflow: hidden;
+ @apply(--layout-fullbleed);
+ @apply(--layout-vertical);
+ }
+
+ .toolbar {
+ position: relative;
+
+ padding: 8px;
+
+ background-color: white;
+
+ z-index: 12;
+ }
+
+ neon-animated-pages {
+ @apply(--layout-flex);
+ }
+
+ neon-animatable {
+ color: white;
+ @apply(--layout-horizontal);
+ @apply(--layout-center-center);
+ @apply(--paper-font-display4);
+ }
+
+ neon-animatable:nth-child(1) {
+ background: var(--paper-red-500);
+ }
+
+ neon-animatable:nth-child(2) {
+ background: var(--paper-blue-500);
+ }
+
+ neon-animatable:nth-child(3) {
+ background: var(--paper-orange-500);
+ }
+
+ neon-animatable:nth-child(4) {
+ background: var(--paper-green-500);
+ }
+
+ neon-animatable:nth-child(5) {
+ background: var(--paper-purple-500);
+ }
+
+ </style>
+
+ </head>
+ <body>
+
+ <template is="dom-bind">
+
+ <div class="toolbar">
+ <button on-click="_onPrevClick">&#8678;</button>
+ <button on-click="_onNextClick">&#8680;</button>
+ <button on-click="_onUpClick">&#8679;</button>
+ <button on-click="_onDownClick">&#8681;</button>
+ </div>
+
+ <neon-animated-pages id="pages" selected="[[selected]]" entry-animation="[[entryAnimation]]" exit-animation="[[exitAnimation]]">
+ <neon-animatable>1</neon-animatable>
+ <neon-animatable>2</neon-animatable>
+ <neon-animatable>3</neon-animatable>
+ <neon-animatable>4</neon-animatable>
+ <neon-animatable>5</neon-animatable>
+ </neon-animated-pages>
+
+ </template>
+
+ <script>
+
+ var scope = document.querySelector('template[is="dom-bind"]');
+ scope.selected = 0;
+
+ scope._onPrevClick = function() {
+ this.entryAnimation = 'slide-from-left-animation';
+ this.exitAnimation = 'slide-right-animation';
+ this.selected = this.selected === 0 ? 4 : (this.selected - 1);
+ }
+
+ scope._onNextClick = function() {
+ this.entryAnimation = 'slide-from-right-animation';
+ this.exitAnimation = 'slide-left-animation';
+ this.selected = this.selected === 4 ? 0 : (this.selected + 1);
+ }
+
+ scope._onUpClick = function() {
+ this.entryAnimation = 'slide-from-top-animation';
+ this.exitAnimation = 'slide-down-animation';
+ this.selected = this.selected === 4 ? 0 : (this.selected + 1);
+ }
+
+ scope._onDownClick = function() {
+ this.entryAnimation = 'slide-from-bottom-animation';
+ this.exitAnimation = 'slide-up-animation';
+ this.selected = this.selected === 0 ? 4 : (this.selected - 1);
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/doc/index.html b/catapult/third_party/polymer/components/neon-animation/demo/doc/index.html
new file mode 100644
index 00000000..8b3875e7
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/doc/index.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animation demo: basic</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="my-animatable.html">
+ <link rel="import" href="my-dialog.html">
+
+ </head>
+ <style>
+ my-animatable {
+ margin-top: 50px;
+ }
+ </style>
+ <body>
+
+ <template is="dom-bind">
+
+ <button on-click="_onCircleButtonClick">toggle circle</button>
+ <button on-click="_onDialogButtonClick">toggle dialog</button>
+
+ <div style="text-align: center">
+ <my-dialog>Hello World!</my-dialog>
+ </div>
+
+ <my-animatable></my-animatable>
+
+ </template>
+
+ <script>
+
+ var scope = document.querySelector('template[is="dom-bind"]');
+
+ scope._onCircleButtonClick = function(event) {
+ var node = document.querySelector('my-animatable');
+ if (node) {
+ node.animate();
+ }
+ };
+
+ scope._onDialogButtonClick = function(event) {
+ var node = document.querySelector('my-dialog');
+ if (node) {
+ if (node.opened) {
+ node.hide();
+ } else {
+ node.show();
+ }
+ }
+ };
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/doc/my-animatable.html b/catapult/third_party/polymer/components/neon-animation/demo/doc/my-animatable.html
new file mode 100644
index 00000000..5063f74b
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/doc/my-animatable.html
@@ -0,0 +1,68 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../neon-animation-runner-behavior.html">
+<link rel="import" href="../../animations/scale-down-animation.html">
+
+<dom-module id="my-animatable">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ border-radius: 50%;
+ width: 300px;
+ height: 300px;
+ background: orange;
+ }
+ </style>
+ <content></content>
+
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'my-animatable',
+
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ properties: {
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ name: 'scale-down-animation',
+ node: this
+ }
+ }
+ }
+
+ },
+
+ listeners: {
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+
+ animate: function() {
+ this.playAnimation();
+ },
+
+ _onNeonAnimationFinish: function() {
+ console.log('animation finish!');
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/doc/my-dialog.html b/catapult/third_party/polymer/components/neon-animation/demo/doc/my-dialog.html
new file mode 100644
index 00000000..bd3344c0
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/doc/my-dialog.html
@@ -0,0 +1,94 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-styles/shadow.html">
+<link rel="import" href="../../neon-animation-runner-behavior.html">
+<link rel="import" href="../../animations/scale-up-animation.html">
+<link rel="import" href="../../animations/fade-out-animation.html">
+
+
+<dom-module id="my-dialog">
+ <template>
+ <style>
+ :host {
+ display: none;
+ padding: 16px;
+ background: white;
+ color: black;
+ margin: 0 auto;
+ z-index: 100;
+ position: absolute;
+ @apply(--shadow-elevation-2dp);
+ }
+ </style>
+ <content></content>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'my-dialog',
+
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ properties: {
+
+ opened: {
+ type: Boolean
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'scale-up-animation',
+ node: this
+ }],
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this
+ }]
+ }
+ }
+ }
+
+ },
+
+ listeners: {
+ 'neon-animation-finish': '_onAnimationFinish'
+ },
+
+ _onAnimationFinish: function() {
+ if (!this.opened) {
+ this.style.display = '';
+ }
+ },
+
+ show: function() {
+ this.opened = true;
+ this.style.display = 'inline-block';
+ this.playAnimation('entry');
+ },
+
+ hide: function() {
+ this.opened = false;
+ this.playAnimation('exit');
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/dropdown/animated-dropdown.html b/catapult/third_party/polymer/components/neon-animation/demo/dropdown/animated-dropdown.html
new file mode 100644
index 00000000..9a61530f
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/dropdown/animated-dropdown.html
@@ -0,0 +1,90 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-styles/shadow.html">
+<link rel="import" href="../../neon-animation-runner-behavior.html">
+
+<dom-module id="animated-dropdown">
+ <template>
+ <style>
+ :host {
+ display: none;
+ padding: 16px;
+ background: white;
+ color: black;
+
+ @apply(--shadow-elevation-2dp);
+ }
+ </style>
+ <content></content>
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'animated-dropdown',
+
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ properties: {
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'scale-up-animation',
+ node: this,
+ transformOrigin: '0 0'
+ }],
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this
+ }]
+ }
+ }
+ },
+
+ _showing: {
+ type: Boolean,
+ value: false
+ }
+
+ },
+
+ listeners: {
+ 'neon-animation-finish': '_onAnimationFinish'
+ },
+
+ _onAnimationFinish: function() {
+ if (this._showing) {
+ } else {
+ this.style.display = '';
+ }
+ },
+
+ show: function() {
+ this.style.display = 'inline-block';
+ this._showing = true;
+ this.playAnimation('entry');
+ },
+
+ hide: function() {
+ this._showing = false;
+ this.playAnimation('exit');
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/dropdown/index.html b/catapult/third_party/polymer/components/neon-animation/demo/dropdown/index.html
new file mode 100644
index 00000000..b238474c
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/dropdown/index.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: dropdown</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../neon-animated-pages.html">
+ <link rel="import" href="../../neon-animations.html">
+ <link rel="import" href="animated-dropdown.html">
+
+ </head>
+ <body>
+
+ <template is="dom-bind">
+
+ <button dropdown-id="dropdown" on-click="_onButtonClick">dropdown</button>
+ <br>
+ <animated-dropdown id="dropdown" on-click="_onDropdownClick">Hello world!</animated-dropdown>
+
+ </template>
+
+ <script>
+
+ var scope = document.querySelector('template[is="dom-bind"]');
+
+ scope._onButtonClick = function(event) {
+ var dropdown = document.querySelector('#' + event.target.getAttribute('dropdown-id'));
+ if (dropdown) {
+ dropdown.show();
+ }
+ };
+
+ scope._onDropdownClick = function(event) {
+ event.target.hide();
+ };
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/grid/animated-grid.html b/catapult/third_party/polymer/components/neon-animation/demo/grid/animated-grid.html
new file mode 100644
index 00000000..a2689364
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/grid/animated-grid.html
@@ -0,0 +1,164 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-styles/typography.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+<link rel="import" href="../shared-styles.html">
+
+<dom-module id="animated-grid">
+ <template>
+ <style include="shared-styles"></style>
+ <style>
+
+ :host {
+ display: block;
+ background: #000;
+ }
+
+ .tile {
+ display: inline-block;
+ float: left;
+ vertical-align: top;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+
+ @apply(--paper-font-title);
+ @apply(--layout-vertical);
+ @apply(--layout-center-center);
+ }
+
+ .tile:nth-of-type(1) {
+ width: calc(100% / 3);
+ height: calc(100% / 3 * 2);
+ }
+
+ .tile:nth-of-type(4) {
+ width: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(5) {
+ width: calc(100% / 3);
+ height: calc(100% / 3 * 2);
+ }
+
+ .tile:nth-of-type(8) {
+ width: calc(100% / 3);
+ height: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(9) {
+ position: absolute;
+ top: calc(100% / 3 * 2);
+ left: 0;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(10) {
+ position: absolute;
+ top: calc(100% / 3 * 2);
+ left: calc(100% / 6);;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+ }
+ </style>
+
+ <template is="dom-repeat" items="[[config]]">
+ <div class$="[[_computeTileClass(item.color)]]">
+ <span>[[item.value]]</span>
+ </div>
+ </template>
+
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'animated-grid',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+
+ config: {
+ type: Array,
+ value: function() {
+ return [
+ {value: 1, color: 'blue'},
+ {value: 2, color: 'red'},
+ {value: 3, color: 'blue'},
+ {value: 4, color: 'green'},
+ {value: 5, color: 'yellow'},
+ {value: 6, color: 'blue'},
+ {value: 7, color: 'red'},
+ {value: 8, color: 'green'},
+ {value: 9, color: 'yellow'},
+ {value: 10, color: 'red'}
+ ]
+ }
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'exit': [{
+ name: 'ripple-animation',
+ id: 'ripple',
+ fromPage: this
+ }, {
+ name: 'hero-animation',
+ id: 'hero',
+ fromPage: this
+ }]
+ }
+ }
+ }
+
+ },
+
+ listeners: {
+ click: '_onClick'
+ },
+
+ _computeTileClass: function(color) {
+ return 'tile ' + color + '-300';
+ },
+
+ _onClick: function(event) {
+ var target = event.target;
+ while (target !== this && !target._templateInstance) {
+ target = target.parentNode;
+ }
+
+ // configure the page animation
+ this.sharedElements = {
+ 'hero': target,
+ 'ripple': target
+ };
+ this.animationConfig['exit'][0].gesture = {
+ x: event.x || event.pageX,
+ y: event.y || event.pageY
+ };
+
+ this.fire('tile-click', {
+ tile: target,
+ data: target._templateInstance.item
+ });
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/grid/fullsize-page-with-card.html b/catapult/third_party/polymer/components/neon-animation/demo/grid/fullsize-page-with-card.html
new file mode 100644
index 00000000..63d35bbf
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/grid/fullsize-page-with-card.html
@@ -0,0 +1,122 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+<link rel="import" href="../../../paper-styles/color.html">
+<link rel="import" href="../shared-styles.html">
+
+<dom-module id="fullsize-page-with-card">
+ <template>
+ <style include="shared-styles"></style>
+ <style>
+
+ :host {
+ display: block;
+ }
+
+ .fixed {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100vh;
+ width: 100vw;
+ }
+
+ .card {
+ position: relative;
+ margin: 200px 100px 0;
+ height: 700px;
+ }
+
+ </style>
+
+ <div id="fixed" class$="[[_computeFixedBackgroundClass(color)]]"></div>
+ <div id="card" class$="[[_computeCardClass(color)]]"></div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'fullsize-page-with-card',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+
+ color: {
+ type: String
+ },
+
+ sharedElements: {
+ type: Object,
+ value: function() {
+ return {
+ 'hero': this.$.card,
+ 'ripple': this.$.fixed
+ }
+ }
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'ripple-animation',
+ id: 'ripple',
+ toPage: this,
+ }, {
+ name: 'hero-animation',
+ id: 'hero',
+ toPage: this,
+ timing: {
+ delay: 150
+ }
+ }],
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this.$.fixed
+ }, {
+ name: 'transform-animation',
+ transformFrom: 'none',
+ transformTo: 'translate(0px,-200vh) scale(0.9,1)',
+ node: this.$.card
+ }]
+ }
+ }
+ }
+
+ },
+
+ _computeCardClass: function(color) {
+ var cls = 'card';
+ if (color) {
+ cls += ' ' + color + '-300';
+ }
+ return cls;
+ },
+
+ _computeFixedBackgroundClass: function(color) {
+ var cls = 'fixed';
+ if (color) {
+ cls += ' ' + color + '-100';
+ }
+ return cls;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/grid/index.html b/catapult/third_party/polymer/components/neon-animation/demo/grid/index.html
new file mode 100644
index 00000000..b102dba4
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/grid/index.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: grid</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../neon-animated-pages.html">
+ <link rel="import" href="../../neon-animations.html">
+ <link rel="import" href="animated-grid.html">
+ <link rel="import" href="fullsize-page-with-card.html">
+
+ <style is="custom-style">
+ body {
+ overflow: hidden;
+ @apply(--layout-fullbleed);
+ }
+
+ neon-animated-pages {
+ height: 100%;
+ }
+ </style>
+
+ </head>
+ <body>
+
+ <template is="dom-bind">
+ <neon-animated-pages id="pages" selected="0">
+ <animated-grid on-tile-click="_onTileClick"></animated-grid>
+ <fullsize-page-with-card id="fullsize-card" on-click="_onFullsizeClick">
+ </fullsize-page-with-card>
+ </neon-animated-pages>
+ </template>
+
+ <script>
+
+ var scope = document.querySelector('template[is="dom-bind"]');
+
+ scope._onTileClick = function(event) {
+ this.$['fullsize-card'].color = event.detail.data.color;
+ this.$.pages.selected = 1;
+ };
+
+ scope._onFullsizeClick = function(event) {
+ this.$.pages.selected = 0;
+ };
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/index.html b/catapult/third_party/polymer/components/neon-animation/demo/index.html
new file mode 100644
index 00000000..1e4e1204
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/index.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html lang="en">
+<head>
+ <title>neon-animated pages demo</title>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+</head>
+<style>
+ a {
+ display: block;
+ margin-bottom: 10px;
+ }
+</style>
+<body unresolved>
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Sample demos</h4>
+ <div class="horizontal-section">
+ <a href="doc/index.html">basic</a>
+ <a href="declarative/index.html">declarative</a>
+ <a href="dropdown/index.html">dropdown</a>
+ <a href="grid/index.html">grid</a>
+ <a href="list/index.html">list</a>
+ <a href="load/index.html">load</a>
+ <a href="tiles/index.html">tiles</a>
+ <a href="card/index.html">card</a>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/list/full-view.html b/catapult/third_party/polymer/components/neon-animation/demo/list/full-view.html
new file mode 100644
index 00000000..565618c2
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/list/full-view.html
@@ -0,0 +1,118 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../../paper-styles/shadow.html">
+<link rel="import" href="../../neon-animatable-behavior.html">
+
+<dom-module id="full-view">
+ <template>
+ <style>
+ :host {
+ @apply(--layout-vertical);
+ }
+
+ .main {
+ background: white;
+ @apply(--layout-flex);
+ @apply(--layout-scroll);
+ @apply(--shadow-elevation-8dp);
+ }
+
+ .image-container {
+ position: relative;
+ width: 100%;
+ padding-bottom: 100%;
+ }
+
+ .image {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background: repeating-linear-gradient(
+ 45deg,
+ #f5f5f5,
+ #f5f5f5 8px,
+ #e0e0e0 8px,
+ #e0e0e0 16px
+ );
+ }
+ </style>
+ <paper-toolbar class="medium-tall">
+ <paper-icon-button id="button" icon="clear" on-click="_onClearButtonClick"></paper-icon-button>
+ </paper-toolbar>
+
+ <div id="main" class="main">
+ <div class="image-container">
+ <div class="image">
+ </div>
+ </div>
+ </div>
+
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'full-view',
+
+ behaviors: [
+ Polymer.NeonAnimatableBehavior
+ ],
+
+ properties: {
+
+ sharedElements: {
+ type: Object,
+ value: function() {
+ return {
+ 'hero': this.$.main
+ };
+ }
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'fade-in-animation',
+ node: this.$.button
+ }, {
+ name: 'hero-animation',
+ id: 'hero',
+ toPage: this
+ }],
+
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this.$.button
+ }, {
+ name: 'scale-down-animation',
+ node: this.$.main,
+ transformOrigin: '50% 50%',
+ axis: 'y'
+ }]
+
+ }
+ }
+ }
+
+ },
+
+ _onClearButtonClick: function() {
+ this.fire('close');
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/list/index.html b/catapult/third_party/polymer/components/neon-animation/demo/list/index.html
new file mode 100644
index 00000000..eabb02e5
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/list/index.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: list</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="list-demo.html">
+
+ </head>
+ <style is="custom-style">
+ body {
+ @apply(--layout-fullbleed);
+ }
+
+ list-demo {
+ @apply(--layout-fit);
+ }
+ </style>
+ <body>
+ <list-demo></list-demo>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/list/list-demo.html b/catapult/third_party/polymer/components/neon-animation/demo/list/list-demo.html
new file mode 100644
index 00000000..45808c8c
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/list/list-demo.html
@@ -0,0 +1,102 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../paper-toolbar/paper-toolbar.html">
+<link rel="import" href="../../neon-animated-pages.html">
+<link rel="import" href="../../neon-animations.html">
+<link rel="import" href="list-view.html">
+<link rel="import" href="full-view.html">
+
+<dom-module id="list-demo">
+ <template>
+ <style>
+ :host {
+ display: block;
+ }
+ neon-animated-pages {
+ height: 100%;
+ }
+ </style>
+ <neon-animated-pages id="pages" selected="0">
+ <list-view data="[[fileData]]" on-item-click="_onItemClick"></list-view>
+ <full-view on-close="_onClose"></full-view>
+ </neon-animated-pages>
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'list-demo',
+
+ properties: {
+
+ fileData: {
+ type: Array,
+ value: function() {
+ return [
+ {fileName: 'IMG_4130.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4131.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4132.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4133.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4134.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4135.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4136.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4137.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4138.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4139.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4140.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4141.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4142.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4143.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4144.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4145.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4146.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4147.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4148.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4149.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4150.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4151.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4152.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4153.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4154.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4155.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4156.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4157.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4158.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4159.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4160.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4161.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4162.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4163.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4164.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4165.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4166.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4167.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4168.jpg', modifiedDate: 'May 12 2015'},
+ {fileName: 'IMG_4169.jpg', modifiedDate: 'May 12 2015'}
+ ]
+ }
+ }
+ },
+
+ _onItemClick: function() {
+ this.$.pages.selected = 1;
+ },
+
+ _onClose: function() {
+ this.$.pages.selected = 0;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/list/list-view.html b/catapult/third_party/polymer/components/neon-animation/demo/list/list-view.html
new file mode 100644
index 00000000..ddb05502
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/list/list-view.html
@@ -0,0 +1,124 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-icons/iron-icons.html">
+<link rel="import" href="../../../iron-icon/iron-icon.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../../paper-icon-button/paper-icon-button.html">
+<link rel="import" href="../../../paper-item/paper-item.html">
+<link rel="import" href="../../../paper-item/paper-item-body.html">
+<link rel="import" href="../../../paper-styles/color.html">
+<link rel="import" href="../../neon-animatable-behavior.html">
+
+<dom-module id="list-view">
+ <template>
+ <style>
+ :host {
+ @apply(--layout-vertical);
+ }
+
+ .main {
+ @apply(--layout-flex);
+ @apply(--layout-scroll);
+ }
+
+ iron-icon {
+ color: var(--google-grey-500);
+ }
+ </style>
+ <paper-toolbar class="medium-tall">
+ <paper-icon-button id="button" icon="arrow-back"></paper-icon-button>
+ </paper-toolbar>
+
+ <div class="main">
+
+ <template is="dom-repeat" items="[[data]]">
+
+ <paper-item>
+ <paper-item-body two-line>
+ <div>[[item.fileName]]</div>
+ <div secondary>[[item.modifiedDate]]</div>
+ </paper-item-body>
+ <iron-icon icon="info"></iron-icon>
+ </paper-item>
+
+ </template>
+
+ </div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'list-view',
+
+ behaviors: [
+ Polymer.NeonAnimatableBehavior
+ ],
+
+ listeners: {
+ 'click': '_onClick'
+ },
+
+ properties: {
+
+ data: {
+ type: Array,
+ value: function() {
+ return [];
+ }
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'fade-in-animation',
+ node: this.$.button
+ }],
+
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this.$.button
+ }, {
+ name: 'hero-animation',
+ id: 'hero',
+ fromPage: this
+ }]
+ };
+ }
+ }
+
+ },
+
+ _onClick: function(event) {
+ var target = event.target;
+ while (target !== this && !target._templateInstance) {
+ target = target.parentNode;
+ }
+
+ // configure the page animation
+ this.sharedElements = {
+ 'hero': target,
+ };
+
+ this.fire('item-click', {
+ item: target,
+ });
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/load/animated-grid.html b/catapult/third_party/polymer/components/neon-animation/demo/load/animated-grid.html
new file mode 100644
index 00000000..c1d52c1a
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/load/animated-grid.html
@@ -0,0 +1,146 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+<link rel="import" href="../../../paper-styles/typography.html">
+<link rel="import" href="../../../paper-styles/color.html">
+<link rel="import" href="../shared-styles.html">
+
+<dom-module id="animated-grid">
+ <template>
+ <style include="shared-styles"></style>
+ <style>
+ :host {
+ display: block;
+ background: #000;
+ }
+
+ .tile {
+ display: inline-block;
+ float: left;
+ vertical-align: top;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+
+ @apply(--paper-font-title);
+ @apply(--layout-vertical);
+ @apply(--layout-center-center);
+ }
+
+ .tile:nth-of-type(1) {
+ width: calc(100% / 3);
+ height: calc(100% / 3 * 2);
+ }
+
+ .tile:nth-of-type(4) {
+ width: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(5) {
+ width: calc(100% / 3);
+ height: calc(100% / 3 * 2);
+ }
+
+ .tile:nth-of-type(8) {
+ width: calc(100% / 3);
+ height: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(9) {
+ position: absolute;
+ top: calc(100% / 3 * 2);
+ left: 0;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(10) {
+ position: absolute;
+ top: calc(100% / 3 * 2);
+ left: calc(100% / 6);;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+ }
+ </style>
+
+ <template is="dom-repeat" items="[[config]]">
+ <div class$="[[_computeTileClass(item.color)]]">
+ <span>[[item.value]]</span>
+ </div>
+ </template>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'animated-grid',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+
+ config: {
+ type: Array,
+ value: function() {
+ return [
+ {value: 1, color: 'blue'},
+ {value: 2, color: 'red'},
+ {value: 3, color: 'blue'},
+ {value: 4, color: 'green'},
+ {value: 5, color: 'yellow'},
+ {value: 6, color: 'blue'},
+ {value: 7, color: 'red'},
+ {value: 8, color: 'green'},
+ {value: 9, color: 'yellow'},
+ {value: 10, color: 'red'}
+ ]
+ }
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'cascaded-animation',
+ animation: 'transform-animation',
+ transformFrom: 'translateY(100%)',
+ transformTo: 'none',
+ timing: {
+ delay: 50
+ }
+ }]
+ }
+ }
+ }
+
+ },
+
+ attached: function() {
+ this.async(function() {
+ var nodeList = Polymer.dom(this.root).querySelectorAll('.tile');
+ this.animationConfig['entry'][0].nodes = Array.prototype.slice.call(nodeList);
+ });
+ },
+
+ _computeTileClass: function(color) {
+ return 'tile ' + color + '-300';
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/load/full-page.html b/catapult/third_party/polymer/components/neon-animation/demo/load/full-page.html
new file mode 100644
index 00000000..61a11bd7
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/load/full-page.html
@@ -0,0 +1,82 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../neon-animatable-behavior.html">
+<link rel="import" href="../../neon-animation-runner-behavior.html">
+<link rel="import" href="../../../paper-styles/shadow.html">
+<link rel="import" href="animated-grid.html">
+
+<dom-module id="full-page">
+ <template>
+ <style>
+ :host {
+ background: black;
+ visibility: hidden;
+ @apply(--layout-vertical);
+ }
+
+ .toolbar {
+ background: #9c27b0;
+ height: 72px;
+ z-index: 1;
+ @apply(--shadow-elevation-2dp);
+ }
+
+ animated-grid {
+ height: calc(100% - 72px);
+ @apply(--layout-flex);
+ }
+ </style>
+
+ <div id="toolbar" class="toolbar"></div>
+ <animated-grid id="grid"></animated-grid>
+
+ </template>
+</dom-module>
+
+<script>
+
+Polymer({
+
+ is: 'full-page',
+
+ behaviors: [
+ Polymer.NeonAnimatableBehavior,
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ properties: {
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'slide-from-top-animation',
+ node: this.$.toolbar
+ }, {
+ animatable: this.$.grid,
+ type: 'entry'
+ }]
+ };
+ }
+ }
+
+ },
+
+ show: function() {
+ this.style.visibility = 'visible';
+ this.playAnimation('entry');
+ }
+
+});
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/load/index.html b/catapult/third_party/polymer/components/neon-animation/demo/load/index.html
new file mode 100644
index 00000000..54c8d68a
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/load/index.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: load</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../neon-animated-pages.html">
+ <link rel="import" href="../../neon-animations.html">
+ <link rel="import" href="full-page.html">
+
+ <style is="custom-style">
+ body {
+ overflow: hidden;
+ @apply(--layout-fullbleed);
+ }
+ full-page {
+ @apply(--layout-fit);
+ }
+ </style>
+ </head>
+ <body>
+
+ <full-page></full-page>
+
+ <script>
+
+ document.addEventListener('WebComponentsReady', function() {
+ document.querySelector('full-page').show();
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/reprojection/animated-grid.html b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/animated-grid.html
new file mode 100644
index 00000000..f1244b44
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/animated-grid.html
@@ -0,0 +1,167 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+<link rel="import" href="../../../paper-styles/typography.html">
+<link rel="import" href="../../../paper-styles/color.html">
+<link rel="import" href="../shared-styles.html">
+
+<dom-module id="animated-grid">
+ <template>
+ <style include="shared-styles"></style>
+ <style>
+
+ :host {
+ display: block;
+ background: #000;
+ }
+
+ .tile {
+ display: inline-block;
+ float: left;
+ vertical-align: top;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+
+ @apply(--paper-font-title);
+ @apply(--layout-vertical);
+ @apply(--layout-center-center);
+ }
+
+ .tile:nth-of-type(1) {
+ width: calc(100% / 3);
+ height: calc(100% / 3 * 2);
+ }
+
+ .tile:nth-of-type(4) {
+ width: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(5) {
+ width: calc(100% / 3);
+ height: calc(100% / 3 * 2);
+ }
+
+ .tile:nth-of-type(8) {
+ width: calc(100% / 3);
+ height: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(9) {
+ position: absolute;
+ top: calc(100% / 3 * 2);
+ left: 0;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+ }
+
+ .tile:nth-of-type(10) {
+ position: absolute;
+ top: calc(100% / 3 * 2);
+ left: calc(100% / 6);;
+ width: calc(100% / 6);
+ height: calc(100% / 3);
+ }
+
+ </style>
+
+ <template is="dom-repeat" items="[[config]]">
+ <div class$="[[_computeTileClass(item.color)]]">
+ <span>[[item.value]]</span>
+ </div>
+ </template>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'animated-grid',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+
+ config: {
+ type: Array,
+ value: function() {
+ return [
+ {value: 1, color: 'blue'},
+ {value: 2, color: 'red'},
+ {value: 3, color: 'blue'},
+ {value: 4, color: 'green'},
+ {value: 5, color: 'yellow'},
+ {value: 6, color: 'blue'},
+ {value: 7, color: 'red'},
+ {value: 8, color: 'green'},
+ {value: 9, color: 'yellow'},
+ {value: 10, color: 'red'}
+ ]
+ }
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'exit': [{
+ name: 'ripple-animation',
+ id: 'ripple',
+ fromPage: this
+ }, {
+ name: 'hero-animation',
+ id: 'hero',
+ fromPage: this
+ }]
+ }
+ }
+ }
+
+ },
+
+ listeners: {
+ click: '_onClick'
+ },
+
+ _computeTileClass: function(color) {
+ return 'tile ' + color + '-300';
+ },
+
+ _onClick: function(event) {
+ var target = event.target;
+ while (target !== this && !target._templateInstance) {
+ target = target.parentNode;
+ }
+
+ // configure the page animation
+ this.sharedElements = {
+ 'hero': target,
+ 'ripple': target
+ };
+ this.animationConfig['exit'][0].gesture = {
+ x: event.x,
+ y: event.y
+ };
+
+ this.fire('tile-click', {
+ tile: target,
+ data: target._templateInstance.item
+ });
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/reprojection/fullsize-page-with-card.html b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/fullsize-page-with-card.html
new file mode 100644
index 00000000..83b8a917
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/fullsize-page-with-card.html
@@ -0,0 +1,120 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+<link rel="import" href="../shared-styles.html">
+
+<dom-module id="fullsize-page-with-card">
+ <template>
+ <style include="shared-styles"></style>
+ <style>
+
+ :host {
+ display: block;
+ }
+
+ .fixed {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100vh;
+ width: 100vw;
+ }
+
+ .card {
+ position: relative;
+ margin: 200px 100px 0;
+ height: 700px;
+ }
+
+ </style>
+
+ <div id="fixed" class$="[[_computeFixedBackgroundClass(color)]]"></div>
+ <div id="card" class$="[[_computeCardClass(color)]]"></div>
+
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'fullsize-page-with-card',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+
+ color: {
+ type: String
+ },
+
+ sharedElements: {
+ type: Object,
+ value: function() {
+ return {
+ 'hero': this.$.card,
+ 'ripple': this.$.fixed
+ }
+ }
+ },
+
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'ripple-animation',
+ id: 'ripple',
+ toPage: this,
+ }, {
+ name: 'hero-animation',
+ id: 'hero',
+ toPage: this,
+ timing: {
+ delay: 150
+ }
+ }],
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this.$.fixed
+ }, {
+ name: 'transform-animation',
+ transformFrom: 'none',
+ transformTo: 'translate(0px,-200vh) scale(0.9,1)',
+ node: this.$.card
+ }]
+ }
+ }
+ }
+
+ },
+
+ _computeCardClass: function(color) {
+ var cls = 'card';
+ if (color) {
+ cls += ' ' + color + '-300';
+ }
+ return cls;
+ },
+
+ _computeFixedBackgroundClass: function(color) {
+ var cls = 'fixed';
+ if (color) {
+ cls += ' ' + color + '-100';
+ }
+ return cls;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/reprojection/index.html b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/index.html
new file mode 100644
index 00000000..591d63c6
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/index.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: grid</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../neon-animations.html">
+ <link rel="import" href="reprojected-pages.html">
+ <link rel="import" href="animated-grid.html">
+ <link rel="import" href="fullsize-page-with-card.html">
+
+ <style is="custom-style">
+ body {
+ overflow: hidden;
+ @apply(--layout-fullbleed);
+ }
+ reprojected-pages {
+ height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+ <template is="dom-bind">
+
+ <reprojected-pages id="pages" selected="0">
+ <animated-grid on-tile-click="_onTileClick"></animated-grid>
+ <fullsize-page-with-card id="fullsize-card" hero-id="hero" on-click="_onFullsizeClick">
+ </fullsize-page-with-card>
+ </reprojected-pages>
+
+ </template>
+
+ <script>
+
+ var scope = document.querySelector('template[is="dom-bind"]');
+
+ scope._onTileClick = function(event) {
+ this.$['fullsize-card'].color = event.detail.data.color;
+ this.$.pages.selected = 1;
+ };
+
+ scope._onFullsizeClick = function(event) {
+ this.$.pages.selected = 0;
+ };
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/reprojection/reprojected-pages.html b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/reprojected-pages.html
new file mode 100644
index 00000000..e71f2e05
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/reprojection/reprojected-pages.html
@@ -0,0 +1,45 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../neon-animated-pages.html">
+
+<dom-module id="reprojected-pages">
+ <template>
+ <style>
+ neon-animated-pages {
+ height: 100%;
+ }
+ </style>
+ <neon-animated-pages selected="{{selected}}">
+ <content></content>
+ </neon-animated-pages>
+
+ </template>
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'reprojected-pages',
+
+ properties: {
+
+ selected: {
+ type: String,
+ notify: true
+ }
+
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/shared-styles.html b/catapult/third_party/polymer/components/neon-animation/demo/shared-styles.html
new file mode 100644
index 00000000..4e48c8c4
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/shared-styles.html
@@ -0,0 +1,47 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<dom-module id="shared-styles">
+ <template>
+ <style>
+ .red-100 {
+ background: var(--google-red-100);
+ }
+
+ .yellow-100 {
+ background: var(--google-yellow-100);
+ }
+
+ .blue-100 {
+ background: var(--google-blue-100);
+ }
+
+ .green-100 {
+ background: var(--google-green-100);
+ }
+
+ .red-300 {
+ background: var(--google-red-300);
+ }
+
+ .yellow-300 {
+ background: var(--google-yellow-300);
+ }
+
+ .blue-300 {
+ background: var(--google-blue-300);
+ }
+
+ .green-300 {
+ background: var(--google-green-300);
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/tiles/circles-page.html b/catapult/third_party/polymer/components/neon-animation/demo/tiles/circles-page.html
new file mode 100644
index 00000000..79dc8e34
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/tiles/circles-page.html
@@ -0,0 +1,107 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+
+<dom-module id="circles-page">
+ <template>
+ <style>
+ :host {
+ @apply(--layout-horizontal);
+ @apply(--layout-center-center);
+ }
+
+ .circle {
+ display: inline-block;
+ box-sizing: border-box;
+ width: 100px;
+ height: 100px;
+ margin: 16px;
+ border-radius: 50%;
+ background: var(--color-one);
+ }
+ </style>
+
+ <div>
+ <div class="circle"></div>
+ <div class="circle"></div>
+ <div class="circle"></div>
+ <div class="circle"></div>
+ </div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'circles-page',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+
+ animationConfig: {
+ value: function() {
+ var circles = Polymer.dom(this.root).querySelectorAll('.circle');
+ var circlesArray = Array.prototype.slice.call(circles);
+ return {
+ 'entry': [{
+ name: 'cascaded-animation',
+ animation: 'scale-up-animation',
+ nodes: circlesArray
+ }],
+
+ 'exit': [{
+ name: 'hero-animation',
+ id: 'hero',
+ fromPage: this
+ }, {
+ name: 'cascaded-animation',
+ animation: 'scale-down-animation'
+ }]
+ };
+ }
+ }
+ },
+
+ listeners: {
+ 'click': '_onClick'
+ },
+
+ _onClick: function(event) {
+ var target = Polymer.dom(event).rootTarget;
+ if (target.classList.contains('circle')) {
+ // configure the page animation
+ this.sharedElements = {
+ 'hero': target
+ };
+
+ var nodesToScale = [];
+ var circles = Polymer.dom(this.root).querySelectorAll('.circle');
+ for (var node, index = 0; node = circles[index]; index++) {
+ if (node !== event.target) {
+ nodesToScale.push(node);
+ }
+ }
+ this.animationConfig['exit'][1].nodes = nodesToScale;
+
+ this.fire('circle-click');
+ }
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/tiles/index.html b/catapult/third_party/polymer/components/neon-animation/demo/tiles/index.html
new file mode 100644
index 00000000..f509f0b5
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/tiles/index.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>neon-animated-pages demo: tiles</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../neon-animated-pages.html">
+ <link rel="import" href="../../neon-animations.html">
+ <link rel="import" href="../../../paper-styles/color.html">
+ <link rel="import" href="circles-page.html">
+ <link rel="import" href="squares-page.html">
+
+ <style is="custom-style">
+ body {
+ overflow: hidden;
+ @apply(--layout-fullbleed);
+ }
+ neon-animated-pages {
+ height: 100%;
+ }
+
+ :root {
+ --color-one: var(--paper-cyan-300);
+ --color-two: var(--paper-orange-500);
+ }
+ </style>
+
+ </head>
+ <body>
+
+ <template is="dom-bind">
+
+ <neon-animated-pages id="pages" selected="0">
+ <circles-page on-circle-click="_onCircleClick"></circles-page>
+ <squares-page on-click="_onSquaresClick"></squares-page>
+ </neon-animated-pages>
+
+ </template>
+
+ <script>
+
+ var scope = document.querySelector('template[is="dom-bind"]');
+
+ scope._onCircleClick = function(event) {
+ this.$.pages.selected = 1;
+ };
+
+ scope._onSquaresClick = function(event) {
+ this.$.pages.selected = 0;
+ };
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/demo/tiles/squares-page.html b/catapult/third_party/polymer/components/neon-animation/demo/tiles/squares-page.html
new file mode 100644
index 00000000..52fe7e18
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/demo/tiles/squares-page.html
@@ -0,0 +1,100 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../../polymer/polymer.html">
+<link rel="import" href="../../neon-shared-element-animatable-behavior.html">
+
+<dom-module id="squares-page">
+ <template>
+ <style>
+ .header {
+ height: 40%;
+ background: var(--color-one);
+ }
+
+ .body {
+ text-align: center;
+ padding: 8px;
+ }
+
+ .square {
+ display: inline-block;
+ width: 150px;
+ height: 150px;
+ margin: 8px;
+ background: var(--color-two);
+ }
+ </style>
+
+ <div id="header" class="header"></div>
+
+ <div class="body">
+ <div class="square"></div>
+ <div class="square"></div>
+ <div class="square"></div>
+ <div class="square"></div>
+ </div>
+
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'squares-page',
+
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior
+ ],
+
+ properties: {
+
+ sharedElements: {
+ value: function() {
+ return {
+ 'hero': this.$.header
+ }
+ }
+ },
+
+ animationConfig: {
+ value: function() {
+ var squares = Polymer.dom(this.root).querySelectorAll('.square');
+ var squaresArray = Array.prototype.slice.call(squares);
+ return {
+ 'entry': [{
+ name: 'hero-animation',
+ id: 'hero',
+ toPage: this
+ }, {
+ name: 'cascaded-animation',
+ animation: 'transform-animation',
+ transformFrom: 'translateY(100%)',
+ nodes: squaresArray
+ }],
+
+ 'exit': [{
+ name: 'slide-up-animation',
+ node: this.$.header
+ }, {
+ name: 'cascaded-animation',
+ animation: 'transform-animation',
+ transformTo: 'translateY(60vh)',
+ nodes: squaresArray
+ }]
+ };
+ }
+ }
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/guides/neon-animation.md b/catapult/third_party/polymer/components/neon-animation/guides/neon-animation.md
new file mode 100644
index 00000000..69727b86
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/guides/neon-animation.md
@@ -0,0 +1,314 @@
+---
+title: neon-animation
+summary: "A short guide to neon-animation and neon-animated-pages"
+tags: ['animation','core-animated-pages']
+elements: ['neon-animation','neon-animated-pages']
+updated: 2015-05-26
+---
+
+# neon-animation
+
+`neon-animation` is a suite of elements and behaviors to implement pluggable animated transitions for Polymer Elements using [Web Animations](https://w3c.github.io/web-animations/).
+
+*Warning: The API may change.*
+
+* [A basic animatable element](#basic)
+* [Animation configuration](#configuration)
+ * [Animation types](#configuration-types)
+ * [Configuration properties](#configuration-properties)
+ * [Using multiple animations](#configuration-multiple)
+ * [Running animations encapsulated in children nodes](#configuration-encapsulation)
+* [Page transitions](#page-transitions)
+ * [Shared element animations](#shared-element)
+ * [Declarative page transitions](#declarative-page)
+* [Included animations](#animations)
+* [Demos](#demos)
+
+<a name="basic"></a>
+## A basic animatable element
+
+Elements that can be animated should implement the `Polymer.NeonAnimatableBehavior` behavior, or `Polymer.NeonAnimationRunnerBehavior` if they're also responsible for running an animation.
+
+```js
+Polymer({
+ is: 'my-animatable',
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+ properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ // provided by neon-animation/animations/scale-down-animation.html
+ name: 'scale-down-animation',
+ node: this
+ }
+ }
+ }
+ },
+ listeners: {
+ // this event is fired when the animation finishes
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+ animate: function() {
+ // run scale-down-animation
+ this.playAnimation();
+ },
+ _onNeonAnimationFinish: function() {
+ console.log('animation done!');
+ }
+});
+```
+
+[Live demo](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/doc/basic.html)
+
+<a name="configuration"></a>
+## Animation configuration
+
+<a name="configuration-types"></a>
+### Animation types
+
+An element might run different animations, for example it might do something different when it enters the view and when it exits from view. You can set the `animationConfig` property to a map from an animation type to configuration.
+
+```js
+Polymer({
+ is: 'my-dialog',
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+ properties: {
+ opened: {
+ type: Boolean
+ },
+ animationConfig: {
+ value: function() {
+ return {
+ 'entry': {
+ // provided by neon-animation/animations/scale-up-animation.html
+ name: 'scale-up-animation',
+ node: this
+ },
+ 'exit': {
+ // provided by neon-animation-animations/fade-out-animation.html
+ name: 'fade-out-animation',
+ node: this
+ }
+ }
+ }
+ }
+ },
+ listeners: {
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+ show: function() {
+ this.opened = true;
+ this.style.display = 'inline-block';
+ // run scale-up-animation
+ this.playAnimation('entry');
+ },
+ hide: function() {
+ this.opened = false;
+ // run fade-out-animation
+ this.playAnimation('exit');
+ },
+ _onNeonAnimationFinish: function() {
+ if (!this.opened) {
+ this.style.display = 'none';
+ }
+ }
+});
+```
+
+[Live demo](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/doc/types.html)
+
+You can also use the convenience properties `entryAnimation` and `exitAnimation` to set `entry` and `exit` animations:
+
+```js
+properties: {
+ entryAnimation: {
+ value: 'scale-up-animation'
+ },
+ exitAnimation: {
+ value: 'fade-out-animation'
+ }
+}
+```
+
+<a name="configuration-properties"></a>
+### Configuration properties
+
+You can pass additional parameters to configure an animation in the animation configuration object.
+All animations should accept the following properties:
+
+ * `name`: The name of an animation, ie. an element implementing `Polymer.NeonAnimationBehavior`.
+ * `node`: The target node to apply the animation to. Defaults to `this`.
+ * `timing`: Timing properties to use in this animation. They match the [Web Animations Animation Effect Timing interface](https://w3c.github.io/web-animations/#the-animationeffecttiming-interface). The
+ properties include the following:
+ * `duration`: The duration of the animation in milliseconds.
+ * `delay`: The delay before the start of the animation in milliseconds.
+ * `easing`: A timing function for the animation. Matches the CSS timing function values.
+
+Animations may define additional configuration properties and they are listed in their documentation.
+
+<a name="configuration-multiple"></a>
+### Using multiple animations
+
+Set the animation configuration to an array to combine animations, like this:
+
+```js
+animationConfig: {
+ value: function() {
+ return {
+ // fade-in-animation is run with a 50ms delay from slide-down-animation
+ 'entry': [{
+ name: 'slide-down-animation',
+ node: this
+ }, {
+ name: 'fade-in-animation',
+ node: this,
+ timing: {delay: 50}
+ }]
+ }
+ }
+}
+```
+
+<a name="configuration-encapsulation"></a>
+### Running animations encapsulated in children nodes
+
+You can include animations in the configuration that are encapsulated in a child element that implement `Polymer.NeonAnimatableBehavior` with the `animatable` property.
+
+```js
+animationConfig: {
+ value: function() {
+ return {
+ // run fade-in-animation on this, and the entry animation on this.$.myAnimatable
+ 'entry': [
+ {name: 'fade-in-animation', node: this},
+ {animatable: this.$.myAnimatable, type: 'entry'}
+ ]
+ }
+ }
+}
+```
+
+<a name="page-transitions"></a>
+## Page transitions
+
+*The artist formerly known as `<core-animated-pages>`*
+
+The `neon-animated-pages` element manages a set of pages to switch between, and runs animations between the page transitions. It implements the `Polymer.IronSelectableBehavior` behavior. Each child node should implement `Polymer.NeonAnimatableBehavior` and define the `entry` and `exit` animations. During a page transition, the `entry` animation is run on the new page and the `exit` animation is run on the old page.
+
+<a name="shared-element"></a>
+### Shared element animations
+
+Shared element animations work on multiple nodes. For example, a "hero" animation is used during a page transition to make two elements from separate pages appear to animate as a single element. Shared element animation configurations have an `id` property that identify they belong in the same animation. Elements containing shared elements also have a `sharedElements` property defines a map from `id` to element, the element involved with the animation.
+
+In the incoming page:
+
+```js
+properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ // the incoming page defines the 'entry' animation
+ 'entry': {
+ name: 'hero-animation',
+ id: 'hero',
+ toPage: this
+ }
+ }
+ }
+ },
+ sharedElements: {
+ value: function() {
+ return {
+ 'hero': this.$.hero
+ }
+ }
+ }
+}
+```
+
+In the outgoing page:
+
+```js
+properties: {
+ animationConfig: {
+ value: function() {
+ return {
+ // the outgoing page defines the 'exit' animation
+ 'exit': {
+ name: 'hero-animation',
+ id: 'hero',
+ fromPage: this
+ }
+ }
+ }
+ },
+ sharedElements: {
+ value: function() {
+ return {
+ 'hero': this.$.otherHero
+ }
+ }
+ }
+}
+```
+
+<a name="declarative-page"></a>
+### Declarative page transitions
+
+For convenience, if you define the `entry-animation` and `exit-animation` attributes on `<neon-animated-pages>`, those animations will apply for all page transitions.
+
+For example:
+
+```js
+<neon-animated-pages id="pages" class="flex" selected="[[selected]]" entry-animation="slide-from-right-animation" exit-animation="slide-left-animation">
+ <neon-animatable>1</neon-animatable>
+ <neon-animatable>2</neon-animatable>
+ <neon-animatable>3</neon-animatable>
+ <neon-animatable>4</neon-animatable>
+ <neon-animatable>5</neon-animatable>
+</neon-animated-pages>
+```
+
+The new page will slide in from the right, and the old page slide away to the left.
+
+<a name="animations"></a>
+## Included animations
+
+Single element animations:
+
+ * `fade-in-animation` Animates opacity from `0` to `1`;
+ * `fade-out-animation` Animates opacity from `1` to `0`;
+ * `scale-down-animation` Animates transform from `scale(1)` to `scale(0)`;
+ * `scale-up-animation` Animates transform from `scale(0)` to `scale(1)`;
+ * `slide-down-animation` Animates transform from `none` to `translateY(100%)`;
+ * `slide-up-animation` Animates transform from `none` to `translateY(-100%)`;
+ * `slide-from-top-animation` Animates transform from `translateY(-100%)` to `none`;
+ * `slide-from-bottom-animation` Animates transform from `translateY(100%)` to `none`;
+ * `slide-left-animation` Animates transform from `none` to `translateX(-100%)`;
+ * `slide-right-animation` Animates transform from `none` to `translateX(100%)`;
+ * `slide-from-left-animation` Animates transform from `translateX(-100%)` to `none`;
+ * `slide-from-right-animation` Animates transform from `translateX(100%)` to `none`;
+ * `transform-animation` Animates a custom transform.
+
+Note that there is a restriction that only one transform animation can be applied on the same element at a time. Use the custom `transform-animation` to combine transform properties.
+
+Shared element animations
+
+ * `hero-animation` Animates an element such that it looks like it scales and transforms from another element.
+ * `ripple-animation` Animates an element to full screen such that it looks like it ripples from another element.
+
+Group animations
+ * `cascaded-animation` Applys an animation to an array of elements with a delay between each.
+
+<a name="demos"></a>
+## Demos
+
+ * [Grid to full screen](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/grid/index.html)
+ * [Animation on load](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/load/index.html)
+ * [List item to detail](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/list/index.html) (For narrow width)
+ * [Dots to squares](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/tiles/index.html)
+ * [Declarative](http://morethanreal.github.io/neon-animation-demo/bower_components/neon-animation/demo/declarative/index.html)
diff --git a/catapult/third_party/polymer/components/neon-animation/index.html b/catapult/third_party/polymer/components/neon-animation/index.html
new file mode 100644
index 00000000..6f5feedf
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>neon-animation</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-animatable-behavior.html b/catapult/third_party/polymer/components/neon-animation/neon-animatable-behavior.html
new file mode 100644
index 00000000..dd58a6cf
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-animatable-behavior.html
@@ -0,0 +1,150 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+
+ /**
+ * `Polymer.NeonAnimatableBehavior` is implemented by elements containing animations for use with
+ * elements implementing `Polymer.NeonAnimationRunnerBehavior`.
+ * @polymerBehavior
+ */
+ Polymer.NeonAnimatableBehavior = {
+
+ properties: {
+
+ /**
+ * Animation configuration. See README for more info.
+ */
+ animationConfig: {
+ type: Object
+ },
+
+ /**
+ * Convenience property for setting an 'entry' animation. Do not set `animationConfig.entry`
+ * manually if using this. The animated node is set to `this` if using this property.
+ */
+ entryAnimation: {
+ observer: '_entryAnimationChanged',
+ type: String
+ },
+
+ /**
+ * Convenience property for setting an 'exit' animation. Do not set `animationConfig.exit`
+ * manually if using this. The animated node is set to `this` if using this property.
+ */
+ exitAnimation: {
+ observer: '_exitAnimationChanged',
+ type: String
+ }
+
+ },
+
+ _entryAnimationChanged: function() {
+ this.animationConfig = this.animationConfig || {};
+ this.animationConfig['entry'] = [{
+ name: this.entryAnimation,
+ node: this
+ }];
+ },
+
+ _exitAnimationChanged: function() {
+ this.animationConfig = this.animationConfig || {};
+ this.animationConfig['exit'] = [{
+ name: this.exitAnimation,
+ node: this
+ }];
+ },
+
+ _copyProperties: function(config1, config2) {
+ // shallowly copy properties from config2 to config1
+ for (var property in config2) {
+ config1[property] = config2[property];
+ }
+ },
+
+ _cloneConfig: function(config) {
+ var clone = {
+ isClone: true
+ };
+ this._copyProperties(clone, config);
+ return clone;
+ },
+
+ _getAnimationConfigRecursive: function(type, map, allConfigs) {
+ if (!this.animationConfig) {
+ return;
+ }
+
+ if(this.animationConfig.value && typeof this.animationConfig.value === 'function') {
+ this._warn(this._logf('playAnimation', "Please put 'animationConfig' inside of your components 'properties' object instead of outside of it."));
+ return;
+ }
+
+ // type is optional
+ var thisConfig;
+ if (type) {
+ thisConfig = this.animationConfig[type];
+ } else {
+ thisConfig = this.animationConfig;
+ }
+
+ if (!Array.isArray(thisConfig)) {
+ thisConfig = [thisConfig];
+ }
+
+ // iterate animations and recurse to process configurations from child nodes
+ if (thisConfig) {
+ for (var config, index = 0; config = thisConfig[index]; index++) {
+ if (config.animatable) {
+ config.animatable._getAnimationConfigRecursive(config.type || type, map, allConfigs);
+ } else {
+ if (config.id) {
+ var cachedConfig = map[config.id];
+ if (cachedConfig) {
+ // merge configurations with the same id, making a clone lazily
+ if (!cachedConfig.isClone) {
+ map[config.id] = this._cloneConfig(cachedConfig)
+ cachedConfig = map[config.id];
+ }
+ this._copyProperties(cachedConfig, config);
+ } else {
+ // put any configs with an id into a map
+ map[config.id] = config;
+ }
+ } else {
+ allConfigs.push(config);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * An element implementing `Polymer.NeonAnimationRunnerBehavior` calls this method to configure
+ * an animation with an optional type. Elements implementing `Polymer.NeonAnimatableBehavior`
+ * should define the property `animationConfig`, which is either a configuration object
+ * or a map of animation type to array of configuration objects.
+ */
+ getAnimationConfig: function(type) {
+ var map = {};
+ var allConfigs = [];
+ this._getAnimationConfigRecursive(type, map, allConfigs);
+ // append the configurations saved in the map to the array
+ for (var key in map) {
+ allConfigs.push(map[key]);
+ }
+ return allConfigs;
+ }
+
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-animatable.html b/catapult/third_party/polymer/components/neon-animation/neon-animatable.html
new file mode 100644
index 00000000..267ae5ed
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-animatable.html
@@ -0,0 +1,54 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="neon-animatable-behavior.html">
+
+<!--
+`<neon-animatable>` is a simple container element implementing `Polymer.NeonAnimatableBehavior`. This is a convenience element for use with `<neon-animated-pages>`.
+
+```
+<neon-animated-pages selected="0"
+ entry-animation="slide-from-right-animation"
+ exit-animation="slide-left-animation">
+ <neon-animatable>1</neon-animatable>
+ <neon-animatable>2</neon-animatable>
+</neon-animated-pages>
+```
+-->
+
+<dom-module id="neon-animatable">
+ <template>
+ <style>
+ :host {
+ display: block;
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+
+ Polymer({
+
+ is: 'neon-animatable',
+
+ behaviors: [
+ Polymer.NeonAnimatableBehavior,
+ Polymer.IronResizableBehavior
+ ]
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-animated-pages.html b/catapult/third_party/polymer/components/neon-animation/neon-animated-pages.html
new file mode 100644
index 00000000..983cb471
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-animated-pages.html
@@ -0,0 +1,220 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="../iron-selector/iron-selectable.html">
+<link rel="import" href="neon-animation-runner-behavior.html">
+
+<!--
+Material design: [Meaningful transitions](https://www.google.com/design/spec/animation/meaningful-transitions.html)
+
+`neon-animated-pages` manages a set of pages and runs an animation when switching between them. Its
+children pages should implement `Polymer.NeonAnimatableBehavior` and define `entry` and `exit`
+animations to be run when switching to or switching out of the page.
+
+@group Neon Elements
+@element neon-animated-pages
+@demo demo/index.html
+-->
+
+<dom-module id="neon-animated-pages">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ }
+
+ :host > ::content > * {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ }
+
+ :host > ::content > :not(.iron-selected):not(.neon-animating) {
+ display: none !important;
+ }
+
+ :host > ::content > .neon-animating {
+ pointer-events: none;
+ }
+ </style>
+
+ <content id="content"></content>
+ </template>
+
+</dom-module>
+
+<script>
+(function() {
+
+ Polymer({
+
+ is: 'neon-animated-pages',
+
+ behaviors: [
+ Polymer.IronResizableBehavior,
+ Polymer.IronSelectableBehavior,
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ properties: {
+
+ activateEvent: {
+ type: String,
+ value: ''
+ },
+
+ // if true, the initial page selection will also be animated according to its animation config.
+ animateInitialSelection: {
+ type: Boolean,
+ value: false
+ }
+
+ },
+
+ listeners: {
+ 'iron-select': '_onIronSelect',
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+
+ _onIronSelect: function(event) {
+ var selectedPage = event.detail.item;
+
+ // Only consider child elements.
+ if (this.items.indexOf(selectedPage) < 0) {
+ return;
+ }
+
+ var oldPage = this._valueToItem(this._prevSelected) || false;
+ this._prevSelected = this.selected;
+
+ // on initial load and if animateInitialSelection is negated, simply display selectedPage.
+ if (!oldPage && !this.animateInitialSelection) {
+ this._completeSelectedChanged();
+ return;
+ }
+
+ this.animationConfig = [];
+
+ // configure selectedPage animations.
+ if (this.entryAnimation) {
+ this.animationConfig.push({
+ name: this.entryAnimation,
+ node: selectedPage
+ });
+ } else {
+ if (selectedPage.getAnimationConfig) {
+ this.animationConfig.push({
+ animatable: selectedPage,
+ type: 'entry'
+ });
+ }
+ }
+
+ // configure oldPage animations iff exists.
+ if (oldPage) {
+
+ // cancel the currently running animation if one is ongoing.
+ if (oldPage.classList.contains('neon-animating')) {
+ this._squelchNextFinishEvent = true;
+ this.cancelAnimation();
+ this._completeSelectedChanged();
+ this._squelchNextFinishEvent = false;
+ }
+
+ // configure the animation.
+ if (this.exitAnimation) {
+ this.animationConfig.push({
+ name: this.exitAnimation,
+ node: oldPage
+ });
+ } else {
+ if (oldPage.getAnimationConfig) {
+ this.animationConfig.push({
+ animatable: oldPage,
+ type: 'exit'
+ });
+ }
+ }
+
+ // display the oldPage during the transition.
+ oldPage.classList.add('neon-animating');
+ }
+
+ // display the selectedPage during the transition.
+ selectedPage.classList.add('neon-animating');
+
+ // actually run the animations.
+ if (this.animationConfig.length >= 1) {
+
+ // on first load, ensure we run animations only after element is attached.
+ if (!this.isAttached) {
+ this.async(function () {
+ this.playAnimation(undefined, {
+ fromPage: null,
+ toPage: selectedPage
+ });
+ });
+
+ } else {
+ this.playAnimation(undefined, {
+ fromPage: oldPage,
+ toPage: selectedPage
+ });
+ }
+
+ } else {
+ this._completeSelectedChanged(oldPage, selectedPage);
+ }
+ },
+
+ /**
+ * @param {Object=} oldPage
+ * @param {Object=} selectedPage
+ */
+ _completeSelectedChanged: function(oldPage, selectedPage) {
+ if (selectedPage) {
+ selectedPage.classList.remove('neon-animating');
+ }
+ if (oldPage) {
+ oldPage.classList.remove('neon-animating');
+ }
+ if (!selectedPage || !oldPage) {
+ var nodes = Polymer.dom(this.$.content).getDistributedNodes();
+ for (var node, index = 0; node = nodes[index]; index++) {
+ node.classList && node.classList.remove('neon-animating');
+ }
+ }
+ this.async(this._notifyPageResize);
+ },
+
+ _onNeonAnimationFinish: function(event) {
+ if (this._squelchNextFinishEvent) {
+ this._squelchNextFinishEvent = false;
+ return;
+ }
+ this._completeSelectedChanged(event.detail.fromPage, event.detail.toPage);
+ },
+
+ _notifyPageResize: function() {
+ var selectedPage = this.selectedItem || this._valueToItem(this.selected);
+ this.resizerShouldNotify = function(element) {
+ return element == selectedPage;
+ }
+ this.notifyResize();
+ }
+
+ })
+
+})();
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-animation-behavior.html b/catapult/third_party/polymer/components/neon-animation/neon-animation-behavior.html
new file mode 100644
index 00000000..6939d342
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-animation-behavior.html
@@ -0,0 +1,86 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-meta/iron-meta.html">
+
+<script>
+
+ /**
+ * Use `Polymer.NeonAnimationBehavior` to implement an animation.
+ * @polymerBehavior
+ */
+ Polymer.NeonAnimationBehavior = {
+
+ properties: {
+
+ /**
+ * Defines the animation timing.
+ */
+ animationTiming: {
+ type: Object,
+ value: function() {
+ return {
+ duration: 500,
+ easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
+ fill: 'both'
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Can be used to determine that elements implement this behavior.
+ */
+ isNeonAnimation: true,
+
+ /**
+ * Do any animation configuration here.
+ */
+ // configure: function(config) {
+ // },
+
+ /**
+ * Returns the animation timing by mixing in properties from `config` to the defaults defined
+ * by the animation.
+ */
+ timingFromConfig: function(config) {
+ if (config.timing) {
+ for (var property in config.timing) {
+ this.animationTiming[property] = config.timing[property];
+ }
+ }
+ return this.animationTiming;
+ },
+
+ /**
+ * Sets `transform` and `transformOrigin` properties along with the prefixed versions.
+ */
+ setPrefixedProperty: function(node, property, value) {
+ var map = {
+ 'transform': ['webkitTransform'],
+ 'transformOrigin': ['mozTransformOrigin', 'webkitTransformOrigin']
+ };
+ var prefixes = map[property];
+ for (var prefix, index = 0; prefix = prefixes[index]; index++) {
+ node.style[prefix] = value;
+ }
+ node.style[property] = value;
+ },
+
+ /**
+ * Called when the animation finishes.
+ */
+ complete: function() {}
+
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-animation-runner-behavior.html b/catapult/third_party/polymer/components/neon-animation/neon-animation-runner-behavior.html
new file mode 100644
index 00000000..67c11b68
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-animation-runner-behavior.html
@@ -0,0 +1,129 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-meta/iron-meta.html">
+<link rel="import" href="neon-animatable-behavior.html">
+
+<script>
+
+ /**
+ * `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations.
+ *
+ * @polymerBehavior Polymer.NeonAnimationRunnerBehavior
+ */
+ Polymer.NeonAnimationRunnerBehaviorImpl = {
+
+ _configureAnimations: function(configs) {
+ var results = [];
+ if (configs.length > 0) {
+ for (var config, index = 0; config = configs[index]; index++) {
+ var neonAnimation = document.createElement(config.name);
+ // is this element actually a neon animation?
+ if (neonAnimation.isNeonAnimation) {
+ var result = null;
+ // configuration or play could fail if polyfills aren't loaded
+ try {
+ result = neonAnimation.configure(config);
+ // Check if we have an Effect rather than an Animation
+ if (typeof result.cancel != 'function') {
+ result = document.timeline.play(result);
+ }
+ } catch (e) {
+ result = null;
+ console.warn('Couldnt play', '(', config.name, ').', e);
+ }
+ if (result) {
+ results.push({
+ neonAnimation: neonAnimation,
+ config: config,
+ animation: result,
+ });
+ }
+ } else {
+ console.warn(this.is + ':', config.name, 'not found!');
+ }
+ }
+ }
+ return results;
+ },
+
+ _shouldComplete: function(activeEntries) {
+ var finished = true;
+ for (var i = 0; i < activeEntries.length; i++) {
+ if (activeEntries[i].animation.playState != 'finished') {
+ finished = false;
+ break;
+ }
+ }
+ return finished;
+ },
+
+ _complete: function(activeEntries) {
+ for (var i = 0; i < activeEntries.length; i++) {
+ activeEntries[i].neonAnimation.complete(activeEntries[i].config);
+ }
+ for (var i = 0; i < activeEntries.length; i++) {
+ activeEntries[i].animation.cancel();
+ }
+ },
+
+ /**
+ * Plays an animation with an optional `type`.
+ * @param {string=} type
+ * @param {!Object=} cookie
+ */
+ playAnimation: function(type, cookie) {
+ var configs = this.getAnimationConfig(type);
+ if (!configs) {
+ return;
+ }
+ this._active = this._active || {};
+ if (this._active[type]) {
+ this._complete(this._active[type]);
+ delete this._active[type];
+ }
+
+ var activeEntries = this._configureAnimations(configs);
+
+ if (activeEntries.length == 0) {
+ this.fire('neon-animation-finish', cookie, {bubbles: false});
+ return;
+ }
+
+ this._active[type] = activeEntries;
+
+ for (var i = 0; i < activeEntries.length; i++) {
+ activeEntries[i].animation.onfinish = function() {
+ if (this._shouldComplete(activeEntries)) {
+ this._complete(activeEntries);
+ delete this._active[type];
+ this.fire('neon-animation-finish', cookie, {bubbles: false});
+ }
+ }.bind(this);
+ }
+ },
+
+ /**
+ * Cancels the currently running animations.
+ */
+ cancelAnimation: function() {
+ for (var k in this._animations) {
+ this._animations[k].cancel();
+ }
+ this._animations = {};
+ }
+ };
+
+ /** @polymerBehavior Polymer.NeonAnimationRunnerBehavior */
+ Polymer.NeonAnimationRunnerBehavior = [
+ Polymer.NeonAnimatableBehavior,
+ Polymer.NeonAnimationRunnerBehaviorImpl
+ ];
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-animation.html b/catapult/third_party/polymer/components/neon-animation/neon-animation.html
new file mode 100644
index 00000000..da645048
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-animation.html
@@ -0,0 +1,18 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="neon-animatable-behavior.html">
+<link rel="import" href="neon-animated-pages.html">
+<link rel="import" href="neon-animatable.html">
+<link rel="import" href="neon-animation-behavior.html">
+<link rel="import" href="neon-animation-runner-behavior.html">
+<link rel="import" href="neon-animations.html">
+<link rel="import" href="neon-shared-element-animatable-behavior.html">
+<link rel="import" href="neon-shared-element-animation-behavior.html">
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-animations.html b/catapult/third_party/polymer/components/neon-animation/neon-animations.html
new file mode 100644
index 00000000..67c4df4c
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-animations.html
@@ -0,0 +1,29 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="animations/cascaded-animation.html">
+<link rel="import" href="animations/fade-in-animation.html">
+<link rel="import" href="animations/fade-out-animation.html">
+<link rel="import" href="animations/hero-animation.html">
+<link rel="import" href="animations/opaque-animation.html">
+<link rel="import" href="animations/ripple-animation.html">
+<link rel="import" href="animations/reverse-ripple-animation.html">
+<link rel="import" href="animations/scale-down-animation.html">
+<link rel="import" href="animations/scale-up-animation.html">
+<link rel="import" href="animations/slide-from-left-animation.html">
+<link rel="import" href="animations/slide-from-right-animation.html">
+<link rel="import" href="animations/slide-from-top-animation.html">
+<link rel="import" href="animations/slide-from-bottom-animation.html">
+<link rel="import" href="animations/slide-left-animation.html">
+<link rel="import" href="animations/slide-right-animation.html">
+<link rel="import" href="animations/slide-up-animation.html">
+<link rel="import" href="animations/slide-down-animation.html">
+<link rel="import" href="animations/transform-animation.html">
+
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-shared-element-animatable-behavior.html b/catapult/third_party/polymer/components/neon-animation/neon-shared-element-animatable-behavior.html
new file mode 100644
index 00000000..e63173d3
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-shared-element-animatable-behavior.html
@@ -0,0 +1,43 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="neon-animatable-behavior.html">
+
+<script>
+
+ /**
+ * Use `Polymer.NeonSharedElementAnimatableBehavior` to implement elements containing shared element
+ * animations.
+ * @polymerBehavior Polymer.NeonSharedElementAnimatableBehavior
+ */
+ Polymer.NeonSharedElementAnimatableBehaviorImpl = {
+
+ properties: {
+
+ /**
+ * A map of shared element id to node.
+ */
+ sharedElements: {
+ type: Object,
+ value: {}
+ }
+
+ }
+
+ };
+
+ /** @polymerBehavior Polymer.NeonSharedElementAnimatableBehavior */
+ Polymer.NeonSharedElementAnimatableBehavior = [
+ Polymer.NeonAnimatableBehavior,
+ Polymer.NeonSharedElementAnimatableBehaviorImpl
+ ];
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/neon-shared-element-animation-behavior.html b/catapult/third_party/polymer/components/neon-animation/neon-shared-element-animation-behavior.html
new file mode 100644
index 00000000..7787615b
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/neon-shared-element-animation-behavior.html
@@ -0,0 +1,72 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="neon-animation-behavior.html">
+
+<script>
+
+ /**
+ * Use `Polymer.NeonSharedElementAnimationBehavior` to implement shared element animations.
+ * @polymerBehavior Polymer.NeonSharedElementAnimationBehavior
+ */
+ Polymer.NeonSharedElementAnimationBehaviorImpl = {
+
+ properties: {
+
+ /**
+ * Cached copy of shared elements.
+ */
+ sharedElements: {
+ type: Object
+ }
+
+ },
+
+ /**
+ * Finds shared elements based on `config`.
+ */
+ findSharedElements: function(config) {
+ var fromPage = config.fromPage;
+ var toPage = config.toPage;
+ if (!fromPage || !toPage) {
+ console.warn(this.is + ':', !fromPage ? 'fromPage' : 'toPage', 'is undefined!');
+ return null;
+ };
+
+ if (!fromPage.sharedElements || !toPage.sharedElements) {
+ console.warn(this.is + ':', 'sharedElements are undefined for', !fromPage.sharedElements ? fromPage : toPage);
+ return null;
+ };
+
+ var from = fromPage.sharedElements[config.id]
+ var to = toPage.sharedElements[config.id];
+
+ if (!from || !to) {
+ console.warn(this.is + ':', 'sharedElement with id', config.id, 'not found in', !from ? fromPage : toPage);
+ return null;
+ }
+
+ this.sharedElements = {
+ from: from,
+ to: to
+ };
+ return this.sharedElements;
+ }
+
+ };
+
+ /** @polymerBehavior Polymer.NeonSharedElementAnimationBehavior */
+ Polymer.NeonSharedElementAnimationBehavior = [
+ Polymer.NeonAnimationBehavior,
+ Polymer.NeonSharedElementAnimationBehaviorImpl
+ ];
+
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/test/index.html b/catapult/third_party/polymer/components/neon-animation/test/index.html
new file mode 100644
index 00000000..81555a2b
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/test/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>neon-animation tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'neon-animated-pages.html',
+ 'neon-animated-pages.html?dom=shadow',
+ 'neon-animated-pages-lazy.html',
+ 'neon-animated-pages-lazy.html?dom=shadow',
+ 'neon-animated-pages-descendant-selection.html',
+ 'neon-animated-pages-descendant-selection.html?dom=shadow',
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-descendant-selection.html b/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-descendant-selection.html
new file mode 100644
index 00000000..0904a395
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-descendant-selection.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<!--
+@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
+-->
+<html>
+<head>
+<meta charset="utf-8">
+<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1">
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+<script src="../../web-component-tester/browser.js"></script>
+<script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+<link rel="import" href="../../test-fixture/test-fixture.html">
+<link rel="import" href="../../neon-animation/neon-animated-pages.html">
+<link rel="import" href="../../neon-animation/neon-animation-behavior.html">
+<link rel="import" href="../../iron-selector/iron-selector.html">
+
+</head>
+<body>
+
+<test-fixture id="descendant-selection">
+ <template>
+ <neon-animated-pages entry-animation="test-animation" animate-initial-selection>
+ <iron-selector selected="0" id="selector">
+ <div>1</div>
+ <div id="target">2</div>
+ </iron-selector>
+ </neon-animated-pages>
+ </template>
+</test-fixture>
+
+<test-fixture id="selecting-item">
+ <template>
+ <neon-animated-pages entry-animation="test-animation" animate-initial-selection>
+ <x-selecting-element></x-selecting-element>
+ <div id="target"></div>
+ </neon-animated-pages>
+ </template>
+</test-fixture>
+
+<dom-module id="x-selecting-element">
+ <template>
+ <iron-selector selected="0" id="selector">
+ <div>1</div>
+ <div id="target">2</div>
+ </iron-selector>
+ </template>
+</dom-module>
+
+<dom-module id="test-element">
+ <template>
+ <neon-animated-pages entry-animation="test-animation" animate-initial-selection>
+ <content></content>
+ </neon-animated-pages>
+ </template>
+</dom-module>
+
+<script>
+ HTMLImports.whenReady(function() {
+ Polymer({ is: 'x-selecting-element' });
+ Polymer({ is: 'test-element' });
+ Polymer({
+ is: 'test-animation',
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+ configure: function(config) {
+ config.node.animated = true;
+ }
+ });
+ });
+</script>
+
+<test-fixture id="distributed-children">
+ <template>
+ <test-element>
+ <div>1</div>
+ <div id="target">2</div>
+ </test-element>
+ </template>
+</test-fixture>
+
+<script>
+suite('descendant selection', function() {
+ test('descendents of other selectors are not animated', function() {
+ var animatedPages = fixture('descendant-selection');
+ var selector = Polymer.dom(animatedPages).querySelector('#selector');
+ var target = Polymer.dom(animatedPages).querySelector('#target');
+ Polymer.dom(selector).setAttribute('selected', '1');
+ assert(!target.animated);
+ });
+ test('elements distributed as children are animated', function() {
+ var testElement = fixture('distributed-children');
+ var target = Polymer.dom(testElement).querySelector('#target');
+ var animatedPages = Polymer.dom(testElement.root).querySelector("neon-animated-pages");
+ Polymer.dom(animatedPages).setAttribute('selected', '1');
+ assert(target.animated);
+ });
+ test('ignores selection from shadow descendants of its items', function() {
+ var pages = fixture('selecting-item');
+ var target = Polymer.dom(pages).querySelector('#target');
+ var selecting = Polymer.dom(pages).querySelector('x-selecting-element');
+ selecting.$.selector.selected = 1;
+ assert(!target.animated);
+ });
+});
+</script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-lazy.html b/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-lazy.html
new file mode 100644
index 00000000..86ef1988
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages-lazy.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+<head>
+
+ <title>neon-animated-pages tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script>Polymer = {lazyRegister: true}</script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../neon-animated-pages.html">
+ <link rel="import" href="../neon-animatable.html">
+ <link rel="import" href="../animations/slide-from-left-animation.html">
+ <link rel="import" href="../animations/slide-right-animation.html">
+ <link rel="import" href="test-resizable-pages.html">
+
+</head>
+<body>
+
+ <test-fixture id="animate-initial-selection">
+ <template>
+ <neon-animated-pages entry-animation="slide-from-left-animation" exit-animation="slide-right-animation" animate-initial-selection>
+ <neon-animatable></neon-animatable>
+ <neon-animatable></neon-animatable>
+ </neon-animated-pages>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('animations found when `lazRegister` setting is true', function() {
+ test('animations are registered', function(done) {
+ var animatedPages = fixture('animate-initial-selection');
+ animatedPages._complete = sinon.spy(animatedPages._complete);
+ assert.isUndefined(animatedPages.selected);
+ var pages = Polymer.dom(animatedPages).children;
+ animatedPages.addEventListener('neon-animation-finish', function(event) {
+ if (animatedPages.selected === 0) {
+ animatedPages.selected = 1;
+ return;
+ }
+ assert.strictEqual(animatedPages.selected, 1);
+ assert.equal(event.detail.fromPage, pages[0]);
+ assert.equal(event.detail.toPage, pages[1]);
+ assert.isTrue(animatedPages._complete.calledTwice);
+ var a$ = animatedPages._complete.getCall(1).args[0];
+ assert.isTrue(a$[0].neonAnimation.isNeonAnimation, 'entry animation is not a registered animation');
+ assert.isTrue(a$[1].neonAnimation.isNeonAnimation, 'exit animation is not a registered animation');
+ done();
+ });
+ animatedPages.selected = 0;
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages.html b/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages.html
new file mode 100644
index 00000000..98220b67
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/test/neon-animated-pages.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>neon-animated-pages tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../neon-animated-pages.html">
+ <link rel="import" href="../neon-animatable.html">
+ <link rel="import" href="../animations/slide-from-left-animation.html">
+ <link rel="import" href="../animations/slide-right-animation.html">
+ <link rel="import" href="test-resizable-pages.html">
+
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <neon-animated-pages>
+ </neon-animated-pages>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="notify-resize">
+ <template>
+ <neon-animated-pages>
+ <a-resizable-page></a-resizable-page>
+ <b-resizable-page></b-resizable-page>
+ <c-resizable-page></c-resizable-page>
+ </neon-animated-pages>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="animate-initial-selection">
+ <template>
+ <neon-animated-pages entry-animation="slide-from-left-animation" exit-animation="slide-right-animation" animate-initial-selection>
+ <neon-animatable></neon-animatable>
+ <neon-animatable></neon-animatable>
+ </neon-animated-pages>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('basic', function() {
+ });
+ suite('notify-resize', function() {
+ test('only a destination page recieves a resize event', function(done) {
+ var animatedPages = fixture('notify-resize');
+ var resizables = Polymer.dom(animatedPages).children;
+ var recieves = {};
+ resizables.forEach(function(page) {
+ page.addEventListener('iron-resize', function(event) {
+ var pageName = event.currentTarget.tagName;
+ recieves[pageName] = pageName in recieves ? recieves[pageName] + 1 : 1;
+ });
+ });
+ animatedPages.selected = 2;
+ setTimeout(function() {
+ assert.deepEqual(recieves, {
+ 'C-RESIZABLE-PAGE': 1
+ });
+ done();
+ }, 50);
+ });
+ });
+ suite('animate-initial-selection', function() {
+ test('\'neon-animation-finish\' event fired after animating initial selection', function(done) {
+ var animatedPages = fixture('animate-initial-selection');
+ assert.isUndefined(animatedPages.selected);
+ var pages = Polymer.dom(animatedPages).children;
+ animatedPages.addEventListener('neon-animation-finish', function(event) {
+ assert.strictEqual(animatedPages.selected, 0);
+ assert.isFalse(event.detail.fromPage);
+ assert.deepEqual(event.detail.toPage, pages[0]);
+ done();
+ });
+ animatedPages.selected = 0;
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/neon-animation/test/test-resizable-pages.html b/catapult/third_party/polymer/components/neon-animation/test/test-resizable-pages.html
new file mode 100644
index 00000000..7330a1ff
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/test/test-resizable-pages.html
@@ -0,0 +1,58 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../neon-shared-element-animatable-behavior.html">
+<link rel="import" href="../../iron-resizable-behavior/iron-resizable-behavior.html">
+
+<dom-module id="a-resizable-page">
+ <template>
+ </template>
+</dom-module>
+
+<dom-module id="b-resizable-page">
+ <template>
+ </template>
+</dom-module>
+
+<dom-module id="c-resizable-page">
+ <template>
+ </template>
+</dom-module>
+
+<script>
+(function() {
+
+ Polymer({
+ is: 'a-resizable-page',
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior,
+ Polymer.IronResizableBehavior
+ ]
+ });
+
+ Polymer({
+ is: 'b-resizable-page',
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior,
+ Polymer.IronResizableBehavior
+ ]
+ });
+
+ Polymer({
+ is: 'c-resizable-page',
+ behaviors: [
+ Polymer.NeonSharedElementAnimatableBehavior,
+ Polymer.IronResizableBehavior
+ ]
+ });
+
+})();
+</script>
diff --git a/catapult/third_party/polymer/components/neon-animation/web-animations.html b/catapult/third_party/polymer/components/neon-animation/web-animations.html
new file mode 100644
index 00000000..c8718540
--- /dev/null
+++ b/catapult/third_party/polymer/components/neon-animation/web-animations.html
@@ -0,0 +1,11 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<script src="../web-animations-js/web-animations-next-lite.min.js"></script>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/.bower.json b/catapult/third_party/polymer/components/paper-behaviors/.bower.json
new file mode 100644
index 00000000..b0eee665
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/.bower.json
@@ -0,0 +1,51 @@
+{
+ "name": "paper-behaviors",
+ "version": "1.0.13",
+ "description": "Common behaviors across the paper elements",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "main": [
+ "paper-button-behavior.html",
+ "paper-checked-element-behavior.html",
+ "paper-inky-focus-behavior.html"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "paper",
+ "behavior"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-behaviors"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-behaviors",
+ "dependencies": {
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#^1.0.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "polymer": "Polymer/polymer#^1.2.1"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "_release": "1.0.13",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.13",
+ "commit": "eb329350960beb6612088bb588f4f3cfeeafd270"
+ },
+ "_source": "https://github.com/PolymerElements/paper-behaviors.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-behaviors"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-behaviors/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-behaviors/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..0a145b76
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-behaviors/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-behaviors/.gitignore b/catapult/third_party/polymer/components/paper-behaviors/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-behaviors/.travis.yml b/catapult/third_party/polymer/components/paper-behaviors/.travis.yml
new file mode 100644
index 00000000..7c55463a
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ TYkyKHfqK31bPGX+x388nfvBWtoGhD7lwYYLdunZi2pwvR2N3ngzblklwK/ADgYVDC0y+lZaJsrODvQW2cUxVdQP8p+DifXoOig5b8S2H7HFFFf83gaXv0SJ50rIaKLQmXfBdI0ICFzeQOeJ9TlKvtF3/yn/LDpYftuzgbXIyp4=
+ - secure: >-
+ eUlJabqBW3P+MRieHvRcr446jhpV2YYRfZasOh3zh2vkAVsi5R5mPJXVBwk6xP4tdNPbRo0M3boKnMHjoK8AgFolxQwXpVaoOt/oiFUsBFIUNcwKnSdfnadQa6ON5VoDmPd9UhDwES/gq7i906XkV/1jwRKKhPkx1DCyCBfuTvM=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-behaviors/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-behaviors/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-behaviors/README.md b/catapult/third_party/polymer/components/paper-behaviors/README.md
new file mode 100644
index 00000000..43475db7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/README.md
@@ -0,0 +1,44 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-button-behavior.html paper-checked-element-behavior.html paper-inky-focus-behavior.html paper-ripple-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-behaviors.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-behaviors)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-behaviors)_
+
+
+<!-- No docs for Polymer.PaperButtonBehavior found. -->
+
+##Polymer.PaperCheckedElementBehavior
+
+Use `Polymer.PaperCheckedElementBehavior` to implement a custom element
+that has a `checked` property similar to `Polymer.IronCheckedElementBehavior`
+and is compatible with having a ripple effect.
+
+
+
+##Polymer.PaperInkyFocusBehavior
+
+`Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus.
+
+
+
+##Polymer.PaperRippleBehavior
+
+`Polymer.PaperRippleBehavior` dynamically implements a ripple
+when the element has focus via pointer or keyboard.
+
+NOTE: This behavior is intended to be used in conjunction with and after
+`Polymer.IronButtonState` and `Polymer.IronControlState`.
+
+
diff --git a/catapult/third_party/polymer/components/paper-behaviors/bower.json b/catapult/third_party/polymer/components/paper-behaviors/bower.json
new file mode 100644
index 00000000..96a8bd62
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/bower.json
@@ -0,0 +1,42 @@
+{
+ "name": "paper-behaviors",
+ "version": "1.0.13",
+ "description": "Common behaviors across the paper elements",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "main": [
+ "paper-button-behavior.html",
+ "paper-checked-element-behavior.html",
+ "paper-inky-focus-behavior.html"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "paper",
+ "behavior"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-behaviors"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-behaviors",
+ "dependencies": {
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#^1.0.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "polymer": "Polymer/polymer#^1.2.1"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "polymerelements/iron-test-helpers#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-behaviors/demo/index.html b/catapult/third_party/polymer/components/paper-behaviors/demo/index.html
new file mode 100644
index 00000000..0303fefc
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/demo/index.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-behaviors demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link href="paper-button.html" rel="import">
+ <link href="paper-radio-button.html" rel="import">
+
+ <style>
+ body {
+ font-family: sans-serif;
+ padding: 24px;
+ margin: 0;
+ }
+ </style>
+ </head>
+ <body unresolved>
+ <h3>Normal</h3>
+ <paper-button tabindex="0">Hello World</paper-button>
+
+ <h3>Toggles</h3>
+ <paper-button toggles tabindex="0">Hello World</paper-button>
+
+ <h3>Disabled</h3>
+ <paper-button disabled tabindex="0">Hello World</paper-button>
+
+ <h3>Radio button with focus state</h3>
+ <paper-radio-button tabindex="0" title="Radio button with focus state"></paper-radio-button>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/demo/paper-button.html b/catapult/third_party/polymer/components/paper-behaviors/demo/paper-button.html
new file mode 100644
index 00000000..238bb170
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/demo/paper-button.html
@@ -0,0 +1,64 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../paper-material/paper-material.html">
+<link rel="import" href="../paper-button-behavior.html">
+
+<dom-module id="paper-button">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ position: relative;
+ background-color: #4285F4;
+ color: #fff;
+ border-radius: 3px;
+ text-transform: uppercase;
+ outline: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ }
+
+ paper-material {
+ border-radius: inherit;
+ padding: 16px;
+ }
+
+ :host([disabled]) {
+ background-color: #888;
+ pointer-events: none;
+ }
+
+ :host([active]),
+ :host([pressed]) {
+ background-color: #3367D6;
+ box-shadow: inset 0 3px 5px rgba(0,0,0,.2);
+ }
+ </style>
+
+ <paper-material class="content" elevation="[[_elevation]]" animated>
+ <content></content>
+ </paper-material>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-button',
+
+ behaviors: [
+ Polymer.PaperButtonBehavior
+ ]
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/demo/paper-radio-button.html b/catapult/third_party/polymer/components/paper-behaviors/demo/paper-radio-button.html
new file mode 100644
index 00000000..b8470860
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/demo/paper-radio-button.html
@@ -0,0 +1,112 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../paper-checked-element-behavior.html">
+
+<dom-module id="paper-radio-button">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ white-space: nowrap;
+ }
+
+ :host(:focus) {
+ outline: none
+ }
+
+ #radioContainer {
+ display: inline-block;
+ position: relative;
+ width: 16px;
+ height: 16px;
+ cursor: pointer;
+ vertical-align: middle;
+ }
+
+ #offRadio {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ border: solid 2px;
+ border-color: black;
+ transition: border-color 0.28s;
+ }
+
+ #onRadio {
+ position: absolute;
+ top: 4px;
+ left: 4px;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: red;
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ transition: -webkit-transform ease 0.28s;
+ transition: transform ease 0.28s;
+ }
+
+ :host([disabled]) {
+ opacity: 0.3;
+ pointer-events: none;
+ }
+
+ :host([pressed]) #offRadio,
+ :host([active]) #offRadio {
+ border-color: red;
+ }
+
+ :host([pressed]) #onRadio,
+ :host([active]) #onRadio {
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ }
+
+ #ink {
+ position: absolute;
+ top: -16px;
+ left: -16px;
+ width: 48px;
+ height: 48px;
+ }
+ </style>
+
+ <div id="radioContainer">
+ <div id="offRadio"></div>
+ <div id="onRadio"></div>
+ </div>
+ </template>
+
+ <script>
+ Polymer({
+ behaviors: [
+ Polymer.PaperCheckedElementBehavior
+ ],
+
+ hostAttributes: {
+ role: 'radio'
+ },
+
+ ready: function() {
+ this.toggles = true;
+ },
+
+ _createRipple: function() {
+ this._rippleContainer = this.$.radioContainer;
+ return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this);
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/index.html b/catapult/third_party/polymer/components/paper-behaviors/index.html
new file mode 100644
index 00000000..37184eaa
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page src="paper-button-behavior.html"></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/paper-button-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/paper-button-behavior.html
new file mode 100644
index 00000000..c4d13302
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/paper-button-behavior.html
@@ -0,0 +1,97 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-behaviors/iron-button-state.html">
+<link rel="import" href="paper-ripple-behavior.html">
+
+<script>
+ /** @polymerBehavior Polymer.PaperButtonBehavior */
+ Polymer.PaperButtonBehaviorImpl = {
+ properties: {
+ /**
+ * The z-depth of this element, from 0-5. Setting to 0 will remove the
+ * shadow, and each increasing number greater than 0 will be "deeper"
+ * than the last.
+ *
+ * @attribute elevation
+ * @type number
+ * @default 1
+ */
+ elevation: {
+ type: Number,
+ reflectToAttribute: true,
+ readOnly: true
+ }
+ },
+
+ observers: [
+ '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)',
+ '_computeKeyboardClass(receivedFocusFromKeyboard)'
+ ],
+
+ hostAttributes: {
+ role: 'button',
+ tabindex: '0',
+ animated: true
+ },
+
+ _calculateElevation: function() {
+ var e = 1;
+ if (this.disabled) {
+ e = 0;
+ } else if (this.active || this.pressed) {
+ e = 4;
+ } else if (this.receivedFocusFromKeyboard) {
+ e = 3;
+ }
+ this._setElevation(e);
+ },
+
+ _computeKeyboardClass: function(receivedFocusFromKeyboard) {
+ this.toggleClass('keyboard-focus', receivedFocusFromKeyboard);
+ },
+
+ /**
+ * In addition to `IronButtonState` behavior, when space key goes down,
+ * create a ripple down effect.
+ *
+ * @param {!KeyboardEvent} event .
+ */
+ _spaceKeyDownHandler: function(event) {
+ Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event);
+ // Ensure that there is at most one ripple when the space key is held down.
+ if (this.hasRipple() && this.getRipple().ripples.length < 1) {
+ this._ripple.uiDownAction();
+ }
+ },
+
+ /**
+ * In addition to `IronButtonState` behavior, when space key goes up,
+ * create a ripple up effect.
+ *
+ * @param {!KeyboardEvent} event .
+ */
+ _spaceKeyUpHandler: function(event) {
+ Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event);
+ if (this.hasRipple()) {
+ this._ripple.uiUpAction();
+ }
+ }
+ };
+
+ /** @polymerBehavior */
+ Polymer.PaperButtonBehavior = [
+ Polymer.IronButtonState,
+ Polymer.IronControlState,
+ Polymer.PaperRippleBehavior,
+ Polymer.PaperButtonBehaviorImpl
+ ];
+</script>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/paper-checked-element-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/paper-checked-element-behavior.html
new file mode 100644
index 00000000..79812a66
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/paper-checked-element-behavior.html
@@ -0,0 +1,57 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-checked-element-behavior/iron-checked-element-behavior.html">
+<link rel="import" href="paper-inky-focus-behavior.html">
+
+<script>
+ /**
+ * Use `Polymer.PaperCheckedElementBehavior` to implement a custom element
+ * that has a `checked` property similar to `Polymer.IronCheckedElementBehavior`
+ * and is compatible with having a ripple effect.
+ * @polymerBehavior Polymer.PaperCheckedElementBehavior
+ */
+ Polymer.PaperCheckedElementBehaviorImpl = {
+ /**
+ * Synchronizes the element's checked state with its ripple effect.
+ */
+ _checkedChanged: function() {
+ Polymer.IronCheckedElementBehaviorImpl._checkedChanged.call(this);
+ if (this.hasRipple()) {
+ if (this.checked) {
+ this._ripple.setAttribute('checked', '');
+ } else {
+ this._ripple.removeAttribute('checked');
+ }
+ }
+ },
+
+ /**
+ * Synchronizes the element's `active` and `checked` state.
+ */
+ _buttonStateChanged: function() {
+ Polymer.PaperRippleBehavior._buttonStateChanged.call(this);
+ if (this.disabled) {
+ return;
+ }
+ if (this.isAttached) {
+ this.checked = this.active;
+ }
+ }
+ };
+
+ /** @polymerBehavior Polymer.PaperCheckedElementBehavior */
+ Polymer.PaperCheckedElementBehavior = [
+ Polymer.PaperInkyFocusBehavior,
+ Polymer.IronCheckedElementBehavior,
+ Polymer.PaperCheckedElementBehaviorImpl
+ ];
+</script>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/paper-inky-focus-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/paper-inky-focus-behavior.html
new file mode 100644
index 00000000..3ecb9ac1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/paper-inky-focus-behavior.html
@@ -0,0 +1,51 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-behaviors/iron-button-state.html">
+<link rel="import" href="paper-ripple-behavior.html">
+
+<script>
+ /**
+ * `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus.
+ *
+ * @polymerBehavior Polymer.PaperInkyFocusBehavior
+ */
+ Polymer.PaperInkyFocusBehaviorImpl = {
+ observers: [
+ '_focusedChanged(receivedFocusFromKeyboard)'
+ ],
+
+ _focusedChanged: function(receivedFocusFromKeyboard) {
+ if (receivedFocusFromKeyboard) {
+ this.ensureRipple();
+ }
+ if (this.hasRipple()) {
+ this._ripple.holdDown = receivedFocusFromKeyboard;
+ }
+ },
+
+ _createRipple: function() {
+ var ripple = Polymer.PaperRippleBehavior._createRipple();
+ ripple.id = 'ink';
+ ripple.setAttribute('center', '');
+ ripple.classList.add('circle');
+ return ripple;
+ }
+ };
+
+ /** @polymerBehavior Polymer.PaperInkyFocusBehavior */
+ Polymer.PaperInkyFocusBehavior = [
+ Polymer.IronButtonState,
+ Polymer.IronControlState,
+ Polymer.PaperRippleBehavior,
+ Polymer.PaperInkyFocusBehaviorImpl
+ ];
+</script>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/paper-ripple-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/paper-ripple-behavior.html
new file mode 100644
index 00000000..aaacf34f
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/paper-ripple-behavior.html
@@ -0,0 +1,126 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../paper-ripple/paper-ripple.html">
+
+<script>
+ /**
+ * `Polymer.PaperRippleBehavior` dynamically implements a ripple
+ * when the element has focus via pointer or keyboard.
+ *
+ * NOTE: This behavior is intended to be used in conjunction with and after
+ * `Polymer.IronButtonState` and `Polymer.IronControlState`.
+ *
+ * @polymerBehavior Polymer.PaperRippleBehavior
+ */
+ Polymer.PaperRippleBehavior = {
+ properties: {
+ /**
+ * If true, the element will not produce a ripple effect when interacted
+ * with via the pointer.
+ */
+ noink: {
+ type: Boolean,
+ observer: '_noinkChanged'
+ },
+
+ /**
+ * @type {Element|undefined}
+ */
+ _rippleContainer: {
+ type: Object,
+ }
+ },
+
+ /**
+ * Ensures a `<paper-ripple>` element is available when the element is
+ * focused.
+ */
+ _buttonStateChanged: function() {
+ if (this.focused) {
+ this.ensureRipple();
+ }
+ },
+
+ /**
+ * In addition to the functionality provided in `IronButtonState`, ensures
+ * a ripple effect is created when the element is in a `pressed` state.
+ */
+ _downHandler: function(event) {
+ Polymer.IronButtonStateImpl._downHandler.call(this, event);
+ if (this.pressed) {
+ this.ensureRipple(event);
+ }
+ },
+
+ /**
+ * Ensures this element contains a ripple effect. For startup efficiency
+ * the ripple effect is dynamically on demand when needed.
+ * @param {!Event=} optTriggeringEvent (optional) event that triggered the
+ * ripple.
+ */
+ ensureRipple: function(optTriggeringEvent) {
+ if (!this.hasRipple()) {
+ this._ripple = this._createRipple();
+ this._ripple.noink = this.noink;
+ var rippleContainer = this._rippleContainer || this.root;
+ if (rippleContainer) {
+ Polymer.dom(rippleContainer).appendChild(this._ripple);
+ }
+ if (optTriggeringEvent) {
+ // Check if the event happened inside of the ripple container
+ // Fall back to host instead of the root because distributed text
+ // nodes are not valid event targets
+ var domContainer = Polymer.dom(this._rippleContainer || this);
+ var target = Polymer.dom(optTriggeringEvent).rootTarget;
+ if (domContainer.deepContains( /** @type {Node} */(target))) {
+ this._ripple.uiDownAction(optTriggeringEvent);
+ }
+ }
+ }
+ },
+
+ /**
+ * Returns the `<paper-ripple>` element used by this element to create
+ * ripple effects. The element's ripple is created on demand, when
+ * necessary, and calling this method will force the
+ * ripple to be created.
+ */
+ getRipple: function() {
+ this.ensureRipple();
+ return this._ripple;
+ },
+
+ /**
+ * Returns true if this element currently contains a ripple effect.
+ * @return {boolean}
+ */
+ hasRipple: function() {
+ return Boolean(this._ripple);
+ },
+
+ /**
+ * Create the element's ripple effect via creating a `<paper-ripple>`.
+ * Override this method to customize the ripple element.
+ * @return {!PaperRippleElement} Returns a `<paper-ripple>` element.
+ */
+ _createRipple: function() {
+ return /** @type {!PaperRippleElement} */ (
+ document.createElement('paper-ripple'));
+ },
+
+ _noinkChanged: function(noink) {
+ if (this.hasRipple()) {
+ this._ripple.noink = noink;
+ }
+ }
+ };
+</script>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/index.html b/catapult/third_party/polymer/components/paper-behaviors/test/index.html
new file mode 100644
index 00000000..693054c0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/index.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="utf-8">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'paper-button-behavior.html',
+ 'paper-radio-button-behavior.html',
+ 'paper-checked-element-behavior.html',
+ 'paper-ripple-behavior.html',
+ 'paper-button-behavior.html?dom=shadow',
+ 'paper-radio-button-behavior.html?dom=shadow',
+ 'paper-checked-element-behavior.html?dom=shadow',
+ 'paper-ripple-behavior.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/paper-button-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/test/paper-button-behavior.html
new file mode 100644
index 00000000..ff859252
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/paper-button-behavior.html
@@ -0,0 +1,113 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+ <title>paper-button-behavior</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="test-button.html">
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <test-button></test-button>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('basic', function() {
+ var button;
+
+ setup(function() {
+ button = fixture('basic');
+ });
+
+ test('normal (no states)', function() {
+ assert.equal(button.elevation, 1);
+ });
+
+ test('set disabled property', function() {
+ button.disabled = true;
+ assert.equal(button.elevation, 0);
+ });
+
+ test('pressed and released', function() {
+ MockInteractions.down(button);
+ assert.equal(button.elevation, 4);
+ MockInteractions.up(button);
+ assert.equal(button.elevation, 1);
+ assert.ok(button.hasRipple());
+ });
+
+ suite('a button with toggles', function() {
+ setup(function() {
+ button.toggles = true;
+ });
+
+ test('activated by tap', function(done) {
+ MockInteractions.downAndUp(button, function() {
+ try {
+ assert.equal(button.elevation, 4);
+ assert.ok(button.hasRipple());
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ });
+ });
+
+ test('receives focused', function() {
+ MockInteractions.focus(button);
+ assert.equal(button.elevation, 3);
+ assert.ok(button.hasRipple());
+ });
+
+ test('space key', function(done) {
+ const SPACE_KEY_CODE = 32;
+ var ripple;
+ MockInteractions.focus(button);
+
+ assert.ok(button.hasRipple());
+
+ ripple = button.getRipple();
+ MockInteractions.keyDownOn(button, SPACE_KEY_CODE);
+
+ assert.equal(ripple.ripples.length, 1);
+
+ MockInteractions.keyDownOn(button, SPACE_KEY_CODE);
+
+ assert.equal(ripple.ripples.length, 1);
+
+ MockInteractions.keyUpOn(button, SPACE_KEY_CODE);
+
+ var transitionEndCalled = false;
+ ripple.addEventListener('transitionend', function() {
+ if (!transitionEndCalled) {
+ transitionEndCalled = true;
+ assert.equal(ripple.ripples.length, 0);
+ done();
+ }
+ });
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/paper-checked-element-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/test/paper-checked-element-behavior.html
new file mode 100644
index 00000000..363cf4c7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/paper-checked-element-behavior.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+ <title>paper-checked-element-behavior</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../paper-checked-element-behavior.html">
+</head>
+<body>
+
+ <dom-module id="test-checked">
+ <template>
+ </template>
+ <script>
+ HTMLImports.whenReady(function() {
+ Polymer({
+ is: 'test-checked',
+ behaviors: [
+ Polymer.PaperCheckedElementBehavior
+ ]
+ });
+ });
+ </script>
+ </dom-module>
+
+ <test-fixture id="basic">
+ <template>
+ <test-checked></test-checked>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('PaperCheckedElementBehavior', function() {
+ var checked;
+
+ setup(function() {
+ checked = fixture('basic');
+ });
+
+ test('element checked when tapped to check', function() {
+ MockInteractions.tap(checked);
+ assert.isTrue(checked.checked);
+ });
+
+ test('element checked when active', function() {
+ checked.active = true;
+ assert.isTrue(checked.checked);
+ });
+
+ test('element not checked when disabled and made active', function() {
+ checked.disabled = true;
+ checked.active = true;
+ assert.isFalse(checked.checked);
+ });
+
+ test('element not checked when disabled and tapped', function() {
+ checked.disabled = true;
+ MockInteractions.tap(checked);
+ assert.isFalse(checked.checked);
+ });
+
+ test('element ripple has checked attribute when element tapped to check', function() {
+ MockInteractions.tap(checked);
+ assert.isTrue(checked.hasRipple());
+ assert.isTrue(checked.getRipple().hasAttribute('checked'));
+ });
+
+ test('element ripple does not have checked attribute when element tapped to uncheck', function() {
+ MockInteractions.tap(checked);
+ MockInteractions.tap(checked);
+ assert.isFalse(checked.getRipple().hasAttribute('checked'));
+ });
+
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/paper-radio-button-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/test/paper-radio-button-behavior.html
new file mode 100644
index 00000000..0418057d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/paper-radio-button-behavior.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+ <title>paper-radio-button-behavior</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="test-radio-button.html">
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <test-radio-button></test-radio-button>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('basic', function() {
+ var button;
+
+ setup(function() {
+ button = fixture('basic');
+ MockInteractions.blur(button);
+ });
+
+ test('normal (no states)', function() {
+ assert.isFalse(button.focused);
+ assert.isFalse(button.hasRipple());
+ });
+
+ test('receives focus', function() {
+ MockInteractions.focus(button);
+
+ assert.isTrue(button.focused);
+ assert.isTrue(button.hasRipple());
+ });
+
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/paper-ripple-behavior.html b/catapult/third_party/polymer/components/paper-behaviors/test/paper-ripple-behavior.html
new file mode 100644
index 00000000..9daf17a4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/paper-ripple-behavior.html
@@ -0,0 +1,335 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+ <title>paper-ripple-behavior</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../polymer/polymer.html">
+ <link rel="import" href="../../iron-behaviors/iron-button-state.html">
+ <link rel="import" href="../paper-ripple-behavior.html">
+ <link rel="import" href="shadowed-ripple.html">
+</head>
+<body>
+
+ <dom-module id="test-ripple">
+ <template>
+ </template>
+ <script>
+ HTMLImports.whenReady(function() {
+ Polymer({
+ is: 'test-ripple',
+ behaviors: [
+ Polymer.IronButtonState,
+ Polymer.IronControlState,
+ Polymer.PaperRippleBehavior
+ ]
+ });
+ });
+ </script>
+ </dom-module>
+
+ <test-fixture id="basic">
+ <template>
+ <test-ripple></test-ripple>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ShadowBasic">
+ <template>
+ <sd-ripple></sd-ripple>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ShadowText">
+ <template>
+ <sd-ripple>Howdy!</sd-ripple>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ShadowElement">
+ <template>
+ <sd-ripple>
+ <div id="source">source!</div>
+ </sd-ripple>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('PaperRippleBehavior', function() {
+ var ripple;
+
+ setup(function() {
+ ripple = fixture('basic');
+ });
+
+ test('no ripple at startup', function() {
+ assert.isFalse(ripple.hasRipple());
+ });
+
+ test('calling getRipple returns ripple', function() {
+ assert.ok(ripple.getRipple());
+ });
+
+ test('focus generates ripple', function() {
+ MockInteractions.focus(ripple);
+ assert.ok(ripple.hasRipple());
+ });
+
+ test('down generates ripple', function() {
+ MockInteractions.down(ripple);
+ assert.ok(ripple.hasRipple());
+ MockInteractions.up(ripple);
+ });
+
+ suite('Correct Targeting', function() {
+
+ function assertInteractionCausesRipple(host, node, expected, msg) {
+ var ripple = host.getRipple();
+ Polymer.dom.flush();
+ MockInteractions.down(node);
+ assert.equal(ripple.ripples.length > 0, expected, msg);
+ MockInteractions.up(node);
+ }
+
+ function assertInteractionAtLocationCausesRipple(host, node, location, expected, msg) {
+ var ripple = host.getRipple();
+ Polymer.dom.flush();
+ MockInteractions.down(node, location);
+ assert.equal(ripple.ripples.length > 0, expected, msg);
+ MockInteractions.up(node);
+ }
+
+ suite('basic', function() {
+ suite('container = host', function() {
+
+ setup(function() {
+ ripple = fixture('ShadowBasic');
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, true, 'ripple');
+ });
+ test('tap #wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, true, '#wrapper');
+ });
+ test('tap #separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, true, '#separate')
+ });
+ });
+
+ suite('container = wrapper', function() {
+
+ setup(function() {
+ ripple = fixture('ShadowBasic');
+ ripple._rippleContainer = ripple.$.wrapper;
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, false, 'ripple');
+ });
+
+ test('tap #wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, true, '#wrapper');
+ });
+
+ test('tap #separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, false, '#separate')
+ });
+ });
+
+ suite('container = separate', function() {
+
+ setup(function() {
+ ripple = fixture('ShadowBasic');
+ ripple._rippleContainer = ripple.$.separate;
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, false, 'ripple');
+ });
+
+ test('tap wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, false, '#wrapper');
+ });
+
+ test('tap separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, true, '#separate')
+ });
+ });
+ });
+
+ suite('distributed text', function() {
+ var textLocation;
+
+ function getTextLocation(ripple) {
+ // build a Range to get the BCR of a given text node
+ var r = document.createRange();
+ r.selectNode(Polymer.dom(ripple.$.content).getDistributedNodes()[0]);
+ return MockInteractions.middleOfNode(r);
+ }
+
+ suite('container = host', function() {
+ setup(function() {
+ ripple = fixture('ShadowText');
+ textLocation = getTextLocation(ripple);
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, true, 'ripple');
+ });
+
+ test('tap wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, true, '#wrapper');
+ });
+
+ test('tap separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, true, '#separate')
+ });
+
+ test('tap text', function() {
+ assertInteractionAtLocationCausesRipple(ripple, ripple.$.wrapper, textLocation, true, 'text');
+ });
+ });
+
+ suite('container = wrapper', function() {
+ setup(function() {
+ ripple = fixture('ShadowText');
+ ripple._rippleContainer = ripple.$.wrapper;
+ textLocation = getTextLocation(ripple);
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, false, 'ripple');
+ });
+
+ test('tap wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, true, '#wrapper');
+ });
+
+ test('tap separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, false, '#separate')
+ });
+
+ test('tap text', function() {
+ assertInteractionAtLocationCausesRipple(ripple, ripple.$.wrapper, textLocation, true, 'text');
+ });
+ });
+
+ suite('container = separate', function() {
+ setup(function() {
+ ripple = fixture('ShadowText');
+ ripple._rippleContainer = ripple.$.separate;
+ textLocation = getTextLocation(ripple);
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, false, 'ripple');
+ });
+
+ test('tap wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, false, '#wrapper');
+ });
+
+ test('tap separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, true, '#separate')
+ });
+
+ test('tap text', function() {
+ assertInteractionAtLocationCausesRipple(ripple, ripple.$.wrapper, textLocation, false, 'text');
+ });
+ });
+ });
+
+ suite('distributed element', function() {
+ var source;
+
+ suite('container = host', function() {
+ setup(function() {
+ ripple = fixture('ShadowElement');
+ source = Polymer.dom(ripple).querySelector('#source');
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, true, 'ripple');
+ });
+
+ test('tap wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, true, '#wrapper');
+ });
+
+ test('tap separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, true, '#separate')
+ });
+
+ test('tap source', function() {
+ assertInteractionCausesRipple(ripple, source, true, '#source');
+ });
+ });
+
+ suite('container = wrapper', function() {
+ setup(function() {
+ ripple = fixture('ShadowElement');
+ ripple._rippleContainer = ripple.$.wrapper;
+ source = Polymer.dom(ripple).querySelector('#source');
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, false, 'ripple');
+ });
+
+ test('tap wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, true, '#wrapper');
+ });
+
+ test('tap separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, false, '#separate')
+ });
+
+ test('tap source', function() {
+ assertInteractionCausesRipple(ripple, source, true, '#source');
+ });
+ });
+
+ suite('container = separate', function() {
+ setup(function() {
+ ripple = fixture('ShadowElement');
+ ripple._rippleContainer = ripple.$.separate;
+ source = Polymer.dom(ripple).querySelector('#source');
+ });
+
+ test('tap host', function() {
+ assertInteractionCausesRipple(ripple, ripple, false, 'ripple');
+ });
+
+ test('tap wrapper', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.wrapper, false, '#wrapper');
+ });
+
+ test('tap separate', function() {
+ assertInteractionCausesRipple(ripple, ripple.$.separate, true, '#separate')
+ });
+
+ test('tap source', function() {
+ assertInteractionCausesRipple(ripple, source, false, '#source');
+ });
+ });
+ });
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/shadowed-ripple.html b/catapult/third_party/polymer/components/paper-behaviors/test/shadowed-ripple.html
new file mode 100644
index 00000000..1ebad13e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/shadowed-ripple.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-behaviors/iron-button-state.html">
+<link rel="import" href="../paper-ripple-behavior.html">
+<dom-module id="sd-ripple">
+ <template>
+ <style>
+ :host {
+ display: block;
+ width: 200px;
+ }
+ #separate, #wrapper {
+ height: 50px;
+ }
+ #separate {
+ background: blue;
+ }
+ #wrapper {
+ background: red;
+ }
+ #wrapper > ::content #source {
+ height: 25px;
+ width: 50px;
+ background: green;
+ }
+ </style>
+ <div id="separate">
+ <div id="target">
+ Internal Text Node
+ </div>
+ </div>
+ <div id="wrapper">
+ <content id="content"></content>
+ </div>
+ </template>
+ <script>
+ Polymer({
+ is: 'sd-ripple',
+ behaviors: [
+ Polymer.IronButtonState,
+ Polymer.IronControlState,
+ Polymer.PaperRippleBehavior
+ ]
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/test-button.html b/catapult/third_party/polymer/components/paper-behaviors/test/test-button.html
new file mode 100644
index 00000000..3bbf356e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/test-button.html
@@ -0,0 +1,34 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../paper-button-behavior.html">
+
+<dom-module id="test-button">
+
+ <template>
+ <content></content>
+ </template>
+
+ <script>
+
+ Polymer({
+
+ is: 'test-button',
+
+ behaviors: [
+ Polymer.PaperButtonBehavior
+ ]
+
+ });
+
+ </script>
+
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-behaviors/test/test-radio-button.html b/catapult/third_party/polymer/components/paper-behaviors/test/test-radio-button.html
new file mode 100644
index 00000000..945a08e1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-behaviors/test/test-radio-button.html
@@ -0,0 +1,41 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../paper-checked-element-behavior.html">
+<link rel="import" href="../../paper-ripple/paper-ripple.html">
+
+<dom-module id="test-radio-button">
+ <template>
+ <style>
+ :host #ink {
+ position: absolute;
+ top: -16px;
+ left: -16px;
+ width: 48px;
+ height: 48px;
+ }
+ </style>
+
+ <div id="container">
+ <paper-ripple id="ink" class="circle" center></paper-ripple>
+ </div>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'test-radio-button',
+
+ behaviors: [
+ Polymer.PaperCheckedElementBehavior
+ ]
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-button/.bower.json b/catapult/third_party/polymer/components/paper-button/.bower.json
new file mode 100644
index 00000000..cfd977ec
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/.bower.json
@@ -0,0 +1,50 @@
+{
+ "name": "paper-button",
+ "version": "1.0.15",
+ "description": "Material design button",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "paper",
+ "button"
+ ],
+ "main": "paper-button.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-button.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-button",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "_release": "1.0.15",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.15",
+ "commit": "804937a43980511e232c44a134ed4fb8d71917ef"
+ },
+ "_source": "https://github.com/PolymerElements/paper-button.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-button"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-button/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-button/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..48a50e98
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-button/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-button/.gitignore b/catapult/third_party/polymer/components/paper-button/.gitignore
new file mode 100644
index 00000000..1e501e3f
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/.gitignore
@@ -0,0 +1,2 @@
+bower_components
+node_modules \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-button/.travis.yml b/catapult/third_party/polymer/components/paper-button/.travis.yml
new file mode 100644
index 00000000..31bce27d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ YhRb8f1f4y3Rjs5wnp4sCzIcEcL07HOBoJcEp6U/KVgIIk/sBxkxMVmZsoUeABrNbKcdE6Gn6OS2K1lFq4VThKppJ0yjvESa1p0FjmQ0Nf1xCYxc46n936xj87khZy3rrTGHxwcOY5vAa5mvNzXI4BYxjmSjmqRsQwsJBsJHWVw=
+ - secure: >-
+ eQjSTRxQKF5vyRa6yK2o+j5xWK480hHCKDh7RJQISEVhyzAaCzPZNmWf2pmaDj+ZoZZrahBcoRwOZAiVdHDaKQ+VLHbEECzV3OVV32/9DHi608BPCBdxVR2MDTtZVt3fGVhPQwCnFhwRTWtVVGx8y8HnTDMv/r4xW1OMSThPsbM=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-button/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-button/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-button/README.md b/catapult/third_party/polymer/components/paper-button/README.md
new file mode 100644
index 00000000..6b36c916
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/README.md
@@ -0,0 +1,73 @@
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-button.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-button)
+[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://beta.webcomponents.org/element/PolymerElements/paper-button)
+
+##&lt;paper-button&gt;
+
+Material design: [Buttons](https://www.google.com/design/spec/components/buttons.html)
+
+`paper-button` is a button. When the user touches the button, a ripple effect emanates
+from the point of contact. It may be flat or raised. A raised button is styled with a
+shadow.
+
+Example:
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-button.html">
+ <link rel="import" href="../paper-styles/color.html">
+ <style is="custom-style">
+ #container {
+ display: flex;
+ }
+ paper-button {
+ font-family: 'Roboto', 'Noto', sans-serif;
+ font-weight: normal;
+ font-size: 14px;
+ -webkit-font-smoothing: antialiased;
+ }
+ paper-button.pink {
+ color: var(--paper-pink-a200);
+ --paper-button-ink-color: var(--paper-pink-a200);
+ }
+ paper-button.pink:hover {
+ background-color: var(--paper-pink-100);
+ }
+ paper-button.indigo {
+ background-color: var(--paper-indigo-500);
+ color: white;
+ --paper-button-raised-keyboard-focus: {
+ background-color: var(--paper-pink-a200) !important;
+ color: white !important;
+ };
+ }
+ paper-button.indigo:hover {
+ background-color: var(--paper-indigo-400);
+ }
+ paper-button.green {
+ background-color: var(--paper-green-500);
+ color: white;
+ }
+ paper-button.green[active] {
+ background-color: var(--paper-red-500);
+ }
+ paper-button.disabled {
+ color: white;
+ }
+ </style>
+ <div id="container">
+ <next-code-block></next-code-block>
+ </div>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-button class="pink">link</paper-button>
+<paper-button raised class="indigo">raised</paper-button>
+<paper-button toggles raised class="green">toggles</paper-button>
+<paper-button disabled class="disabled">disabled</paper-button>
+```
diff --git a/catapult/third_party/polymer/components/paper-button/bower.json b/catapult/third_party/polymer/components/paper-button/bower.json
new file mode 100644
index 00000000..a2c957a5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/bower.json
@@ -0,0 +1,41 @@
+{
+ "name": "paper-button",
+ "version": "1.0.15",
+ "description": "Material design button",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "paper",
+ "button"
+ ],
+ "main": "paper-button.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-button.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-button",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-button/demo/index.html b/catapult/third_party/polymer/components/paper-button/demo/index.html
new file mode 100644
index 00000000..c2130909
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/demo/index.html
@@ -0,0 +1,138 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-button demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../paper-button.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ .vertical-section-container {
+ max-width: 500px;
+ }
+ paper-button {
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Buttons can be flat, raised, toggleable, or disabled</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button>link</paper-button>
+ <paper-button raised>raised</paper-button>
+ <paper-button toggles raised>toggles</paper-button>
+ <paper-button disabled>disabled</paper-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can have icons</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button><iron-icon icon="check"></iron-icon>link</paper-button>
+ <paper-button raised><iron-icon icon="file-download"></iron-icon>raised</paper-button>
+ <paper-button toggles raised><iron-icon icon="favorite"></iron-icon>toggles</paper-button>
+ <paper-button disabled><iron-icon icon="block"></iron-icon>disabled</paper-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can hide the ripple effect using the <i>noink</i> attribute</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button noink>link</paper-button>
+ <paper-button raised noink>raised</paper-button>
+ <paper-button toggles raised noink>toggles</paper-button>
+ <paper-button disabled noink>disabled</paper-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can be styled using custom properties</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button class="custom pink">link</paper-button>
+ <paper-button raised class="custom indigo">raised</paper-button>
+ <paper-button toggles raised class="custom green">toggles</paper-button>
+ <paper-button disabled class="custom disabled">disabled</paper-button>
+
+ <style is="custom-style">
+ paper-button.custom {
+ --paper-button-ink-color: var(--paper-pink-a200);
+ /* These could also be individually defined for each of the
+ specific css classes, but we'll just do it once as an example */
+ --paper-button-flat-keyboard-focus: {
+ background-color: var(--paper-pink-a200);
+ color: white !important;
+ };
+ --paper-button-raised-keyboard-focus: {
+ background-color: var(--paper-pink-a200) !important;
+ color: white !important;
+ };
+ }
+ paper-button.custom:hover {
+ background-color: var(--paper-indigo-100);
+ }
+ paper-button.pink {
+ color: var(--paper-pink-a200);
+
+ }
+ paper-button.indigo {
+ background-color: var(--paper-indigo-500);
+ color: white;
+ --paper-button-raised-keyboard-focus: {
+ background-color: var(--paper-pink-a200) !important;
+ color: white !important;
+ };
+ }
+ paper-button.green {
+ background-color: var(--paper-green-500);
+ color: white;
+ }
+ paper-button.green[active] {
+ background-color: var(--paper-red-500);
+ }
+ paper-button.disabled {
+ color: white;
+ }
+ </style>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can be used as a link</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <a href="https://www.polymer-project.org/" tabindex="-1">
+ <paper-button raised>Polymer Project</paper-button>
+ </a>
+ <style>
+ a paper-button,
+ a:active paper-button,
+ a:visited paper-button {
+ color: #000;
+ }
+ </style>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-button/index.html b/catapult/third_party/polymer/components/paper-button/index.html
new file mode 100644
index 00000000..487bb5c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-button/package.json b/catapult/third_party/polymer/components/paper-button/package.json
new file mode 100644
index 00000000..24f4d4d0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "paper-button",
+ "version": "2.0.0",
+ "private": true,
+ "main": "index.js",
+ "repository": "https://github.com/PolymerElements/paper-button.git",
+ "author": "Justin Fagnani <justinfagnani@google.com>",
+ "license": "MIT"
+}
diff --git a/catapult/third_party/polymer/components/paper-button/paper-button.html b/catapult/third_party/polymer/components/paper-button/paper-button.html
new file mode 100644
index 00000000..c93a3f51
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/paper-button.html
@@ -0,0 +1,186 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-behaviors/paper-button-behavior.html">
+<link rel="import" href="../paper-material/paper-material-shared-styles.html">
+
+<!--
+Material design: [Buttons](https://www.google.com/design/spec/components/buttons.html)
+
+`paper-button` is a button. When the user touches the button, a ripple effect emanates
+from the point of contact. It may be flat or raised. A raised button is styled with a
+shadow.
+
+Example:
+
+ <paper-button>Flat button</paper-button>
+ <paper-button raised>Raised button</paper-button>
+ <paper-button noink>No ripple effect</paper-button>
+ <paper-button toggles>Toggle-able button</paper-button>
+
+A button that has `toggles` true will remain `active` after being clicked (and
+will have an `active` attribute set). For more information, see the `Polymer.IronButtonState`
+behavior.
+
+You may use custom DOM in the button body to create a variety of buttons. For example, to
+create a button with an icon and some text:
+
+ <paper-button>
+ <iron-icon icon="favorite"></iron-icon>
+ custom button content
+ </paper-button>
+
+To use `paper-button` as a link, wrap it in an anchor tag. Since `paper-button` will already
+receive focus, you may want to prevent the anchor tag from receiving focus as well by setting
+its tabindex to -1.
+
+ <a href="https://www.polymer-project.org/" tabindex="-1">
+ <paper-button raised>Polymer Project</paper-button>
+ </a>
+
+### Styling
+
+Style the button with CSS as you would a normal DOM element.
+
+ paper-button.fancy {
+ background: green;
+ color: yellow;
+ }
+
+ paper-button.fancy:hover {
+ background: lime;
+ }
+
+ paper-button[disabled],
+ paper-button[toggles][active] {
+ background: red;
+ }
+
+By default, the ripple is the same color as the foreground at 25% opacity. You may
+customize the color using the `--paper-button-ink-color` custom property.
+
+The following custom properties and mixins are also available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-button-ink-color` | Background color of the ripple | `Based on the button's color`
+`--paper-button` | Mixin applied to the button | `{}`
+`--paper-button-disabled` | Mixin applied to the disabled button. Note that you can also use the `paper-button[disabled]` selector | `{}`
+`--paper-button-flat-keyboard-focus` | Mixin applied to a flat button after it's been focused using the keyboard | `{}`
+`--paper-button-raised-keyboard-focus` | Mixin applied to a raised button after it's been focused using the keyboard | `{}`
+
+@demo demo/index.html
+-->
+
+<dom-module id="paper-button">
+ <template strip-whitespace>
+ <style include="paper-material-shared-styles">
+ :host {
+ @apply(--layout-inline);
+ @apply(--layout-center-center);
+ position: relative;
+ box-sizing: border-box;
+ min-width: 5.14em;
+ margin: 0 0.29em;
+ background: transparent;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ font: inherit;
+ text-transform: uppercase;
+ outline-width: 0;
+ border-radius: 3px;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ z-index: 0;
+ padding: 0.7em 0.57em;
+
+ @apply(--paper-font-common-base);
+ @apply(--paper-button);
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ :host([raised].keyboard-focus) {
+ font-weight: bold;
+ @apply(--paper-button-raised-keyboard-focus);
+ }
+
+ :host(:not([raised]).keyboard-focus) {
+ font-weight: bold;
+ @apply(--paper-button-flat-keyboard-focus);
+ }
+
+ :host([disabled]) {
+ background: #eaeaea;
+ color: #a8a8a8;
+ cursor: auto;
+ pointer-events: none;
+
+ @apply(--paper-button-disabled);
+ }
+
+ :host([animated]) {
+ @apply(--shadow-transition);
+ }
+
+ paper-ripple {
+ color: var(--paper-button-ink-color);
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+</dom-module>
+<script>
+Polymer({
+ is: 'paper-button',
+
+ behaviors: [
+ Polymer.PaperButtonBehavior
+ ],
+
+ properties: {
+ /**
+ * If true, the button should be styled with a shadow.
+ */
+ raised: {
+ type: Boolean,
+ reflectToAttribute: true,
+ value: false,
+ observer: '_calculateElevation'
+ }
+ },
+
+ _calculateElevation: function() {
+ if (!this.raised) {
+ this._setElevation(0);
+ } else {
+ Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this);
+ }
+ }
+
+ /**
+ Fired when the animation finishes.
+ This is useful if you want to wait until
+ the ripple animation finishes to perform some action.
+
+ @event transitionend
+ Event param: {{node: Object}} detail Contains the animated node.
+ */
+});
+</script>
diff --git a/catapult/third_party/polymer/components/paper-button/test/index.html b/catapult/third_party/polymer/components/paper-button/test/index.html
new file mode 100644
index 00000000..50048191
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-button tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'paper-button.html',
+ 'paper-button.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-button/test/paper-button.html b/catapult/third_party/polymer/components/paper-button/test/paper-button.html
new file mode 100644
index 00000000..54200e63
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-button/test/paper-button.html
@@ -0,0 +1,108 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-button basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../paper-button.html">
+
+</head>
+<body>
+
+ <test-fixture id="TrivialButton">
+ <template>
+ <paper-button>Button</paper-button>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('<paper-button>', function() {
+ var button;
+
+ setup(function() {
+ button = fixture('TrivialButton');
+ });
+
+ test('can be raised imperatively', function(done) {
+ button.raised = true;
+
+ expect(button.hasAttribute('raised')).to.be.eql(true);
+
+ Polymer.Base.async(function() {
+ try {
+ expect(button.elevation).to.be.eql(1);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }, 1);
+ });
+
+ test('can be unraised after being raised imperatively', function(done) {
+ button.raised = true;
+ expect(button.hasAttribute('raised')).to.be.eql(true);
+
+ Polymer.Base.async(function() {
+ expect(button.elevation).to.be.eql(1);
+
+ button.raised = false;
+ expect(button.hasAttribute('raised')).to.be.eql(false);
+ Polymer.Base.async(function() {
+ expect(button.elevation).to.be.eql(0);
+ done();
+ }, 1);
+ }, 1);
+ });
+
+ test('can be disabled imperatively', function() {
+ button.disabled = true;
+ expect(button.getAttribute('aria-disabled')).to.be.eql('true');
+ expect(button.hasAttribute('disabled')).to.be.eql(true);
+ });
+
+ test('can be triggered with space', function(done) {
+ button.addEventListener('click', function() {
+ done();
+ });
+ MockInteractions.pressSpace(button);
+ });
+
+ test('can be triggered with enter', function(done) {
+ button.addEventListener('click', function() {
+ done();
+ });
+ MockInteractions.pressEnter(button);
+ });
+ });
+
+ suite('<paper-button>', function() {
+ var button;
+
+ setup(function() {
+ button = fixture('TrivialButton');
+ });
+
+ test('has aria role "button"', function() {
+ expect(button.getAttribute('role')).to.be.eql('button');
+ });
+
+ a11ySuite('TrivialButton');
+ });
+
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-card/.gitignore b/catapult/third_party/polymer/components/paper-card/.gitignore
new file mode 100755
index 00000000..fbe05fc9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/.gitignore
@@ -0,0 +1 @@
+bower_components/
diff --git a/catapult/third_party/polymer/components/paper-card/.travis.yml b/catapult/third_party/polymer/components/paper-card/.travis.yml
new file mode 100755
index 00000000..dd8f99e3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/.travis.yml
@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: KuASkT/4CBzdndyZ91e/plGIXpvostP2XOjiVc+Uwpfgu4ktzPdGxZS18NowS5sp0eXhQQRxtON28P4+xNcCULQ6gUe6HYErZUY9ijWEvYRdnt8HZyywZZpI4H1ZlXGFnCmsTvOI9azSaw56AwWnN7buVnEK0k7BHvnuofTs8qpcgZR3P7cMjoT/jcxu+S+nfO/hrsFBRwjw8vT1AitHp/tVTzLoiv26noLcPm3k8fQaAAVKWtioFlUV50fBCbCfDkbPwUMTlZiQxhVHbYxwNfPzgoJp+ETtJpS3VApXAClKWg4CvAinf3kVQI8cw6AG4hnaz9IRNnkwPUviQXrojb3QjZI14MRdTS+hjwGM7FypX3ZzKw2ttAGHLquJZKTX4G7Yh2LsWRvzzKPv4J/DIBKsGbxwgcHf71KSoPKY6pLqlAF74rcAb3MeahmeYT98gxmr1Bs9tKjR5/iw8knEqPa2K4KUTr2iv+qqkpoiQZOx5IM6niLv4IcAIlaYzJQTL/IuFSJ5/vAYk7B3GiBxtGfmh2C7Kc0xlNIwbYMqtI4vwOSGCUXPhnrxqvuD2ffGLgO+8jbGK37k3T4egbCc5ekA01I7faMK+hOrTyrIx6rwwvCuuoTPrqwCGE3en7oovzRBw3TzuD+HyixTYA6Q1AOTKH57qXPTInRh9YO220w=
+ - secure: xPkO7EnyvasJh1KedwftJzAx9kcxV0f+lmIvUIJyGCAvBhrdGrIuWDzTzYu9iZnTcRJKIN6FBd8pimUxs3LEY8QNl/eZ+iLqNstKCdzHKvkHhn9M4cBOyKo4+iI7z7mBmw1+qDBEC6hJ6Wr097ojF9IonAQqvXYXbbm5QLujThuY3tPviRUQuBWOSG8PVIcOtt+ExvjsRhqo7hS2hlxeSHcGiBNtWsCXdI+wFBPBCu0mXwZnOvpIrTapPyhm97NzDJmgb5/SPuZmiXAACjRPWQKAemHLyhBBvq4RFpDZQvQaNEiGeurF2pCwtQ1G3XfuWhlAE+hp0dSluOrRFPrqiLjtuk7sXrDTt8MzfA1lwksWr7o/05H37sk5Ay3Un25jmWz1ZYjn0Fzpgm73ycgljQqaeizl05Q0YVtNbv8asKFbZ4qAs/MXFcubw6z7LxjS7bEiSAT5P2SLQbKUI3hYsT1LbqZNAKTZw4zgPyS/kOq1MSTgILy3c5R4vSi3P8/dn72lqvmXveRH2EchQPwO+kwvPcQ1NWqwAUj1FNtvsc6ahBsJ9JQLl3Yo8/nIp/ssg0OtgqTOmm0f3RQ5Jzbg65iRguKqCpq1CKaXmIH2zlVUBW2NQpqc/bwulbYHMjR1dV+T3WbpRJ1X6g7WazsS1NZVzNB/qw+kTZ6SFA+8VHM=
+node_js: stable
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+ - xvfb-run wct
+ - "if [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then wct -s 'default'; fi"
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-card/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-card/CONTRIBUTING.md
new file mode 100755
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-card/README.md b/catapult/third_party/polymer/components/paper-card/README.md
new file mode 100755
index 00000000..94466db7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/README.md
@@ -0,0 +1,65 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-card.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-card.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-card)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-card)_
+
+
+##&lt;paper-card&gt;
+
+Material design: [Cards](https://www.google.com/design/spec/components/cards.html)
+
+`paper-card` is a container with a drop shadow.
+
+Example:
+
+```html
+<paper-card heading="Card Title">
+ <div class="card-content">Some content</div>
+ <div class="card-actions">
+ <paper-button>Some action</paper-button>
+ </div>
+</paper-card>
+```
+
+Example - top card image:
+
+```html
+<paper-card heading="Card Title" image="/path/to/image.png">
+ ...
+</paper-card>
+```
+
+### Accessibility
+
+By default, the `aria-label` will be set to the value of the `heading` attribute.
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-card-background-color` | The background color of the card | `--primary-background-color` |
+| `--paper-card-header-color` | The color of the header text | `#000` |
+| `--paper-card-header` | Mixin applied to the card header section | `{}` |
+| `--paper-card-header-text` | Mixin applied to the title in the card header section | `{}` |
+| `--paper-card-header-image` | Mixin applied to the image in the card header section | `{}` |
+| `--paper-card-header-image-text` | Mixin applied to the text overlapping the image in the card header section | `{}` |
+| `--paper-card-content` | Mixin applied to the card content section | `{}` |
+| `--paper-card-actions` | Mixin applied to the card action section | `{}` |
+| `--paper-card` | Mixin applied to the card | `{}` |
+
+
diff --git a/catapult/third_party/polymer/components/paper-card/bower.json b/catapult/third_party/polymer/components/paper-card/bower.json
new file mode 100755
index 00000000..b17e3fa8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/bower.json
@@ -0,0 +1,41 @@
+{
+ "name": "paper-card",
+ "version": "1.1.1",
+ "description": "Material design piece of paper with unique related data",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "card"
+ ],
+ "main": "paper-card.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-card.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-card",
+ "ignore": [],
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-image": "PolymerElements/iron-image#^1.2.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-checkbox": "PolymerElements/paper-checkbox#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-card/demo/cafe.png b/catapult/third_party/polymer/components/paper-card/demo/cafe.png
new file mode 100644
index 00000000..a3dc6a8a
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/demo/cafe.png
Binary files differ
diff --git a/catapult/third_party/polymer/components/paper-card/demo/donuts.png b/catapult/third_party/polymer/components/paper-card/demo/donuts.png
new file mode 100644
index 00000000..5f668800
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/demo/donuts.png
Binary files differ
diff --git a/catapult/third_party/polymer/components/paper-card/demo/house.png b/catapult/third_party/polymer/components/paper-card/demo/house.png
new file mode 100644
index 00000000..a7f1c24e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/demo/house.png
Binary files differ
diff --git a/catapult/third_party/polymer/components/paper-card/demo/index.html b/catapult/third_party/polymer/components/paper-card/demo/index.html
new file mode 100755
index 00000000..c4dfbe99
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/demo/index.html
@@ -0,0 +1,323 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html lang="en">
+<head>
+ <title>paper-card demo</title>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+
+ <link rel="import" href="../../iron-collapse/iron-collapse.html">
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../iron-icons/communication-icons.html">
+ <link rel="import" href="../../iron-icons/hardware-icons.html">
+ <link rel="import" href="../../iron-icons/social-icons.html">
+ <link rel="import" href="../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../paper-button/paper-button.html">
+ <link rel="import" href="../../paper-checkbox/paper-checkbox.html">
+ <link rel="import" href="../../paper-icon-button/paper-icon-button.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../paper-styles/typography.html">
+ <link rel="import" href="../paper-card.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ demo-snippet {
+ --demo-snippet-demo: {
+ background: var(--paper-grey-200);
+ padding: 8px;
+ };
+ --demo-snippet-code: {
+ max-height: 300px;
+ };
+ }
+
+ paper-card {
+ width: 100%;
+ }
+
+ .horizontal {
+ @apply(--layout-horizontal);
+ }
+
+ .justified {
+ @apply(--layout-justified);
+ }
+
+ .amber {
+ background: var(--paper-amber-900);
+ }
+
+ .lime {
+ background: var(--paper-lime-500);
+ }
+
+ .cyan {
+ background: var(--paper-cyan-500);
+ }
+
+ .dark {
+ background: var(--paper-blue-grey-500);
+ }
+ paper-card.dark, paper-card.amber, paper-card.lime, paper-card.cyan {
+ color: white;
+ --paper-card-header-color: white;
+ }
+
+ paper-checkbox {
+ display: block;
+ margin-bottom: 4px;
+ --paper-checkbox-label-color: white;
+ --paper-checkbox-unchecked-color: white;
+ }
+
+ paper-icon-button {
+ color: var(--paper-grey-600);
+ }
+
+ paper-icon-button.white {
+ color: white !important;
+ }
+ </style>
+
+</head>
+<body unresolved>
+ <body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>A paper-card with a simple heading, header image, body content, and actions</h3>
+ <demo-snippet>
+ <template>
+ <paper-card heading="Emmental" image="http://placehold.it/350x150/FFC107/000000">
+ <div class="card-content">
+ Emmentaler or Emmental is a yellow, medium-hard cheese that originated in the area around Emmental, Switzerland. It is one of the cheeses of Switzerland, and is sometimes known as Swiss cheese.
+ </div>
+ <div class="card-actions">
+ <paper-button>Share</paper-button>
+ <paper-button>Explore!</paper-button>
+ </div>
+ </paper-card>
+ </template>
+ </demo-snippet>
+
+ <h3>Paper-cards can contain advanced content</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .cafe-header { @apply(--paper-font-headline); }
+ .cafe-light { color: var(--paper-grey-600); }
+ .cafe-location {
+ float: right;
+ font-size: 15px;
+ vertical-align: middle;
+ }
+ .cafe-reserve { color: var(--google-blue-500); }
+ iron-icon.star {
+ --iron-icon-width: 16px;
+ --iron-icon-height: 16px;
+ color: var(--paper-amber-500);
+ }
+ iron-icon.star:last-of-type { color: var(--paper-grey-500); }
+ </style>
+ <paper-card image="./donuts.png">
+ <div class="card-content">
+ <div class="cafe-header">Cafe Basilico
+ <div class="cafe-location cafe-light">
+ <iron-icon icon="communication:location-on"></iron-icon>
+ <span>250ft</span>
+ </div>
+ </div>
+ <div class="cafe-rating">
+ <iron-icon class="star" icon="star"></iron-icon>
+ <iron-icon class="star" icon="star"></iron-icon>
+ <iron-icon class="star" icon="star"></iron-icon>
+ <iron-icon class="star" icon="star"></iron-icon>
+ <iron-icon class="star" icon="star"></iron-icon>
+ </div>
+ <p>$・Italian, Cafe</p>
+ <p class="cafe-light">Small plates, salads &amp; sandwiches in an intimate setting with 12 indoor seats plus patio seating.</p>
+ </div>
+ <div class="card-actions">
+ <p>Tonight's availability</p>
+ <div class="horizontal justified">
+ <paper-icon-button icon="icons:event"></paper-icon-button>
+ <paper-button>5:30PM</paper-button>
+ <paper-button>7:30PM</paper-button>
+ <paper-button>9:00PM</paper-button>
+ </div>
+ <paper-button class="cafe-reserve">Reserve</paper-button>
+ </div>
+ </paper-card>
+ </template>
+ </demo-snippet>
+
+ <h3>Paper-cards can have a horizontal image</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ paper-card.rate { @apply(--layout-horizontal); }
+ .rate-image {
+ width: 100px;
+ height: 170px;
+ background: url('./donuts.png');
+ background-size: cover;
+ }
+ .rate-content {
+ @apply(--layout-flex);
+ float: left;
+ }
+ .rate-header { @apply(--paper-font-headline); }
+ .rate-name { color: var(--paper-grey-600); margin: 10px 0; }
+ paper-icon-button.rate-icon {
+ --iron-icon-fill-color: white;
+ --iron-icon-stroke-color: var(--paper-grey-600);
+ }
+ </style>
+ <paper-card class="rate">
+ <div class="rate-content">
+ <div class="card-content">
+ <div class="rate-header">Rate this album</div>
+ <div class="rate-name">Mac Miller</div>
+ <div>Live from space</div>
+ </div>
+ <div class="card-actions">
+ <paper-icon-button class="rate-icon" icon="star"></paper-icon-button>
+ <paper-icon-button class="rate-icon" icon="star"></paper-icon-button>
+ <paper-icon-button class="rate-icon" icon="star"></paper-icon-button>
+ <paper-icon-button class="rate-icon" icon="star"></paper-icon-button>
+ <paper-icon-button class="rate-icon" icon="star"></paper-icon-button>
+ </div>
+ </div>
+ <div class="rate-image"></div>
+ </paper-card>
+ </template>
+ </demo-snippet>
+
+ <h3>Paper-cards can have expanded supporting text</h3>
+ <demo-snippet>
+ <template>
+ <paper-card heading="Top western road trips" image="./trip.png" class="white">
+ <div class="card-content">1,000 miles of wonder</div>
+ <div class="card-actions">
+ <paper-button>Share</paper-button>
+ <paper-button>Explore</paper-button>
+ <paper-icon-button
+ icon="hardware:keyboard-arrow-up"
+ title="more info"
+ onclick="_toggle()"
+ style="float:right;">
+ </paper-icon-button>
+ <iron-collapse id="more-info" style="width:100%;">
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent enim ante, tempus eget volutpat ac, cursus ac ante. Nulla facilisi. Praesent sed lacinia ligula. Donec malesuada nisl eget quam iaculis, vel placerat justo cursus.
+ </iron-collapse>
+ <script>
+ function _toggle() {
+ var moreInfo = document.getElementById('more-info');
+ var iconButton = Polymer.dom(event).localTarget;
+ iconButton.icon = moreInfo.opened ? 'hardware:keyboard-arrow-up'
+ : 'hardware:keyboard-arrow-down';
+ moreInfo.toggle();
+ }
+ </script>
+ </div>
+ </paper-card>
+ </template>
+ </demo-snippet>
+
+ <h3>Paper-cards can be organized in different collections</h3>
+
+ <h3>Same layout, different content</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ paper-card.white {
+ --paper-card-header-color: #fff;
+ }
+ </style>
+ <paper-card heading="Pre-fab homes" image="./house.png" class="white" style="margin-bottom:8px;">
+ <div class="card-actions">
+ <paper-icon-button icon="favorite"></paper-icon-button>
+ <paper-icon-button icon="bookmark"></paper-icon-button>
+ <paper-icon-button icon="social:share"></paper-icon-button>
+ </div>
+ </paper-card>
+ <div class="horizontal">
+ <paper-card heading="Favorite trips" image="./trip.png" class="white" style="margin-right:4px;">
+ <div class="card-actions horizontal justified">
+ <paper-icon-button icon="favorite"></paper-icon-button>
+ <paper-icon-button icon="bookmark"></paper-icon-button>
+ <paper-icon-button icon="social:share"></paper-icon-button>
+ </div>
+ </paper-card>
+ <paper-card heading="Best airlines" image="./travel.png" class="white" style="margin-left:4px;">
+ <div class="card-actions horizontal justified">
+ <paper-icon-button icon="favorite"></paper-icon-button>
+ <paper-icon-button icon="bookmark"></paper-icon-button>
+ <paper-icon-button icon="social:share"></paper-icon-button>
+ </div>
+ </paper-card>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>Different layout and content</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ #notes {
+ @apply(--layout-vertical);
+ @apply(--layout-wrap);
+ height: 344px;
+ width: 384px;
+ }
+
+ #notes > paper-card {
+ box-sizing: border-box;
+ max-width: 184px;
+ margin: 4px;
+ flex: 0 0 auto;
+ }
+ </style>
+ <div id="notes">
+ <paper-card heading="Call Jennifer" class="cyan">
+ <div class="card-actions">
+ <paper-icon-button icon="communication:call" style="color:white;"></paper-icon-button>
+ <span>March 19, 2017</span>
+ </div>
+ </paper-card>
+ <paper-card class="dark">
+ <div class="card-content">
+ <p>Groceries:</p>
+ <paper-checkbox>almond milk</paper-checkbox>
+ <paper-checkbox>coconut water</paper-checkbox>
+ <paper-checkbox>cheese</paper-checkbox>
+ <paper-checkbox>green apples</paper-checkbox>
+ </div>
+ <div class="card-actions">
+ <paper-icon-button icon="communication:location-on" style="color:white"></paper-icon-button>
+ <span>Campbell</span>
+ </div>
+ </paper-card>
+ <paper-card heading="clean desk" class="lime"></paper-card>
+ <paper-card image="./donuts.png" class="amber">
+ <div class="card-content">New cafe opened on Valencia St.</div>
+ </paper-card>
+ <paper-card heading="Yuna tickets on sale 6/24" class="cyan">
+ </paper-card>
+ </div>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-card/demo/travel.png b/catapult/third_party/polymer/components/paper-card/demo/travel.png
new file mode 100644
index 00000000..7d8e8215
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/demo/travel.png
Binary files differ
diff --git a/catapult/third_party/polymer/components/paper-card/demo/trip.png b/catapult/third_party/polymer/components/paper-card/demo/trip.png
new file mode 100644
index 00000000..746d0b30
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/demo/trip.png
Binary files differ
diff --git a/catapult/third_party/polymer/components/paper-card/index.html b/catapult/third_party/polymer/components/paper-card/index.html
new file mode 100755
index 00000000..fab428a1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <title>paper-card</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+<iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-card/paper-card.html b/catapult/third_party/polymer/components/paper-card/paper-card.html
new file mode 100755
index 00000000..20c462c4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/paper-card.html
@@ -0,0 +1,226 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../iron-image/iron-image.html">
+<link rel="import" href="../paper-material/paper-material.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<!--
+Material design: [Cards](https://www.google.com/design/spec/components/cards.html)
+
+`paper-card` is a container with a drop shadow.
+
+Example:
+
+ <paper-card heading="Card Title">
+ <div class="card-content">Some content</div>
+ <div class="card-actions">
+ <paper-button>Some action</paper-button>
+ </div>
+ </paper-card>
+
+Example - top card image:
+
+ <paper-card heading="Card Title" image="/path/to/image.png">
+ ...
+ </paper-card>
+
+### Accessibility
+
+By default, the `aria-label` will be set to the value of the `heading` attribute.
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-card-background-color` | The background color of the card | `--primary-background-color`
+`--paper-card-header-color` | The color of the header text | `#000`
+`--paper-card-header` | Mixin applied to the card header section | `{}`
+`--paper-card-header-text` | Mixin applied to the title in the card header section | `{}`
+`--paper-card-header-image` | Mixin applied to the image in the card header section | `{}`
+`--paper-card-header-image-text` | Mixin applied to the text overlapping the image in the card header section | `{}`
+`--paper-card-content` | Mixin applied to the card content section| `{}`
+`--paper-card-actions` | Mixin applied to the card action section | `{}`
+`--paper-card` | Mixin applied to the card | `{}`
+
+@group Paper Elements
+@element paper-card
+@demo demo/index.html
+-->
+
+<dom-module id="paper-card">
+ <template>
+ <style include="paper-material">
+ :host {
+ display: inline-block;
+ position: relative;
+ box-sizing: border-box;
+ background-color: var(--paper-card-background-color, --primary-background-color);
+ border-radius: 2px;
+
+ @apply(--paper-font-common-base);
+ @apply(--paper-card);
+ }
+
+ /* IE 10 support for HTML5 hidden attr */
+ [hidden] {
+ display: none !important;
+ }
+
+ .header {
+ position: relative;
+ border-top-left-radius: inherit;
+ border-top-right-radius: inherit;
+ overflow: hidden;
+
+ @apply(--paper-card-header);
+ }
+
+ .header iron-image {
+ display: block;
+ width: 100%;
+ --iron-image-width: 100%;
+ pointer-events: none;
+
+ @apply(--paper-card-header-image);
+ }
+
+ .header .title-text {
+ padding: 16px;
+ font-size: 24px;
+ font-weight: 400;
+ color: var(--paper-card-header-color, #000);
+
+ @apply(--paper-card-header-text);
+ }
+
+ .header .title-text.over-image {
+ position: absolute;
+ bottom: 0px;
+
+ @apply(--paper-card-header-image-text);
+ }
+
+ :host ::content .card-content {
+ padding: 16px;
+ position:relative;
+
+ @apply(--paper-card-content);
+ }
+
+ :host ::content .card-actions {
+ border-top: 1px solid #e8e8e8;
+ padding: 5px 16px;
+ position:relative;
+
+ @apply(--paper-card-actions);
+ }
+ </style>
+
+ <div class="header">
+ <iron-image hidden$="[[!image]]" src="[[image]]" preload$="[[preloadImage]]" fade$="[[fadeImage]]"></iron-image>
+ <div hidden$="[[!heading]]" class$="[[_computeHeadingClass(image)]]">[[heading]]</div>
+ </div>
+
+ <content></content>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-card',
+
+ properties: {
+ /**
+ * The title of the card.
+ */
+ heading: {
+ type: String,
+ value: '',
+ observer: '_headingChanged'
+ },
+
+ /**
+ * The url of the title image of the card.
+ */
+ image: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * When `true`, any change to the image url property will cause the
+ * `placeholder` image to be shown until the image is fully rendered.
+ */
+ preloadImage: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * When `preloadImage` is true, setting `fadeImage` to true will cause the
+ * image to fade into place.
+ */
+ fadeImage: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The z-depth of the card, from 0-5.
+ */
+ elevation: {
+ type: Number,
+ value: 1,
+ reflectToAttribute: true
+ },
+
+ /**
+ * Set this to true to animate the card shadow when setting a new
+ * `z` value.
+ */
+ animatedShadow: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Read-only property used to pass down the `animatedShadow` value to
+ * the underlying paper-material style (since they have different names).
+ */
+ animated: {
+ type: Boolean,
+ reflectToAttribute: true,
+ readOnly: true,
+ computed: '_computeAnimated(animatedShadow)'
+ }
+ },
+
+ _headingChanged: function(heading) {
+ var label = this.getAttribute('aria-label');
+ this.setAttribute('aria-label', heading);
+ },
+
+ _computeHeadingClass: function(image) {
+ var cls = 'title-text';
+ if (image)
+ cls += ' over-image';
+ return cls;
+ },
+
+ _computeAnimated: function(animatedShadow) {
+ return animatedShadow;
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-card/test/basic.html b/catapult/third_party/polymer/components/paper-card/test/basic.html
new file mode 100755
index 00000000..c1db5600
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/test/basic.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-card a11y tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../paper-card.html">
+
+ <style>
+ paper-card {
+ width: 400px;
+ }
+ </style>
+
+</head>
+<body>
+ <test-fixture id="basic">
+ <template>
+ <paper-card heading="header">
+ <div class="card-content"><p>Sample content</p></div>
+ </paper-card>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('a11y', function() {
+ var f;
+ setup(function () {
+ f = fixture('basic');
+ });
+
+ test('aria-label set on card', function() {
+ assert.strictEqual(f.getAttribute('aria-label'), f.heading);
+ });
+
+ test('aria-label can be updated', function() {
+ assert.strictEqual(f.getAttribute('aria-label'), f.heading);
+ f.heading = 'batman';
+ assert.strictEqual(f.getAttribute('aria-label'), 'batman');
+ });
+ });
+ suite('header image', function() {
+ var f, img;
+ setup(function () {
+ f = fixture('basic');
+ img = f.$$('iron-image');
+ });
+
+ test('is iron-image', function(){
+ expect(img).to.be.ok;
+ });
+
+ test('width properly setup', function() {
+ assert.strictEqual(img.offsetWidth, 0);
+ f.image = 'some-img-url';
+ assert.strictEqual(img.src, f.image);
+ assert.strictEqual(img.offsetWidth, f.offsetWidth);
+ });
+
+ test('preload properly setup', function() {
+ assert.strictEqual(img.preload, f.preloadImage);
+ f.preloadImage = !f.preloadImage;
+ assert.strictEqual(img.preload, f.preloadImage);
+ });
+
+ test('fade properly setup', function() {
+ assert.strictEqual(img.fade, f.fadeImage);
+ f.fadeImage = !f.fadeImage;
+ assert.strictEqual(img.fade, f.fadeImage);
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-card/test/index.html b/catapult/third_party/polymer/components/paper-card/test/index.html
new file mode 100755
index 00000000..24719fd3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-card/test/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-card tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-checkbox/.bower.json b/catapult/third_party/polymer/components/paper-checkbox/.bower.json
new file mode 100644
index 00000000..ce6a91d7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/.bower.json
@@ -0,0 +1,49 @@
+{
+ "name": "paper-checkbox",
+ "version": "1.4.2",
+ "description": "A material design checkbox",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "paper",
+ "checkbox",
+ "control"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-checkbox"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-checkbox",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.6.0",
+ "iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0"
+ },
+ "main": "paper-checkbox.html",
+ "_release": "1.4.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.4.2",
+ "commit": "597add130503e789ca9e831280d084c1ad64d8a0"
+ },
+ "_source": "https://github.com/PolymerElements/paper-checkbox.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-checkbox"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-checkbox/.eslintrc.json b/catapult/third_party/polymer/components/paper-checkbox/.eslintrc.json
new file mode 100644
index 00000000..cc13518e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "extends": "eslint:recommended",
+ "rules": {
+ "no-console": 0
+ },
+ "env": {
+ "browser": true
+ },
+ "plugins": [
+ "html"
+ ],
+ "globals": {
+ "CustomElements": false,
+ "HTMLImports": false,
+ "Polymer": false,
+ "WeakMap": false
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-checkbox/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-checkbox/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..3b5f5fd1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-checkbox/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-checkbox/.gitignore b/catapult/third_party/polymer/components/paper-checkbox/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-checkbox/.travis.yml b/catapult/third_party/polymer/components/paper-checkbox/.travis.yml
new file mode 100644
index 00000000..305c9cc1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/.travis.yml
@@ -0,0 +1,25 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester eslint eslint-plugin-html
+ - bower install
+ - polylint
+ - eslint *.html test/*.html
+env:
+ global:
+ - secure: >-
+ yp6I0VbSey7lRWOKQQkBU7CBvQd0hN0TZiZnbo8fzWWfOjANeDICCa/xyY2+ZM0TU5mxvehTg38mjkpRPkMm4UrsE5og49GBFqaDLk9U+Bp7fP37tDtWfT3l4VgpzSwKAdIEh8EcmUPblZxQxJlH6+HHImnPCWSKy9YvVEGirzY=
+ - secure: >-
+ XjG814z1IZBtPOsZBknOcJaqSJHjF7jWzAw1Cl9sKcmHPKSJW2a0gILdpXx2Au0KGzLzn7Z6/p6u2+rv4tbYWBq21EWgbHmn3iAF4pAZt6DgGqJpfCsNdyuKUYN8ZfLFbK3GC/js6IJDGV+zq5HxtZmwZh+eQ85bbx2xNyRKZis=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-checkbox/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-checkbox/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-checkbox/README.md b/catapult/third_party/polymer/components/paper-checkbox/README.md
new file mode 100644
index 00000000..8b72a6fb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/README.md
@@ -0,0 +1,63 @@
+[![Build status](https://travis-ci.org/PolymerElements/paper-checkbox.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-checkbox)
+
+##&lt;paper-checkbox&gt;
+
+`paper-checkbox` is a [material design checkbox](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-checkbox).
+User can tap the checkbox to check or uncheck it. Usually you use checkboxes
+to allow user to select multiple options from a set. If you have a single
+ON/OFF option, avoid using a single checkbox and use `paper-toggle-button`
+instead.
+
+Example:
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-checkbox.html">
+ <style is="custom-style">
+ paper-checkbox {
+ font-family: 'Roboto', sans-serif;
+ margin: 24px;
+ }
+
+ paper-checkbox:first-child {
+ --primary-color: #ff5722;
+ }
+
+ paper-checkbox.styled {
+ align-self: center;
+ border: 1px solid var(--paper-green-200);
+ padding: 8px 16px;
+ --paper-checkbox-checked-color: var(--paper-green-500);
+ --paper-checkbox-checked-ink-color: var(--paper-green-500);
+ --paper-checkbox-unchecked-color: var(--paper-green-900);
+ --paper-checkbox-unchecked-ink-color: var(--paper-green-900);
+ --paper-checkbox-label-color: var(--paper-green-500);
+ --paper-checkbox-label-spacing: 0;
+ --paper-checkbox-margin: 8px 16px 8px 0;
+ --paper-checkbox-vertical-align: top;
+ }
+
+ paper-checkbox .subtitle {
+ display: block;
+ font-size: 0.8em;
+ margin-top: 2px;
+ max-width: 150px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-checkbox checked>Checked</paper-checkbox>
+<paper-checkbox class="styled">
+ Checkbox
+ <span class="subtitle">
+ With a longer label
+ </span>
+</paper-checkbox>
+<paper-checkbox disabled>Disabled</paper-checkbox>
+```
diff --git a/catapult/third_party/polymer/components/paper-checkbox/bower.json b/catapult/third_party/polymer/components/paper-checkbox/bower.json
new file mode 100644
index 00000000..61224293
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "paper-checkbox",
+ "version": "1.4.2",
+ "description": "A material design checkbox",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "paper",
+ "checkbox",
+ "control"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-checkbox"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-checkbox",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.6.0",
+ "iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0"
+ },
+ "main": "paper-checkbox.html"
+}
diff --git a/catapult/third_party/polymer/components/paper-checkbox/demo/index.html b/catapult/third_party/polymer/components/paper-checkbox/demo/index.html
new file mode 100644
index 00000000..c52eef05
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/demo/index.html
@@ -0,0 +1,116 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>paper-checkbox demo</title>
+
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../paper-checkbox.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-checkbox {
+ display: block;
+ margin-right: 24px;
+ }
+
+ demo-snippet {
+ margin-bottom: 40px;
+ }
+ .vertical-section-container {
+ max-width: 550px;
+ }
+ </style>
+ </head>
+ <body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Checkboxes can be checked or unchecked, or disabled entirely</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-checkbox>Checkbox</paper-checkbox>
+ <paper-checkbox checked>Checkbox</paper-checkbox>
+ <paper-checkbox disabled>Disabled</paper-checkbox>
+ </template>
+ </demo-snippet>
+
+ <h3>Checkboxes can hide the ripple effect using the <i>noink</i> attribute</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-checkbox noink>Checkbox</paper-checkbox>
+ </template>
+ </demo-snippet>
+
+ <h3>Checkboxes can be styled using custom properties</h3>
+ <demo-snippet class="centered-demo small-text">
+ <template>
+ <style is="custom-style">
+ paper-checkbox.red {
+ --paper-checkbox-checked-color: var(--paper-red-500);
+ --paper-checkbox-checked-ink-color: var(--paper-red-500);
+ --paper-checkbox-unchecked-color: var(--paper-red-900);
+ --paper-checkbox-unchecked-ink-color: var(--paper-red-900);
+ --paper-checkbox-label-color: var(--paper-red-500);
+ --paper-checkbox-vertical-align: top;
+ }
+
+ paper-checkbox.green {
+ align-self: center;
+ border: 1px solid var(--paper-green-200);
+ padding: 8px 16px;
+
+ --paper-checkbox-checked-color: var(--paper-green-500);
+ --paper-checkbox-checked-ink-color: var(--paper-green-500);
+ --paper-checkbox-unchecked-color: var(--paper-green-900);
+ --paper-checkbox-unchecked-ink-color: var(--paper-green-900);
+ --paper-checkbox-label-color: var(--paper-green-500);
+ --paper-checkbox-label-spacing: 0;
+ --paper-checkbox-margin: 8px 16px 8px 0;
+ --paper-checkbox-vertical-align: top;
+ }
+
+ paper-checkbox.blue {
+ --paper-checkbox-checked-color: var(--paper-blue-500);
+ --paper-checkbox-checked-ink-color: var(--paper-blue-500);
+ --paper-checkbox-unchecked-color: var(--paper-blue-900);
+ --paper-checkbox-unchecked-ink-color: var(--paper-blue-900);
+ --paper-checkbox-label-color: var(--paper-blue-500);
+ --paper-checkbox-label-checked-color: var(--paper-blue-900);
+ }
+
+ paper-checkbox .subtitle {
+ display: block;
+ font-size: 0.8em;
+ margin-top: 2px;
+ max-width: 150px;
+ }
+ </style>
+
+ <paper-checkbox class="red">
+ Checkbox
+ <span class="subtitle">With a subtitle</span>
+ </paper-checkbox>
+ <paper-checkbox checked class="green">
+ Checkbox
+ <span class="subtitle">
+ With a longer subtitle that wraps to another line
+ </span>
+ </paper-checkbox>
+ <paper-checkbox class="blue">Checkbox</paper-checkbox>
+ </template>
+ </demo-snippet>
+ </div>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-checkbox/index.html b/catapult/third_party/polymer/components/paper-checkbox/index.html
new file mode 100644
index 00000000..b368797b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-checkbox</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+ </head>
+ <body>
+
+ <iron-component-page></iron-component-page>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-checkbox/paper-checkbox.html b/catapult/third_party/polymer/components/paper-checkbox/paper-checkbox.html
new file mode 100644
index 00000000..14452454
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/paper-checkbox.html
@@ -0,0 +1,310 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../paper-behaviors/paper-checked-element-behavior.html">
+
+<!--
+Material design: [Checkbox](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-checkbox)
+
+`paper-checkbox` is a button that can be either checked or unchecked. User
+can tap the checkbox to check or uncheck it. Usually you use checkboxes
+to allow user to select multiple options from a set. If you have a single
+ON/OFF option, avoid using a single checkbox and use `paper-toggle-button`
+instead.
+
+Example:
+
+ <paper-checkbox>label</paper-checkbox>
+
+ <paper-checkbox checked> label</paper-checkbox>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-checkbox-unchecked-background-color` | Checkbox background color when the input is not checked | `transparent`
+`--paper-checkbox-unchecked-color` | Checkbox border color when the input is not checked | `--primary-text-color`
+`--paper-checkbox-unchecked-ink-color` | Selected/focus ripple color when the input is not checked | `--primary-text-color`
+`--paper-checkbox-checked-color` | Checkbox color when the input is checked | `--primary-color`
+`--paper-checkbox-checked-ink-color` | Selected/focus ripple color when the input is checked | `--primary-color`
+`--paper-checkbox-checkmark-color` | Checkmark color | `white`
+`--paper-checkbox-label-color` | Label color | `--primary-text-color`
+`--paper-checkbox-label-checked-color` | Label color when the input is checked | `--paper-checkbox-label-color`
+`--paper-checkbox-label-spacing` | Spacing between the label and the checkbox | `8px`
+`--paper-checkbox-label` | Mixin applied to the label | `{}`
+`--paper-checkbox-label-checked` | Mixin applied to the label when the input is checked | `{}`
+`--paper-checkbox-error-color` | Checkbox color when invalid | `--error-color`
+`--paper-checkbox-size` | Size of the checkbox | `18px`
+`--paper-checkbox-ink-size` | Size of the ripple | `48px`
+`--paper-checkbox-margin` | Margin around the checkbox container | `initial`
+`--paper-checkbox-vertical-align` | Vertical alignment of the checkbox container | `middle`
+
+This element applies the mixin `--paper-font-common-base` but does not import `paper-styles/typography.html`.
+In order to apply the `Roboto` font to this element, make sure you've imported `paper-styles/typography.html`.
+
+@demo demo/index.html
+-->
+
+<dom-module id="paper-checkbox">
+ <template strip-whitespace>
+ <style>
+ :host {
+ display: inline-block;
+ white-space: nowrap;
+ cursor: pointer;
+ --calculated-paper-checkbox-size: var(--paper-checkbox-size, 18px);
+ /* -1px is a sentinel for the default and is replaced in `attached`. */
+ --calculated-paper-checkbox-ink-size: var(--paper-checkbox-ink-size, -1px);
+ @apply(--paper-font-common-base);
+ line-height: 0;
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ :host(:focus) {
+ outline: none;
+ }
+
+ .hidden {
+ display: none;
+ }
+
+ #checkboxContainer {
+ display: inline-block;
+ position: relative;
+ width: var(--calculated-paper-checkbox-size);
+ height: var(--calculated-paper-checkbox-size);
+ min-width: var(--calculated-paper-checkbox-size);
+ margin: var(--paper-checkbox-margin, initial);
+ vertical-align: var(--paper-checkbox-vertical-align, middle);
+ background-color: var(--paper-checkbox-unchecked-background-color, transparent);
+ }
+
+ #ink {
+ position: absolute;
+
+ /* Center the ripple in the checkbox by negative offsetting it by
+ * (inkWidth - rippleWidth) / 2 */
+ top: calc(0px - (var(--calculated-paper-checkbox-ink-size) - var(--calculated-paper-checkbox-size)) / 2);
+ left: calc(0px - (var(--calculated-paper-checkbox-ink-size) - var(--calculated-paper-checkbox-size)) / 2);
+ width: var(--calculated-paper-checkbox-ink-size);
+ height: var(--calculated-paper-checkbox-ink-size);
+ color: var(--paper-checkbox-unchecked-ink-color, var(--primary-text-color));
+ opacity: 0.6;
+ pointer-events: none;
+ }
+
+ :host-context([dir="rtl"]) #ink {
+ right: calc(0px - (var(--calculated-paper-checkbox-ink-size) - var(--calculated-paper-checkbox-size)) / 2);
+ left: auto;
+ }
+
+ #ink[checked] {
+ color: var(--paper-checkbox-checked-ink-color, var(--primary-color));
+ }
+
+ #checkbox {
+ position: relative;
+ box-sizing: border-box;
+ height: 100%;
+ border: solid 2px;
+ border-color: var(--paper-checkbox-unchecked-color, var(--primary-text-color));
+ border-radius: 2px;
+ pointer-events: none;
+ -webkit-transition: background-color 140ms, border-color 140ms;
+ transition: background-color 140ms, border-color 140ms;
+ }
+
+ /* checkbox checked animations */
+ #checkbox.checked #checkmark {
+ -webkit-animation: checkmark-expand 140ms ease-out forwards;
+ animation: checkmark-expand 140ms ease-out forwards;
+ }
+
+ @-webkit-keyframes checkmark-expand {
+ 0% {
+ -webkit-transform: scale(0, 0) rotate(45deg);
+ }
+ 100% {
+ -webkit-transform: scale(1, 1) rotate(45deg);
+ }
+ }
+
+ @keyframes checkmark-expand {
+ 0% {
+ transform: scale(0, 0) rotate(45deg);
+ }
+ 100% {
+ transform: scale(1, 1) rotate(45deg);
+ }
+ }
+
+ #checkbox.checked {
+ background-color: var(--paper-checkbox-checked-color, var(--primary-color));
+ border-color: var(--paper-checkbox-checked-color, var(--primary-color));
+ }
+
+ #checkmark {
+ position: absolute;
+ width: 36%;
+ height: 70%;
+ border-style: solid;
+ border-top: none;
+ border-left: none;
+ border-right-width: calc(2/15 * var(--calculated-paper-checkbox-size));
+ border-bottom-width: calc(2/15 * var(--calculated-paper-checkbox-size));
+ border-color: var(--paper-checkbox-checkmark-color, white);
+ -webkit-transform-origin: 97% 86%;
+ transform-origin: 97% 86%;
+ box-sizing: content-box; /* protect against page-level box-sizing */
+ }
+
+ :host-context([dir="rtl"]) #checkmark {
+ -webkit-transform-origin: 50% 14%;
+ transform-origin: 50% 14%;
+ }
+
+ /* label */
+ #checkboxLabel {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ padding-left: var(--paper-checkbox-label-spacing, 8px);
+ white-space: normal;
+ line-height: normal;
+ color: var(--paper-checkbox-label-color, var(--primary-text-color));
+ @apply(--paper-checkbox-label);
+ }
+
+ :host([checked]) #checkboxLabel {
+ color: var(--paper-checkbox-label-checked-color, var(--paper-checkbox-label-color, var(--primary-text-color)));
+ @apply(--paper-checkbox-label-checked);
+ }
+
+ :host-context([dir="rtl"]) #checkboxLabel {
+ padding-right: var(--paper-checkbox-label-spacing, 8px);
+ padding-left: 0;
+ }
+
+ #checkboxLabel[hidden] {
+ display: none;
+ }
+
+ /* disabled state */
+
+ :host([disabled]) #checkbox {
+ opacity: 0.5;
+ border-color: var(--paper-checkbox-unchecked-color, var(--primary-text-color));
+ }
+
+ :host([disabled][checked]) #checkbox {
+ background-color: var(--paper-checkbox-unchecked-color, var(--primary-text-color));
+ opacity: 0.5;
+ }
+
+ :host([disabled]) #checkboxLabel {
+ opacity: 0.65;
+ }
+
+ /* invalid state */
+ #checkbox.invalid:not(.checked) {
+ border-color: var(--paper-checkbox-error-color, var(--error-color));
+ }
+ </style>
+
+ <div id="checkboxContainer">
+ <div id="checkbox" class$="[[_computeCheckboxClass(checked, invalid)]]">
+ <div id="checkmark" class$="[[_computeCheckmarkClass(checked)]]"></div>
+ </div>
+ </div>
+
+ <div id="checkboxLabel"><content></content></div>
+ </template>
+
+</dom-module>
+<script>
+Polymer({
+ is: 'paper-checkbox',
+
+ behaviors: [
+ Polymer.PaperCheckedElementBehavior
+ ],
+
+ hostAttributes: {
+ role: 'checkbox',
+ 'aria-checked': false,
+ tabindex: 0
+ },
+
+ properties: {
+ /**
+ * Fired when the checked state changes due to user interaction.
+ *
+ * @event change
+ */
+
+ /**
+ * Fired when the checked state changes.
+ *
+ * @event iron-change
+ */
+ ariaActiveAttribute: {
+ type: String,
+ value: 'aria-checked'
+ }
+ },
+
+ attached: function() {
+ var inkSize = this.getComputedStyleValue('--calculated-paper-checkbox-ink-size').trim();
+ // If unset, compute and set the default `--paper-checkbox-ink-size`.
+ if (inkSize === '-1px') {
+ var checkboxSize = parseFloat(this.getComputedStyleValue('--calculated-paper-checkbox-size').trim());
+ var defaultInkSize = Math.floor((8 / 3) * checkboxSize);
+
+ // The checkbox and ripple need to have the same parity so that their
+ // centers align.
+ if (defaultInkSize % 2 !== checkboxSize % 2) {
+ defaultInkSize++;
+ }
+
+ this.customStyle['--paper-checkbox-ink-size'] = defaultInkSize + 'px';
+ this.updateStyles();
+ }
+ },
+
+ _computeCheckboxClass: function(checked, invalid) {
+ var className = '';
+ if (checked) {
+ className += 'checked ';
+ }
+ if (invalid) {
+ className += 'invalid';
+ }
+ return className;
+ },
+
+ _computeCheckmarkClass: function(checked) {
+ return checked ? '' : 'hidden';
+ },
+
+ // create ripple inside the checkboxContainer
+ _createRipple: function() {
+ this._rippleContainer = this.$.checkboxContainer;
+ return Polymer.PaperInkyFocusBehaviorImpl._createRipple.call(this);
+ }
+});
+</script>
diff --git a/catapult/third_party/polymer/components/paper-checkbox/test/.eslintrc.json b/catapult/third_party/polymer/components/paper-checkbox/test/.eslintrc.json
new file mode 100644
index 00000000..7c2ce11b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/test/.eslintrc.json
@@ -0,0 +1,14 @@
+{
+ "env": {
+ "mocha": true
+ },
+ "globals": {
+ "assert": true,
+ "sinon": true,
+ "WCT": true,
+ "fixture": true,
+ "Polymer": true,
+ "MockInteractions": true,
+ "a11ySuite": true
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-checkbox/test/basic.html b/catapult/third_party/polymer/components/paper-checkbox/test/basic.html
new file mode 100644
index 00000000..8626a07b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/test/basic.html
@@ -0,0 +1,275 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-checkbox basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../paper-checkbox.html">
+
+ <style is="custom-style">
+ paper-checkbox.no-label-spacing {
+ --paper-checkbox-label-spacing: 0;
+ }
+
+ paper-checkbox.tiny {
+ --paper-checkbox-size: 5px;
+ }
+
+ paper-checkbox.medium {
+ --paper-checkbox-size: 37px;
+ }
+
+ paper-checkbox.giant {
+ --paper-checkbox-size: 50px;
+ }
+
+ paper-checkbox.enormous {
+ --paper-checkbox-size: 71px;
+ }
+
+ paper-checkbox.custom-ink-size {
+ --paper-checkbox-size: 25px;
+ --paper-checkbox-ink-size: 30px;
+ }
+
+ paper-checkbox.large-line-height {
+ line-height: 3;
+ }
+
+ paper-checkbox.small-line-height {
+ line-height: 0.25;
+ }
+ </style>
+</head>
+<body>
+
+ <test-fixture id="NoLabel">
+ <template>
+ <paper-checkbox id="check1"></paper-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithLabel">
+ <template>
+ <paper-checkbox id="check2">Batman</paper-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="AriaLabel">
+ <template>
+ <paper-checkbox id="check3" aria-label="Batman">Robin</paper-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithDifferentSizes">
+ <template>
+ <paper-checkbox class="no-label-spacing"></paper-checkbox>
+ <paper-checkbox class="no-label-spacing giant"></paper-checkbox>
+ <paper-checkbox class="no-label-spacing tiny"></paper-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithDifferentSizes2">
+ <template>
+ <paper-checkbox class="tiny"></paper-checkbox>
+ <paper-checkbox></paper-checkbox>
+ <paper-checkbox class="medium"></paper-checkbox>
+ <paper-checkbox class="giant"></paper-checkbox>
+ <paper-checkbox class="enormous"></paper-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="CustomInkSize">
+ <template>
+ <paper-checkbox class="custom-ink-size"></paper-checkbox>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithLineHeight">
+ <template>
+ <paper-checkbox class="large-line-height">Checkbox</paper-checkbox>
+ <paper-checkbox class="small-line-height">Checkbox</paper-checkbox>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('defaults', function() {
+ var c1;
+
+ setup(function() {
+ c1 = fixture('NoLabel');
+ });
+
+ test('check checkbox via click', function(done) {
+ c1.addEventListener('click', function() {
+ assert.isTrue(c1.getAttribute('aria-checked') == 'true');
+ assert.isTrue(c1.checked);
+ done();
+ });
+ MockInteractions.tap(c1);
+ });
+
+ test('toggle checkbox via click', function(done) {
+ c1.checked = true;
+ c1.addEventListener('click', function() {
+ assert.isFalse(c1.getAttribute('aria-checked') != 'false');
+ assert.isFalse(c1.checked);
+ done();
+ });
+ MockInteractions.tap(c1);
+ });
+
+ test('disabled checkbox cannot be clicked', function(done) {
+ c1.disabled = true;
+ c1.checked = true;
+ MockInteractions.tap(c1);
+ setTimeout(function() {
+ assert.isTrue(c1.getAttribute('aria-checked') == 'true');
+ assert.isTrue(c1.checked);
+ done();
+ }, 1);
+ });
+
+ test('checkbox can be validated', function() {
+ c1.required = true;
+ assert.isFalse(c1.validate());
+
+ c1.checked = true;
+ assert.isTrue(c1.validate());
+ });
+
+ test('disabled checkbox is always valid', function() {
+ c1.disabled = true;
+ c1.required = true;
+ assert.isTrue(c1.validate());
+
+ c1.checked = true;
+ assert.isTrue(c1.validate());
+ });
+
+ test('checkbox can check sizes', function() {
+ var c2 = fixture('WithDifferentSizes');
+ var normal = c2[0].getBoundingClientRect();
+ var giant = c2[1].getBoundingClientRect();
+ var tiny = c2[2].getBoundingClientRect();
+
+ assert.isTrue(5 === tiny.height);
+ assert.isTrue(tiny.height < normal.height);
+ assert.isTrue(normal.height < giant.height);
+ assert.isTrue(giant.height <= 50);
+
+ assert.isTrue(5 === tiny.width);
+ assert.isTrue(tiny.width < normal.width);
+ assert.isTrue(normal.width < giant.width);
+ assert.isTrue(giant.width === 50);
+ });
+
+ suite('checkbox line-height', function() {
+ var large;
+ var small;
+
+ setup(function() {
+ var checkboxes = fixture('WithLineHeight');
+ large = checkboxes[0];
+ small = checkboxes[1];
+ });
+
+ test('checkboxes with >1 line-height have an equal height', function() {
+ var largeRect = large.getBoundingClientRect();
+ var largeStyle = getComputedStyle(large);
+
+ assert.isTrue(largeRect.height === 3 * parseFloat(largeStyle.fontSize));
+ });
+
+ test('checkbox with <1 line-height are at least 1em tall', function() {
+ var smallRect = small.getBoundingClientRect();
+ var smallStyle = getComputedStyle(small);
+
+ assert.isTrue(smallRect.height >= 1 * parseFloat(smallStyle.fontSize));
+ });
+ });
+
+ suite('ink size', function() {
+ var checkboxes;
+
+ setup(function() {
+ checkboxes = fixture('WithDifferentSizes2');
+ });
+
+ test('`--paper-checkbox-ink-size` sets the ink size', function() {
+ var checkbox = fixture('CustomInkSize');
+ assert.equal(checkbox.getComputedStyleValue('--calculated-paper-checkbox-size').trim(), '25px');
+ assert.equal(checkbox.getComputedStyleValue('--calculated-paper-checkbox-ink-size').trim(), '30px');
+ });
+
+ test('ink sizes are near (8/3 * checkbox size) by default', function() {
+ checkboxes.forEach(function(checkbox) {
+ var size = parseFloat(checkbox.getComputedStyleValue('--calculated-paper-checkbox-size'), 10);
+ var inkSize = parseFloat(checkbox.getComputedStyleValue('--calculated-paper-checkbox-ink-size'), 10);
+ assert.approximately(inkSize / size, 8 / 3, 0.1);
+ });
+ });
+
+ test('ink sizes are integers', function() {
+ checkboxes.forEach(function(checkbox) {
+ var unparsedInkSize = checkbox.getComputedStyleValue('--calculated-paper-checkbox-ink-size');
+ var floatInkSize = parseFloat(unparsedInkSize, 10);
+ var intInkSize = parseInt(unparsedInkSize, 10);
+ assert.equal(floatInkSize, intInkSize);
+ });
+ });
+
+ test('ink size parity matches checkbox size parity (centers are aligned)', function() {
+ checkboxes.forEach(function(checkbox) {
+ var size = parseInt(checkbox.getComputedStyleValue('--calculated-paper-checkbox-size'), 10);
+ var inkSize = parseInt(checkbox.getComputedStyleValue('--calculated-paper-checkbox-ink-size'), 10);
+ assert.equal(size % 2, inkSize % 2);
+ });
+ });
+ });
+ });
+
+ suite('a11y', function() {
+ var c1;
+ var c2;
+
+ setup(function() {
+ c1 = fixture('NoLabel');
+ c2 = fixture('WithLabel');
+ });
+
+ test('has aria role "checkbox"', function() {
+ assert.isTrue(c1.getAttribute('role') == 'checkbox');
+ assert.isTrue(c2.getAttribute('role') == 'checkbox');
+ });
+
+ test('checkbox with no label has no aria label', function() {
+ assert.isTrue(!c1.getAttribute('aria-label'));
+ });
+
+ test('checkbox respects the user set aria-label', function() {
+ var c = fixture('AriaLabel');
+ assert.isTrue(c.getAttribute('aria-label') == "Batman");
+ });
+
+ a11ySuite('NoLabel');
+ a11ySuite('WithLabel');
+ a11ySuite('AriaLabel');
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-checkbox/test/index.html b/catapult/third_party/polymer/components/paper-checkbox/test/index.html
new file mode 100644
index 00000000..94f72bfd
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-checkbox/test/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-checkbox tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow',
+ // To enable `useNativeCSSProperties`, the native Shadow DOM and lazy
+ // registration flags must also be enabled.
+ // https://github.com/Polymer/polymer/blob/ff6e884ef4f309d41491333860a8bc9c2f178696/src/lib/settings.html#L55
+ 'basic.html?dom=shadow&lazyRegister=true&useNativeCSSProperties=true'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/.bower.json b/catapult/third_party/polymer/components/paper-dialog-behavior/.bower.json
new file mode 100644
index 00000000..0add9133
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/.bower.json
@@ -0,0 +1,49 @@
+{
+ "name": "paper-dialog-behavior",
+ "version": "1.2.9",
+ "description": "Implements a behavior used for material design dialogs",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "dialog",
+ "overlay",
+ "behavior"
+ ],
+ "main": "paper-dialog-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-dialog-behavior"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-dialog-behavior",
+ "ignore": [],
+ "dependencies": {
+ "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-listbox": "PolymerElements/paper-listbox#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.2.9",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.9",
+ "commit": "89f4269be54796eafbf49996a9bb1d76e39b38b7"
+ },
+ "_source": "https://github.com/PolymerElements/paper-dialog-behavior.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-dialog-behavior"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-dialog-behavior/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..f544977f
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-dialog-behavior/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/.gitignore b/catapult/third_party/polymer/components/paper-dialog-behavior/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/.travis.yml b/catapult/third_party/polymer/components/paper-dialog-behavior/.travis.yml
new file mode 100644
index 00000000..c81ed731
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ TJSGIyeS77tnbqO9zSa5jAThGUOwhfDTdMBXIVkjDwPqtB2u8y6QB6rerGvNpDLdWTZh6dNUByc4vHJLGKmTArbWc/XH90H7msZs3OxMlXjgILX4//QSxM4uwBwfTh44PVb/wDJxgX950qXyF646Lo9A8UwKk2JL9LBeuiO+tQs=
+ - secure: >-
+ QXJDFGdZ+94ktzeT8u1TjkgCSbku5UT+YIyOOyw4yrphg/0HSnlZ8bu5i3z9iPJu3mzxNdIXCAW7dLKfYgKK6S21o/3QWlMDuAt11wnSZLW/GWrLDTQNotFN+cLFghGuZkM3bsMp+CLjvvjZZwyK3B340g1gP66DbUQuoUYaRao=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-dialog-behavior/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/README.md b/catapult/third_party/polymer/components/paper-dialog-behavior/README.md
new file mode 100644
index 00000000..c3c52980
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/README.md
@@ -0,0 +1,54 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-dialog-behavior.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-dialog-behavior.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-dialog-behavior)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-dialog-behavior)_
+
+
+##Polymer.PaperDialogBehavior
+
+Use `Polymer.PaperDialogBehavior` and `paper-dialog-shared-styles.html` to implement a Material Design
+dialog.
+
+For example, if `<paper-dialog-impl>` implements this behavior:
+
+```html
+<paper-dialog-impl>
+ <h2>Header</h2>
+ <div>Dialog body</div>
+ <div class="buttons">
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm>Accept</paper-button>
+ </div>
+</paper-dialog-impl>
+```
+
+`paper-dialog-shared-styles.html` provide styles for a header, content area, and an action area for buttons.
+Use the `<h2>` tag for the header and the `buttons` class for the action area. You can use the
+`paper-dialog-scrollable` element (in its own repository) if you need a scrolling content area.
+
+Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive controls to close the
+dialog. If the user dismisses the dialog with `dialog-confirm`, the `closingReason` will update
+to include `confirmed: true`.
+
+### Accessibility
+
+This element has `role="dialog"` by default. Depending on the context, it may be more appropriate
+to override this attribute with `role="alertdialog"`.
+
+If `modal` is set, the element will prevent the focus from exiting the element.
+It will also ensure that focus remains in the dialog.
+
+
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/bower.json b/catapult/third_party/polymer/components/paper-dialog-behavior/bower.json
new file mode 100644
index 00000000..dcc5eb80
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "paper-dialog-behavior",
+ "version": "1.2.9",
+ "description": "Implements a behavior used for material design dialogs",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "dialog",
+ "overlay",
+ "behavior"
+ ],
+ "main": "paper-dialog-behavior.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-dialog-behavior"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-dialog-behavior",
+ "ignore": [],
+ "dependencies": {
+ "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-listbox": "PolymerElements/paper-listbox#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/demo/index.html b/catapult/third_party/polymer/components/paper-dialog-behavior/demo/index.html
new file mode 100644
index 00000000..193dddcc
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/demo/index.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-dialog-behavior demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="simple-dialog.html">
+
+ <link rel="import" href="../../paper-button/paper-button.html">
+ <link rel="import" href="../../paper-dropdown-menu/paper-dropdown-menu.html">
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../paper-listbox/paper-listbox.html">
+ <link rel="import" href="../../paper-dialog-scrollable/paper-dialog-scrollable.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles"></style>
+</head>
+<body unresolved class="centered">
+
+ <h3>An element with <code>PaperDialogBehavior</code> can be opened, closed, toggled. Use <code>h2</code> for the title</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button raised onclick="dialog.toggle()">dialog</paper-button>
+ <simple-dialog id="dialog">
+ <h2>Dialog Title</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </simple-dialog>
+ </template>
+ </demo-snippet>
+
+ <h3>An element with <code>PaperDialogBehavior</code> can be modal. Use the attributes <code>dialog-dismiss</code> and <code>dialog-confirm</code> on the children to close it.</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button raised onclick="modalAlert.toggle()">modal alert</paper-button>
+ <simple-dialog id="modalAlert" modal role="alertdialog">
+ <h2>Alert</h2>
+ <paper-dropdown-menu label="Draft to discard">
+ <paper-listbox class="dropdown-content">
+ <paper-item>Draft 1</paper-item>
+ <paper-item>Draft 2</paper-item>
+ <paper-item>Draft 3</paper-item>
+ <paper-item>Draft 4</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+ <div class="buttons">
+ <paper-button onclick="modalDetails.toggle()">More details</paper-button>
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm autofocus>Discard</paper-button>
+ </div>
+ </simple-dialog>
+ <simple-dialog id="modalDetails" modal>
+ <h2>Details</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <div class="buttons">
+ <paper-button dialog-confirm autofocus>OK</paper-button>
+ </div>
+ </simple-dialog>
+ </template>
+ </demo-snippet>
+
+ <h3>Use <code>paper-dialog-scrollable</code> for scrolling content</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button raised onclick="scrolling.toggle()">scrolling</paper-button>
+ <simple-dialog id="scrolling">
+ <h2>Scrolling</h2>
+ <paper-dialog-scrollable>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </paper-dialog-scrollable>
+ <div class="buttons">
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm>OK</paper-button>
+ </div>
+ </simple-dialog>
+ </template>
+ </demo-snippet>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/demo/simple-dialog.html b/catapult/third_party/polymer/components/paper-dialog-behavior/demo/simple-dialog.html
new file mode 100644
index 00000000..a2043723
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/demo/simple-dialog.html
@@ -0,0 +1,39 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../paper-dialog-behavior.html">
+<link rel="import" href="../paper-dialog-shared-styles.html">
+
+<dom-module id="simple-dialog">
+ <template>
+ <style include="paper-dialog-shared-styles"></style>
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'simple-dialog',
+
+ behaviors: [
+ Polymer.PaperDialogBehavior
+ ]
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/hero.svg b/catapult/third_party/polymer/components/paper-dialog-behavior/hero.svg
new file mode 100755
index 00000000..564ccd38
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/hero.svg
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <polygon points="76.7,98 79.2,98 74,91.1 74,94.4 "/>
+ <polygon points="74,81.4 74,84.7 84.1,98 86.6,98 "/>
+ <polygon points="74,71.7 74,75 91.5,98 94,98 "/>
+ <polygon points="74,62 74,65.3 98.9,98 101.4,98 "/>
+ <polygon points="94.3,79 92,79 92,76 74,52.3 74,55.6 106.2,98 108.7,98 "/>
+ <polygon points="92,69.6 92,66.3 74,42.6 74,45.9 "/>
+ <polygon points="101.7,79 99.2,79 113.6,98 116.1,98 "/>
+ <polygon points="92,59.9 92,56.6 74,32.9 74,36.2 "/>
+ <polygon points="109.1,79 106.5,79 121,98 123.5,98 "/>
+ <polygon points="92,50.2 92,47 92.1,47 77.7,28 75.2,28 "/>
+ <polygon points="116.4,79 113.9,79 128.4,98 130.9,98 "/>
+ <polygon points="97,47 99.5,47 85,28 82.5,28 "/>
+ <polygon points="123.8,79 121.3,79 135.7,98 138.2,98 "/>
+ <polygon points="104.4,47 106.9,47 92.4,28 89.9,28 "/>
+ <polygon points="131.2,79 128.7,79 143.1,98 145.6,98 "/>
+ <polygon points="132,70.4 132,73.7 150,97.4 150,94.1 "/>
+ <polygon points="111.7,47 114.2,47 99.8,28 97.3,28 "/>
+ <polygon points="132,60.7 132,64 150,87.7 150,84.3 "/>
+ <polygon points="119.1,47 121.6,47 107.2,28 104.7,28 "/>
+ <polygon points="132,51 132,54.3 150,77.9 150,74.6 "/>
+ <polygon points="114.6,28 112,28 126.5,47 129,47 "/>
+ <polygon points="121.9,28 119.4,28 150,68.2 150,64.9 "/>
+ <polygon points="129.3,28 126.8,28 150,58.5 150,55.2 "/>
+ <polygon points="136.7,28 134.2,28 150,48.8 150,45.5 "/>
+ <polygon points="144.1,28 141.5,28 150,39.1 150,35.8 "/>
+ <polygon points="150,29.4 150,28 148.9,28 "/>
+ </g>
+ <path d="M133,80H91V46h42V80z M93,78h38V48H93V78z"/>
+ <path d="M151,99H73V27h78V99z M75,97h74V29H75V97z"/>
+ <circle cx="74" cy="28" r="4"/>
+ <circle cx="150" cy="28" r="4"/>
+ <circle cx="150" cy="98" r="4"/>
+ <circle cx="74" cy="98" r="4"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/index.html b/catapult/third_party/polymer/components/paper-dialog-behavior/index.html
new file mode 100644
index 00000000..af98b85a
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-dialog-behavior</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-behavior.html b/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-behavior.html
new file mode 100644
index 00000000..49d724cb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-behavior.html
@@ -0,0 +1,139 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-overlay-behavior/iron-overlay-behavior.html">
+
+<script>
+
+/**
+Use `Polymer.PaperDialogBehavior` and `paper-dialog-shared-styles.html` to implement a Material Design
+dialog.
+
+For example, if `<paper-dialog-impl>` implements this behavior:
+
+ <paper-dialog-impl>
+ <h2>Header</h2>
+ <div>Dialog body</div>
+ <div class="buttons">
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm>Accept</paper-button>
+ </div>
+ </paper-dialog-impl>
+
+`paper-dialog-shared-styles.html` provide styles for a header, content area, and an action area for buttons.
+Use the `<h2>` tag for the header and the `buttons` class for the action area. You can use the
+`paper-dialog-scrollable` element (in its own repository) if you need a scrolling content area.
+
+Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive controls to close the
+dialog. If the user dismisses the dialog with `dialog-confirm`, the `closingReason` will update
+to include `confirmed: true`.
+
+### Accessibility
+
+This element has `role="dialog"` by default. Depending on the context, it may be more appropriate
+to override this attribute with `role="alertdialog"`.
+
+If `modal` is set, the element will prevent the focus from exiting the element.
+It will also ensure that focus remains in the dialog.
+
+@hero hero.svg
+@demo demo/index.html
+@polymerBehavior Polymer.PaperDialogBehavior
+*/
+ Polymer.PaperDialogBehaviorImpl = {
+
+ hostAttributes: {
+ 'role': 'dialog',
+ 'tabindex': '-1'
+ },
+
+ properties: {
+
+ /**
+ * If `modal` is true, this implies `no-cancel-on-outside-click`, `no-cancel-on-esc-key` and `with-backdrop`.
+ */
+ modal: {
+ type: Boolean,
+ value: false
+ }
+
+ },
+
+ observers: [
+ '_modalChanged(modal, _readied)'
+ ],
+
+ listeners: {
+ 'tap': '_onDialogClick'
+ },
+
+ ready: function () {
+ // Only now these properties can be read.
+ this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;
+ this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;
+ this.__prevWithBackdrop = this.withBackdrop;
+ },
+
+ _modalChanged: function(modal, readied) {
+ // modal implies noCancelOnOutsideClick, noCancelOnEscKey and withBackdrop.
+ // We need to wait for the element to be ready before we can read the
+ // properties values.
+ if (!readied) {
+ return;
+ }
+
+ if (modal) {
+ this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;
+ this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;
+ this.__prevWithBackdrop = this.withBackdrop;
+ this.noCancelOnOutsideClick = true;
+ this.noCancelOnEscKey = true;
+ this.withBackdrop = true;
+ } else {
+ // If the value was changed to false, let it false.
+ this.noCancelOnOutsideClick = this.noCancelOnOutsideClick &&
+ this.__prevNoCancelOnOutsideClick;
+ this.noCancelOnEscKey = this.noCancelOnEscKey &&
+ this.__prevNoCancelOnEscKey;
+ this.withBackdrop = this.withBackdrop && this.__prevWithBackdrop;
+ }
+ },
+
+ _updateClosingReasonConfirmed: function(confirmed) {
+ this.closingReason = this.closingReason || {};
+ this.closingReason.confirmed = confirmed;
+ },
+
+ /**
+ * Will dismiss the dialog if user clicked on an element with dialog-dismiss
+ * or dialog-confirm attribute.
+ */
+ _onDialogClick: function(event) {
+ // Search for the element with dialog-confirm or dialog-dismiss,
+ // from the root target until this (excluded).
+ var path = Polymer.dom(event).path;
+ for (var i = 0; i < path.indexOf(this); i++) {
+ var target = path[i];
+ if (target.hasAttribute && (target.hasAttribute('dialog-dismiss') || target.hasAttribute('dialog-confirm'))) {
+ this._updateClosingReasonConfirmed(target.hasAttribute('dialog-confirm'));
+ this.close();
+ event.stopPropagation();
+ break;
+ }
+ }
+ }
+
+ };
+
+ /** @polymerBehavior */
+ Polymer.PaperDialogBehavior = [Polymer.IronOverlayBehavior, Polymer.PaperDialogBehaviorImpl];
+
+</script>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-common.css b/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-common.css
new file mode 100644
index 00000000..560b0a56
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-common.css
@@ -0,0 +1,57 @@
+/*
+@license
+Copyright (c) 2015 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
+*/
+
+:host {
+ display: block;
+ margin: 24px 40px;
+
+ background: var(--paper-dialog-background-color, --primary-background-color);
+ color: var(--paper-dialog-color, --primary-text-color);
+
+ @apply(--paper-font-body1);
+ @apply(--shadow-elevation-16dp);
+ @apply(--paper-dialog);
+}
+
+:host > ::content > * {
+ margin-top: 20px;
+ padding: 0 24px;
+}
+
+:host > ::content > .no-padding {
+ padding: 0;
+}
+
+:host > ::content > *:first-child {
+ margin-top: 24px;
+}
+
+:host > ::content > *:last-child {
+ margin-bottom: 24px;
+}
+
+:host > ::content h2 {
+ position: relative;
+ margin: 0;
+ @apply(--paper-font-title);
+
+ @apply(--paper-dialog-title);
+}
+
+:host > ::content .buttons {
+ position: relative;
+ padding: 8px 8px 8px 24px;
+ margin: 0;
+
+ color: var(--paper-dialog-button-color, --primary-color);
+
+ @apply(--layout-horizontal);
+ @apply(--layout-end-justified);
+}
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-shared-styles.html b/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-shared-styles.html
new file mode 100644
index 00000000..35ea74bd
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/paper-dialog-shared-styles.html
@@ -0,0 +1,83 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../paper-styles/typography.html">
+<link rel="import" href="../paper-styles/shadow.html">
+
+<!--
+### Styling
+
+The following custom properties and mixins are available for styling.
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-dialog-background-color` | Dialog background color | `--primary-background-color`
+`--paper-dialog-color` | Dialog foreground color | `--primary-text-color`
+`--paper-dialog` | Mixin applied to the dialog | `{}`
+`--paper-dialog-title` | Mixin applied to the title (`<h2>`) element | `{}`
+`--paper-dialog-button-color` | Button area foreground color | `--default-primary-color`
+-->
+
+<dom-module id="paper-dialog-shared-styles">
+ <template>
+ <style>
+ :host {
+ display: block;
+ margin: 24px 40px;
+
+ background: var(--paper-dialog-background-color, --primary-background-color);
+ color: var(--paper-dialog-color, --primary-text-color);
+
+ @apply(--paper-font-body1);
+ @apply(--shadow-elevation-16dp);
+ @apply(--paper-dialog);
+ }
+
+ :host > ::content > * {
+ margin-top: 20px;
+ padding: 0 24px;
+ }
+
+ :host > ::content > .no-padding {
+ padding: 0;
+ }
+
+ :host > ::content > *:first-child {
+ margin-top: 24px;
+ }
+
+ :host > ::content > *:last-child {
+ margin-bottom: 24px;
+ }
+
+ :host > ::content h2 {
+ position: relative;
+ margin: 0;
+ @apply(--paper-font-title);
+
+ @apply(--paper-dialog-title);
+ }
+
+ :host > ::content .buttons {
+ position: relative;
+ padding: 8px 8px 8px 24px;
+ margin: 0;
+
+ color: var(--paper-dialog-button-color, --primary-color);
+
+ @apply(--layout-horizontal);
+ @apply(--layout-end-justified);
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/test/index.html b/catapult/third_party/polymer/components/paper-dialog-behavior/test/index.html
new file mode 100644
index 00000000..cb0a8702
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/test/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>paper-dialog tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'paper-dialog-behavior.html',
+ 'paper-dialog-behavior.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/test/paper-dialog-behavior.html b/catapult/third_party/polymer/components/paper-dialog-behavior/test/paper-dialog-behavior.html
new file mode 100644
index 00000000..d7c260e7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/test/paper-dialog-behavior.html
@@ -0,0 +1,408 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+ <head>
+
+ <title>paper-dialog-behavior tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../paper-icon-button/paper-icon-button.html">
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+ <link rel="import" href="test-dialog.html">
+ <link rel="import" href="test-buttons.html">
+
+ </head>
+
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <test-dialog>
+ <p>Dialog</p>
+ <div class="buttons">
+ <button extra>extra</button>
+ <button dialog-dismiss>dismiss</button>
+ <button dialog-confirm>confirm</button>
+ </div>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="buttons">
+ <template>
+ <test-dialog>
+ <p>Dialog with test-buttons</p>
+ <test-buttons class="buttons"></test-buttons>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="custom-element-button">
+ <template>
+ <test-dialog>
+ <p>Dialog</p>
+ <div class="buttons">
+ <paper-icon-button icon="cancel" dialog-dismiss></paper-icon-button>
+ <paper-icon-button icon="add-circle" dialog-confirm></paper-icon-button>
+ <paper-icon-button icon="favorite"></paper-icon-button>
+ </div>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="modal">
+ <template>
+ <test-dialog modal>
+ <p>Dialog</p>
+ <div class="buttons">
+ <button dialog-dismiss>dismiss</button>
+ <button dialog-confirm autofocus>confirm</button>
+ </div>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="like-modal">
+ <template>
+ <test-dialog no-cancel-on-esc-key no-cancel-on-outside-click with-backdrop>
+ <p>Dialog</p>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="header">
+ <template>
+ <test-dialog>
+ <h2>Dialog</h2>
+ <div class="buttons">
+ <button dialog-dismiss>dismiss</button>
+ <button dialog-confirm autofocus>confirm</button>
+ </div>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="header-with-id">
+ <template>
+ <test-dialog>
+ <h2 id="header">Dialog</h2>
+ <div class="buttons">
+ <button dialog-dismiss>dismiss</button>
+ <button dialog-confirm autofocus>confirm</button>
+ </div>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="multiple">
+ <template>
+ <test-dialog modal id="dialog1">
+ <p>Dialog 1</p>
+ </test-dialog>
+ <test-dialog modal id="dialog2">
+ <p>Dialog 2</p>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="nestedmodals">
+ <template>
+ <test-dialog modal opened>
+ <p>Dialog 1</p>
+ <div class="buttons">
+ <button dialog-dismiss>dismiss</button>
+ <button dialog-confirm autofocus>confirm</button>
+ </div>
+
+ <test-dialog modal opened>
+ <p>Dialog 2</p>
+ <div class="buttons">
+ <button dialog-dismiss>dismiss</button>
+ <button dialog-confirm autofocus>confirm</button>
+ </div>
+ </test-dialog>
+ </test-dialog>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ function runAfterOpen(dialog, callback) {
+ dialog.addEventListener('iron-overlay-opened', callback);
+ dialog.open();
+ }
+
+ suite('basic', function() {
+
+ test('clicking dialog does not cancel the dialog', function(done) {
+ var dialog = fixture('basic');
+ runAfterOpen(dialog, function() {
+ dialog.addEventListener('iron-overlay-closed', function() {
+ assert('dialog should not close');
+ });
+ MockInteractions.tap(dialog);
+ setTimeout(function() {
+ done();
+ }, 100);
+ });
+ });
+
+ test('clicking dialog-dismiss button closes the dialog without confirmation', function(done) {
+ var dialog = fixture('basic');
+ runAfterOpen(dialog, function() {
+ dialog.addEventListener('iron-overlay-closed', function(event) {
+ assert.isFalse(event.detail.canceled, 'dialog is not canceled');
+ assert.isFalse(event.detail.confirmed, 'dialog is not confirmed');
+ done();
+ });
+ MockInteractions.tap(Polymer.dom(dialog).querySelector('[dialog-dismiss]'));
+ });
+ });
+
+ test('dialog-dismiss on a custom element is handled', function(done) {
+ var dialog = fixture('custom-element-button');
+ runAfterOpen(dialog, function() {
+ dialog.addEventListener('iron-overlay-closed', function(event) {
+ assert.isFalse(event.detail.canceled, 'dialog is not canceled');
+ assert.isFalse(event.detail.confirmed, 'dialog is not confirmed');
+ done();
+ });
+ MockInteractions.tap(Polymer.dom(dialog).querySelector('[dialog-dismiss]'));
+ });
+ });
+
+ test('dialog-dismiss button inside a custom element is handled', function(done) {
+ var dialog = fixture('buttons');
+ runAfterOpen(dialog, function() {
+ dialog.addEventListener('iron-overlay-closed', function(event) {
+ assert.isFalse(event.detail.canceled, 'dialog is not canceled');
+ assert.isFalse(event.detail.confirmed, 'dialog is not confirmed');
+ done();
+ });
+ MockInteractions.tap(Polymer.dom(dialog).querySelector('test-buttons').$.dismiss);
+ });
+ });
+
+ test('clicking dialog-confirm button closes the dialog with confirmation', function(done) {
+ var dialog = fixture('basic');
+ runAfterOpen(dialog, function() {
+ dialog.addEventListener('iron-overlay-closed', function(event) {
+ assert.isFalse(event.detail.canceled, 'dialog is not canceled');
+ assert.isTrue(event.detail.confirmed, 'dialog is confirmed');
+ done();
+ });
+ MockInteractions.tap(Polymer.dom(dialog).querySelector('[dialog-confirm]'));
+ });
+ });
+
+ test('dialog-confirm on a custom element handled', function(done) {
+ var dialog = fixture('custom-element-button');
+ runAfterOpen(dialog, function() {
+ dialog.addEventListener('iron-overlay-closed', function(event) {
+ assert.isFalse(event.detail.canceled, 'dialog is not canceled');
+ assert.isTrue(event.detail.confirmed, 'dialog is confirmed');
+ done();
+ });
+ MockInteractions.tap(Polymer.dom(dialog).querySelector('[dialog-confirm]'));
+ });
+ });
+
+ test('dialog-confirm button inside a custom element is handled', function(done) {
+ var dialog = fixture('buttons');
+ runAfterOpen(dialog, function() {
+ dialog.addEventListener('iron-overlay-closed', function(event) {
+ assert.isFalse(event.detail.canceled, 'dialog is not canceled');
+ assert.isTrue(event.detail.confirmed, 'dialog is confirmed');
+ done();
+ });
+ MockInteractions.tap(Polymer.dom(dialog).querySelector('test-buttons').$.confirm);
+ });
+ });
+
+ test('clicking dialog-dismiss button closes only the dialog where is contained', function(done) {
+ var dialog = fixture('nestedmodals');
+ var innerDialog = Polymer.dom(dialog).querySelector('test-dialog');
+ MockInteractions.tap(Polymer.dom(innerDialog).querySelector('[dialog-dismiss]'));
+ setTimeout(function() {
+ assert.isFalse(innerDialog.opened, 'inner dialog is closed');
+ assert.isTrue(dialog.opened, 'dialog is still open');
+ done();
+ }, 10);
+ });
+
+ test('clicking dialog-confirm button closes only the dialog where is contained', function(done) {
+ var dialog = fixture('nestedmodals');
+ var innerDialog = Polymer.dom(dialog).querySelector('test-dialog');
+ MockInteractions.tap(Polymer.dom(innerDialog).querySelector('[dialog-confirm]'));
+ setTimeout(function() {
+ assert.isFalse(innerDialog.opened, 'inner dialog is closed');
+ assert.isTrue(dialog.opened, 'dialog is still open');
+ done();
+ }, 10);
+ });
+
+ var properties = ['noCancelOnEscKey', 'noCancelOnOutsideClick', 'withBackdrop'];
+ properties.forEach(function(property) {
+
+ test('modal sets ' + property + ' to true', function() {
+ var dialog = fixture('modal');
+ assert.isTrue(dialog[property], property);
+ });
+
+ test('modal toggling keeps current value of ' + property, function() {
+ var dialog = fixture('modal');
+ // Changed to false while modal is true.
+ dialog[property] = false;
+ dialog.modal = false;
+ assert.isFalse(dialog[property], property + ' is false');
+ });
+
+ test('modal toggling keeps previous value of ' + property, function() {
+ var dialog = fixture('basic');
+ // Changed before modal is true.
+ dialog[property] = true;
+ // Toggle twice to trigger observer.
+ dialog.modal = true;
+ dialog.modal = false;
+ assert.isTrue(dialog[property], property + ' is still true');
+ });
+
+ test('default modal does not override ' + property +' (attribute)', function() {
+ // Property is set on ready from attribute.
+ var dialog = fixture('like-modal');
+ assert.isTrue(dialog[property], property + ' is true');
+ });
+
+ test('modal toggling keeps previous value of ' + property + ' (attribute)', function() {
+ // Property is set on ready from attribute.
+ var dialog = fixture('like-modal');
+ // Toggle twice to trigger observer.
+ dialog.modal = true;
+ dialog.modal = false;
+ assert.isTrue(dialog[property], property + ' is still true');
+ });
+
+ });
+
+ test('clicking outside a modal dialog does not move focus from dialog', function(done) {
+ var dialog = fixture('modal');
+ runAfterOpen(dialog, function() {
+ MockInteractions.tap(document.body);
+ setTimeout(function() {
+ assert.equal(document.activeElement, Polymer.dom(dialog).querySelector('[autofocus]'), 'document.activeElement is the autofocused button');
+ done();
+ }, 10);
+ });
+ });
+
+ test('removing a child element on click does not cause an exception', function(done) {
+ var dialog = fixture('basic');
+ runAfterOpen(dialog, function() {
+ var button = Polymer.dom(dialog).querySelector('[extra]');
+ button.addEventListener('click', function(event) {
+ Polymer.dom(event.target.parentNode).removeChild(event.target);
+ // should not throw exception here
+ done();
+ });
+ MockInteractions.tap(button);
+ });
+ });
+
+ test('multiple modal dialogs opened, handle focus change', function(done) {
+ var dialogs = fixture('multiple');
+
+ runAfterOpen(dialogs[0], function() {
+ runAfterOpen(dialogs[1], function() {
+ // Each modal dialog will trap the focus within its children.
+ // Multiple modal dialogs doing it might result in an infinite loop
+ // dialog1 focus -> dialog2 focus -> dialog1 focus -> dialog2 focus...
+ // causing a "Maximum call stack size exceeded" error.
+ // Wait 50ms and verify this does not happen.
+ Polymer.Base.async(function() {
+ // Should not enter in an infinite loop.
+ done();
+ }, 50);
+ });
+ });
+ });
+
+ test('multiple modal dialogs opened, handle outside click', function(done) {
+ var dialogs = fixture('multiple');
+
+ runAfterOpen(dialogs[0], function() {
+ runAfterOpen(dialogs[1], function() {
+ // Click should be handled only by dialogs[1].
+ MockInteractions.tap(document.body);
+ // Each modal dialog will trap the focus within its children.
+ // Multiple modal dialogs doing it might result in an infinite loop
+ // dialog1 focus -> dialog2 focus -> dialog1 focus -> dialog2 focus...
+ // causing a "Maximum call stack size exceeded" error.
+ // Wait 50ms and verify this does not happen.
+ Polymer.Base.async(function() {
+ // Should not enter in an infinite loop.
+ done();
+ }, 50);
+ });
+ });
+ });
+
+ test('focus is given to the autofocus element when clicking on backdrop', function(done) {
+ var dialog = fixture('modal');
+ dialog.addEventListener('iron-overlay-opened', onFirstOpen);
+ dialog.open();
+
+ function onFirstOpen() {
+ dialog.removeEventListener('iron-overlay-opened', onFirstOpen);
+ dialog.addEventListener('iron-overlay-closed', onFirstClose);
+ // Set the focus on dismiss button
+ MockInteractions.focus(Polymer.dom(dialog).querySelector('[dialog-dismiss]'));
+ // Close the dialog
+ dialog.close();
+ }
+
+ function onFirstClose() {
+ dialog.removeEventListener('iron-overlay-closed', onFirstClose);
+ dialog.addEventListener('iron-overlay-opened', onSecondOpen);
+ dialog.open();
+ }
+
+ function onSecondOpen() {
+ MockInteractions.tap(document.body);
+ setTimeout(function() {
+ assert.equal(document.activeElement, Polymer.dom(dialog).querySelector('[autofocus]'), 'document.activeElement is the autofocused button');
+ done();
+ }, 10);
+ }
+ });
+
+ });
+
+ suite('a11y', function() {
+
+ test('dialog has role="dialog"', function() {
+ var dialog = fixture('basic');
+ assert.equal(dialog.getAttribute('role'), 'dialog', 'has role="dialog"');
+ });
+
+ });
+ </script>
+
+ </body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/test/test-buttons.html b/catapult/third_party/polymer/components/paper-dialog-behavior/test/test-buttons.html
new file mode 100644
index 00000000..9f0d09c4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/test/test-buttons.html
@@ -0,0 +1,30 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+
+<dom-module id="test-buttons">
+ <template>
+ <button dialog-dismiss id="dismiss">dismiss</button>
+ <button dialog-confirm id="confirm">confirm</button>
+ </template>
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+ is: 'test-buttons'
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/paper-dialog-behavior/test/test-dialog.html b/catapult/third_party/polymer/components/paper-dialog-behavior/test/test-dialog.html
new file mode 100644
index 00000000..ccaf4e81
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog-behavior/test/test-dialog.html
@@ -0,0 +1,39 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../paper-dialog-behavior.html">
+<link rel="import" href="../paper-dialog-shared-styles.html">
+
+<dom-module id="test-dialog">
+ <template>
+ <style include="paper-dialog-shared-styles"></style>
+ <content></content>
+ </template>
+
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'test-dialog',
+
+ behaviors: [
+ Polymer.PaperDialogBehavior
+ ]
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/paper-dialog/.bower.json b/catapult/third_party/polymer/components/paper-dialog/.bower.json
new file mode 100644
index 00000000..3749eae5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/.bower.json
@@ -0,0 +1,49 @@
+{
+ "name": "paper-dialog",
+ "description": "A Material Design dialog",
+ "version": "1.1.0",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "dialog",
+ "overlay"
+ ],
+ "main": "paper-dialog.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-dialog"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-dialog",
+ "ignore": [],
+ "dependencies": {
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0",
+ "paper-dialog-behavior": "PolymerElements/paper-dialog-behavior#^1.0.0",
+ "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^1.7.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.1.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.0",
+ "commit": "7f31fa918fbd562b516eb262f1ed527dcaf7b7c0"
+ },
+ "_source": "https://github.com/PolymerElements/paper-dialog.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-dialog"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-dialog/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-dialog/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..823a0209
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-dialog/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-dialog/.gitignore b/catapult/third_party/polymer/components/paper-dialog/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-dialog/.travis.yml b/catapult/third_party/polymer/components/paper-dialog/.travis.yml
new file mode 100644
index 00000000..7a3743aa
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/.travis.yml
@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+before_script:
+- npm install -g bower polylint web-component-tester
+- bower install
+- polylint
+env:
+ global:
+ - secure: QgTx1GfP8J91Uzv03iETT60WI0ZhgvbT5tFkDp53oeQrNvXwZMCDN9BE+LsvE+3WJdhwV//gD3B72y+nSTa89UGSHwXaLiXPxL1OW7B/36X0ri1yPVVW9TJNramsVmjkFbjqbD6txUPE/j2STYIxGueng3R/bzt/Vx/ZjHnTJSVkyfQ0HCmMvgVvKBzP6wHHoggXcXeRhViQUbHW2v32Ptj2lw/qukQxv0rggugz/m+4miLFMEtAFlSJ+9Q4rA8pXMgrDJbd7T7BPNs9yeP0rVNab+dRE0wMkv0iRk0cL1ZPH/FB5F6everMc/7zc3mCEVXJM0fbj1XQVM640C413ncpDz/zK24e3lew4Erf7Ld9dl/xWRr60zBvGfFHz5JSBzL4/OsvnRFHfpxd6nwyVJhjv9uqyGNNqaDZ1AaPZ/ZCVDq+7KyJhM/Pug18cEzcDhUc0u5SIdjLnaiqgqAx0axAUdGPAN9Ssb58kDaqgt38VfoCc7ki/Qsk84ylxVu10vAeK4piklUb/PtR0gTKQ4GcYWnwZBgTlSthRFheOEp5BSDsKXi8Y2UCb/FJr1+oMbjbHLKnKvq7UDaLXXXKfvF1RjF1npgtYw4HBbHQ8Rsr7ZT2gU/85UDfpTgFNlpRWwajpEGQ7eK/CKQZnE8eGy+DR0KNuXxBkZpW5xOyq4g=
+ - secure: GzJ6KvrESPTW2J2fqjTq4tXzaSLdjVuWHp4LV7isIBQk5qeh0l+AN/ZRzYj63AdhKBeQG68lLMayV/GFtJsKkLQWn1xZzxVjwdpWQjwnKWtoGYkwdHzuY6FNfLL9FTIGu5G6RCmfFfTDsUHkhSXW+cBaN7nJWtojXAOrr+9ivfU6acfoON8RN8m5N2o5uVJgwwlveYnCYayRHC50neHOVC1IzQlagW9TdpKMqHeg0XpSFhdjQua3ETiAiQDjpQz1BeNZh+/DjnlV08zAx64bjZTh441100HRP0qg4/Y6IcX/qCiRdQHYBvIAoka+ZySPu1zYfvn+7eJYwQ4shSuElKX9y6g+uk3g5djJJ7yoKIQnsYlO1yPB0E8fvIhP2K/ZCwXK86snR+pIfrpVudQy4RZObuvyJIqxmaUjlTg1zU/S0ZNqOnXtqO6pG6YNH1JpAt0Bdzy7hT/q44ROlBPYuZK8shzzLa4KBr3Rb7kknyi5YXempGjhLnUFFd+gGQUkfqoDmyLreDyO95Ch8+YOcTjAzWh/yrEw1of0l/an9TxtWjnFaioJDcVS/dQphufMHgxyushDaocm/My06ZNWfwe69IbH7lqdDn9aW8hdn9cgIKw/sNqVtksN0/tnoURD/8wuHmepzsCR7xs9NrAozaynK+NdPfDdUtLRjtcEy0A=
+node_js: stable
+addons:
+ firefox: '46.0'
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+- xvfb-run wct
+- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'default'; fi
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-dialog/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-dialog/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-dialog/README.md b/catapult/third_party/polymer/components/paper-dialog/README.md
new file mode 100644
index 00000000..a958f067
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/README.md
@@ -0,0 +1,75 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-dialog.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-dialog.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-dialog)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-dialog)_
+
+
+##&lt;paper-dialog&gt;
+
+Material design: [Dialogs](https://www.google.com/design/spec/components/dialogs.html)
+
+`<paper-dialog>` is a dialog with Material Design styling and optional animations when it is
+opened or closed. It provides styles for a header, content area, and an action area for buttons.
+You can use the `<paper-dialog-scrollable>` element (in its own repository) if you need a scrolling
+content area. To autofocus a specific child element after opening the dialog, give it the `autofocus`
+attribute. See `Polymer.PaperDialogBehavior` and `Polymer.IronOverlayBehavior` for specifics.
+
+For example, the following code implements a dialog with a header, scrolling content area and
+buttons. Focus will be given to the `dialog-confirm` button when the dialog is opened.
+
+```html
+<paper-dialog>
+ <h2>Header</h2>
+ <paper-dialog-scrollable>
+ Lorem ipsum...
+ </paper-dialog-scrollable>
+ <div class="buttons">
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm autofocus>Accept</paper-button>
+ </div>
+</paper-dialog>
+```
+
+### Styling
+
+See the docs for `Polymer.PaperDialogBehavior` for the custom properties available for styling
+this element.
+
+### Animations
+
+Set the `entry-animation` and/or `exit-animation` attributes to add an animation when the dialog
+is opened or closed. See the documentation in
+[PolymerElements/neon-animation](https://github.com/PolymerElements/neon-animation) for more info.
+
+For example:
+
+```html
+<link rel="import" href="components/neon-animation/animations/scale-up-animation.html">
+<link rel="import" href="components/neon-animation/animations/fade-out-animation.html">
+
+<paper-dialog entry-animation="scale-up-animation"
+ exit-animation="fade-out-animation">
+ <h2>Header</h2>
+ <div>Dialog body</div>
+</paper-dialog>
+```
+
+### Accessibility
+
+See the docs for `Polymer.PaperDialogBehavior` for accessibility features implemented by this
+element.
+
+
diff --git a/catapult/third_party/polymer/components/paper-dialog/bower.json b/catapult/third_party/polymer/components/paper-dialog/bower.json
new file mode 100644
index 00000000..e7882a55
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "paper-dialog",
+ "description": "A Material Design dialog",
+ "version": "1.1.0",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "dialog",
+ "overlay"
+ ],
+ "main": "paper-dialog.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-dialog"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-dialog",
+ "ignore": [],
+ "dependencies": {
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0",
+ "paper-dialog-behavior": "PolymerElements/paper-dialog-behavior#^1.0.0",
+ "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^1.7.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#^1.0.0",
+ "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-dialog/demo/index.html b/catapult/third_party/polymer/components/paper-dialog/demo/index.html
new file mode 100644
index 00000000..fc1a1a6d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/demo/index.html
@@ -0,0 +1,226 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-dialog demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../paper-dialog.html">
+ <link rel="import" href="../../paper-button/paper-button.html">
+ <link rel="import" href="../../paper-dialog-scrollable/paper-dialog-scrollable.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../../neon-animation/neon-animations.html">
+ <link rel="import" href="../../paper-dropdown-menu/paper-dropdown-menu.html">
+ <link rel="import" href="../../paper-menu/paper-menu.html">
+ <link rel="import" href="../../paper-item/paper-item.html">
+
+
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ .centered {
+ min-width: 500px;
+ }
+
+ demo-snippet {
+ --demo-snippet-code: {
+ max-height: 250px;
+ }
+ }
+ </style>
+
+</head>
+
+<body unresolved class="centered">
+ <h3>Dialog layouts</h3>
+ <demo-snippet>
+ <template>
+ <paper-button raised onclick="dialog.open()">plain dialog</paper-button>
+ <paper-button raised onclick="modal.open()">modal dialog</paper-button>
+ <paper-button raised onclick="scrolling.open()">scrolling dialog</paper-button>
+ <paper-button raised onclick="actions.open()">dialog with actions</paper-button>
+ <paper-button raised onclick="dropdownDialog.open()">dialog with dropdown</paper-button>
+ <paper-button raised onclick="nested.open()">dialog with nested dialog</paper-button>
+
+ <paper-dialog id="dialog">
+ <h2>Dialog Title</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </paper-dialog>
+
+ <paper-dialog id="scrolling">
+ <h2>Scrolling</h2>
+ <paper-dialog-scrollable>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </paper-dialog-scrollable>
+ <div class="buttons">
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm autofocus>OK</paper-button>
+ </div>
+ </paper-dialog>
+
+ <paper-dialog id="actions">
+ <h2>Dialog Title</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <div class="buttons">
+ <paper-button>More Info...</paper-button>
+ <paper-button dialog-dismiss>Decline</paper-button>
+ <paper-button dialog-confirm autofocus>Accept</paper-button>
+ </div>
+ </paper-dialog>
+
+ <paper-dialog id="modal" modal>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
+ <div class="buttons">
+ <paper-button dialog-confirm autofocus>Tap me to close</paper-button>
+ </div>
+ </paper-dialog>
+
+ <paper-dialog id="dropdownDialog">
+ <h2>Dialog Title</h2>
+ <paper-dropdown-menu label="Value">
+ <paper-menu class="dropdown-content">
+ <paper-item>1</paper-item>
+ <paper-item>2</paper-item>
+ <paper-item>3</paper-item>
+ <paper-item>4</paper-item>
+ <paper-item>5</paper-item>
+ <paper-item>6</paper-item>
+ <paper-item>7</paper-item>
+ <paper-item>8</paper-item>
+ <paper-item>9</paper-item>
+ <paper-item>10</paper-item>
+ </paper-menu>
+ </paper-dropdown-menu>
+ </paper-dialog>
+
+ <paper-dialog id="nested">
+ <h2>Dialog Title</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ <div class="buttons">
+ <paper-button onclick="innerDialog.open()">Open nested dialog</paper-button>
+ </div>
+ </paper-dialog>
+
+ <paper-dialog id="innerDialog">
+ <h2>Dialog Title</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
+ </paper-dialog>
+ </template>
+ </demo-snippet>
+
+ <h3>Styling and positioning</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ paper-dialog.colored {
+ border: 2px solid;
+ border-color: var(--paper-green-500);
+ background-color: var(--paper-light-green-50);
+ color: var(--paper-green-500);
+ }
+
+ paper-dialog.size-position {
+ position: fixed;
+ top: 16px;
+ right: 16px;
+ width: 300px;
+ height: 300px;
+ overflow: auto;
+ }
+ </style>
+ <paper-button raised onclick="colors.open()">colors</paper-button>
+ <paper-button raised onclick="position.open()">size &amp; position</paper-button>
+
+ <paper-dialog id="colors" class="colored">
+ <h2>Custom Colors</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
+ dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </paper-dialog>
+
+ <paper-dialog id="position" class="size-position">
+ <h2>Custom Size &amp; Position</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
+ dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </paper-dialog>
+ </template>
+ </demo-snippet>
+
+ <h3>Position with <code>positionTarget</code></h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ #alignedDialog {
+ margin: 0;
+ }
+ </style>
+ <paper-button raised onclick="openBy(this)">Open</paper-button>
+ <paper-button raised onclick="openBy(this)">Open</paper-button>
+ <paper-button raised onclick="openBy(this)">Open</paper-button>
+
+ <paper-dialog id="alignedDialog" no-overlap horizontal-align="left" vertical-align="top">
+ <h2>Aligned dialog</h2>
+ <paper-dialog-scrollable>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
+ dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </paper-dialog-scrollable>
+ </paper-dialog>
+
+ <script>
+ function openBy(element) {
+ alignedDialog.positionTarget = element;
+ alignedDialog.open();
+ }
+ </script>
+ </template>
+ </demo-snippet>
+
+ <h3>Transitions with neon-animation</h3>
+ <demo-snippet>
+ <template>
+ <paper-button raised onclick="animated.open()">open</paper-button>
+ <paper-dialog id="animated" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop>
+ <h2>Dialog Title</h2>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+ </paper-dialog>
+ </template>
+ </demo-snippet>
+
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dialog/hero.svg b/catapult/third_party/polymer/components/paper-dialog/hero.svg
new file mode 100755
index 00000000..36283185
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/hero.svg
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <polygon points="0,124 0,126 2,126 "/>
+ <polygon points="0,111.6 0,114.4 11.6,126 14.4,126 "/>
+ <polygon points="0,99.1 0,101.9 24.1,126 26.9,126 "/>
+ <polygon points="0,86.6 0,89.5 36.5,126 39.4,126 "/>
+ <polygon points="0,74.2 0,77 49,126 51.8,126 "/>
+ <polygon points="0,61.7 0,64.5 61.5,126 64.3,126 "/>
+ <polygon points="0,49.2 0,52.1 73.9,126 76.8,126 "/>
+ <polygon points="0,36.8 0,39.6 86.4,126 89.2,126 "/>
+ <polygon points="0,24.3 0,27.1 98.9,126 101.7,126 "/>
+ <polygon points="75.2,87 74,87 74,85.8 0,11.8 0,14.7 111.3,126 114.2,126 "/>
+ <polygon points="87.6,87 84.8,87 123.8,126 126.6,126 "/>
+ <polygon points="74,76.2 74,73.4 0.6,0 0,0 0,2.2 "/>
+ <polygon points="74,63.7 74,60.9 13.1,0 10.3,0 "/>
+ <polygon points="100.1,87 97.3,87 136.3,126 139.1,126 "/>
+ <polygon points="112.6,87 109.7,87 148.7,126 151.6,126 "/>
+ <polygon points="74,51.3 74,48.4 25.6,0 22.7,0 "/>
+ <polygon points="125,87 122.2,87 161.2,126 164,126 "/>
+ <polygon points="74.2,39 77,39 38,0 35.2,0 "/>
+ <polygon points="86.7,39 89.5,39 50.5,0 47.7,0 "/>
+ <polygon points="137.5,87 134.7,87 173.7,126 176.5,126 "/>
+ <polygon points="150,87 147.1,87 186.1,126 189,126 "/>
+ <polygon points="99.1,39 102,39 63,0 60.1,0 "/>
+ <polygon points="150,74.6 150,77.4 198.6,126 201.4,126 "/>
+ <polygon points="111.6,39 114.4,39 75.4,0 72.6,0 "/>
+ <polygon points="150,62.1 150,64.9 211.1,126 213.9,126 "/>
+ <polygon points="124.1,39 126.9,39 87.9,0 85.1,0 "/>
+ <polygon points="100.3,0 97.5,0 136.5,39 139.3,39 "/>
+ <polygon points="150,49.7 150,52.5 223.5,126 225,126 225,124.7 "/>
+ <polygon points="112.8,0 110,0 149,39 150,39 150,40 225,115 225,112.2 "/>
+ <polygon points="125.3,0 122.5,0 225,102.5 225,99.7 "/>
+ <polygon points="137.7,0 134.9,0 225,90.1 225,87.3 "/>
+ <polygon points="150.2,0 147.4,0 225,77.6 225,74.8 "/>
+ <polygon points="162.7,0 159.8,0 225,65.2 225,62.3 "/>
+ <polygon points="175.1,0 172.3,0 225,52.7 225,49.9 "/>
+ <polygon points="187.6,0 184.8,0 225,40.2 225,37.4 "/>
+ <polygon points="200.1,0 197.2,0 225,27.8 225,24.9 "/>
+ <polygon points="212.5,0 209.7,0 225,15.3 225,12.5 "/>
+ <polygon points="225,0 222.2,0 225,2.8 225,0 "/>
+ </g>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+ <path d="M151,88H73V38h78V88z M75,86h74V40H75V86z"/>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-dialog/index.html b/catapult/third_party/polymer/components/paper-dialog/index.html
new file mode 100644
index 00000000..6304b8dc
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-dialog</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dialog/paper-dialog.html b/catapult/third_party/polymer/components/paper-dialog/paper-dialog.html
new file mode 100644
index 00000000..512ca048
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/paper-dialog.html
@@ -0,0 +1,117 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../neon-animation/neon-animation-runner-behavior.html">
+<link rel="import" href="../paper-dialog-behavior/paper-dialog-behavior.html">
+<link rel="import" href="../paper-dialog-behavior/paper-dialog-shared-styles.html">
+<!--
+Material design: [Dialogs](https://www.google.com/design/spec/components/dialogs.html)
+
+`<paper-dialog>` is a dialog with Material Design styling and optional animations when it is
+opened or closed. It provides styles for a header, content area, and an action area for buttons.
+You can use the `<paper-dialog-scrollable>` element (in its own repository) if you need a scrolling
+content area. To autofocus a specific child element after opening the dialog, give it the `autofocus`
+attribute. See `Polymer.PaperDialogBehavior` and `Polymer.IronOverlayBehavior` for specifics.
+
+For example, the following code implements a dialog with a header, scrolling content area and
+buttons. Focus will be given to the `dialog-confirm` button when the dialog is opened.
+
+ <paper-dialog>
+ <h2>Header</h2>
+ <paper-dialog-scrollable>
+ Lorem ipsum...
+ </paper-dialog-scrollable>
+ <div class="buttons">
+ <paper-button dialog-dismiss>Cancel</paper-button>
+ <paper-button dialog-confirm autofocus>Accept</paper-button>
+ </div>
+ </paper-dialog>
+
+### Styling
+
+See the docs for `Polymer.PaperDialogBehavior` for the custom properties available for styling
+this element.
+
+### Animations
+
+Set the `entry-animation` and/or `exit-animation` attributes to add an animation when the dialog
+is opened or closed. See the documentation in
+[PolymerElements/neon-animation](https://github.com/PolymerElements/neon-animation) for more info.
+
+For example:
+
+ <link rel="import" href="components/neon-animation/animations/scale-up-animation.html">
+ <link rel="import" href="components/neon-animation/animations/fade-out-animation.html">
+
+ <paper-dialog entry-animation="scale-up-animation"
+ exit-animation="fade-out-animation">
+ <h2>Header</h2>
+ <div>Dialog body</div>
+ </paper-dialog>
+
+### Accessibility
+
+See the docs for `Polymer.PaperDialogBehavior` for accessibility features implemented by this
+element.
+
+@group Paper Elements
+@element paper-dialog
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-dialog">
+ <template>
+ <style include="paper-dialog-shared-styles"></style>
+ <content></content>
+ </template>
+</dom-module>
+
+<script>
+
+(function() {
+
+ Polymer({
+
+ is: 'paper-dialog',
+
+ behaviors: [
+ Polymer.PaperDialogBehavior,
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ listeners: {
+ 'neon-animation-finish': '_onNeonAnimationFinish'
+ },
+
+ _renderOpened: function() {
+ this.cancelAnimation();
+ this.playAnimation('entry');
+ },
+
+ _renderClosed: function() {
+ this.cancelAnimation();
+ this.playAnimation('exit');
+ },
+
+ _onNeonAnimationFinish: function() {
+ if (this.opened) {
+ this._finishRenderOpened();
+ } else {
+ this._finishRenderClosed();
+ }
+ }
+
+ });
+
+})();
+
+</script>
diff --git a/catapult/third_party/polymer/components/paper-dialog/test/index.html b/catapult/third_party/polymer/components/paper-dialog/test/index.html
new file mode 100644
index 00000000..3d0c8eec
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/test/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>paper-dialog tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'paper-dialog.html',
+ 'paper-dialog.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-dialog/test/paper-dialog.html b/catapult/third_party/polymer/components/paper-dialog/test/paper-dialog.html
new file mode 100644
index 00000000..02ed5090
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dialog/test/paper-dialog.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+
+ <title>paper-dialog tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../paper-dialog.html">
+</head>
+
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-dialog>
+ <p>Dialog</p>
+ </paper-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="modal">
+ <template>
+ <paper-dialog modal>
+ <p>Dialog</p>
+ </paper-dialog>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="opened-modals">
+ <template>
+ <paper-dialog modal opened>
+ <p>Dialog 1</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
+ </paper-dialog>
+ <paper-dialog modal opened>
+ <p>Dialog 2</p>
+ </paper-dialog>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('modal', function() {
+
+ test('backdrop element remains opened when closing top modal, closes when all modals are closed', function(done) {
+ var modals = fixture('opened-modals');
+ modals[1].addEventListener('iron-overlay-opened', function() {
+ assert.isTrue(modals[1].backdropElement.opened, 'backdrop is open');
+ modals[1].close();
+ });
+ modals[1].addEventListener('iron-overlay-closed', function() {
+ assert.isTrue(modals[1].backdropElement.opened, 'backdrop is still open');
+ modals[0].close();
+ });
+ modals[0].addEventListener('iron-overlay-closed', function() {
+ assert.isFalse(modals[0].backdropElement.opened, 'backdrop is closed');
+ done();
+ });
+ });
+
+ });
+
+ suite('a11y', function() {
+ a11ySuite('basic', []);
+
+ a11ySuite('modal', []);
+
+ test('dialog has role="dialog"', function() {
+ var dialog = fixture('basic');
+ assert.equal(dialog.getAttribute('role'), 'dialog', 'has role="dialog"');
+ });
+
+ });
+ </script>
+
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/.bower.json b/catapult/third_party/polymer/components/paper-dropdown-menu/.bower.json
new file mode 100644
index 00000000..4fe866fb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/.bower.json
@@ -0,0 +1,57 @@
+{
+ "name": "paper-dropdown-menu",
+ "version": "1.5.1",
+ "description": "An element that works similarly to a native browser select",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "dropdown",
+ "select"
+ ],
+ "main": "paper-dropdown-menu.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-dropdown-menu.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-dropdown-menu",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-input": "PolymerElements/paper-input#^1.0.9",
+ "paper-menu-button": "PolymerElements/paper-menu-button#^1.3.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-listbox": "PolymerElements/paper-listbox#^1.0.0",
+ "paper-tabs": "PolymerElements/paper-tabs#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "_release": "1.5.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.1",
+ "commit": "38a5dcecffe911c716b0b829299a5348289fd289"
+ },
+ "_source": "https://github.com/PolymerElements/paper-dropdown-menu.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-dropdown-menu"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-dropdown-menu/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..4563afc1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-dropdown-menu/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/.gitignore b/catapult/third_party/polymer/components/paper-dropdown-menu/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/.travis.yml b/catapult/third_party/polymer/components/paper-dropdown-menu/.travis.yml
new file mode 100644
index 00000000..6775ca62
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ wtzQz1xvw2c7t6EIf+64Nf5uIv6Buc8j78bFIcStT97Tq1MHvhTrY8AKvF/vQyD+CR8t0rVf+Og5Np1yxVM8SmTMG+2uaafNoU7Vh6x2owcYeR1ftryG7wp2+PlPyE4yrVW/gULb7jgjilTnUaa3k52zOTePr1VmU23E8XKs37csTmoke917E092MN4yQYJLDyBQNKmfTwiWQb11YyhR+XbwEkgnvUMPO2ArVubzcmIIOrZ5y82tmodxgsCBWQ05KJUDR7NbskUTrkXdzWTMe0yypwVm9rc3dX+z85M9zHCQqPf/XB65qYrLg4+uJeyRr3IZKUYrxH+9UL0PyyEYj0XL+oWBj9xdZu6mxX2YuzVBJnt/qKzUr3hCVJDMmgUkfALeKZ8xtaGQHTtzLLkKw8UZKY+npiEvWHaLjzg4qRaR+vs2+XkC7ELD5+A4kxSwUvIhS8ql8mnpOdV4bkw+REtl1xa+5+pzwpItiNOSrzHT1DCtYt8Ci2GaM6VytMPbLcFkxVMlvKzI/mtwjgLFPfCcbQ8UFDRijFsjjnmLcAI0R2VBBUKihUTDeetxC4EuLxSm5GuL7QXsLmXzI/VILY/MwGbMEXOMQqqlPzCPKBy6J4JzOBUEBEgNkLXXiLNwGhmiSm7o2j3JWNnLrocUrENkNjnTjwkUfLrPp4H0VpA=
+ - secure: >-
+ BJgAfJkK2TPh6WsvYVhs61wCcvamTABjbFfF2x/mBc4Kt/5LUSI8BCwPpsW7pKx0Qoc+qA2Tew4OAO5voWoMdX5ghj7xpFpYojiXolNWreLZ0PZZv7MTCnFu5d+Enh+1YH0Do1Y1Zfg6W4a60oI/VxCLtrBbVUZ7ilMlbzAGNl4OGDCLOpFkJyEWIfmUkiPgAv3/6ZdJKVfttiaSoBXzZGOcyG0KBIHT7Z/C6IOodjhCLqOhCGejjyHBeId9VWUwFfMsg3FkKMJpfY5g/7Yc2+1QWkL4VAeaOc0N6IcJLRChXF6QxMqPq7vQG7WlweVAuC1ojEM0rAtxnLGpKLuDTSMQvMiPgtQ5j5wLIt2X+QymlE9UFlDgW5lKT5MZxhMbkB1PQDgfuFzThM79uqt02N3XTiYBkn3XspLRbg9ZE/TR2YezipxyzEwLxTRfurWjV3S5PeGqNVAACrtc+npMKuBIMx2ULu+P8LB57J+mGbwpTvSa4TyFhJtKkRaSdJ2+q7oY+4dPAENc3WXof6+max60MFe9dg3P1DMeE/S4vjDzqdeHaF1SFmIZxh39OJ4MsKbvE3AeemTl25exJWJyOfGEfYMNz2WoDvGfkbU2kR2EJE2sUwTPmMwC0txGarXZ8tWlh1wcQHcdB3mgaPRIdnEx91PHHGmcihWdcjsxCCY=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-dropdown-menu/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/README.md b/catapult/third_party/polymer/components/paper-dropdown-menu/README.md
new file mode 100644
index 00000000..ff150808
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/README.md
@@ -0,0 +1,44 @@
+[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://beta.webcomponents.org/element/PolymerElements/paper-dropdown-menu)
+[![Build status](https://travis-ci.org/PolymerElements/paper-dropdown-menu.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-dropdown-menu)
+
+##&lt;paper-dropdown-menu&gt;
+
+Material design: [Dropdown menus](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)
+
+`paper-dropdown-menu` is similar to a native browser select element.
+`paper-dropdown-menu` works with selectable content.
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-dropdown-menu.html">
+ <link rel="import" href="../paper-item/paper-item.html">
+ <link rel="import" href="../paper-listbox/paper-listbox.html">
+ <link rel="import" href="../iron-demo-helpers/demo-pages-shared-styles.html">
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-dropdown-menu, paper-listbox {
+ width: 250px;
+ }
+ paper-dropdown-menu {
+ height: 200px;
+ margin: auto;
+ display: block;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-dropdown-menu label="Dinosaurs">
+ <paper-listbox class="dropdown-content" selected="1">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+</paper-dropdown-menu>
+```
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/bower.json b/catapult/third_party/polymer/components/paper-dropdown-menu/bower.json
new file mode 100644
index 00000000..aac4ec74
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/bower.json
@@ -0,0 +1,48 @@
+{
+ "name": "paper-dropdown-menu",
+ "version": "1.5.1",
+ "description": "An element that works similarly to a native browser select",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "dropdown",
+ "select"
+ ],
+ "main": "paper-dropdown-menu.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-dropdown-menu.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-dropdown-menu",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
+ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-input": "PolymerElements/paper-input#^1.0.9",
+ "paper-menu-button": "PolymerElements/paper-menu-button#^1.3.0",
+ "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-listbox": "PolymerElements/paper-listbox#^1.0.0",
+ "paper-tabs": "PolymerElements/paper-tabs#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/demo/index.html b/catapult/third_party/polymer/components/paper-dropdown-menu/demo/index.html
new file mode 100644
index 00000000..4bb258e6
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/demo/index.html
@@ -0,0 +1,295 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-dropdown-menu demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../paper-listbox/paper-listbox.html">
+ <link rel="import" href="../../paper-tabs/paper-tabs.html">
+ <link rel="import" href="../paper-dropdown-menu.html">
+ <link rel="import" href="../paper-dropdown-menu-light.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-tabs {
+ width: 400px;
+ }
+
+ .vertical-section-container {
+ max-width: 500px;
+ }
+
+ paper-dropdown-menu {
+ width: 200px;
+ margin-right: 20px;
+ }
+ </style>
+
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h4>This is a plain paper-dropdown-menu</h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-dropdown-menu label="Dinosaurs">
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+
+ <paper-dropdown-menu-light label="Dinosaurs (light)">
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </demo-snippet>
+
+ <h4>You can pre-select a value using the <i>selected</i> attribute</h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-dropdown-menu label="Dinosaurs">
+ <paper-listbox class="dropdown-content" selected="1">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+
+ <paper-dropdown-menu-light label="Dinosaurs (light)">
+ <paper-listbox class="dropdown-content" selected="1">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </demo-snippet>
+
+ <h4>You can change the direction in which the menu opens</h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-dropdown-menu label="Upwards and to the left!" vertical-align="bottom" horizontal-align="left">
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+
+ <paper-dropdown-menu-light label="Upwards and to the left! (light)" vertical-align="bottom" horizontal-align="left">
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </demo-snippet>
+
+
+ <h4>A paper-dropdown-menu can be disabled</h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-dropdown-menu label="Disabled dinosaurs" disabled>
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+
+ <paper-dropdown-menu-light label="Disabled dinosaurs (light)" disabled>
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </demo-snippet>
+
+ <!-- TODO(noms): enable this demo when the webcomponentsjs bug is fixed -->
+ <!-- <h4>Here is an example of a long, scrolling menu, using a <i>dom-repeat</i></h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <template is="dom-bind" id="Demo">
+ <paper-dropdown-menu label="Dinosaurs">
+ <paper-listbox class="dropdown-content">
+ <template is="dom-repeat" items='[[dinosaurs]]' as="dinosaur">
+ <paper-item>[[dinosaur]]</paper-item>
+ </template>
+ </paper-listbox>
+ </paper-dropdown-menu>
+ </template>
+ </template>
+ </demo-snippet> -->
+
+ <h4>A paper-dropdown-menu can contain any kind of content, such as tabs</h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-dropdown-menu label="Menu tabs!?">
+ <paper-tabs class="dropdown-content">
+ <paper-tab>cheddar</paper-tab>
+ <paper-tab>stilton</paper-tab>
+ <paper-tab>emmental</paper-tab>
+ </paper-tabs>
+ </paper-dropdown-menu>
+
+ <paper-dropdown-menu-light label="Menu tabs!? (light)">
+ <paper-tabs class="dropdown-content">
+ <paper-tab>cheddar</paper-tab>
+ <paper-tab>stilton</paper-tab>
+ <paper-tab>emmental</paper-tab>
+ </paper-tabs>
+ </paper-dropdown-menu-light>
+ </template>
+ </demo-snippet>
+
+ <h4>You can remove the ripple and the animations</h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-dropdown-menu label="Dinosaurs" noink no-animations>
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+
+ <paper-dropdown-menu-light label="Dinosaurs (light)" noink no-animations>
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </demo-snippet>
+
+ <h4>You can style a paper-dropdown-menu using custom properties</h4>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-dropdown-menu.custom {
+ --paper-input-container-label: {
+ color: var(--paper-pink-500);
+ font-style: italic;
+ text-align: center;
+ font-weight: bold;
+ };
+ --paper-input-container-input: {
+ color: var(--paper-indigo-500);
+ font-style: normal;
+ font-family: serif;
+ text-transform: uppercase;
+ }
+ /* no underline */
+ --paper-input-container-underline: {
+ display: none;
+ };
+ }
+
+ paper-dropdown-menu-light.custom {
+ --paper-dropdown-menu-label: {
+ color: var(--paper-pink-500);
+ font-style: italic;
+ text-align: center;
+ font-weight: bold;
+ };
+ --paper-dropdown-menu-input: {
+ color: var(--paper-indigo-500);
+ font-style: normal;
+ font-family: serif;
+ text-transform: uppercase;
+ /* no underline */
+ border-bottom: none;
+ }
+ }
+ </style>
+ <paper-dropdown-menu class="custom" label="Custom" no-label-float>
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+
+ <paper-dropdown-menu-light class="custom" label="Custom (light)" no-label-float>
+ <paper-listbox class="dropdown-content">
+ <paper-item>allosaurus</paper-item>
+ <paper-item>brontosaurus</paper-item>
+ <paper-item>carcharodontosaurus</paper-item>
+ <paper-item>diplodocus</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </demo-snippet>
+ </div>
+
+ <script>
+ // document.addEventListener('WebComponentsReady', function() {
+ // Demo.dinosaurs = [
+ // 'allosaurus',
+ // 'brontosaurus',
+ // 'carcharodontosaurus',
+ // 'diplodocus',
+ // 'ekrixinatosaurus',
+ // 'fukuiraptor',
+ // 'gallimimus',
+ // 'hadrosaurus',
+ // 'iguanodon',
+ // 'jainosaurus',
+ // 'kritosaurus',
+ // 'liaoceratops',
+ // 'megalosaurus',
+ // 'nemegtosaurus',
+ // 'ornithomimus',
+ // 'protoceratops',
+ // 'quetecsaurus',
+ // 'rajasaurus',
+ // 'stegosaurus',
+ // 'triceratops',
+ // 'utahraptor',
+ // 'vulcanodon',
+ // 'wannanosaurus',
+ // 'xenoceratops',
+ // 'yandusaurus',
+ // 'zephyrosaurus'
+ // ];
+ // });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/index.html b/catapult/third_party/polymer/components/paper-dropdown-menu/index.html
new file mode 100644
index 00000000..b8053bfb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-dropdown-menu</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-icons.html b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-icons.html
new file mode 100644
index 00000000..d8ca87c0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-icons.html
@@ -0,0 +1,17 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+
+<iron-iconset-svg name="paper-dropdown-menu" size="24">
+<svg><defs>
+<g id="arrow-drop-down"><path d="M7 10l5 5 5-5z"></path></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html
new file mode 100644
index 00000000..edaabf85
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html
@@ -0,0 +1,597 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="../iron-behaviors/iron-button-state.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html">
+<link rel="import" href="../paper-behaviors/paper-ripple-behavior.html">
+<link rel="import" href="../paper-menu-button/paper-menu-button.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<link rel="import" href="paper-dropdown-menu-icons.html">
+<link rel="import" href="paper-dropdown-menu-shared-styles.html">
+
+<!--
+Material design: [Dropdown menus](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)
+
+This is a faster, lighter version of `paper-dropdown-menu`, that does not
+use a `<paper-input>` internally. Use this element if you're concerned about
+the performance of this element, i.e., if you plan on using many dropdowns on
+the same page. Note that this element has a slightly different styling API
+than `paper-dropdown-menu`.
+
+`paper-dropdown-menu-light` is similar to a native browser select element.
+`paper-dropdown-menu-light` works with selectable content. The currently selected
+item is displayed in the control. If no item is selected, the `label` is
+displayed instead.
+
+Example:
+
+ <paper-dropdown-menu-light label="Your favourite pastry">
+ <paper-listbox class="dropdown-content">
+ <paper-item>Croissant</paper-item>
+ <paper-item>Donut</paper-item>
+ <paper-item>Financier</paper-item>
+ <paper-item>Madeleine</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+
+This example renders a dropdown menu with 4 options.
+
+The child element with the class `dropdown-content` is used as the dropdown
+menu. This can be a [`paper-listbox`](paper-listbox), or any other or
+element that acts like an [`iron-selector`](iron-selector).
+
+Specifically, the menu child must fire an
+[`iron-select`](iron-selector#event-iron-select) event when one of its
+children is selected, and an [`iron-deselect`](iron-selector#event-iron-deselect)
+event when a child is deselected. The selected or deselected item must
+be passed as the event's `detail.item` property.
+
+Applications can listen for the `iron-select` and `iron-deselect` events
+to react when options are selected and deselected.
+
+### Styling
+
+The following custom properties and mixins are also available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-dropdown-menu` | A mixin that is applied to the element host | `{}`
+`--paper-dropdown-menu-disabled` | A mixin that is applied to the element host when disabled | `{}`
+`--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple | `{}`
+`--paper-dropdown-menu-button` | A mixin that is applied to the internal menu button | `{}`
+`--paper-dropdown-menu-icon` | A mixin that is applied to the internal icon | `{}`
+`--paper-dropdown-menu-disabled-opacity` | The opacity of the dropdown when disabled | `0.33`
+`--paper-dropdown-menu-color` | The color of the input/label/underline when the dropdown is unfocused | `--primary-text-color`
+`--paper-dropdown-menu-focus-color` | The color of the label/underline when the dropdown is focused | `--primary-color`
+`--paper-dropdown-error-color` | The color of the label/underline when the dropdown is invalid | `--error-color`
+`--paper-dropdown-menu-label` | Mixin applied to the label | `{}`
+`--paper-dropdown-menu-input` | Mixin appled to the input | `{}`
+
+Note that in this element, the underline is just the bottom border of the "input".
+To style it:
+
+ <style is=custom-style>
+ paper-dropdown-menu-light.custom {
+ --paper-dropdown-menu-input: {
+ border-bottom: 2px dashed lavender;
+ };
+ </style>
+
+@group Paper Elements
+@element paper-dropdown-menu-light
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-dropdown-menu-light">
+ <template>
+ <style include="paper-dropdown-menu-shared-styles">
+ :host(:focus) {
+ outline: none;
+ }
+
+ :host {
+ width: 200px; /* Default size of an <input> */
+ }
+
+ /**
+ * All of these styles below are for styling the fake-input display
+ */
+ .dropdown-trigger {
+ box-sizing: border-box;
+ position: relative;
+ width: 100%;
+ padding: 16px 0 8px 0;
+ }
+
+ :host([disabled]) .dropdown-trigger {
+ pointer-events: none;
+ opacity: var(--paper-dropdown-menu-disabled-opacity, 0.33);
+ }
+
+ :host([no-label-float]) .dropdown-trigger {
+ padding-top: 8px; /* If there's no label, we need less space up top. */
+ }
+
+ #input {
+ @apply(--paper-font-subhead);
+ @apply(--paper-font-common-nowrap);
+ line-height: 1.5;
+ border-bottom: 1px solid var(--paper-dropdown-menu-color, --secondary-text-color);
+ color: var(--paper-dropdown-menu-color, --primary-text-color);
+ width: 100%;
+ box-sizing: border-box;
+ padding: 12px 20px 0 0; /* Right padding so that text doesn't overlap the icon */
+ outline: none;
+ @apply(--paper-dropdown-menu-input);
+ }
+
+ :host-context([dir="rtl"]) #input {
+ padding-right: 0px;
+ padding-left: 20px;
+ }
+
+ :host([disabled]) #input {
+ border-bottom: 1px dashed var(--paper-dropdown-menu-color, --secondary-text-color);
+ }
+
+ :host([invalid]) #input {
+ border-bottom: 2px solid var(--paper-dropdown-error-color, --error-color);
+ }
+
+ :host([no-label-float]) #input {
+ padding-top: 0; /* If there's no label, we need less space up top. */
+ }
+
+ label {
+ @apply(--paper-font-subhead);
+ @apply(--paper-font-common-nowrap);
+ display: block;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ /**
+ * The container has a 16px top padding, and there's 12px of padding
+ * between the input and the label (from the input's padding-top)
+ */
+ top: 28px;
+ box-sizing: border-box;
+ width: 100%;
+ padding-right: 20px; /* Right padding so that text doesn't overlap the icon */
+ text-align: left;
+ transition-duration: .2s;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ color: var(--paper-dropdown-menu-color, --secondary-text-color);
+ @apply(--paper-dropdown-menu-label);
+ }
+
+ :host-context([dir="rtl"]) label {
+ padding-right: 0px;
+ padding-left: 20px;
+ }
+
+ :host([no-label-float]) label {
+ top: 8px;
+ /* Since the label doesn't need to float, remove the animation duration
+ which slows down visibility changes (i.e. when a selection is made) */
+ transition-duration: 0s;
+ }
+
+ label.label-is-floating {
+ font-size: 12px;
+ top: 8px;
+ }
+
+ label.label-is-hidden {
+ visibility: hidden;
+ }
+
+ :host([focused]) label.label-is-floating {
+ color: var(--paper-dropdown-menu-focus-color, --primary-color);
+ }
+
+ :host([invalid]) label.label-is-floating {
+ color: var(--paper-dropdown-error-color, --error-color);
+ }
+
+ /**
+ * Sets up the focused underline. It's initially hidden, and becomes
+ * visible when it's focused.
+ */
+ label:after {
+ background-color: var(--paper-dropdown-menu-focus-color, --primary-color);
+ bottom: 7px; /* The container has an 8px bottom padding */
+ content: '';
+ height: 2px;
+ left: 45%;
+ position: absolute;
+ transition-duration: .2s;
+ transition-timing-function: cubic-bezier(.4,0,.2,1);
+ visibility: hidden;
+ width: 8px;
+ z-index: 10;
+ }
+
+ :host([invalid]) label:after {
+ background-color: var(--paper-dropdown-error-color, --error-color);
+ }
+
+ :host([no-label-float]) label:after {
+ bottom: 7px; /* The container has a 8px bottom padding */
+ }
+
+ :host([focused]:not([disabled])) label:after {
+ left: 0;
+ visibility: visible;
+ width: 100%;
+ }
+
+ iron-icon {
+ position: absolute;
+ right: 0px;
+ bottom: 8px; /* The container has an 8px bottom padding */
+ @apply(--paper-font-subhead);
+ color: var(--disabled-text-color);
+ @apply(--paper-dropdown-menu-icon);
+ }
+
+ :host-context([dir="rtl"]) iron-icon {
+ left: 0;
+ right: auto;
+ }
+
+ :host([no-label-float]) iron-icon {
+ margin-top: 0px;
+ }
+
+ .error {
+ display: inline-block;
+ visibility: hidden;
+ color: var(--paper-dropdown-error-color, --error-color);
+ @apply(--paper-font-caption);
+ position: absolute;
+ left:0;
+ right:0;
+ bottom: -12px;
+ }
+
+ :host([invalid]) .error {
+ visibility: visible;
+ }
+ </style>
+
+ <!-- this div fulfills an a11y requirement for combobox, do not remove -->
+ <span role="button"></span>
+ <paper-menu-button
+ id="menuButton"
+ vertical-align="[[verticalAlign]]"
+ horizontal-align="[[horizontalAlign]]"
+ vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat)]]"
+ disabled="[[disabled]]"
+ no-animations="[[noAnimations]]"
+ on-iron-select="_onIronSelect"
+ on-iron-deselect="_onIronDeselect"
+ opened="{{opened}}"
+ close-on-activate
+ allow-outside-scroll="[[allowOutsideScroll]]">
+ <div class="dropdown-trigger">
+ <label class$="[[_computeLabelClass(noLabelFloat,alwaysFloatLabel,hasContent)]]">
+ [[label]]
+ </label>
+ <div id="input" tabindex="-1">&nbsp;</div>
+ <iron-icon icon="paper-dropdown-menu:arrow-drop-down"></iron-icon>
+ <span class="error">[[errorMessage]]</span>
+ </div>
+ <content id="content" select=".dropdown-content"></content>
+ </paper-menu-button>
+ </template>
+
+ <script>
+ (function() {
+ 'use strict';
+
+ Polymer({
+ is: 'paper-dropdown-menu-light',
+
+ behaviors: [
+ Polymer.IronButtonState,
+ Polymer.IronControlState,
+ Polymer.PaperRippleBehavior,
+ Polymer.IronFormElementBehavior,
+ Polymer.IronValidatableBehavior
+ ],
+
+ properties: {
+ /**
+ * The derived "label" of the currently selected item. This value
+ * is the `label` property on the selected item if set, or else the
+ * trimmed text content of the selected item.
+ */
+ selectedItemLabel: {
+ type: String,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The last selected item. An item is selected if the dropdown menu has
+ * a child with class `dropdown-content`, and that child triggers an
+ * `iron-select` event with the selected `item` in the `detail`.
+ *
+ * @type {?Object}
+ */
+ selectedItem: {
+ type: Object,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The value for this element that will be used when submitting in
+ * a form. It is read only, and will always have the same value
+ * as `selectedItemLabel`.
+ */
+ value: {
+ type: String,
+ notify: true,
+ readOnly: true,
+ observer: '_valueChanged',
+ },
+
+ /**
+ * The label for the dropdown.
+ */
+ label: {
+ type: String
+ },
+
+ /**
+ * The placeholder for the dropdown.
+ */
+ placeholder: {
+ type: String
+ },
+
+ /**
+ * True if the dropdown is open. Otherwise, false.
+ */
+ opened: {
+ type: Boolean,
+ notify: true,
+ value: false,
+ observer: '_openedChanged'
+ },
+
+ /**
+ * By default, the dropdown will constrain scrolling on the page
+ * to itself when opened.
+ * Set to true in order to prevent scroll from being constrained
+ * to the dropdown when it opens.
+ */
+ allowOutsideScroll: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable the floating label. Bind this to the
+ * `<paper-input-container>`'s `noLabelFloat` property.
+ */
+ noLabelFloat: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+
+ /**
+ * Set to true to always float the label. Bind this to the
+ * `<paper-input-container>`'s `alwaysFloatLabel` property.
+ */
+ alwaysFloatLabel: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable animations when opening and closing the
+ * dropdown.
+ */
+ noAnimations: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * horizontally relative to the dropdown trigger.
+ */
+ horizontalAlign: {
+ type: String,
+ value: 'right'
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * vertically relative to the dropdown trigger.
+ */
+ verticalAlign: {
+ type: String,
+ value: 'top'
+ },
+
+ hasContent: {
+ type: Boolean,
+ readOnly: true
+ }
+ },
+
+ listeners: {
+ 'tap': '_onTap'
+ },
+
+ keyBindings: {
+ 'up down': 'open',
+ 'esc': 'close'
+ },
+
+ hostAttributes: {
+ tabindex: 0,
+ role: 'combobox',
+ 'aria-autocomplete': 'none',
+ 'aria-haspopup': 'true'
+ },
+
+ observers: [
+ '_selectedItemChanged(selectedItem)'
+ ],
+
+ attached: function() {
+ // NOTE(cdata): Due to timing, a preselected value in a `IronSelectable`
+ // child will cause an `iron-select` event to fire while the element is
+ // still in a `DocumentFragment`. This has the effect of causing
+ // handlers not to fire. So, we double check this value on attached:
+ var contentElement = this.contentElement;
+ if (contentElement && contentElement.selectedItem) {
+ this._setSelectedItem(contentElement.selectedItem);
+ }
+ },
+
+ /**
+ * The content element that is contained by the dropdown menu, if any.
+ */
+ get contentElement() {
+ return Polymer.dom(this.$.content).getDistributedNodes()[0];
+ },
+
+ /**
+ * Show the dropdown content.
+ */
+ open: function() {
+ this.$.menuButton.open();
+ },
+
+ /**
+ * Hide the dropdown content.
+ */
+ close: function() {
+ this.$.menuButton.close();
+ },
+
+ /**
+ * A handler that is called when `iron-select` is fired.
+ *
+ * @param {CustomEvent} event An `iron-select` event.
+ */
+ _onIronSelect: function(event) {
+ this._setSelectedItem(event.detail.item);
+ },
+
+ /**
+ * A handler that is called when `iron-deselect` is fired.
+ *
+ * @param {CustomEvent} event An `iron-deselect` event.
+ */
+ _onIronDeselect: function(event) {
+ this._setSelectedItem(null);
+ },
+
+ /**
+ * A handler that is called when the dropdown is tapped.
+ *
+ * @param {CustomEvent} event A tap event.
+ */
+ _onTap: function(event) {
+ if (Polymer.Gestures.findOriginalTarget(event) === this) {
+ this.open();
+ }
+ },
+
+ /**
+ * Compute the label for the dropdown given a selected item.
+ *
+ * @param {Element} selectedItem A selected Element item, with an
+ * optional `label` property.
+ */
+ _selectedItemChanged: function(selectedItem) {
+ var value = '';
+ if (!selectedItem) {
+ value = '';
+ } else {
+ value = selectedItem.label || selectedItem.getAttribute('label') || selectedItem.textContent.trim();
+ }
+
+ this._setValue(value);
+ this._setSelectedItemLabel(value);
+ },
+
+ /**
+ * Compute the vertical offset of the menu based on the value of
+ * `noLabelFloat`.
+ *
+ * @param {boolean} noLabelFloat True if the label should not float
+ * above the input, otherwise false.
+ */
+ _computeMenuVerticalOffset: function(noLabelFloat) {
+ // NOTE(cdata): These numbers are somewhat magical because they are
+ // derived from the metrics of elements internal to `paper-input`'s
+ // template. The metrics will change depending on whether or not the
+ // input has a floating label.
+ return noLabelFloat ? -4 : 8;
+ },
+
+ /**
+ * Returns false if the element is required and does not have a selection,
+ * and true otherwise.
+ * @param {*=} _value Ignored.
+ * @return {boolean} true if `required` is false, or if `required` is true
+ * and the element has a valid selection.
+ */
+ _getValidity: function(_value) {
+ return this.disabled || !this.required || (this.required && !!this.value);
+ },
+
+ _openedChanged: function() {
+ var openState = this.opened ? 'true' : 'false';
+ var e = this.contentElement;
+ if (e) {
+ e.setAttribute('aria-expanded', openState);
+ }
+ },
+
+ _computeLabelClass: function(noLabelFloat, alwaysFloatLabel, hasContent) {
+ var cls = '';
+ if (noLabelFloat === true) {
+ return hasContent ? 'label-is-hidden' : '';
+ }
+
+ if (hasContent || alwaysFloatLabel === true) {
+ cls += ' label-is-floating';
+ }
+ return cls;
+ },
+
+ _valueChanged: function() {
+ // Only update if it's actually different.
+ if (this.$.input && this.$.input.textContent !== this.value) {
+ this.$.input.textContent = this.value;
+ }
+ this._setHasContent(!!this.value);
+ },
+ });
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-shared-styles.html b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-shared-styles.html
new file mode 100644
index 00000000..1130805d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-shared-styles.html
@@ -0,0 +1,78 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<dom-module id="paper-dropdown-menu-shared-styles">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ position: relative;
+ text-align: left;
+
+ /* NOTE(cdata): Both values are needed, since some phones require the
+ * value to be `transparent`.
+ */
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ -webkit-tap-highlight-color: transparent;
+
+ --paper-input-container-input: {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: 100%;
+ box-sizing: border-box;
+ cursor: pointer;
+ };
+
+ @apply(--paper-dropdown-menu);
+ }
+
+ :host([disabled]) {
+ @apply(--paper-dropdown-menu-disabled);
+ }
+
+ :host([noink]) paper-ripple {
+ display: none;
+ }
+
+ :host([no-label-float]) paper-ripple {
+ top: 8px;
+ }
+
+ paper-ripple {
+ top: 12px;
+ left: 0px;
+ bottom: 8px;
+ right: 0px;
+
+ @apply(--paper-dropdown-menu-ripple);
+ }
+
+ paper-menu-button {
+ display: block;
+ padding: 0;
+
+ @apply(--paper-dropdown-menu-button);
+ }
+
+ paper-input {
+ @apply(--paper-dropdown-menu-input);
+ }
+
+ iron-icon {
+ color: var(--disabled-text-color);
+
+ @apply(--paper-dropdown-menu-icon);
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.html b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.html
new file mode 100644
index 00000000..98ebeccf
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.html
@@ -0,0 +1,415 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="../iron-behaviors/iron-button-state.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html">
+<link rel="import" href="../paper-input/paper-input.html">
+<link rel="import" href="../paper-menu-button/paper-menu-button.html">
+<link rel="import" href="../paper-ripple/paper-ripple.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<link rel="import" href="paper-dropdown-menu-icons.html">
+<link rel="import" href="paper-dropdown-menu-shared-styles.html">
+
+<!--
+Material design: [Dropdown menus](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)
+
+`paper-dropdown-menu` is similar to a native browser select element.
+`paper-dropdown-menu` works with selectable content. The currently selected
+item is displayed in the control. If no item is selected, the `label` is
+displayed instead.
+
+Example:
+
+ <paper-dropdown-menu label="Your favourite pastry">
+ <paper-listbox class="dropdown-content">
+ <paper-item>Croissant</paper-item>
+ <paper-item>Donut</paper-item>
+ <paper-item>Financier</paper-item>
+ <paper-item>Madeleine</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+
+This example renders a dropdown menu with 4 options.
+
+The child element with the class `dropdown-content` is used as the dropdown
+menu. This can be a [`paper-listbox`](paper-listbox), or any other or
+element that acts like an [`iron-selector`](iron-selector).
+
+Specifically, the menu child must fire an
+[`iron-select`](iron-selector#event-iron-select) event when one of its
+children is selected, and an [`iron-deselect`](iron-selector#event-iron-deselect)
+event when a child is deselected. The selected or deselected item must
+be passed as the event's `detail.item` property.
+
+Applications can listen for the `iron-select` and `iron-deselect` events
+to react when options are selected and deselected.
+
+### Styling
+
+The following custom properties and mixins are also available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-dropdown-menu` | A mixin that is applied to the element host | `{}`
+`--paper-dropdown-menu-disabled` | A mixin that is applied to the element host when disabled | `{}`
+`--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple | `{}`
+`--paper-dropdown-menu-button` | A mixin that is applied to the internal menu button | `{}`
+`--paper-dropdown-menu-input` | A mixin that is applied to the internal paper input | `{}`
+`--paper-dropdown-menu-icon` | A mixin that is applied to the internal icon | `{}`
+
+You can also use any of the `paper-input-container` and `paper-menu-button`
+style mixins and custom properties to style the internal input and menu button
+respectively.
+
+@group Paper Elements
+@element paper-dropdown-menu
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-dropdown-menu">
+ <template>
+ <style include="paper-dropdown-menu-shared-styles"></style>
+
+ <!-- this div fulfills an a11y requirement for combobox, do not remove -->
+ <span role="button"></span>
+ <paper-menu-button
+ id="menuButton"
+ vertical-align="[[verticalAlign]]"
+ horizontal-align="[[horizontalAlign]]"
+ dynamic-align="[[dynamicAlign]]"
+ vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat)]]"
+ disabled="[[disabled]]"
+ no-animations="[[noAnimations]]"
+ on-iron-select="_onIronSelect"
+ on-iron-deselect="_onIronDeselect"
+ opened="{{opened}}"
+ close-on-activate
+ allow-outside-scroll="[[allowOutsideScroll]]"
+ restore-focus-on-close="[[restoreFocusOnClose]]">
+ <div class="dropdown-trigger">
+ <paper-ripple></paper-ripple>
+ <!-- paper-input has type="text" for a11y, do not remove -->
+ <paper-input
+ type="text"
+ invalid="[[invalid]]"
+ readonly
+ disabled="[[disabled]]"
+ value="[[selectedItemLabel]]"
+ placeholder="[[placeholder]]"
+ error-message="[[errorMessage]]"
+ always-float-label="[[alwaysFloatLabel]]"
+ no-label-float="[[noLabelFloat]]"
+ label="[[label]]">
+ <iron-icon icon="paper-dropdown-menu:arrow-drop-down" suffix></iron-icon>
+ </paper-input>
+ </div>
+ <content id="content" select=".dropdown-content"></content>
+ </paper-menu-button>
+ </template>
+
+ <script>
+ (function() {
+ 'use strict';
+
+ Polymer({
+ is: 'paper-dropdown-menu',
+
+ behaviors: [
+ Polymer.IronButtonState,
+ Polymer.IronControlState,
+ Polymer.IronFormElementBehavior,
+ Polymer.IronValidatableBehavior
+ ],
+
+ properties: {
+ /**
+ * The derived "label" of the currently selected item. This value
+ * is the `label` property on the selected item if set, or else the
+ * trimmed text content of the selected item.
+ */
+ selectedItemLabel: {
+ type: String,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The last selected item. An item is selected if the dropdown menu has
+ * a child with class `dropdown-content`, and that child triggers an
+ * `iron-select` event with the selected `item` in the `detail`.
+ *
+ * @type {?Object}
+ */
+ selectedItem: {
+ type: Object,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The value for this element that will be used when submitting in
+ * a form. It is read only, and will always have the same value
+ * as `selectedItemLabel`.
+ */
+ value: {
+ type: String,
+ notify: true,
+ readOnly: true
+ },
+
+ /**
+ * The label for the dropdown.
+ */
+ label: {
+ type: String
+ },
+
+ /**
+ * The placeholder for the dropdown.
+ */
+ placeholder: {
+ type: String
+ },
+
+ /**
+ * The error message to display when invalid.
+ */
+ errorMessage: {
+ type: String
+ },
+
+ /**
+ * True if the dropdown is open. Otherwise, false.
+ */
+ opened: {
+ type: Boolean,
+ notify: true,
+ value: false,
+ observer: '_openedChanged'
+ },
+
+ /**
+ * By default, the dropdown will constrain scrolling on the page
+ * to itself when opened.
+ * Set to true in order to prevent scroll from being constrained
+ * to the dropdown when it opens.
+ */
+ allowOutsideScroll: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable the floating label. Bind this to the
+ * `<paper-input-container>`'s `noLabelFloat` property.
+ */
+ noLabelFloat: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+
+ /**
+ * Set to true to always float the label. Bind this to the
+ * `<paper-input-container>`'s `alwaysFloatLabel` property.
+ */
+ alwaysFloatLabel: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable animations when opening and closing the
+ * dropdown.
+ */
+ noAnimations: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * horizontally relative to the dropdown trigger.
+ */
+ horizontalAlign: {
+ type: String,
+ value: 'right'
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * vertically relative to the dropdown trigger.
+ */
+ verticalAlign: {
+ type: String,
+ value: 'top'
+ },
+
+ /**
+ * If true, the `horizontalAlign` and `verticalAlign` properties will
+ * be considered preferences instead of strict requirements when
+ * positioning the dropdown and may be changed if doing so reduces
+ * the area of the dropdown falling outside of `fitInto`.
+ */
+ dynamicAlign: {
+ type: Boolean
+ },
+
+ /**
+ * Whether focus should be restored to the dropdown when the menu closes.
+ */
+ restoreFocusOnClose: {
+ type: Boolean,
+ value: true
+ },
+ },
+
+ listeners: {
+ 'tap': '_onTap'
+ },
+
+ keyBindings: {
+ 'up down': 'open',
+ 'esc': 'close'
+ },
+
+ hostAttributes: {
+ role: 'combobox',
+ 'aria-autocomplete': 'none',
+ 'aria-haspopup': 'true'
+ },
+
+ observers: [
+ '_selectedItemChanged(selectedItem)'
+ ],
+
+ attached: function() {
+ // NOTE(cdata): Due to timing, a preselected value in a `IronSelectable`
+ // child will cause an `iron-select` event to fire while the element is
+ // still in a `DocumentFragment`. This has the effect of causing
+ // handlers not to fire. So, we double check this value on attached:
+ var contentElement = this.contentElement;
+ if (contentElement && contentElement.selectedItem) {
+ this._setSelectedItem(contentElement.selectedItem);
+ }
+ },
+
+ /**
+ * The content element that is contained by the dropdown menu, if any.
+ */
+ get contentElement() {
+ return Polymer.dom(this.$.content).getDistributedNodes()[0];
+ },
+
+ /**
+ * Show the dropdown content.
+ */
+ open: function() {
+ this.$.menuButton.open();
+ },
+
+ /**
+ * Hide the dropdown content.
+ */
+ close: function() {
+ this.$.menuButton.close();
+ },
+
+ /**
+ * A handler that is called when `iron-select` is fired.
+ *
+ * @param {CustomEvent} event An `iron-select` event.
+ */
+ _onIronSelect: function(event) {
+ this._setSelectedItem(event.detail.item);
+ },
+
+ /**
+ * A handler that is called when `iron-deselect` is fired.
+ *
+ * @param {CustomEvent} event An `iron-deselect` event.
+ */
+ _onIronDeselect: function(event) {
+ this._setSelectedItem(null);
+ },
+
+ /**
+ * A handler that is called when the dropdown is tapped.
+ *
+ * @param {CustomEvent} event A tap event.
+ */
+ _onTap: function(event) {
+ if (Polymer.Gestures.findOriginalTarget(event) === this) {
+ this.open();
+ }
+ },
+
+ /**
+ * Compute the label for the dropdown given a selected item.
+ *
+ * @param {Element} selectedItem A selected Element item, with an
+ * optional `label` property.
+ */
+ _selectedItemChanged: function(selectedItem) {
+ var value = '';
+ if (!selectedItem) {
+ value = '';
+ } else {
+ value = selectedItem.label || selectedItem.getAttribute('label') || selectedItem.textContent.trim();
+ }
+
+ this._setValue(value);
+ this._setSelectedItemLabel(value);
+ },
+
+ /**
+ * Compute the vertical offset of the menu based on the value of
+ * `noLabelFloat`.
+ *
+ * @param {boolean} noLabelFloat True if the label should not float
+ * above the input, otherwise false.
+ */
+ _computeMenuVerticalOffset: function(noLabelFloat) {
+ // NOTE(cdata): These numbers are somewhat magical because they are
+ // derived from the metrics of elements internal to `paper-input`'s
+ // template. The metrics will change depending on whether or not the
+ // input has a floating label.
+ return noLabelFloat ? -4 : 8;
+ },
+
+ /**
+ * Returns false if the element is required and does not have a selection,
+ * and true otherwise.
+ * @param {*=} _value Ignored.
+ * @return {boolean} true if `required` is false, or if `required` is true
+ * and the element has a valid selection.
+ */
+ _getValidity: function(_value) {
+ return this.disabled || !this.required || (this.required && !!this.value);
+ },
+
+ _openedChanged: function() {
+ var openState = this.opened ? 'true' : 'false';
+ var e = this.contentElement;
+ if (e) {
+ e.setAttribute('aria-expanded', openState);
+ }
+ }
+ });
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/test/index.html b/catapult/third_party/polymer/components/paper-dropdown-menu/test/index.html
new file mode 100644
index 00000000..a3d0a282
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/test/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-dropdown-menu tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'paper-dropdown-menu.html',
+ 'paper-dropdown-menu.html?dom=shadow',
+ 'paper-dropdown-menu-light.html',
+ 'paper-dropdown-menu-light.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu-light.html b/catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu-light.html
new file mode 100644
index 00000000..183ca86f
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu-light.html
@@ -0,0 +1,219 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-dropdown-menu-light basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../paper-listbox/paper-listbox.html">
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../paper-dropdown-menu-light.html">
+</head>
+<body>
+
+ <test-fixture id="TrivialDropdownMenu">
+ <template>
+ <paper-dropdown-menu-light no-animations>
+ <paper-listbox class="dropdown-content">
+ <paper-item>Foo</paper-item>
+ <paper-item>Bar</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="PreselectedDropdownMenu">
+ <template>
+ <paper-dropdown-menu-light no-animations>
+ <paper-listbox class="dropdown-content" selected="1">
+ <paper-item>Foo</paper-item>
+ <paper-item>Bar</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="LabelsDropdownMenu">
+ <template>
+ <paper-dropdown-menu-light no-animations>
+ <paper-listbox class="dropdown-content">
+ <paper-item label="Foo label property">Foo textContent</paper-item>
+ <paper-item label="Foo label attribute">Foo textContent</paper-item>
+ <paper-item>Foo textContent</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu-light>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ function runAfterOpen(menu, callback) {
+ menu.$.menuButton.$.dropdown.addEventListener('iron-overlay-opened', function() {
+ Polymer.Base.async(callback, 1);
+ });
+ MockInteractions.tap(menu);
+ }
+
+ suite('<paper-dropdown-menu-light>', function() {
+ var dropdownMenu;
+
+ setup(function() {
+ dropdownMenu = fixture('TrivialDropdownMenu');
+ content = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ });
+
+ test('opens when tapped', function(done) {
+ var contentRect = content.getBoundingClientRect();
+
+ expect(contentRect.width).to.be.equal(0);
+ expect(contentRect.height).to.be.equal(0);
+
+ runAfterOpen(dropdownMenu, function() {
+ contentRect = content.getBoundingClientRect();
+
+ expect(dropdownMenu.opened).to.be.equal(true);
+
+ expect(contentRect.width).to.be.greaterThan(0);
+ expect(contentRect.height).to.be.greaterThan(0);
+ done();
+ });
+
+ expect(dropdownMenu.opened).to.be.equal(true);
+ });
+
+ test('closes when an item is activated', function(done) {
+ runAfterOpen(dropdownMenu, function() {
+ var firstItem = Polymer.dom(content).querySelector('paper-item');
+
+ MockInteractions.tap(firstItem);
+
+ Polymer.Base.async(function() {
+ expect(dropdownMenu.opened).to.be.equal(false);
+ done();
+ });
+ });
+ });
+
+ test('sets selected item to the activated item', function(done) {
+ runAfterOpen(dropdownMenu, function() {
+ var firstItem = Polymer.dom(content).querySelector('paper-item');
+
+ MockInteractions.tap(firstItem);
+
+ Polymer.Base.async(function() {
+ expect(dropdownMenu.selectedItem).to.be.equal(firstItem);
+ done();
+ });
+ });
+ });
+
+ suite('when a value is preselected', function() {
+ setup(function() {
+ dropdownMenu = fixture('PreselectedDropdownMenu');
+ });
+
+ test('the input area shows the correct selection', function() {
+ Polymer.dom.flush();
+ var secondItem = Polymer.dom(dropdownMenu).querySelectorAll('paper-item')[1];
+ expect(dropdownMenu.selectedItem).to.be.equal(secondItem);
+ });
+ });
+
+ suite('deselecting', function() {
+ var menu;
+
+ setup(function() {
+ dropdownMenu = fixture('PreselectedDropdownMenu');
+ menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ });
+
+ test('an `iron-deselect` event clears the current selection', function() {
+ Polymer.dom.flush();
+ menu.selected = null;
+ expect(dropdownMenu.selectedItem).to.be.equal(null);
+ });
+ });
+
+ suite('validation', function() {
+ test('a non required dropdown is valid regardless of its selection', function() {
+ var dropdownMenu = fixture('TrivialDropdownMenu');
+ menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+
+ // no selection.
+ expect(dropdownMenu.validate()).to.be.true;
+ expect(dropdownMenu.invalid).to.be.false;
+ expect(dropdownMenu.value).to.not.be.ok;
+
+ // some selection.
+ menu.selected = 1;
+ expect(dropdownMenu.validate()).to.be.true;
+ expect(dropdownMenu.invalid).to.be.false;
+ expect(dropdownMenu.value).to.be.equal('Bar');
+ });
+
+ test('a required dropdown is invalid without a selection', function() {
+ var dropdownMenu = fixture('TrivialDropdownMenu');
+ dropdownMenu.required = true;
+
+ // no selection.
+ expect(dropdownMenu.validate()).to.be.false;
+ expect(dropdownMenu.invalid).to.be.true;
+ expect(dropdownMenu.value).to.not.be.ok;
+ });
+
+ test('a required dropdown is valid with a selection', function() {
+ var dropdownMenu = fixture('PreselectedDropdownMenu');
+ Polymer.dom.flush();
+
+ dropdownMenu.required = true;
+
+ expect(dropdownMenu.validate()).to.be.true;
+ expect(dropdownMenu.invalid).to.be.false;
+ expect(dropdownMenu.value).to.be.equal('Bar');
+ });
+ });
+
+ suite('selectedItemLabel', function() {
+ test('label property', function() {
+ var dropdownMenu = fixture('LabelsDropdownMenu');
+ var menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ menu.selected = 0;
+ //Fake a label property since paper-item doesn't have one
+ dropdownMenu.selectedItem.label = dropdownMenu.selectedItem.getAttribute('label');
+ expect(dropdownMenu.selectedItemLabel).to.be.equal('Foo label property');
+ });
+
+ test('label attribute', function() {
+ var dropdownMenu = fixture('LabelsDropdownMenu');
+ var menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ menu.selected = 1;
+ expect(dropdownMenu.selectedItemLabel).to.be.equal('Foo label attribute');
+ });
+
+ test('textContent', function() {
+ var dropdownMenu = fixture('LabelsDropdownMenu');
+ var menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ menu.selected = 2;
+ expect(dropdownMenu.selectedItemLabel).to.be.equal('Foo textContent');
+ });
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu.html b/catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu.html
new file mode 100644
index 00000000..3587f983
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-dropdown-menu/test/paper-dropdown-menu.html
@@ -0,0 +1,220 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-dropdown-menu basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../paper-listbox/paper-listbox.html">
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../paper-dropdown-menu.html">
+
+</head>
+<body>
+
+ <test-fixture id="TrivialDropdownMenu">
+ <template>
+ <paper-dropdown-menu no-animations>
+ <paper-listbox class="dropdown-content">
+ <paper-item>Foo</paper-item>
+ <paper-item>Bar</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="PreselectedDropdownMenu">
+ <template>
+ <paper-dropdown-menu no-animations>
+ <paper-listbox class="dropdown-content" selected="1">
+ <paper-item>Foo</paper-item>
+ <paper-item>Bar</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="LabelsDropdownMenu">
+ <template>
+ <paper-dropdown-menu no-animations>
+ <paper-listbox class="dropdown-content">
+ <paper-item label="Foo label property">Foo textContent</paper-item>
+ <paper-item label="Foo label attribute">Foo textContent</paper-item>
+ <paper-item>Foo textContent</paper-item>
+ </paper-listbox>
+ </paper-dropdown-menu>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ function runAfterOpen(menu, callback) {
+ menu.$.menuButton.$.dropdown.addEventListener('iron-overlay-opened', function() {
+ Polymer.Base.async(callback, 1);
+ });
+ MockInteractions.tap(menu);
+ }
+
+ suite('<paper-dropdown-menu>', function() {
+ var dropdownMenu;
+
+ setup(function() {
+ dropdownMenu = fixture('TrivialDropdownMenu');
+ content = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ });
+
+ test('opens when tapped', function(done) {
+ var contentRect = content.getBoundingClientRect();
+
+ expect(contentRect.width).to.be.equal(0);
+ expect(contentRect.height).to.be.equal(0);
+
+ runAfterOpen(dropdownMenu, function() {
+ contentRect = content.getBoundingClientRect();
+
+ expect(dropdownMenu.opened).to.be.equal(true);
+
+ expect(contentRect.width).to.be.greaterThan(0);
+ expect(contentRect.height).to.be.greaterThan(0);
+ done();
+ });
+
+ expect(dropdownMenu.opened).to.be.equal(true);
+ });
+
+ test('closes when an item is activated', function(done) {
+ runAfterOpen(dropdownMenu, function() {
+ var firstItem = Polymer.dom(content).querySelector('paper-item');
+
+ MockInteractions.tap(firstItem);
+
+ Polymer.Base.async(function() {
+ expect(dropdownMenu.opened).to.be.equal(false);
+ done();
+ });
+ });
+ });
+
+ test('sets selected item to the activated item', function(done) {
+ runAfterOpen(dropdownMenu, function() {
+ var firstItem = Polymer.dom(content).querySelector('paper-item');
+
+ MockInteractions.tap(firstItem);
+
+ Polymer.Base.async(function() {
+ expect(dropdownMenu.selectedItem).to.be.equal(firstItem);
+ done();
+ });
+ });
+ });
+
+ suite('when a value is preselected', function() {
+ setup(function() {
+ dropdownMenu = fixture('PreselectedDropdownMenu');
+ });
+
+ test('the input area shows the correct selection', function() {
+ Polymer.dom.flush();
+ var secondItem = Polymer.dom(dropdownMenu).querySelectorAll('paper-item')[1];
+ expect(dropdownMenu.selectedItem).to.be.equal(secondItem);
+ });
+ });
+
+ suite('deselecting', function() {
+ var menu;
+
+ setup(function() {
+ dropdownMenu = fixture('PreselectedDropdownMenu');
+ menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ });
+
+ test('an `iron-deselect` event clears the current selection', function() {
+ Polymer.dom.flush();
+ menu.selected = null;
+ expect(dropdownMenu.selectedItem).to.be.equal(null);
+ });
+ });
+
+ suite('validation', function() {
+ test('a non required dropdown is valid regardless of its selection', function() {
+ var dropdownMenu = fixture('TrivialDropdownMenu');
+ menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+
+ // no selection.
+ expect(dropdownMenu.validate()).to.be.true;
+ expect(dropdownMenu.invalid).to.be.false;
+ expect(dropdownMenu.value).to.not.be.ok;
+
+ // some selection.
+ menu.selected = 1;
+ expect(dropdownMenu.validate()).to.be.true;
+ expect(dropdownMenu.invalid).to.be.false;
+ expect(dropdownMenu.value).to.be.equal('Bar');
+ });
+
+ test('a required dropdown is invalid without a selection', function() {
+ var dropdownMenu = fixture('TrivialDropdownMenu');
+ dropdownMenu.required = true;
+
+ // no selection.
+ expect(dropdownMenu.validate()).to.be.false;
+ expect(dropdownMenu.invalid).to.be.true;
+ expect(dropdownMenu.value).to.not.be.ok;
+ });
+
+ test('a required dropdown is valid with a selection', function() {
+ var dropdownMenu = fixture('PreselectedDropdownMenu');
+ Polymer.dom.flush();
+
+ dropdownMenu.required = true;
+
+ expect(dropdownMenu.validate()).to.be.true;
+ expect(dropdownMenu.invalid).to.be.false;
+ expect(dropdownMenu.value).to.be.equal('Bar');
+ });
+ });
+
+ suite('selectedItemLabel', function() {
+ test('label property', function() {
+ var dropdownMenu = fixture('LabelsDropdownMenu');
+ var menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ menu.selected = 0;
+ //Fake a label property since paper-item doesn't have one
+ dropdownMenu.selectedItem.label = dropdownMenu.selectedItem.getAttribute('label');
+ expect(dropdownMenu.selectedItemLabel).to.be.equal('Foo label property');
+ });
+
+ test('label attribute', function() {
+ var dropdownMenu = fixture('LabelsDropdownMenu');
+ var menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ menu.selected = 1;
+ expect(dropdownMenu.selectedItemLabel).to.be.equal('Foo label attribute');
+ });
+
+ test('textContent', function() {
+ var dropdownMenu = fixture('LabelsDropdownMenu');
+ var menu = Polymer.dom(dropdownMenu).querySelector('.dropdown-content');
+ menu.selected = 2;
+ expect(dropdownMenu.selectedItemLabel).to.be.equal('Foo textContent');
+ });
+ });
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-fab/.bower.json b/catapult/third_party/polymer/components/paper-fab/.bower.json
new file mode 100644
index 00000000..9e335469
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/.bower.json
@@ -0,0 +1,48 @@
+{
+ "name": "paper-fab",
+ "version": "1.2.2",
+ "description": "A material design floating action button",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "button"
+ ],
+ "main": "paper-fab.html",
+ "ignore": [],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-fab.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-fab",
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.5",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.2.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.2",
+ "commit": "064cfb43ea7d38c1a28df45dfef8f564857d8c6b"
+ },
+ "_source": "https://github.com/PolymerElements/paper-fab.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-fab"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-fab/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-fab/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..4116981d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-fab/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-fab/.gitignore b/catapult/third_party/polymer/components/paper-fab/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-fab/.travis.yml b/catapult/third_party/polymer/components/paper-fab/.travis.yml
new file mode 100644
index 00000000..f7319c0d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ pFhwMVY4fjQHC2PUZM8ueVJT0NdhIfgNJcExYRsDRS32Qx7RXMPEVd15r9GcBFy9YjTzHJEAiemqmD6gsQzBBcTECiLDfXK+NCDiiqdgq298aZ2mXk9wJpOyOBkD2baLlSKkcUey0PMcSgudT62qzONJDk2qxe0L3tJf4knrVwS1emGLdSwLdFHhNxTuGvmJsJeMdkliPbNQUiC0LnsQT6t59/DQx2BgQC9hY6/YR43s3X8splMZ7w/fUTZe67D9JTV5xva65Ya7yslF60Cn0WSlply5CaSbLzsqHUFTXqy2pt9k39xGDjRg7C7tmoKsjVVDS5BZ6CeWc899D5oNhZ6xlzJEhu22BeMeH6xh9MG3vQeeAicExOthq63IwhyBcdTqstQmOrNmoUZffSIdNIQJZJAfatS+ZvIjYutJYUzg/5wfUQf3J7XseYuaaK2nROICceCfn9IJFdZgpSehmbVdHdW3qu7FCrWif1Qwfe6GwT8z5Af257msUBNlxMTIFnQ8khxFo1ddb4k3pn408hEQUkI18/wb7imJQViWzIjeFlo+3LXynUdOwEQaw+RU/3tBKi3kHvbtnLhHWiEezX+qkJ14h3k6deLAyCSbL4739+xBnRam0ff1KVxI+fQ/8Wcr5mqwovalbW1ii0r6HwxEv4CaE9rqE7HJBphLqg8=
+ - secure: >-
+ i0EEW7GEgn9QfKCQMaiyI5ezZtUaG0tgaHiSUg2uDW1fzfWb8MYh47dyXNPNe3/qj7fBNnjum0tZhF/l2woFvQUzFHgw1Q1+sJl52TJBK8i4DaNK2FhiieB2yNOHpWP3Lgdc4kEJpMIy5pydOYi+3KRT+2GdSZp50r6I9aJnmvtdP55n8SHrHmRsZkc2KWtP8qCFbh4IHjhYOIZ62GyeFg4FIGQ1yuwQIf2Uv4XaUcbiZk17o+dd02xN/AtH7OVWqb4GsdkMOzo6K0sJo8SWNZV7UQOPxvo2JRMumvd4DuLO1Cq7da4rDpQR6E43cW3Iwax6r7x/mxt/PhELMid/jFOzWWmYj+IEk2ZcnpwkVZB62RTwDomCFF3r2Yh0v1kcp62y+yZ2sxTuy3+od4wKVubpFLQwxo+tUnNDrbsCgrhkwYPqriZvz+vKJItWb/hAAAh5gR2lgsQGXH0NM3Rt4qnQmUF6P8tmF7HZFfEhSuG3L4HiDibbRNlY4UwnxVfypxFx1NWpl2hTn3rnM+yOmOlE/trlrvsrVgDpfK1fQ2+45KCF2uPoTeB01OCCKjQ3w3EWc2S+K9yNlqKUn3zLhLtsGgoM4dGhBgwBLqPj9FBwkXevmewRJg4Y7qpsbx/1SIuEK0pjL28/WINQsy8A17ndzDHyxc+t/kbnstbk6PQ=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-fab/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-fab/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-fab/README.md b/catapult/third_party/polymer/components/paper-fab/README.md
new file mode 100644
index 00000000..3967c1b2
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/README.md
@@ -0,0 +1,53 @@
+[![Build status](https://travis-ci.org/PolymerElements/paper-fab.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-fab)
+
+##&lt;paper-fab&gt;
+
+Material design: [Floating Action Button](https://www.google.com/design/spec/components/buttons-floating-action-button.html)
+
+`paper-fab` is a floating action button. It contains an image placed in the center and
+comes in two sizes: regular size and a smaller size by applying the attribute `mini`. When
+the user touches the button, a ripple effect emanates from the center of the button.
+
+You may import `iron-icons` to use with this element, or provide a URL to a custom icon.
+See `iron-iconset` for more information about how to use a custom icon set.
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-fab.html">
+ <link rel="import" href="../iron-icons/iron-icons.html">
+ <style is="custom-style">
+ paper-fab {
+ display: inline-block;
+ margin: 8px;
+ }
+
+ paper-fab[mini] {
+ --paper-fab-background: #FF5722;
+ }
+
+ paper-fab[label] {
+ font-size: 20px;
+ --paper-fab-background: #2196F3;
+ }
+
+ .container {
+ display: flex;
+ align-items: center;
+ }
+ </style>
+ <div class="container">
+ <next-code-block></next-code-block>
+ </div>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-fab icon="favorite"></paper-fab>
+<paper-fab mini icon="reply"></paper-fab>
+<paper-fab label="😻"></paper-fab>
+```
+
diff --git a/catapult/third_party/polymer/components/paper-fab/bower.json b/catapult/third_party/polymer/components/paper-fab/bower.json
new file mode 100644
index 00000000..40639264
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/bower.json
@@ -0,0 +1,39 @@
+{
+ "name": "paper-fab",
+ "version": "1.2.2",
+ "description": "A material design floating action button",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "button"
+ ],
+ "main": "paper-fab.html",
+ "ignore": [],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-fab.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-fab",
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-material": "PolymerElements/paper-material#^1.0.5",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-fab/demo/index.html b/catapult/third_party/polymer/components/paper-fab/demo/index.html
new file mode 100644
index 00000000..06e3d674
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/demo/index.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-fab demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../paper-fab.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-fab {
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+
+ .vertical-section-container {
+ max-width: 550px;
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>FABs can be enabled or disabled</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style>
+ paper-fab.label {
+ font-size: 20px;
+ }
+ </style>
+ <paper-fab icon="favorite" title="heart"></paper-fab>
+ <paper-fab disabled icon="reply" title="reply"></paper-fab>
+ <paper-fab class="label" label="😻" title="heart eyes cat"></paper-fab>
+ </template>
+ </demo-snippet>
+
+ <h3>FABs can be made smaller using the <i>mini</i> attribute</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-fab mini icon="favorite" title="heart"></paper-fab>
+ <paper-fab mini disabled icon="reply" title="reply"></paper-fab>
+ <paper-fab mini class="label" label="😻" title="heart eyes cat"></paper-fab>
+ </template>
+ </demo-snippet>
+
+ <h3>FABs can hide the ripple effect using the <i>noink</i> attribute</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-fab noink icon="favorite" title="heart"></paper-fab>
+ </template>
+ </demo-snippet>
+
+ <h3>FABs can be styled using custom properties</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-fab.blue {
+ --paper-fab-background: var(--paper-light-blue-500);
+ --paper-fab-keyboard-focus-background: var(--paper-light-blue-900);
+ }
+ paper-fab.orange {
+ --paper-fab-background: var(--paper-orange-500);
+ --paper-fab-keyboard-focus-background: var(--paper-orange-900);
+ }
+ </style>
+
+ <paper-fab icon="favorite" title="heart" class="blue"></paper-fab>
+ <paper-fab icon="favorite" title="heart" class="orange"></paper-fab>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-fab/index.html b/catapult/third_party/polymer/components/paper-fab/index.html
new file mode 100644
index 00000000..b0ced26a
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-fab</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-fab/paper-fab.html b/catapult/third_party/polymer/components/paper-fab/paper-fab.html
new file mode 100644
index 00000000..c7f05783
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/paper-fab.html
@@ -0,0 +1,187 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../paper-behaviors/paper-button-behavior.html">
+<link rel="import" href="../paper-material/paper-material-shared-styles.html">
+<link rel="import" href="../paper-styles/color.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<!--
+Material design: [Floating Action Button](https://www.google.com/design/spec/components/buttons-floating-action-button.html)
+
+`paper-fab` is a floating action button. It contains an image placed in the center and
+comes in two sizes: regular size and a smaller size by applying the attribute `mini`. When
+the user touches the button, a ripple effect emanates from the center of the button.
+
+You may import `iron-icons` to use with this element, or provide a URL to a custom icon.
+See `iron-iconset` for more information about how to use a custom icon set.
+
+Example:
+
+ <link href="path/to/iron-icons/iron-icons.html" rel="import">
+
+ <paper-fab icon="add"></paper-fab>
+ <paper-fab mini icon="favorite"></paper-fab>
+ <paper-fab src="star.png"></paper-fab>
+
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-fab-background` | The background color of the button | `--accent-color`
+`--paper-fab-keyboard-focus-background` | The background color of the button when focused | `--paper-pink-900`
+`--paper-fab-disabled-background` | The background color of the button when it's disabled | `--paper-grey-300`
+`--paper-fab-disabled-text` | The text color of the button when it's disabled | `--paper-grey-500`
+`--paper-fab` | Mixin applied to the button | `{}`
+`--paper-fab-mini` | Mixin applied to a mini button | `{}`
+`--paper-fab-disabled` | Mixin applied to a disabled button | `{}`
+`--paper-fab-iron-icon` | Mixin applied to the iron-icon within the button | `{}`
+`--paper-fab-label` | Mixin applied to the label within the button | `{}`
+
+@group Paper Elements
+@demo demo/index.html
+-->
+
+<dom-module id="paper-fab">
+ <template strip-whitespace>
+ <style include="paper-material-shared-styles">
+ :host {
+ @apply(--layout-vertical);
+ @apply(--layout-center-center);
+
+ background: var(--paper-fab-background, --accent-color);
+ border-radius: 50%;
+ box-sizing: border-box;
+ color: var(--text-primary-color);
+ cursor: pointer;
+ height: 56px;
+ min-width: 0;
+ outline: none;
+ padding: 16px;
+ position: relative;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ width: 56px;
+ z-index: 0;
+
+ /* NOTE: Both values are needed, since some phones require the value `transparent`. */
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ -webkit-tap-highlight-color: transparent;
+
+ @apply(--paper-fab);
+ }
+
+ [hidden] {
+ display: none !important;
+ }
+
+ :host([mini]) {
+ width: 40px;
+ height: 40px;
+ padding: 8px;
+
+ @apply(--paper-fab-mini);
+ }
+
+ :host([disabled]) {
+ color: var(--paper-fab-disabled-text, --paper-grey-500);
+ background: var(--paper-fab-disabled-background, --paper-grey-300);
+
+ @apply(--paper-fab-disabled);
+ }
+
+ iron-icon {
+ @apply(--paper-fab-iron-icon);
+ }
+
+ span {
+ width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-align: center;
+
+ @apply(--paper-fab-label);
+ }
+
+ :host(.keyboard-focus) {
+ background: var(--paper-fab-keyboard-focus-background, --paper-pink-900);
+ }
+ </style>
+
+ <iron-icon id="icon" hidden$="{{!_computeIsIconFab(icon, src)}}" src="[[src]]" icon="[[icon]]"></iron-icon>
+ <span hidden$="{{_computeIsIconFab(icon, src)}}">{{label}}</span>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-fab',
+
+ behaviors: [
+ Polymer.PaperButtonBehavior
+ ],
+
+ properties: {
+ /**
+ * The URL of an image for the icon. If the src property is specified,
+ * the icon property should not be.
+ */
+ src: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * Specifies the icon name or index in the set of icons available in
+ * the icon's icon set. If the icon property is specified,
+ * the src property should not be.
+ */
+ icon: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * Set this to true to style this is a "mini" FAB.
+ */
+ mini: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ },
+
+ /**
+ * The label displayed in the badge. The label is centered, and ideally
+ * should have very few characters.
+ */
+ label: {
+ type: String,
+ observer: '_labelChanged'
+ }
+ },
+
+ _labelChanged: function() {
+ this.setAttribute('aria-label', this.label);
+ },
+
+ _computeIsIconFab: function(icon, src) {
+ return (icon.length > 0) || (src.length > 0);
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-fab/test/a11y.html b/catapult/third_party/polymer/components/paper-fab/test/a11y.html
new file mode 100644
index 00000000..7029aff5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/test/a11y.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-fab a11y tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../paper-fab.html">
+
+</head>
+<body>
+
+ <test-fixture id="A11yFabs">
+ <template>
+ <paper-fab id="fab1" icon="add"></paper-fab>
+ <paper-fab id="fab2" icon="add" disabled></paper-fab>
+ <paper-fab id="fab3" icon="add" aria-label="custom"></paper-fab>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ var f1;
+ var f2;
+ var f3;
+
+ setup(function() {
+ var fabs = fixture('A11yFabs');
+
+ f1 = fabs[0];
+ f2 = fabs[1];
+ f3 = fabs[2];
+ });
+
+ test('aria role is a button', function() {
+ assert.strictEqual(f1.getAttribute('role'), 'button');
+ });
+
+ test('aria-disabled is set', function() {
+ assert.ok(f2.hasAttribute('aria-disabled'));
+ f2.removeAttribute('disabled');
+ assert.strictEqual(f2.getAttribute('aria-disabled'), 'false');
+ });
+
+ test('user-defined aria-label is preserved', function() {
+ assert.strictEqual(f3.getAttribute('aria-label'), 'custom');
+ f3.icon = 'arrow-forward';
+ assert.strictEqual(f3.getAttribute('aria-label'), 'custom');
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-fab/test/basic.html b/catapult/third_party/polymer/components/paper-fab/test/basic.html
new file mode 100644
index 00000000..a0ae42d3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/test/basic.html
@@ -0,0 +1,141 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-fab basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../paper-fab.html">
+
+</head>
+<body>
+
+ <test-fixture id="TrivialFab">
+ <template>
+ <div style="line-height:30px;">
+ <paper-fab id="fab1" icon="add"></paper-fab>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="SrcFab">
+ <template>
+ <paper-fab src="add.png"></paper-fab>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="icon-fab">
+ <template>
+ <paper-fab icon="favorite" label="favorite icon"></paper-fab>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="icon-src-fab">
+ <template>
+ <paper-fab src="add.png" label="add icon"></paper-fab>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="label-fab">
+ <template>
+ <paper-fab label="HTML"></paper-fab>
+ </template>
+ </test-fixture>
+
+ <script>
+ var f1;
+ var f2;
+ var f3;
+ var f4;
+ var f5;
+
+ function centerOf(element) {
+ var rect = element.getBoundingClientRect();
+ return {left: rect.left + rect.width / 2, top: rect.top + rect.height / 2};
+ }
+
+ function approxEqual(p1, p2) {
+ return Math.round(p1.left) == Math.round(p2.left) && Math.round(p1.top) == Math.round(p2.top);
+ }
+
+ function isHidden(element) {
+ var rect = element.getBoundingClientRect();
+ return (rect.width == 0 && rect.height == 0);
+ }
+
+ setup(function() {
+ f1 = fixture('TrivialFab').querySelector('#fab1');
+ f2 = fixture('SrcFab');
+ f3 = fixture('icon-fab');
+ f4 = fixture('icon-src-fab');
+ f5 = fixture('label-fab');
+ });
+
+ test('applies an icon specified by the `icon` attribute', function() {
+ assert.isFalse(!!f1.$.icon.usesSrcAttribute);
+ assert.ok(Polymer.dom(f1.$.icon.root).querySelector('svg'));
+ });
+
+ test('applies an icon specified by the `src` attribute', function() {
+ assert.isFalse(f2.$.icon._usesIconset());
+ assert.ok(f2.$.icon._img);
+ });
+
+ test('renders correctly independent of line height', function() {
+ assert.ok(approxEqual(centerOf(f1.$.icon), centerOf(f1)));
+ });
+
+ test('fab displays icon with `icon` and `label` attributes', function(done) {
+ Polymer.Base.async(function() {
+ var icon = f3.$$('iron-icon');
+ var text = f3.$$('span');
+ expect(icon).not.to.be.null;
+ assert.isFalse(isHidden(icon));
+ assert.isTrue(isHidden(text));
+ expect(icon.icon).to.be.equal(f3.icon);
+ expect(f3.getAttribute('aria-label')).to.be.equal(f3.label);
+ done();
+ });
+ });
+
+ test('fab displays icon with `src` and `label` attributes', function(done) {
+ Polymer.Base.async(function() {
+ var icon = f4.$$('iron-icon');
+ var text = f4.$$('span');
+ expect(icon).not.to.be.null;
+ assert.isFalse(isHidden(icon));
+ assert.isTrue(isHidden(text));
+ expect(icon.src).to.be.equal(f4.src);
+ expect(f4.getAttribute('aria-label')).to.be.equal(f4.label);
+ done();
+ });
+ });
+
+ test('fab displays label with `label` attribute correctly', function(done) {
+ Polymer.Base.async(function() {
+ var icon = f5.$$('iron-icon');
+ var text = f5.$$('span');
+ expect(text).not.to.be.null;
+ assert.isTrue(isHidden(icon));
+ assert.isFalse(isHidden(text));
+ expect(text.innerHTML).to.be.equal(f5.label);
+ expect(f5.getAttribute('aria-label')).to.be.equal(f5.label);
+ done();
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-fab/test/index.html b/catapult/third_party/polymer/components/paper-fab/test/index.html
new file mode 100644
index 00000000..ca535dbf
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-fab/test/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-fab tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'a11y.html',
+ 'basic.html?dom=shadow',
+ 'a11y.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/.bower.json b/catapult/third_party/polymer/components/paper-icon-button/.bower.json
new file mode 100644
index 00000000..1bccd85d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/.bower.json
@@ -0,0 +1,50 @@
+{
+ "name": "paper-icon-button",
+ "private": true,
+ "version": "1.1.6",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "A material design icon button",
+ "main": [
+ "paper-icon-button.html",
+ "paper-icon-button-light.html"
+ ],
+ "author": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "button",
+ "icon",
+ "control"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-icon-button.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/paper-icon-button",
+ "_release": "1.1.6",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.6",
+ "commit": "22fd17e262f5e1dd543f67cb4b7fee6ad94c759c"
+ },
+ "_source": "https://github.com/PolymerElements/paper-icon-button.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-icon-button"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-icon-button/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-icon-button/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..c80193d9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-icon-button/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-icon-button/.gitignore b/catapult/third_party/polymer/components/paper-icon-button/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-icon-button/.travis.yml b/catapult/third_party/polymer/components/paper-icon-button/.travis.yml
new file mode 100644
index 00000000..00278f42
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ HdHTMNdAamEtP+Nl5/mL2X7IKba4p2+ljpDSnvkA6gyoLzrWRZAyUjJxGjVRutQejTa8ZLOGXnBYKugSIMrGv8cblvngbhEmIHd2HBnPjd140KNwtsCtzPRDMdJMrvekVOXgpCkecmGq9G/j0WOoOnYY9oJrHB5dFy0y7LfPy6w=
+ - secure: >-
+ XjZRR/HW23UneacknoaYle/dg2Diupr9p9UOWLR7w6ejvwIP99nUF0Bh4trZkUiXBBt29HOBoETwBTMizDHNg8vKe544EgR26Fq4Lwk5EuEHy3xeyroiHlwc56ZC9Obye1kdM3QjU6rzJ7F9NuRL2E0ttgZjt/OnM+vKx3kqGOY=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-icon-button/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-icon-button/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-icon-button/README.md b/catapult/third_party/polymer/components/paper-icon-button/README.md
new file mode 100644
index 00000000..e724d755
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/README.md
@@ -0,0 +1,95 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-icon-button-light.html paper-icon-button.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-icon-button.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-icon-button)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-icon-button)_
+
+
+##&lt;paper-icon-button&gt;
+
+Material design: [Icon toggles](https://www.google.com/design/spec/components/buttons.html#buttons-toggle-buttons)
+
+`paper-icon-button` is a button with an image placed at the center. When the user touches
+the button, a ripple effect emanates from the center of the button.
+
+`paper-icon-button` includes a default icon set. Use `icon` to specify which icon
+from the icon set to use.
+
+<!---
+```html
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-icon-button.html">
+ <link rel="import" href="../iron-icons/iron-icons.html">
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-icon-button icon="favorite"></paper-icon-button>
+<paper-icon-button icon="menu"></paper-icon-button>
+<paper-icon-button icon="star"></paper-icon-button>
+```
+
+See [`iron-iconset`](https://github.com/PolymerElements/iron-iconset) for more information about
+how to use a custom icon set.
+
+Example:
+
+```html
+<link href="path/to/iron-icons/iron-icons.html" rel="import">
+
+<paper-icon-button icon="favorite"></paper-icon-button>
+<paper-icon-button src="star.png"></paper-icon-button>
+```
+
+To use `paper-icon-button` as a link, wrap it in an anchor tag. Since `paper-icon-button`
+will already receive focus, you may want to prevent the anchor tag from receiving focus
+as well by setting its tabindex to -1.
+
+```html
+<a href="https://www.polymer-project.org" tabindex="-1">
+ <paper-icon-button icon="polymer"></paper-icon-button>
+</a>
+```
+
+### Styling
+
+Style the button with CSS as you would a normal DOM element. If you are using the icons
+provided by `iron-icons`, they will inherit the foreground color of the button.
+
+```html
+/* make a red "favorite" button */
+<paper-icon-button icon="favorite" style="color: red;"></paper-icon-button>
+```
+
+By default, the ripple is the same color as the foreground at 25% opacity. You may
+customize the color using the `--paper-icon-button-ink-color` custom property.
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-icon-button-disabled-text` | The color of the disabled button | `--disabled-text-color` |
+| `--paper-icon-button-ink-color` | Selected/focus ripple color | `--primary-text-color` |
+| `--paper-icon-button` | Mixin for a button | `{}` |
+| `--paper-icon-button-disabled` | Mixin for a disabled button | `{}` |
+| `--paper-icon-button-hover` | Mixin for button on hover | `{}` |
+
+
+
+<!-- No docs for <paper-icon-button-light> found. -->
diff --git a/catapult/third_party/polymer/components/paper-icon-button/bower.json b/catapult/third_party/polymer/components/paper-icon-button/bower.json
new file mode 100644
index 00000000..ca1f27d3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "paper-icon-button",
+ "private": true,
+ "version": "1.1.6",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "A material design icon button",
+ "main": [
+ "paper-icon-button.html",
+ "paper-icon-button-light.html"
+ ],
+ "author": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "button",
+ "icon",
+ "control"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-icon-button.git"
+ },
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-icon-button/demo/index.html b/catapult/third_party/polymer/components/paper-icon-button/demo/index.html
new file mode 100644
index 00000000..98d496f8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/demo/index.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>paper-icon-button demo</title>
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../paper-icon-button.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-icon-button {
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+ </style>
+ </head>
+ <body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Buttons can use iron-icons or external images, and can be disabled</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-icon-button icon="favorite" title="heart"></paper-icon-button>
+ <paper-icon-button src="https://assets-cdn.github.com/images/modules/logos_page/Octocat.png" alt="octocat" title="octocat"></paper-icon-button>
+ <paper-icon-button disabled icon="reply" title="reply"></paper-icon-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can hide the ripple effect using the <i>noink</i> attribute</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-icon-button noink icon="find-in-page" title="find"></paper-icon-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can be styled using regular CSS and custom properties</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-icon-button.pink {
+ color: var(--paper-pink-500);
+ --paper-icon-button-ink-color: var(--paper-indigo-500);
+ }
+ paper-icon-button.pink:hover {
+ background-color: var(--paper-pink-500);
+ color: white;
+ }
+ paper-icon-button.blue {
+ --paper-icon-button-ink-color: var(--paper-orange-500);
+ background-color: var(--paper-light-blue-500);
+ color: white;
+ border-radius: 3px;
+ padding: 2px;
+ }
+ </style>
+ <paper-icon-button icon="favorite" title="heart" class="pink"></paper-icon-button>
+ <paper-icon-button icon="flight-takeoff" title="take off" class="blue"></paper-icon-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can be resized</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-icon-button.giant {
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+ <paper-icon-button icon="alarm-on" title="wake up" class="giant"></paper-icon-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Buttons can be used as a link</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <a href="https://www.polymer-project.org" tabindex="-1">
+ <paper-icon-button icon="polymer"></paper-icon-button>
+ </a>
+ <style>
+ a paper-icon-button,
+ a:active paper-icon-button,
+ a:visited paper-icon-button {
+ color: #000000;
+ }
+ </style>
+ </template>
+ </demo-snippet>
+ </div>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/demo/paper-icon-button-light.html b/catapult/third_party/polymer/components/paper-icon-button/demo/paper-icon-button-light.html
new file mode 100644
index 00000000..6bae8e37
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/demo/paper-icon-button-light.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<!--
+@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
+-->
+<html>
+ <head>
+ <title>paper-icon-button-light demo</title>
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../paper-icon-button-light.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles"></style>
+ </head>
+ <body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>paper-icon-button-light can contain iron-icons or external images and can be disabled</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ button[is=paper-icon-button-light] {
+ width: 40px;
+ height: 40px;
+ padding: 8px;
+ margin: 10px;
+ }
+
+ button[is=paper-icon-button-light] > img {
+ width: 24px;
+ height: 24px;
+ }
+ </style>
+
+ <button is="paper-icon-button-light" title="heart">
+ <iron-icon icon="favorite"></iron-icon>
+ </button>
+ <button is="paper-icon-button-light" title="octocat">
+ <img src="https://assets-cdn.github.com/images/modules/logos_page/Octocat.png" alt="octocat">
+ </button>
+ <button is="paper-icon-button-light" title="reply" disabled>
+ <iron-icon icon="reply"></iron-icon>
+ </button>
+ </template>
+ </demo-snippet>
+ </div>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/index.html b/catapult/third_party/polymer/components/paper-icon-button/index.html
new file mode 100644
index 00000000..78f963c7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/index.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <script src="../webcomponentsjs/webcomponents.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/paper-icon-button-light.html b/catapult/third_party/polymer/components/paper-icon-button/paper-icon-button-light.html
new file mode 100644
index 00000000..11e771b4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/paper-icon-button-light.html
@@ -0,0 +1,98 @@
+<!--
+@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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../paper-behaviors/paper-ripple-behavior.html">
+
+<!--
+The following custom properties and mixins are also available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-icon-button-light-ripple` | Mixin applied to the paper ripple | `{}`
+
+@group Paper Elements
+@element paper-icon-button-light
+@demo demo/paper-icon-button-light.html
+-->
+<dom-module id="paper-icon-button-light">
+ <template strip-whitespace>
+ <style>
+ :host {
+ vertical-align: middle;
+ color: inherit;
+ outline: none;
+ width: 24px;
+ height: 24px;
+ background: none;
+ margin: 0;
+ border: none;
+ padding: 0;
+
+ position: relative;
+ cursor: pointer;
+
+ /* NOTE: Both values are needed, since some phones require the value to be `transparent`. */
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ :host([disabled]) {
+ color: #9b9b9b;
+ pointer-events: none;
+ cursor: auto;
+ }
+
+ paper-ripple {
+ opacity: 0.6;
+ color: currentColor;
+ @apply(--paper-icon-button-light-ripple);
+ }
+ </style>
+ <content></content>
+ </template>
+ <script>
+ Polymer({
+ is: 'paper-icon-button-light',
+ extends: 'button',
+
+ behaviors: [
+ Polymer.PaperRippleBehavior
+ ],
+
+ listeners: {
+ 'down': '_rippleDown',
+ 'up': '_rippleUp',
+ 'focus': '_rippleDown',
+ 'blur': '_rippleUp',
+ },
+
+ _rippleDown: function() {
+ this.getRipple().uiDownAction();
+ },
+
+ _rippleUp: function() {
+ this.getRipple().uiUpAction();
+ },
+
+ /**
+ * @param {...*} var_args
+ */
+ ensureRipple: function(var_args) {
+ var lastRipple = this._ripple;
+ Polymer.PaperRippleBehavior.ensureRipple.apply(this, arguments);
+ if (this._ripple && this._ripple !== lastRipple) {
+ this._ripple.center = true;
+ this._ripple.classList.add('circle');
+ }
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/paper-icon-button.html b/catapult/third_party/polymer/components/paper-icon-button/paper-icon-button.html
new file mode 100644
index 00000000..19f2574c
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/paper-icon-button.html
@@ -0,0 +1,176 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../paper-behaviors/paper-inky-focus-behavior.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<!--
+Material design: [Icon toggles](https://www.google.com/design/spec/components/buttons.html#buttons-toggle-buttons)
+
+`paper-icon-button` is a button with an image placed at the center. When the user touches
+the button, a ripple effect emanates from the center of the button.
+
+`paper-icon-button` includes a default icon set. Use `icon` to specify which icon
+from the icon set to use.
+
+ <paper-icon-button icon="menu"></paper-icon-button>
+
+See [`iron-iconset`](iron-iconset) for more information about
+how to use a custom icon set.
+
+Example:
+
+ <link href="path/to/iron-icons/iron-icons.html" rel="import">
+
+ <paper-icon-button icon="favorite"></paper-icon-button>
+ <paper-icon-button src="star.png"></paper-icon-button>
+
+To use `paper-icon-button` as a link, wrap it in an anchor tag. Since `paper-icon-button`
+will already receive focus, you may want to prevent the anchor tag from receiving focus
+as well by setting its tabindex to -1.
+
+ <a href="https://www.polymer-project.org" tabindex="-1">
+ <paper-icon-button icon="polymer"></paper-icon-button>
+ </a>
+
+### Styling
+
+Style the button with CSS as you would a normal DOM element. If you are using the icons
+provided by `iron-icons`, they will inherit the foreground color of the button.
+
+ /* make a red "favorite" button */
+ <paper-icon-button icon="favorite" style="color: red;"></paper-icon-button>
+
+By default, the ripple is the same color as the foreground at 25% opacity. You may
+customize the color using the `--paper-icon-button-ink-color` custom property.
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-icon-button-disabled-text` | The color of the disabled button | `--disabled-text-color`
+`--paper-icon-button-ink-color` | Selected/focus ripple color | `--primary-text-color`
+`--paper-icon-button` | Mixin for a button | `{}`
+`--paper-icon-button-disabled` | Mixin for a disabled button | `{}`
+`--paper-icon-button-hover` | Mixin for button on hover | `{}`
+
+@group Paper Elements
+@element paper-icon-button
+@demo demo/index.html
+-->
+
+<dom-module id="paper-icon-button">
+ <template strip-whitespace>
+ <style>
+ :host {
+ display: inline-block;
+ position: relative;
+ padding: 8px;
+ outline: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ z-index: 0;
+ line-height: 1;
+
+ width: 40px;
+ height: 40px;
+
+ /* NOTE: Both values are needed, since some phones require the value to be `transparent`. */
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+
+ /* Because of polymer/2558, this style has lower specificity than * */
+ box-sizing: border-box !important;
+
+ @apply(--paper-icon-button);
+ }
+
+ :host #ink {
+ color: var(--paper-icon-button-ink-color, --primary-text-color);
+ opacity: 0.6;
+ }
+
+ :host([disabled]) {
+ color: var(--paper-icon-button-disabled-text, --disabled-text-color);
+ pointer-events: none;
+ cursor: auto;
+
+ @apply(--paper-icon-button-disabled);
+ }
+
+ :host(:hover) {
+ @apply(--paper-icon-button-hover);
+ }
+
+ iron-icon {
+ --iron-icon-width: 100%;
+ --iron-icon-height: 100%;
+ }
+ </style>
+
+ <iron-icon id="icon" src="[[src]]" icon="[[icon]]" alt$="[[alt]]"></iron-icon>
+ </template>
+
+</dom-module>
+<script>
+Polymer({
+ is: 'paper-icon-button',
+
+ hostAttributes: {
+ role: 'button',
+ tabindex: '0'
+ },
+
+ behaviors: [
+ Polymer.PaperInkyFocusBehavior
+ ],
+
+ properties: {
+ /**
+ * The URL of an image for the icon. If the src property is specified,
+ * the icon property should not be.
+ */
+ src: {
+ type: String
+ },
+
+ /**
+ * Specifies the icon name or index in the set of icons available in
+ * the icon's icon set. If the icon property is specified,
+ * the src property should not be.
+ */
+ icon: {
+ type: String
+ },
+
+ /**
+ * Specifies the alternate text for the button, for accessibility.
+ */
+ alt: {
+ type: String,
+ observer: "_altChanged"
+ }
+ },
+
+ _altChanged: function(newValue, oldValue) {
+ var label = this.getAttribute('aria-label');
+
+ // Don't stomp over a user-set aria-label.
+ if (!label || oldValue == label) {
+ this.setAttribute('aria-label', newValue);
+ }
+ }
+});
+</script>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/test/a11y.html b/catapult/third_party/polymer/components/paper-icon-button/test/a11y.html
new file mode 100644
index 00000000..a56c9f45
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/test/a11y.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-icon-button a11y tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../paper-icon-button.html">
+
+</head>
+<body>
+
+ <test-fixture id="A11yIconButtons">
+ <template>
+ <paper-icon-button id="iconButton1" icon="add"></paper-icon-button>
+ <paper-icon-button id="iconButton2" icon="add" disabled></paper-icon-button>
+ <paper-icon-button id="iconButton3" icon="add" aria-label="custom"></paper-icon-button>
+ <paper-icon-button id="iconButton4" icon="add" alt="alt text"></paper-icon-button>
+ <paper-icon-button id="iconButton5" icon="add" aria-label="custom" alt="alt text" ></paper-icon-button>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ var b1;
+ var b2;
+ var b3;
+ var b4;
+ var b5;
+
+ setup(function() {
+ var iconButtons = fixture('A11yIconButtons');
+
+ b1 = iconButtons[0];
+ b2 = iconButtons[1];
+ b3 = iconButtons[2];
+ b4 = iconButtons[3];
+ b5 = iconButtons[4];
+ });
+
+ test('aria role is a button', function() {
+ assert.strictEqual(b1.getAttribute('role'), 'button');
+ });
+
+ test('aria-disabled is set', function() {
+ assert.strictEqual(b2.getAttribute('aria-disabled'), 'true');
+ b2.removeAttribute('disabled');
+ assert.strictEqual(b2.getAttribute('aria-disabled'), 'false');
+ });
+
+ test('user-defined aria-label is preserved', function() {
+ assert.strictEqual(b3.getAttribute('aria-label'), 'custom');
+ b3.icon = 'arrow-forward';
+ assert.strictEqual(b3.getAttribute('aria-label'), 'custom');
+ });
+
+ test('alt attribute is used for the aria-label', function() {
+ assert.strictEqual(b4.getAttribute('aria-label'), 'alt text');
+ b4.icon = 'arrow-forward';
+ assert.strictEqual(b4.getAttribute('aria-label'), 'alt text');
+ });
+
+ test('aria-label wins over alt attribute', function() {
+ assert.strictEqual(b5.getAttribute('aria-label'), 'custom');
+ b5.icon = 'arrow-forward';
+ b5.alt = 'other alt'
+ assert.strictEqual(b5.getAttribute('aria-label'), 'custom');
+ });
+
+ test('alt attribute can be updated', function() {
+ assert.strictEqual(b4.getAttribute('aria-label'), 'alt text');
+ b4.alt = 'alt again';
+ assert.strictEqual(b4.getAttribute('aria-label'), 'alt again');
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/test/basic.html b/catapult/third_party/polymer/components/paper-icon-button/test/basic.html
new file mode 100644
index 00000000..fa3afad8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/test/basic.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-icon-button basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../paper-icon-button.html">
+
+</head>
+<body>
+
+ <test-fixture id="TrivialIconButton">
+ <template>
+ <div style="line-height:30px;">
+ <paper-icon-button id="fab1" icon="add"></paper-icon-button>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="SrcIconButton">
+ <template>
+ <paper-icon-button src="add.png"></paper-icon-button>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ var b1;
+ var b2;
+
+ function centerOf(element) {
+ var rect = element.getBoundingClientRect();
+ return {left: rect.left + rect.width / 2, top: rect.top + rect.height / 2};
+ }
+
+ function approxEqual(p1, p2) {
+ return Math.abs(p1.left - p2.left) <= 2 && Math.abs(p1.top-p2.top) <= 2;
+ }
+
+ setup(function() {
+ b1 = fixture('TrivialIconButton').querySelector('#fab1');
+ b2 = fixture('SrcIconButton');
+ });
+
+ test('applies an icon specified by the `icon` attribute', function() {
+ assert.strictEqual(!!b1.$.icon.src, false);
+ assert.ok(Polymer.dom(b1.$.icon.root).querySelector('svg'));
+ });
+
+ test('applies an icon specified by the `src` attribute', function() {
+
+ assert.strictEqual(!!b2.$.icon.src, true);
+ assert.ok(b2.$.icon.src);
+ });
+
+ test('renders correctly independent of line height', function() {
+ assert.ok(approxEqual(centerOf(b1.$.icon), centerOf(b1)));
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-icon-button/test/index.html b/catapult/third_party/polymer/components/paper-icon-button/test/index.html
new file mode 100644
index 00000000..2069ec79
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-icon-button/test/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-icon-button tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'a11y.html',
+ 'basic.html?dom=shadow',
+ 'a11y.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-input/.bower.json b/catapult/third_party/polymer/components/paper-input/.bower.json
new file mode 100644
index 00000000..cfd146db
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/.bower.json
@@ -0,0 +1,60 @@
+{
+ "name": "paper-input",
+ "version": "1.1.24",
+ "description": "Material design text fields",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "input"
+ ],
+ "main": [
+ "paper-input.html",
+ "paper-textarea.html",
+ "paper-input-behavior.html",
+ "paper-input-container.html",
+ "paper-input-error.html",
+ "paper-input-addon-behavior.html",
+ "paper-input-char-counter.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-input.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-input",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.2.0",
+ "iron-autogrow-textarea": "PolymerElements/iron-autogrow-textarea#^1.0.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
+ "iron-input": "PolymerElements/iron-input#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.4",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.1.24",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.24",
+ "commit": "881f532e82699f1eb93fd1170e0bb7ff4b3db0cd"
+ },
+ "_source": "https://github.com/PolymerElements/paper-input.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-input"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-input/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-input/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..99b9301c
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-input/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-input/.gitignore b/catapult/third_party/polymer/components/paper-input/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-input/.travis.yml b/catapult/third_party/polymer/components/paper-input/.travis.yml
new file mode 100644
index 00000000..b24d7f73
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ Hg2KIEhKqOw0J8ZW2C3Pdgp5N4HD/f9jhdluH0tiYRCU4I/jf5grQuA3ohqsbqnJKV5l06gWVIDCdxBDwDEH0h8v9uUG5z/i2diiuLQc94SLQu8kWKkUPDOx+pUyXmfRKj6KnaRTotTLFrwlyuKDi9OfGjQbLZWTvmJUWoFwh4g=
+ - secure: >-
+ U6/Hp/V0ezT/yxeP2bv4S99LSLScKEaOfYwQUbe0+v5dPbN5XZaCUS6iSbNP2K8Mtb1UQUEyL8uN6Zn+khFlJ8/KJshppJ6HJi235CykahBhh9/Cv7EapgDUoss14ntE8EKpm6Ijo4LvVyPVmhgqKk9wP5ykDFtvhoKD4C3guVU=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-input/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-input/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-input/README.md b/catapult/third_party/polymer/components/paper-input/README.md
new file mode 100644
index 00000000..80c6bc33
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/README.md
@@ -0,0 +1,38 @@
+[![Build status](https://travis-ci.org/PolymerElements/paper-input.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-input)
+[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://beta.webcomponents.org/element/PolymerElements/paper-input)
+
+## &lt;paper-input&gt;
+
+Material design: [Text fields](https://www.google.com/design/spec/components/text-fields.html)
+
+`<paper-input>` is a single-line text field with Material Design styling.
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-input.html">
+ <link rel="import" href="../iron-icons/iron-icons.html">
+ <style>
+ paper-input {
+ max-width: 400px;
+ margin: auto;
+ }
+ iron-icon, div[suffix] {
+ color: hsl(0, 0%, 50%);
+ margin-right: 12px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-input always-float-label label="Floating label"></paper-input>
+<paper-input label="username">
+ <iron-icon icon="mail" prefix></iron-icon>
+ <div suffix>@email.com</div>
+</paper-input>
+```
diff --git a/catapult/third_party/polymer/components/paper-input/all-imports.html b/catapult/third_party/polymer/components/paper-input/all-imports.html
new file mode 100644
index 00000000..0f457718
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/all-imports.html
@@ -0,0 +1,12 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="paper-input.html">
+<link rel="import" href="paper-textarea.html">
diff --git a/catapult/third_party/polymer/components/paper-input/bower.json b/catapult/third_party/polymer/components/paper-input/bower.json
new file mode 100644
index 00000000..84a2e99d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/bower.json
@@ -0,0 +1,51 @@
+{
+ "name": "paper-input",
+ "version": "1.1.24",
+ "description": "Material design text fields",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "input"
+ ],
+ "main": [
+ "paper-input.html",
+ "paper-textarea.html",
+ "paper-input-behavior.html",
+ "paper-input-container.html",
+ "paper-input-error.html",
+ "paper-input-addon-behavior.html",
+ "paper-input-char-counter.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-input.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-input",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.2.0",
+ "iron-autogrow-textarea": "PolymerElements/iron-autogrow-textarea#^1.0.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
+ "iron-input": "PolymerElements/iron-input#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.4",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-input/demo/index.html b/catapult/third_party/polymer/components/paper-input/demo/index.html
new file mode 100644
index 00000000..f84799de
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/demo/index.html
@@ -0,0 +1,155 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-input demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-icon/iron-icon.html">
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../iron-input/iron-input.html">
+ <link rel="import" href="../../paper-icon-button/paper-icon-button.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../paper-input-container.html">
+ <link rel="import" href="../paper-input-error.html">
+ <link rel="import" href="../paper-input.html">
+ <link rel="import" href="../paper-textarea.html">
+ <link rel="import" href="ssn-input.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-input {
+ display: block;
+ }
+
+ demo-snippet.horizontal {
+ --demo-snippet-demo: {
+ @apply(--layout-horizontal);
+ @apply(--layout-justified);
+ @apply(--layout-wrap);
+ }
+ }
+ demo-snippet.horizontal paper-input {
+ display: inline-block;
+ }
+
+ button {
+ width: 70px;
+ }
+
+ #inputForValidation {
+ display: inline-block;
+ width: calc(100% - 75px);
+ }
+
+ .vertical-section-container {
+ max-width: 600px;
+ }
+
+ paper-icon-button {
+ color: var(--paper-red-300);
+ --paper-icon-button-ink-color: var(--paper-red-a100);
+ width: 23px; /* 15px + 2*4px for padding */
+ height: 23px;
+ padding: 0px 4px;
+ }
+
+ iron-icon {
+ padding-right: 5px;
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Inputs can have different types, and be disabled</h3>
+ <demo-snippet>
+ <template>
+ <paper-input label="text input"></paper-input>
+ <paper-textarea label="autoresizing textarea input"></paper-textarea>
+ <paper-input label="password input" type="password"></paper-input>
+ <paper-input label="disabled input" disabled></paper-input>
+ </template>
+ </demo-snippet>
+
+ <h3>Inputs can have character counters</h3>
+ <demo-snippet>
+ <template>
+ <paper-input label="simple character counter" char-counter></paper-input>
+ <paper-input label="input with at most 10 characters" char-counter maxlength="10"></paper-input>
+ </template>
+ </demo-snippet>
+
+ <h3>The label can have different floating states</h3>
+ <demo-snippet>
+ <template>
+ <paper-input label="this label floats after typing"></paper-input>
+ <paper-input label="this label is always floating" always-float-label></paper-input>
+ <paper-input label="this label never floats" no-label-float></paper-input>
+ <paper-input label="this label is always floating" always-float-label placeholder="placeholder text"></paper-input>
+ </template>
+ </demo-snippet>
+
+ <h3>Inputs can validate automatically or on demand, and can have custom error messages</h3>
+ <demo-snippet>
+ <template>
+ <paper-input label="this input requires some text" required auto-validate error-message="needs some text!"></paper-input>
+ <paper-input label="this input requires letters only" auto-validate pattern="[a-zA-Z]*" error-message="letters only!"></paper-input>
+ <paper-input label="this input will only let you type letters" auto-validate allowed-pattern="[a-zA-Z]"></paper-input>
+ <paper-input id="inputForValidation" required label="this input is manually validated" pattern="[a-zA-Z]*" error-message="letters only!"></paper-input>
+ <button onclick="validate()">Validate!</button>
+ </template>
+ </demo-snippet>
+
+ <h3>Inputs can have prefixes and suffixes</h3>
+ <demo-snippet class="horizontal">
+ <template>
+ <paper-input label="total" type="number">
+ <div prefix>$</div>
+ </paper-input>
+ <paper-input label="username" id="inputWithButton">
+ <iron-icon icon="mail" prefix></iron-icon>
+ <div suffix>@email.com</div>
+ <paper-icon-button suffix onclick="clearInput()"
+ icon="clear" alt="clear" title="clear">
+ </paper-icon-button>
+ </paper-input>
+ </template>
+ </demo-snippet>
+
+ <h3>Inputs can have custom logic</h3>
+ <demo-snippet>
+ <template>
+ <paper-input-container always-float-label auto-validate attr-for-value="value">
+ <label>Social Security Number</label>
+ <ssn-input class="paper-input-input"></ssn-input>
+ <paper-input-error>SSN invalid!</paper-input-error>
+ </paper-input-container>
+ </template>
+ </demo-snippet>
+ </div>
+
+ <script>
+ function validate() {
+ document.getElementById('inputForValidation').validate();
+ }
+
+ function clearInput() {
+ document.getElementById('inputWithButton').value = '';
+ }
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-input/demo/ssn-input.html b/catapult/third_party/polymer/components/paper-input/demo/ssn-input.html
new file mode 100644
index 00000000..c7ab51dd
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/demo/ssn-input.html
@@ -0,0 +1,96 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-input/iron-input.html">
+<link rel="import" href="../../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="ssn-validator.html">
+
+<dom-module id="ssn-input">
+ <template>
+
+ <style>
+ :host {
+ display: inline-block;
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ input[is="iron-input"] {
+ font: inherit;
+ outline: none;
+ box-shadow: none;
+ border: none;
+ width: auto;
+ text-align: center;
+ }
+
+ .container {
+ @apply(--layout-horizontal);
+ }
+ </style>
+
+ <ssn-validator></ssn-validator>
+
+ <div class="container">
+
+ <input is="iron-input" maxlength="3" bind-value="{{_ssn1}}" size="3" aria-label="First 3 digits of social security number">
+ -
+ <input is="iron-input" maxlength="2" bind-value="{{_ssn2}}" size="2" aria-label="Middle 2 digits of social security number">
+ -
+ <input is="iron-input" maxlength="4" bind-value="{{_ssn3}}" size="4" aria-label="Last 4 digits of social security number">
+
+ </div>
+
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'ssn-input',
+
+ behaviors: [
+ Polymer.IronValidatableBehavior
+ ],
+
+ properties: {
+ value: {
+ notify: true,
+ type: String
+ },
+
+ _ssn1: {
+ type: String
+ },
+
+ _ssn2: {
+ type: String
+ },
+
+ _ssn3: {
+ type: String
+ },
+
+ validator: {
+ type: String,
+ value: 'ssn-validator'
+ }
+ },
+
+ observers: [
+ '_computeValue(_ssn1,_ssn2,_ssn3)'
+ ],
+
+ _computeValue: function(ssn1, ssn2, ssn3) {
+ this.value = ssn1.trim() + '-' + ssn2.trim() + '-' + ssn3.trim();
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/demo/ssn-validator.html b/catapult/third_party/polymer/components/paper-input/demo/ssn-validator.html
new file mode 100644
index 00000000..e45365b5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/demo/ssn-validator.html
@@ -0,0 +1,27 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-validator-behavior/iron-validator-behavior.html">
+
+<script>
+ Polymer({
+ is: 'ssn-validator',
+
+ behaviors: [
+ Polymer.IronValidatorBehavior
+ ],
+
+ validate: function(value) {
+ // this regex validates incomplete ssn's (by design)
+ return !value || value.match(/^[0-9]{0,3}-[0-9]{0,2}-[0-9]{0,4}$/);
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/hero.svg b/catapult/third_party/polymer/components/paper-input/hero.svg
new file mode 100755
index 00000000..e72ebd30
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/hero.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <rect x="49" y="53" width="2" height="18"/>
+ <path d="M188,78H37V44h151V78z M39,76h147V46H39V76z"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-input/index.html b/catapult/third_party/polymer/components/paper-input/index.html
new file mode 100644
index 00000000..e6c9fadc
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <title>paper-input</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page src="all-imports.html"></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-input/paper-input-addon-behavior.html b/catapult/third_party/polymer/components/paper-input/paper-input-addon-behavior.html
new file mode 100644
index 00000000..41081c71
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-input-addon-behavior.html
@@ -0,0 +1,47 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+
+ /**
+ * Use `Polymer.PaperInputAddonBehavior` to implement an add-on for `<paper-input-container>`. A
+ * add-on appears below the input, and may display information based on the input value and
+ * validity such as a character counter or an error message.
+ * @polymerBehavior
+ */
+ Polymer.PaperInputAddonBehavior = {
+
+ hostAttributes: {
+ 'add-on': ''
+ },
+
+ attached: function() {
+ this.fire('addon-attached');
+ },
+
+ /**
+ * The function called by `<paper-input-container>` when the input value or validity changes.
+ * @param {{
+ * inputElement: (Element|undefined),
+ * value: (string|undefined),
+ * invalid: boolean
+ * }} state -
+ * inputElement: The input element.
+ * value: The input value.
+ * invalid: True if the input value is invalid.
+ */
+ update: function(state) {
+ }
+
+ };
+
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/paper-input-behavior.html b/catapult/third_party/polymer/components/paper-input/paper-input-behavior.html
new file mode 100644
index 00000000..33e2a2db
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-input-behavior.html
@@ -0,0 +1,569 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+
+<script>
+
+ // Generate unique, monotonically increasing IDs for labels (needed by
+ // aria-labelledby) and add-ons.
+ Polymer.PaperInputHelper = {};
+ Polymer.PaperInputHelper.NextLabelID = 1;
+ Polymer.PaperInputHelper.NextAddonID = 1;
+
+ /**
+ * Use `Polymer.PaperInputBehavior` to implement inputs with `<paper-input-container>`. This
+ * behavior is implemented by `<paper-input>`. It exposes a number of properties from
+ * `<paper-input-container>` and `<input is="iron-input">` and they should be bound in your
+ * template.
+ *
+ * The input element can be accessed by the `inputElement` property if you need to access
+ * properties or methods that are not exposed.
+ * @polymerBehavior Polymer.PaperInputBehavior
+ */
+ Polymer.PaperInputBehaviorImpl = {
+
+ properties: {
+ /**
+ * Fired when the input changes due to user interaction.
+ *
+ * @event change
+ */
+
+ /**
+ * The label for this input. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * `<label>`'s content and `hidden` property, e.g.
+ * `<label hidden$="[[!label]]">[[label]]</label>` in your `template`
+ */
+ label: {
+ type: String
+ },
+
+ /**
+ * The value for this input. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * the `<input is="iron-input">`'s `bindValue`
+ * property, or the value property of your input that is `notify:true`.
+ */
+ value: {
+ notify: true,
+ type: String
+ },
+
+ /**
+ * Set to true to disable this input. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * both the `<paper-input-container>`'s and the input's `disabled` property.
+ */
+ disabled: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Returns true if the value is invalid. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to both the
+ * `<paper-input-container>`'s and the input's `invalid` property.
+ *
+ * If `autoValidate` is true, the `invalid` attribute is managed automatically,
+ * which can clobber attempts to manage it manually.
+ */
+ invalid: {
+ type: Boolean,
+ value: false,
+ notify: true
+ },
+
+ /**
+ * Set to true to prevent the user from entering invalid input. If you're
+ * using PaperInputBehavior to implement your own paper-input-like element,
+ * bind this to `<input is="iron-input">`'s `preventInvalidInput` property.
+ */
+ preventInvalidInput: {
+ type: Boolean
+ },
+
+ /**
+ * Set this to specify the pattern allowed by `preventInvalidInput`. If
+ * you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `allowedPattern`
+ * property.
+ */
+ allowedPattern: {
+ type: String
+ },
+
+ /**
+ * The type of the input. The supported types are `text`, `number` and `password`.
+ * If you're using PaperInputBehavior to implement your own paper-input-like element,
+ * bind this to the `<input is="iron-input">`'s `type` property.
+ */
+ type: {
+ type: String
+ },
+
+ /**
+ * The datalist of the input (if any). This should match the id of an existing `<datalist>`.
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `list` property.
+ */
+ list: {
+ type: String
+ },
+
+ /**
+ * A pattern to validate the `input` with. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * the `<input is="iron-input">`'s `pattern` property.
+ */
+ pattern: {
+ type: String
+ },
+
+ /**
+ * Set to true to mark the input as required. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * the `<input is="iron-input">`'s `required` property.
+ */
+ required: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The error message to display when the input is invalid. If you're using
+ * PaperInputBehavior to implement your own paper-input-like element,
+ * bind this to the `<paper-input-error>`'s content, if using.
+ */
+ errorMessage: {
+ type: String
+ },
+
+ /**
+ * Set to true to show a character counter.
+ */
+ charCounter: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable the floating label. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * the `<paper-input-container>`'s `noLabelFloat` property.
+ */
+ noLabelFloat: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to always float the label. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * the `<paper-input-container>`'s `alwaysFloatLabel` property.
+ */
+ alwaysFloatLabel: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to auto-validate the input value. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * the `<paper-input-container>`'s `autoValidate` property.
+ */
+ autoValidate: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Name of the validator to use. If you're using PaperInputBehavior to
+ * implement your own paper-input-like element, bind this to
+ * the `<input is="iron-input">`'s `validator` property.
+ */
+ validator: {
+ type: String
+ },
+
+ // HTMLInputElement attributes for binding if needed
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `autocomplete` property.
+ */
+ autocomplete: {
+ type: String,
+ value: 'off'
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `autofocus` property.
+ */
+ autofocus: {
+ type: Boolean,
+ observer: '_autofocusChanged'
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `inputmode` property.
+ */
+ inputmode: {
+ type: String
+ },
+
+ /**
+ * The minimum length of the input value.
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `minlength` property.
+ */
+ minlength: {
+ type: Number
+ },
+
+ /**
+ * The maximum length of the input value.
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `maxlength` property.
+ */
+ maxlength: {
+ type: Number
+ },
+
+ /**
+ * The minimum (numeric or date-time) input value.
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `min` property.
+ */
+ min: {
+ type: String
+ },
+
+ /**
+ * The maximum (numeric or date-time) input value.
+ * Can be a String (e.g. `"2000-01-01"`) or a Number (e.g. `2`).
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `max` property.
+ */
+ max: {
+ type: String
+ },
+
+ /**
+ * Limits the numeric or date-time increments.
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `step` property.
+ */
+ step: {
+ type: String
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `name` property.
+ */
+ name: {
+ type: String
+ },
+
+ /**
+ * A placeholder string in addition to the label. If this is set, the label will always float.
+ */
+ placeholder: {
+ type: String,
+ // need to set a default so _computeAlwaysFloatLabel is run
+ value: ''
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `readonly` property.
+ */
+ readonly: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `size` property.
+ */
+ size: {
+ type: Number
+ },
+
+ // Nonstandard attributes for binding if needed
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `autocapitalize` property.
+ */
+ autocapitalize: {
+ type: String,
+ value: 'none'
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `autocorrect` property.
+ */
+ autocorrect: {
+ type: String,
+ value: 'off'
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `autosave` property,
+ * used with type=search.
+ */
+ autosave: {
+ type: String
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `results` property,
+ * used with type=search.
+ */
+ results: {
+ type: Number
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the `<input is="iron-input">`'s `accept` property,
+ * used with type=file.
+ */
+ accept: {
+ type: String
+ },
+
+ /**
+ * If you're using PaperInputBehavior to implement your own paper-input-like
+ * element, bind this to the`<input is="iron-input">`'s `multiple` property,
+ * used with type=file.
+ */
+ multiple: {
+ type: Boolean
+ },
+
+ _ariaDescribedBy: {
+ type: String,
+ value: ''
+ },
+
+ _ariaLabelledBy: {
+ type: String,
+ value: ''
+ }
+
+ },
+
+ listeners: {
+ 'addon-attached': '_onAddonAttached',
+ },
+
+ keyBindings: {
+ 'shift+tab:keydown': '_onShiftTabDown'
+ },
+
+ hostAttributes: {
+ tabindex: 0
+ },
+
+ /**
+ * Returns a reference to the input element.
+ */
+ get inputElement() {
+ return this.$.input;
+ },
+
+ /**
+ * Returns a reference to the focusable element.
+ */
+ get _focusableElement() {
+ return this.inputElement;
+ },
+
+ registered: function() {
+ // These types have some default placeholder text; overlapping
+ // the label on top of it looks terrible. Auto-float the label in this case.
+ this._typesThatHaveText = ["date", "datetime", "datetime-local", "month",
+ "time", "week", "file"];
+ },
+
+ attached: function() {
+ this._updateAriaLabelledBy();
+
+ if (this.inputElement &&
+ this._typesThatHaveText.indexOf(this.inputElement.type) !== -1) {
+ this.alwaysFloatLabel = true;
+ }
+ },
+
+ _appendStringWithSpace: function(str, more) {
+ if (str) {
+ str = str + ' ' + more;
+ } else {
+ str = more;
+ }
+ return str;
+ },
+
+ _onAddonAttached: function(event) {
+ var target = event.path ? event.path[0] : event.target;
+ if (target.id) {
+ this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, target.id);
+ } else {
+ var id = 'paper-input-add-on-' + Polymer.PaperInputHelper.NextAddonID++;
+ target.id = id;
+ this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, id);
+ }
+ },
+
+ /**
+ * Validates the input element and sets an error style if needed.
+ *
+ * @return {boolean}
+ */
+ validate: function() {
+ return this.inputElement.validate();
+ },
+
+ /**
+ * Forward focus to inputElement. Overriden from IronControlState.
+ */
+ _focusBlurHandler: function(event) {
+ Polymer.IronControlState._focusBlurHandler.call(this, event);
+
+ // Forward the focus to the nested input.
+ if (this.focused && !this._shiftTabPressed)
+ this._focusableElement.focus();
+ },
+
+ /**
+ * Handler that is called when a shift+tab keypress is detected by the menu.
+ *
+ * @param {CustomEvent} event A key combination event.
+ */
+ _onShiftTabDown: function(event) {
+ var oldTabIndex = this.getAttribute('tabindex');
+ this._shiftTabPressed = true;
+ this.setAttribute('tabindex', '-1');
+ this.async(function() {
+ this.setAttribute('tabindex', oldTabIndex);
+ this._shiftTabPressed = false;
+ }, 1);
+ },
+
+ /**
+ * If `autoValidate` is true, then validates the element.
+ */
+ _handleAutoValidate: function() {
+ if (this.autoValidate)
+ this.validate();
+ },
+
+ /**
+ * Restores the cursor to its original position after updating the value.
+ * @param {string} newValue The value that should be saved.
+ */
+ updateValueAndPreserveCaret: function(newValue) {
+ // Not all elements might have selection, and even if they have the
+ // right properties, accessing them might throw an exception (like for
+ // <input type=number>)
+ try {
+ var start = this.inputElement.selectionStart;
+ this.value = newValue;
+
+ // The cursor automatically jumps to the end after re-setting the value,
+ // so restore it to its original position.
+ this.inputElement.selectionStart = start;
+ this.inputElement.selectionEnd = start;
+ } catch (e) {
+ // Just set the value and give up on the caret.
+ this.value = newValue;
+ }
+ },
+
+ _computeAlwaysFloatLabel: function(alwaysFloatLabel, placeholder) {
+ return placeholder || alwaysFloatLabel;
+ },
+
+ _updateAriaLabelledBy: function() {
+ var label = Polymer.dom(this.root).querySelector('label');
+ if (!label) {
+ this._ariaLabelledBy = '';
+ return;
+ }
+ var labelledBy;
+ if (label.id) {
+ labelledBy = label.id;
+ } else {
+ labelledBy = 'paper-input-label-' + Polymer.PaperInputHelper.NextLabelID++;
+ label.id = labelledBy;
+ }
+ this._ariaLabelledBy = labelledBy;
+ },
+
+ _onChange:function(event) {
+ // In the Shadow DOM, the `change` event is not leaked into the
+ // ancestor tree, so we must do this manually.
+ // See https://w3c.github.io/webcomponents/spec/shadow/#events-that-are-not-leaked-into-ancestor-trees.
+ if (this.shadowRoot) {
+ this.fire(event.type, {sourceEvent: event}, {
+ node: this,
+ bubbles: event.bubbles,
+ cancelable: event.cancelable
+ });
+ }
+ },
+
+ _autofocusChanged: function() {
+ // Firefox doesn't respect the autofocus attribute if it's applied after
+ // the page is loaded (Chrome/WebKit do respect it), preventing an
+ // autofocus attribute specified in markup from taking effect when the
+ // element is upgraded. As a workaround, if the autofocus property is set,
+ // and the focus hasn't already been moved elsewhere, we take focus.
+ if (this.autofocus && this._focusableElement) {
+
+ // In IE 11, the default document.activeElement can be the page's
+ // outermost html element, but there are also cases (under the
+ // polyfill?) in which the activeElement is not a real HTMLElement, but
+ // just a plain object. We identify the latter case as having no valid
+ // activeElement.
+ var activeElement = document.activeElement;
+ var isActiveElementValid = activeElement instanceof HTMLElement;
+
+ // Has some other element has already taken the focus?
+ var isSomeElementActive = isActiveElementValid &&
+ activeElement !== document.body &&
+ activeElement !== document.documentElement; /* IE 11 */
+ if (!isSomeElementActive) {
+ // No specific element has taken the focus yet, so we can take it.
+ this._focusableElement.focus();
+ }
+ }
+ }
+ };
+
+ /** @polymerBehavior */
+ Polymer.PaperInputBehavior = [
+ Polymer.IronControlState,
+ Polymer.IronA11yKeysBehavior,
+ Polymer.PaperInputBehaviorImpl
+ ];
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/paper-input-char-counter.html b/catapult/third_party/polymer/components/paper-input/paper-input-char-counter.html
new file mode 100644
index 00000000..1a09a06d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-input-char-counter.html
@@ -0,0 +1,99 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../paper-styles/typography.html">
+<link rel="import" href="paper-input-addon-behavior.html">
+
+<!--
+`<paper-input-char-counter>` is a character counter for use with `<paper-input-container>`. It
+shows the number of characters entered in the input and the max length if it is specified.
+
+ <paper-input-container>
+ <input is="iron-input" maxlength="20">
+ <paper-input-char-counter></paper-input-char-counter>
+ </paper-input-container>
+
+### Styling
+
+The following mixin is available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-input-char-counter` | Mixin applied to the element | `{}`
+-->
+
+<dom-module id="paper-input-char-counter">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ float: right;
+
+ @apply(--paper-font-caption);
+ @apply(--paper-input-char-counter);
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ :host-context([dir="rtl"]) {
+ float: left;
+ }
+ </style>
+
+ <span>[[_charCounterStr]]</span>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'paper-input-char-counter',
+
+ behaviors: [
+ Polymer.PaperInputAddonBehavior
+ ],
+
+ properties: {
+ _charCounterStr: {
+ type: String,
+ value: '0'
+ }
+ },
+
+ /**
+ * This overrides the update function in PaperInputAddonBehavior.
+ * @param {{
+ * inputElement: (Element|undefined),
+ * value: (string|undefined),
+ * invalid: boolean
+ * }} state -
+ * inputElement: The input element.
+ * value: The input value.
+ * invalid: True if the input value is invalid.
+ */
+ update: function(state) {
+ if (!state.inputElement) {
+ return;
+ }
+
+ state.value = state.value || '';
+
+ var counter = state.value.toString().length.toString();
+
+ if (state.inputElement.hasAttribute('maxlength')) {
+ counter += '/' + state.inputElement.getAttribute('maxlength');
+ }
+
+ this._charCounterStr = counter;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/paper-input-container.html b/catapult/third_party/polymer/components/paper-input/paper-input-container.html
new file mode 100644
index 00000000..2ef06efa
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-input-container.html
@@ -0,0 +1,653 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../paper-styles/typography.html">
+
+<!--
+`<paper-input-container>` is a container for a `<label>`, an `<input is="iron-input">` or
+`<textarea>` and optional add-on elements such as an error message or character
+counter, used to implement Material Design text fields.
+
+For example:
+
+ <paper-input-container>
+ <label>Your name</label>
+ <input is="iron-input">
+ </paper-input-container>
+
+Do not wrap `<paper-input-container>` around elements that already include it, such as `<paper-input>`.
+Doing so may cause events to bounce infintely between the container and its contained element.
+
+### Listening for input changes
+
+By default, it listens for changes on the `bind-value` attribute on its children nodes and perform
+tasks such as auto-validating and label styling when the `bind-value` changes. You can configure
+the attribute it listens to with the `attr-for-value` attribute.
+
+### Using a custom input element
+
+You can use a custom input element in a `<paper-input-container>`, for example to implement a
+compound input field like a social security number input. The custom input element should have the
+`paper-input-input` class, have a `notify:true` value property and optionally implements
+`Polymer.IronValidatableBehavior` if it is validatable.
+
+ <paper-input-container attr-for-value="ssn-value">
+ <label>Social security number</label>
+ <ssn-input class="paper-input-input"></ssn-input>
+ </paper-input-container>
+
+
+If you're using a `<paper-input-container>` imperatively, it's important to make sure
+that you attach its children (the `iron-input` and the optional `label`) before you
+attach the `<paper-input-container>` itself, so that it can be set up correctly.
+
+### Validation
+
+If the `auto-validate` attribute is set, the input container will validate the input and update
+the container styling when the input value changes.
+
+### Add-ons
+
+Add-ons are child elements of a `<paper-input-container>` with the `add-on` attribute and
+implements the `Polymer.PaperInputAddonBehavior` behavior. They are notified when the input value
+or validity changes, and may implement functionality such as error messages or character counters.
+They appear at the bottom of the input.
+
+### Prefixes and suffixes
+These are child elements of a `<paper-input-container>` with the `prefix`
+or `suffix` attribute, and are displayed inline with the input, before or after.
+
+ <paper-input-container>
+ <div prefix>$</div>
+ <label>Total</label>
+ <input is="iron-input">
+ <paper-icon-button suffix icon="clear"></paper-icon-button>
+ </paper-input-container>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-input-container-color` | Label and underline color when the input is not focused | `--secondary-text-color`
+`--paper-input-container-focus-color` | Label and underline color when the input is focused | `--primary-color`
+`--paper-input-container-invalid-color` | Label and underline color when the input is is invalid | `--error-color`
+`--paper-input-container-input-color` | Input foreground color | `--primary-text-color`
+`--paper-input-container` | Mixin applied to the container | `{}`
+`--paper-input-container-disabled` | Mixin applied to the container when it's disabled | `{}`
+`--paper-input-container-label` | Mixin applied to the label | `{}`
+`--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}`
+`--paper-input-container-label-floating` | Mixin applied to the label when floating | `{}`
+`--paper-input-container-input` | Mixin applied to the input | `{}`
+`--paper-input-container-input-focus` | Mixin applied to the input when focused | `{}`
+`--paper-input-container-input-invalid` | Mixin applied to the input when invalid | `{}`
+`--paper-input-container-input-webkit-spinner` | Mixin applied to the webkit spinner | `{}`
+`--paper-input-container-input-webkit-clear` | Mixin applied to the webkit clear button | `{}`
+`--paper-input-container-ms-clear` | Mixin applied to the Internet Explorer clear button | `{}`
+`--paper-input-container-underline` | Mixin applied to the underline | `{}`
+`--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focused | `{}`
+`--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}`
+`--paper-input-prefix` | Mixin applied to the input prefix | `{}`
+`--paper-input-suffix` | Mixin applied to the input suffix | `{}`
+
+This element is `display:block` by default, but you can set the `inline` attribute to make it
+`display:inline-block`.
+-->
+
+<dom-module id="paper-input-container">
+ <template>
+ <style>
+ :host {
+ display: block;
+ padding: 8px 0;
+
+ @apply(--paper-input-container);
+ }
+
+ :host([inline]) {
+ display: inline-block;
+ }
+
+ :host([disabled]) {
+ pointer-events: none;
+ opacity: 0.33;
+
+ @apply(--paper-input-container-disabled);
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ .floated-label-placeholder {
+ @apply(--paper-font-caption);
+ }
+
+ .underline {
+ height: 2px;
+ position: relative;
+ }
+
+ .focused-line {
+ @apply(--layout-fit);
+
+ border-bottom: 2px solid var(--paper-input-container-focus-color, --primary-color);
+
+ -webkit-transform-origin: center center;
+ transform-origin: center center;
+ -webkit-transform: scale3d(0,1,1);
+ transform: scale3d(0,1,1);
+
+ @apply(--paper-input-container-underline-focus);
+ }
+
+ .underline.is-highlighted .focused-line {
+ -webkit-transform: none;
+ transform: none;
+ -webkit-transition: -webkit-transform 0.25s;
+ transition: transform 0.25s;
+
+ @apply(--paper-transition-easing);
+ }
+
+ .underline.is-invalid .focused-line {
+ border-color: var(--paper-input-container-invalid-color, --error-color);
+ -webkit-transform: none;
+ transform: none;
+ -webkit-transition: -webkit-transform 0.25s;
+ transition: transform 0.25s;
+
+ @apply(--paper-transition-easing);
+ }
+
+ .unfocused-line {
+ @apply(--layout-fit);
+
+ border-bottom: 1px solid var(--paper-input-container-color, --secondary-text-color);
+
+ @apply(--paper-input-container-underline);
+ }
+
+ :host([disabled]) .unfocused-line {
+ border-bottom: 1px dashed;
+ border-color: var(--paper-input-container-color, --secondary-text-color);
+
+ @apply(--paper-input-container-underline-disabled);
+ }
+
+ .label-and-input-container {
+ @apply(--layout-flex-auto);
+ @apply(--layout-relative);
+
+ width: 100%;
+ max-width: 100%;
+ }
+
+ .input-content {
+ @apply(--layout-horizontal);
+ @apply(--layout-center);
+
+ position: relative;
+ }
+
+ .input-content ::content label,
+ .input-content ::content .paper-input-label {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ width: 100%;
+ font: inherit;
+ color: var(--paper-input-container-color, --secondary-text-color);
+ -webkit-transition: -webkit-transform 0.25s, width 0.25s;
+ transition: transform 0.25s, width 0.25s;
+ -webkit-transform-origin: left top;
+ transform-origin: left top;
+
+ @apply(--paper-font-common-nowrap);
+ @apply(--paper-font-subhead);
+ @apply(--paper-input-container-label);
+ @apply(--paper-transition-easing);
+ }
+
+ .input-content.label-is-floating ::content label,
+ .input-content.label-is-floating ::content .paper-input-label {
+ -webkit-transform: translateY(-75%) scale(0.75);
+ transform: translateY(-75%) scale(0.75);
+
+ /* Since we scale to 75/100 of the size, we actually have 100/75 of the
+ original space now available */
+ width: 133%;
+
+ @apply(--paper-input-container-label-floating);
+ }
+
+ :host-context([dir="rtl"]) .input-content.label-is-floating ::content label,
+ :host-context([dir="rtl"]) .input-content.label-is-floating ::content .paper-input-label {
+ /* TODO(noms): Figure out why leaving the width at 133% before the animation
+ * actually makes
+ * it wider on the right side, not left side, as you would expect in RTL */
+ width: 100%;
+ -webkit-transform-origin: right top;
+ transform-origin: right top;
+ }
+
+ .input-content.label-is-highlighted ::content label,
+ .input-content.label-is-highlighted ::content .paper-input-label {
+ color: var(--paper-input-container-focus-color, --primary-color);
+
+ @apply(--paper-input-container-label-focus);
+ }
+
+ .input-content.is-invalid ::content label,
+ .input-content.is-invalid ::content .paper-input-label {
+ color: var(--paper-input-container-invalid-color, --error-color);
+ }
+
+ .input-content.label-is-hidden ::content label,
+ .input-content.label-is-hidden ::content .paper-input-label {
+ visibility: hidden;
+ }
+
+ .input-content ::content input,
+ .input-content ::content textarea,
+ .input-content ::content iron-autogrow-textarea,
+ .input-content ::content .paper-input-input {
+ position: relative; /* to make a stacking context */
+ outline: none;
+ box-shadow: none;
+ padding: 0;
+ width: 100%;
+ max-width: 100%;
+ background: transparent;
+ border: none;
+ color: var(--paper-input-container-input-color, --primary-text-color);
+ -webkit-appearance: none;
+ text-align: inherit;
+ vertical-align: bottom;
+
+ @apply(--paper-font-subhead);
+ @apply(--paper-input-container-input);
+ }
+
+ .input-content.focused ::content input,
+ .input-content.focused ::content textarea,
+ .input-content.focused ::content iron-autogrow-textarea,
+ .input-content.focused ::content .paper-input-input {
+ @apply(--paper-input-container-input-focus);
+ }
+
+ .input-content.is-invalid ::content input,
+ .input-content.is-invalid ::content textarea,
+ .input-content.is-invalid ::content iron-autogrow-textarea,
+ .input-content.is-invalid ::content .paper-input-input {
+ @apply(--paper-input-container-input-invalid);
+ }
+
+ .input-content ::content input::-webkit-outer-spin-button,
+ .input-content ::content input::-webkit-inner-spin-button {
+ @apply(--paper-input-container-input-webkit-spinner);
+ }
+
+ ::content [prefix] {
+ @apply(--paper-font-subhead);
+
+ @apply(--paper-input-prefix);
+ @apply(--layout-flex-none);
+ }
+
+ ::content [suffix] {
+ @apply(--paper-font-subhead);
+
+ @apply(--paper-input-suffix);
+ @apply(--layout-flex-none);
+ }
+
+ /* Firefox sets a min-width on the input, which can cause layout issues */
+ .input-content ::content input {
+ min-width: 0;
+ }
+
+ .input-content ::content textarea {
+ resize: none;
+ }
+
+ .add-on-content {
+ position: relative;
+ }
+
+ .add-on-content.is-invalid ::content * {
+ color: var(--paper-input-container-invalid-color, --error-color);
+ }
+
+ .add-on-content.is-highlighted ::content * {
+ color: var(--paper-input-container-focus-color, --primary-color);
+ }
+ </style>
+
+ <template is="dom-if" if="[[!noLabelFloat]]">
+ <div class="floated-label-placeholder" aria-hidden="true">&nbsp;</div>
+ </template>
+
+ <div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]">
+ <content select="[prefix]" id="prefix"></content>
+
+ <div class="label-and-input-container" id="labelAndInputContainer">
+ <content select=":not([add-on]):not([prefix]):not([suffix])"></content>
+ </div>
+
+ <content select="[suffix]"></content>
+ </div>
+
+ <div class$="[[_computeUnderlineClass(focused,invalid)]]">
+ <div class="unfocused-line"></div>
+ <div class="focused-line"></div>
+ </div>
+
+ <div class$="[[_computeAddOnContentClass(focused,invalid)]]">
+ <content id="addOnContent" select="[add-on]"></content>
+ </div>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'paper-input-container',
+
+ properties: {
+ /**
+ * Set to true to disable the floating label. The label disappears when the input value is
+ * not null.
+ */
+ noLabelFloat: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to always float the floating label.
+ */
+ alwaysFloatLabel: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The attribute to listen for value changes on.
+ */
+ attrForValue: {
+ type: String,
+ value: 'bind-value'
+ },
+
+ /**
+ * Set to true to auto-validate the input value when it changes.
+ */
+ autoValidate: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * True if the input is invalid. This property is set automatically when the input value
+ * changes if auto-validating, or when the `iron-input-validate` event is heard from a child.
+ */
+ invalid: {
+ observer: '_invalidChanged',
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * True if the input has focus.
+ */
+ focused: {
+ readOnly: true,
+ type: Boolean,
+ value: false,
+ notify: true
+ },
+
+ _addons: {
+ type: Array
+ // do not set a default value here intentionally - it will be initialized lazily when a
+ // distributed child is attached, which may occur before configuration for this element
+ // in polyfill.
+ },
+
+ _inputHasContent: {
+ type: Boolean,
+ value: false
+ },
+
+ _inputSelector: {
+ type: String,
+ value: 'input,textarea,.paper-input-input'
+ },
+
+ _boundOnFocus: {
+ type: Function,
+ value: function() {
+ return this._onFocus.bind(this);
+ }
+ },
+
+ _boundOnBlur: {
+ type: Function,
+ value: function() {
+ return this._onBlur.bind(this);
+ }
+ },
+
+ _boundOnInput: {
+ type: Function,
+ value: function() {
+ return this._onInput.bind(this);
+ }
+ },
+
+ _boundValueChanged: {
+ type: Function,
+ value: function() {
+ return this._onValueChanged.bind(this);
+ }
+ }
+ },
+
+ listeners: {
+ 'addon-attached': '_onAddonAttached',
+ 'iron-input-validate': '_onIronInputValidate'
+ },
+
+ get _valueChangedEvent() {
+ return this.attrForValue + '-changed';
+ },
+
+ get _propertyForValue() {
+ return Polymer.CaseMap.dashToCamelCase(this.attrForValue);
+ },
+
+ get _inputElement() {
+ return Polymer.dom(this).querySelector(this._inputSelector);
+ },
+
+ get _inputElementValue() {
+ return this._inputElement[this._propertyForValue] || this._inputElement.value;
+ },
+
+ ready: function() {
+ if (!this._addons) {
+ this._addons = [];
+ }
+ this.addEventListener('focus', this._boundOnFocus, true);
+ this.addEventListener('blur', this._boundOnBlur, true);
+ },
+
+ attached: function() {
+ if (this.attrForValue) {
+ this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged);
+ } else {
+ this.addEventListener('input', this._onInput);
+ }
+
+ // Only validate when attached if the input already has a value.
+ if (this._inputElementValue != '') {
+ this._handleValueAndAutoValidate(this._inputElement);
+ } else {
+ this._handleValue(this._inputElement);
+ }
+ },
+
+ _onAddonAttached: function(event) {
+ if (!this._addons) {
+ this._addons = [];
+ }
+ var target = event.target;
+ if (this._addons.indexOf(target) === -1) {
+ this._addons.push(target);
+ if (this.isAttached) {
+ this._handleValue(this._inputElement);
+ }
+ }
+ },
+
+ _onFocus: function() {
+ this._setFocused(true);
+ },
+
+ _onBlur: function() {
+ this._setFocused(false);
+ this._handleValueAndAutoValidate(this._inputElement);
+ },
+
+ _onInput: function(event) {
+ this._handleValueAndAutoValidate(event.target);
+ },
+
+ _onValueChanged: function(event) {
+ this._handleValueAndAutoValidate(event.target);
+ },
+
+ _handleValue: function(inputElement) {
+ var value = this._inputElementValue;
+
+ // type="number" hack needed because this.value is empty until it's valid
+ if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) {
+ this._inputHasContent = true;
+ } else {
+ this._inputHasContent = false;
+ }
+
+ this.updateAddons({
+ inputElement: inputElement,
+ value: value,
+ invalid: this.invalid
+ });
+ },
+
+ _handleValueAndAutoValidate: function(inputElement) {
+ if (this.autoValidate) {
+ var valid;
+ if (inputElement.validate) {
+ valid = inputElement.validate(this._inputElementValue);
+ } else {
+ valid = inputElement.checkValidity();
+ }
+ this.invalid = !valid;
+ }
+
+ // Call this last to notify the add-ons.
+ this._handleValue(inputElement);
+ },
+
+ _onIronInputValidate: function(event) {
+ this.invalid = this._inputElement.invalid;
+ },
+
+ _invalidChanged: function() {
+ if (this._addons) {
+ this.updateAddons({invalid: this.invalid});
+ }
+ },
+
+ /**
+ * Call this to update the state of add-ons.
+ * @param {Object} state Add-on state.
+ */
+ updateAddons: function(state) {
+ for (var addon, index = 0; addon = this._addons[index]; index++) {
+ addon.update(state);
+ }
+ },
+
+ _computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) {
+ var cls = 'input-content';
+ if (!noLabelFloat) {
+ var label = this.querySelector('label');
+
+ if (alwaysFloatLabel || _inputHasContent) {
+ cls += ' label-is-floating';
+ // If the label is floating, ignore any offsets that may have been
+ // applied from a prefix element.
+ this.$.labelAndInputContainer.style.position = 'static';
+
+ if (invalid) {
+ cls += ' is-invalid';
+ } else if (focused) {
+ cls += " label-is-highlighted";
+ }
+ } else {
+ // When the label is not floating, it should overlap the input element.
+ if (label) {
+ this.$.labelAndInputContainer.style.position = 'relative';
+ }
+ if (invalid) {
+ cls += ' is-invalid';
+ }
+ }
+ } else {
+ if (_inputHasContent) {
+ cls += ' label-is-hidden';
+ }
+ if (invalid) {
+ cls += ' is-invalid';
+ }
+ }
+ if (focused) {
+ cls += ' focused';
+ }
+ return cls;
+ },
+
+ _computeUnderlineClass: function(focused, invalid) {
+ var cls = 'underline';
+ if (invalid) {
+ cls += ' is-invalid';
+ } else if (focused) {
+ cls += ' is-highlighted'
+ }
+ return cls;
+ },
+
+ _computeAddOnContentClass: function(focused, invalid) {
+ var cls = 'add-on-content';
+ if (invalid) {
+ cls += ' is-invalid';
+ } else if (focused) {
+ cls += ' is-highlighted'
+ }
+ return cls;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/paper-input-error.html b/catapult/third_party/polymer/components/paper-input/paper-input-error.html
new file mode 100644
index 00000000..645f1e72
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-input-error.html
@@ -0,0 +1,94 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../paper-styles/typography.html">
+<link rel="import" href="paper-input-addon-behavior.html">
+
+<!--
+`<paper-input-error>` is an error message for use with `<paper-input-container>`. The error is
+displayed when the `<paper-input-container>` is `invalid`.
+
+ <paper-input-container>
+ <input is="iron-input" pattern="[0-9]*">
+ <paper-input-error>Only numbers are allowed!</paper-input-error>
+ </paper-input-container>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-input-container-invalid-color` | The foreground color of the error | `--error-color`
+`--paper-input-error` | Mixin applied to the error | `{}`
+-->
+
+<dom-module id="paper-input-error">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ visibility: hidden;
+
+ color: var(--paper-input-container-invalid-color, --error-color);
+
+ @apply(--paper-font-caption);
+ @apply(--paper-input-error);
+ position: absolute;
+ left:0;
+ right:0;
+ }
+
+ :host([invalid]) {
+ visibility: visible;
+ };
+ </style>
+
+ <content></content>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'paper-input-error',
+
+ behaviors: [
+ Polymer.PaperInputAddonBehavior
+ ],
+
+ properties: {
+ /**
+ * True if the error is showing.
+ */
+ invalid: {
+ readOnly: true,
+ reflectToAttribute: true,
+ type: Boolean
+ }
+ },
+
+ /**
+ * This overrides the update function in PaperInputAddonBehavior.
+ * @param {{
+ * inputElement: (Element|undefined),
+ * value: (string|undefined),
+ * invalid: boolean
+ * }} state -
+ * inputElement: The input element.
+ * value: The input value.
+ * invalid: True if the input value is invalid.
+ */
+ update: function(state) {
+ this._setInvalid(state.invalid);
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/paper-input.html b/catapult/third_party/polymer/components/paper-input/paper-input.html
new file mode 100644
index 00000000..3eb0024e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-input.html
@@ -0,0 +1,183 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
+<link rel="import" href="../iron-input/iron-input.html">
+<link rel="import" href="paper-input-behavior.html">
+<link rel="import" href="paper-input-char-counter.html">
+<link rel="import" href="paper-input-container.html">
+<link rel="import" href="paper-input-error.html">
+
+<!--
+Material design: [Text fields](https://www.google.com/design/spec/components/text-fields.html)
+
+`<paper-input>` is a single-line text field with Material Design styling.
+
+ <paper-input label="Input label"></paper-input>
+
+It may include an optional error message or character counter.
+
+ <paper-input error-message="Invalid input!" label="Input label"></paper-input>
+ <paper-input char-counter label="Input label"></paper-input>
+
+It can also include custom prefix or suffix elements, which are displayed
+before or after the text input itself. In order for an element to be
+considered as a prefix, it must have the `prefix` attribute (and similarly
+for `suffix`).
+
+ <paper-input label="total">
+ <div prefix>$</div>
+ <paper-icon-button suffix icon="clear"></paper-icon-button>
+ </paper-input>
+
+A `paper-input` can use the native `type=search` or `type=file` features.
+However, since we can't control the native styling of the input (search icon,
+file button, date placeholder, etc.), in these cases the label will be
+automatically floated. The `placeholder` attribute can still be used for
+additional informational text.
+
+ <paper-input label="search!" type="search"
+ placeholder="search for cats" autosave="test" results="5">
+ </paper-input>
+
+See `Polymer.PaperInputBehavior` for more API docs.
+
+### Focus
+
+To focus a paper-input, you can call the native `focus()` method as long as the
+paper input has a tab index.
+
+### Styling
+
+See `Polymer.PaperInputContainer` for a list of custom properties used to
+style this element.
+
+
+@group Paper Elements
+@element paper-input
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-input">
+ <template>
+ <style>
+ :host {
+ display: block;
+ }
+
+ :host([focused]) {
+ outline: none;
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ input::-webkit-outer-spin-button,
+ input::-webkit-inner-spin-button {
+ @apply(--paper-input-container-input-webkit-spinner);
+ }
+
+ input::-webkit-clear-button {
+ @apply(--paper-input-container-input-webkit-clear);
+ }
+
+ input::-webkit-input-placeholder {
+ color: var(--paper-input-container-color, --secondary-text-color);
+ }
+
+ input:-moz-placeholder {
+ color: var(--paper-input-container-color, --secondary-text-color);
+ }
+
+ input::-moz-placeholder {
+ color: var(--paper-input-container-color, --secondary-text-color);
+ }
+
+ input::-ms-clear {
+ @apply(--paper-input-container-ms-clear);
+ }
+
+ input:-ms-input-placeholder {
+ color: var(--paper-input-container-color, --secondary-text-color);
+ }
+
+ label {
+ pointer-events: none;
+ }
+ </style>
+
+ <paper-input-container no-label-float="[[noLabelFloat]]" always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]" auto-validate$="[[autoValidate]]" disabled$="[[disabled]]" invalid="[[invalid]]">
+
+ <content select="[prefix]"></content>
+
+ <label hidden$="[[!label]]" aria-hidden="true" for="input">[[label]]</label>
+
+ <input is="iron-input" id="input"
+ aria-labelledby$="[[_ariaLabelledBy]]"
+ aria-describedby$="[[_ariaDescribedBy]]"
+ disabled$="[[disabled]]"
+ title$="[[title]]"
+ bind-value="{{value}}"
+ invalid="{{invalid}}"
+ prevent-invalid-input="[[preventInvalidInput]]"
+ allowed-pattern="[[allowedPattern]]"
+ validator="[[validator]]"
+ type$="[[type]]"
+ pattern$="[[pattern]]"
+ required$="[[required]]"
+ autocomplete$="[[autocomplete]]"
+ autofocus$="[[autofocus]]"
+ inputmode$="[[inputmode]]"
+ minlength$="[[minlength]]"
+ maxlength$="[[maxlength]]"
+ min$="[[min]]"
+ max$="[[max]]"
+ step$="[[step]]"
+ name$="[[name]]"
+ placeholder$="[[placeholder]]"
+ readonly$="[[readonly]]"
+ list$="[[list]]"
+ size$="[[size]]"
+ autocapitalize$="[[autocapitalize]]"
+ autocorrect$="[[autocorrect]]"
+ on-change="_onChange"
+ tabindex$="[[tabindex]]"
+ autosave$="[[autosave]]"
+ results$="[[results]]"
+ accept$="[[accept]]"
+ multiple$="[[multiple]]">
+
+ <content select="[suffix]"></content>
+
+ <template is="dom-if" if="[[errorMessage]]">
+ <paper-input-error aria-live="assertive">[[errorMessage]]</paper-input-error>
+ </template>
+
+ <template is="dom-if" if="[[charCounter]]">
+ <paper-input-char-counter></paper-input-char-counter>
+ </template>
+
+ </paper-input-container>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'paper-input',
+
+ behaviors: [
+ Polymer.IronFormElementBehavior,
+ Polymer.PaperInputBehavior
+ ]
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/paper-textarea.html b/catapult/third_party/polymer/components/paper-input/paper-textarea.html
new file mode 100644
index 00000000..cfc06520
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-textarea.html
@@ -0,0 +1,145 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
+<link rel="import" href="paper-input-behavior.html">
+<link rel="import" href="paper-input-char-counter.html">
+<link rel="import" href="paper-input-container.html">
+<link rel="import" href="paper-input-error.html">
+
+<!--
+`<paper-textarea>` is a multi-line text field with Material Design styling.
+
+ <paper-textarea label="Textarea label"></paper-textarea>
+
+See `Polymer.PaperInputBehavior` for more API docs.
+
+### Validation
+
+Currently only `required` and `maxlength` validation is supported.
+
+### Styling
+
+See `Polymer.PaperInputContainer` for a list of custom properties used to
+style this element.
+-->
+
+<dom-module id="paper-textarea">
+ <template>
+ <style>
+ :host {
+ display: block;
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ label {
+ pointer-events: none;
+ }
+ </style>
+
+ <paper-input-container no-label-float$="[[noLabelFloat]]" always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]" auto-validate$="[[autoValidate]]" disabled$="[[disabled]]" invalid="[[invalid]]">
+
+ <label hidden$="[[!label]]" aria-hidden="true">[[label]]</label>
+
+ <iron-autogrow-textarea id="input" class="paper-input-input"
+ bind-value="{{value}}"
+ invalid="{{invalid}}"
+ validator$="[[validator]]"
+ disabled$="[[disabled]]"
+ autocomplete$="[[autocomplete]]"
+ autofocus$="[[autofocus]]"
+ inputmode$="[[inputmode]]"
+ name$="[[name]]"
+ placeholder$="[[placeholder]]"
+ readonly$="[[readonly]]"
+ required$="[[required]]"
+ minlength$="[[minlength]]"
+ maxlength$="[[maxlength]]"
+ autocapitalize$="[[autocapitalize]]"
+ rows$="[[rows]]"
+ max-rows$="[[maxRows]]"
+ on-change="_onChange"></iron-autogrow-textarea>
+
+ <template is="dom-if" if="[[errorMessage]]">
+ <paper-input-error>[[errorMessage]]</paper-input-error>
+ </template>
+
+ <template is="dom-if" if="[[charCounter]]">
+ <paper-input-char-counter></paper-input-char-counter>
+ </template>
+
+ </paper-input-container>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'paper-textarea',
+
+ behaviors: [
+ Polymer.PaperInputBehavior,
+ Polymer.IronFormElementBehavior
+ ],
+
+ properties: {
+ _ariaLabelledBy: {
+ observer: '_ariaLabelledByChanged',
+ type: String
+ },
+
+ _ariaDescribedBy: {
+ observer: '_ariaDescribedByChanged',
+ type: String
+ },
+
+ /**
+ * The initial number of rows.
+ *
+ * @attribute rows
+ * @type number
+ * @default 1
+ */
+ rows: {
+ type: Number,
+ value: 1
+ },
+
+ /**
+ * The maximum number of rows this element can grow to until it
+ * scrolls. 0 means no maximum.
+ *
+ * @attribute maxRows
+ * @type number
+ * @default 0
+ */
+ maxRows: {
+ type: Number,
+ value: 0
+ }
+ },
+
+ _ariaLabelledByChanged: function(ariaLabelledBy) {
+ this.$.input.textarea.setAttribute('aria-labelledby', ariaLabelledBy);
+ },
+
+ _ariaDescribedByChanged: function(ariaDescribedBy) {
+ this.$.input.textarea.setAttribute('aria-describedby', ariaDescribedBy);
+ },
+
+ get _focusableElement() {
+ return this.$.input.textarea;
+ },
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/test/index.html b/catapult/third_party/polymer/components/paper-input/test/index.html
new file mode 100644
index 00000000..48aa82fd
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/test/index.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2014 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-input tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'paper-input.html',
+ 'paper-textarea.html',
+ 'paper-input-container.html',
+ 'paper-input-error.html',
+ 'paper-input-char-counter.html',
+ 'paper-input.html?dom=shadow',
+ 'paper-textarea.html?dom=shadow',
+ 'paper-input-container.html?dom=shadow',
+ 'paper-input-error.html?dom=shadow',
+ 'paper-input-char-counter.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-input/test/letters-only.html b/catapult/third_party/polymer/components/paper-input/test/letters-only.html
new file mode 100644
index 00000000..bfc301c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/test/letters-only.html
@@ -0,0 +1,30 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../iron-validator-behavior/iron-validator-behavior.html">
+
+<script>
+
+ Polymer({
+
+ is: 'letters-only',
+
+ behaviors: [
+ Polymer.IronValidatorBehavior
+ ],
+
+ validate: function(value) {
+ return !value || value.match(/^[a-zA-Z]*$/) !== null;
+ }
+
+ });
+
+</script>
diff --git a/catapult/third_party/polymer/components/paper-input/test/paper-input-char-counter.html b/catapult/third_party/polymer/components/paper-input/test/paper-input-char-counter.html
new file mode 100644
index 00000000..65d2d140
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/test/paper-input-char-counter.html
@@ -0,0 +1,109 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-input-counter tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/test-helpers.js"></script>
+
+ <link rel="import" href="../../iron-input/iron-input.html">
+ <link rel="import" href="../paper-input-container.html">
+ <link rel="import" href="../paper-input-char-counter.html">
+ <link rel="import" href="../paper-textarea.html">
+
+</head>
+<body>
+
+ <test-fixture id="counter">
+ <template>
+ <paper-input-container>
+ <label id="l">label</label>
+ <input id="i" value="foobar">
+ <paper-input-char-counter id="c"></paper-input-char-counter>
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="counter-with-max">
+ <template>
+ <paper-input-container>
+ <label id="l">label</label>
+ <input id="i" value="foobar" maxlength="10">
+ <paper-input-char-counter id="c"></paper-input-char-counter>
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="textarea">
+ <template>
+ <paper-textarea char-counter value="foobar"></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="textarea-with-max">
+ <template>
+ <paper-textarea char-counter value="foobar" maxlength="100"></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('character counter shows the value length', function() {
+ var container = fixture('counter');
+ var input = Polymer.dom(container).querySelector('#i');
+ var counter = Polymer.dom(container).querySelector('#c');
+ assert.equal(counter._charCounterStr, input.value.length, 'character counter shows input value length');
+ });
+
+ test('character counter shows the value length with maxlength', function() {
+ var container = fixture('counter-with-max');
+ var input = Polymer.dom(container).querySelector('#i');
+ var counter = Polymer.dom(container).querySelector('#c');
+ assert.equal(counter._charCounterStr, input.value.length + '/' + input.maxLength, 'character counter shows input value length and maxLength');
+ });
+
+ test('character counter shows the value length with maxlength', function() {
+ var input = fixture('textarea-with-max');
+ forceXIfStamp(input);
+
+ var counter = Polymer.dom(input.root).querySelector('paper-input-char-counter');
+ assert.ok(counter, 'paper-input-char-counter exists');
+
+ assert.equal(counter._charCounterStr, input.value.length + '/' + input.inputElement.textarea.getAttribute('maxlength'), 'character counter shows input value length and maxLength');
+ });
+
+ test('character counter counts new lines in textareas correctly', function() {
+ var input = fixture('textarea');
+ input.value = 'foo\nbar';
+ forceXIfStamp(input);
+
+ var counter = Polymer.dom(input.root).querySelector('paper-input-char-counter')
+ assert.ok(counter, 'paper-input-char-counter exists');
+
+ assert.equal(counter._charCounterStr, input.value.length, 'character counter shows the value length');
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-input/test/paper-input-container.html b/catapult/third_party/polymer/components/paper-input/test/paper-input-container.html
new file mode 100644
index 00000000..6af730d2
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/test/paper-input-container.html
@@ -0,0 +1,333 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-input-container tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../iron-input/iron-input.html">
+ <link rel="import" href="../paper-input-container.html">
+ <link rel="import" href="letters-only.html">
+
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-input-container>
+ <label id="l">label</label>
+ <input id="i">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="prefix">
+ <template>
+ <paper-input-container>
+ <div prefix>$</div>
+ <label id="l">label</label>
+ <input is="iron-input" id="i">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="prefix-has-value">
+ <template>
+ <paper-input-container>
+ <div prefix>$</div>
+ <label id="l">label</label>
+ <input is="iron-input" id="i" value="foo">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="has-value">
+ <template>
+ <paper-input-container>
+ <label id="l">label</label>
+ <input id="i" value="value">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="no-float-has-value">
+ <template>
+ <paper-input-container no-label-float>
+ <label id="l">label</label>
+ <input id="i" value="value">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="always-float">
+ <template>
+ <paper-input-container always-float-label>
+ <label id="l">label</label>
+ <input id="i" value="value">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="auto-validate-numbers">
+ <template>
+ <paper-input-container auto-validate>
+ <label id="l">label</label>
+ <input is="iron-input" id="i" pattern="[0-9]*">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="manual-validate-numbers">
+ <template>
+ <paper-input-container>
+ <label id="l">label</label>
+ <input is="iron-input" id="i" pattern="[0-9]*">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="required-validate">
+ <template>
+ <paper-input-container>
+ <label id="l">label</label>
+ <input is="iron-input" id="i" required>
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <letters-only></letters-only>
+
+ <test-fixture id="auto-validate-validator">
+ <template>
+ <paper-input-container auto-validate>
+ <label id="l">label</label>
+ <input is="iron-input" id="i" pattern="[0-9]*" validator="letters-only">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="auto-validate-validator-has-invalid-value">
+ <template>
+ <paper-input-container auto-validate>
+ <label id="l">label</label>
+ <input is="iron-input" id="i" validator="letters-only" value="123123">
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ function getTransform(node) {
+ var style = getComputedStyle(node);
+ return style.transform || style.webkitTransform;
+ }
+
+ suite('basic', function() {
+ test('can be created imperatively', function() {
+ var container = document.createElement('paper-input-container');
+ var input = document.createElement('input', 'iron-input');
+ input.className = 'paper-input-input';
+ input.id = 'input';
+
+ var label = document.createElement('label');
+ label.innerHTML = 'label';
+
+ Polymer.dom(container).appendChild(label);
+ Polymer.dom(container).appendChild(input);
+
+ document.body.appendChild(container);
+ assert.isOk(container);
+ document.body.removeChild(container);
+ });
+ });
+
+ suite('label position', function() {
+
+ test('label is visible by default', function() {
+ var container = fixture('basic');
+ assert.equal(getComputedStyle(container.querySelector('#l')).visibility, 'visible', 'label has visibility:visible');
+ });
+
+ test('label is floated if value is initialized to not null', function(done) {
+ var container = fixture('has-value');
+ requestAnimationFrame(function() {
+ assert.notEqual(getTransform(container.querySelector('#l')), 'none', 'label has transform');
+ done();
+ });
+ });
+
+ test('label is invisible if no-label-float and value is initialized to not null', function() {
+ var container = fixture('no-float-has-value');
+ assert.equal(getComputedStyle(container.querySelector('#l')).visibility, 'hidden', 'label has visibility:hidden');
+ });
+
+ test('label is floated if always-float-label is true', function() {
+ var container = fixture('always-float');
+ assert.notEqual(getTransform(container.querySelector('#l')), 'none', 'label has transform');
+ });
+
+ test('label is floated correctly with a prefix', function(done) {
+ var container = fixture('prefix');
+ var label = Polymer.dom(container).querySelector('#l');
+ var input = Polymer.dom(container).querySelector('#i');
+
+ // Label is initially visible.
+ assert.equal(getComputedStyle(label).visibility, 'visible', 'label has visibility:visible');
+
+ // After entering text, the label floats, and it is not indented.
+ input.bindValue = 'foobar';
+ requestAnimationFrame(function() {
+ assert.notEqual(getTransform(label), 'none', 'label has transform');
+ assert.equal(label.getBoundingClientRect().left, container.getBoundingClientRect().left);
+ done();
+ });
+ });
+
+ test('label is floated correctly with a prefix and prefilled value', function(done) {
+ var container = fixture('prefix-has-value');
+ var label = Polymer.dom(container).querySelector('#l');
+
+ // The label floats, and it is not indented.
+ requestAnimationFrame(function() {
+ assert.notEqual(getTransform(label), 'none', 'label has transform');
+ assert.equal(label.getBoundingClientRect().left, container.getBoundingClientRect().left);
+ done();
+ });
+ });
+
+ });
+
+ suite('focused styling', function() {
+
+ test('label is colored when input is focused and has value', function(done) {
+ var container = fixture('has-value');
+ var label = Polymer.dom(container).querySelector('#l');
+ var input = Polymer.dom(container).querySelector('#i');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ MockInteractions.focus(input);
+ requestAnimationFrame(function() {
+ assert.isTrue(container.focused, 'focused is true');
+ assert.isTrue(inputContent.classList.contains('label-is-highlighted'), 'label is highlighted when input has focus');
+ done();
+ });
+ });
+
+ test('label is not colored when input is focused and has null value', function(done) {
+ var container = fixture('basic');
+ var label = Polymer.dom(container).querySelector('#l');
+ var input = Polymer.dom(container).querySelector('#i');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ MockInteractions.focus(input);
+ requestAnimationFrame(function() {
+ assert.isFalse(inputContent.classList.contains('label-is-highlighted'), 'label is not highlighted when input has focus and has null value');
+ done();
+ });
+ });
+
+ test('underline is colored when input is focused', function(done) {
+ var container = fixture('basic');
+ var input = Polymer.dom(container).querySelector('#i');
+ var line = Polymer.dom(container.root).querySelector('.underline');
+ assert.isFalse(line.classList.contains('is-highlighted'), 'line is not highlighted when input is not focused');
+ MockInteractions.focus(input);
+ requestAnimationFrame(function() {
+ assert.isTrue(line.classList.contains('is-highlighted'), 'line is highlighted when input is focused');
+ done();
+ });
+ });
+
+ test('focused class added to input content', function(done) {
+ var container = fixture('basic');
+ var input = Polymer.dom(container).querySelector('#i');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ assert.isFalse(inputContent.classList.contains('focused'), 'input content does not have class "focused" when input is not focused');
+ MockInteractions.focus(input);
+ requestAnimationFrame(function() {
+ assert.isTrue(inputContent.classList.contains('focused'), 'input content has class "focused" when input is focused');
+ done();
+ });
+ });
+
+ });
+
+ suite('validation', function() {
+
+ test('styled when the input is set to an invalid value with auto-validate', function() {
+ var container = fixture('auto-validate-numbers');
+ var input = Polymer.dom(container).querySelector('#i');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ var line = Polymer.dom(container.root).querySelector('.underline');
+
+ input.bindValue = 'foobar';
+
+ assert.isTrue(container.invalid, 'invalid is true');
+ assert.isTrue(inputContent.classList.contains('is-invalid'), 'label has invalid styling when input is invalid');
+ assert.isTrue(line.classList.contains('is-invalid'), 'underline has invalid styling when input is invalid');
+ });
+
+ test('styled when the input is set to an invalid value with auto-validate, with validator', function() {
+ var container = fixture('auto-validate-validator');
+ var input = Polymer.dom(container).querySelector('#i');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ var line = Polymer.dom(container.root).querySelector('.underline');
+
+ input.bindValue = '123123';
+
+ assert.isTrue(container.invalid, 'invalid is true');
+ assert.isTrue(inputContent.classList.contains('is-invalid'), 'label has invalid styling when input is invalid');
+ assert.isTrue(line.classList.contains('is-invalid'), 'underline has invalid styling when input is invalid');
+ });
+
+ test('styled when the input is set initially to an invalid value with auto-validate, with validator', function() {
+ var container = fixture('auto-validate-validator-has-invalid-value');
+ assert.isTrue(container.invalid, 'invalid is true');
+ assert.isTrue(Polymer.dom(container.root).querySelector('.underline').classList.contains('is-invalid'), 'underline has is-invalid class');
+ });
+
+ test('styled when the input is set to an invalid value with manual validation', function() {
+ var container = fixture('manual-validate-numbers');
+ var input = Polymer.dom(container).querySelector('#i');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ var line = Polymer.dom(container.root).querySelector('.underline');
+
+ input.bindValue = 'foobar';
+ input.validate();
+
+ assert.isTrue(container.invalid, 'invalid is true');
+ assert.isTrue(inputContent.classList.contains('is-invalid'), 'label has invalid styling when input is invalid');
+ assert.isTrue(line.classList.contains('is-invalid'), 'underline has invalid styling when input is invalid');
+ });
+
+ test('styled when the input is manually validated and required', function() {
+ var container = fixture('required-validate');
+ var input = Polymer.dom(container).querySelector('#i');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ assert.isFalse(container.invalid, 'invalid is false');
+ input.validate();
+ assert.isTrue(container.invalid, 'invalid is true');
+ assert.isTrue(inputContent.classList.contains('is-invalid'), 'input content has is-invalid class');
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-input/test/paper-input-error.html b/catapult/third_party/polymer/components/paper-input/test/paper-input-error.html
new file mode 100644
index 00000000..9337a95e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/test/paper-input-error.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-input-error tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../../iron-input/iron-input.html">
+ <link rel="import" href="../paper-input-container.html">
+ <link rel="import" href="../paper-input-error.html">
+
+</head>
+<body>
+
+ <paper-input-container id="container">
+ <input is="iron-input">
+ <paper-input-error>error</paper-input-error>
+ </paper-input-container>
+
+ <test-fixture id="auto-validate-numbers">
+ <template>
+ <paper-input-container auto-validate attr-for-value="bind-value">
+ <label id="l">label</label>
+ <input is="iron-input" id="i" pattern="[0-9]*">
+ <paper-input-error id="e">error</paper-input-error>
+ </paper-input-container>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('error message only appears when input is invalid', function() {
+ var container = fixture('auto-validate-numbers');
+ var input = Polymer.dom(container).querySelector('#i');
+ var error = Polymer.dom(container).querySelector('#e');
+ assert.equal(getComputedStyle(error).visibility, 'hidden', 'error is visibility:hidden');
+ input.bindValue = 'foobar';
+ assert.notEqual(getComputedStyle(error).visibility, 'hidden', 'error is not visibility:hidden');
+ });
+
+ test('error message add on is registered', function() {
+ var container = document.getElementById('container');
+ assert.isTrue(container._addons && container._addons.length === 1, 'add on is registered');
+ });
+
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-input/test/paper-input.html b/catapult/third_party/polymer/components/paper-input/test/paper-input.html
new file mode 100644
index 00000000..77f68c58
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/test/paper-input.html
@@ -0,0 +1,398 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-input tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/test-helpers.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../paper-input.html">
+ <link rel="import" href="letters-only.html">
+
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-input></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="has-tabindex">
+ <template>
+ <paper-input tabindex="0"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="label">
+ <template>
+ <paper-input label="foo"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="label-has-value">
+ <template>
+ <paper-input label="foo" value="bar"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="error">
+ <template>
+ <paper-input auto-validate pattern="[0-9]*" value="foobar" error-message="error"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="required">
+ <template>
+ <paper-input auto-validate required error-message="error"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="required-no-auto-validate">
+ <template>
+ <paper-input required error-message="error"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="required-char-counter">
+ <template>
+ <paper-input auto-validate char-counter required error-message="error"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="char-counter">
+ <template>
+ <paper-input char-counter value="foobar"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="type-number-char-counter">
+ <template>
+ <paper-input type="number" char-counter value="1138"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="always-float-label">
+ <template>
+ <paper-input always-float-label label="foo"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="placeholder">
+ <template>
+ <paper-input label="foo" placeholder="bar"></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="date">
+ <template>
+ <paper-input label="foo" type="date"></paper-input>
+ </template>
+ </test-fixture>
+
+ <letters-only></letters-only>
+
+ <test-fixture id="validator">
+ <template>
+ <paper-input value="123123" validator="letters-only" auto-validate></paper-input>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="multiple-inputs">
+ <template>
+ <paper-input label="one"></paper-input>
+ <paper-input label="two"></paper-input>
+ <paper-input label="three"></paper-input>
+ <paper-input label="four"></paper-input>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('setting value sets the input value', function() {
+ var input = fixture('basic');
+ input.value = 'foobar';
+ assert.equal(input.inputElement.value, input.value, 'inputElement.value equals input.value');
+ });
+
+ test('placeholder does not overlap label', function() {
+ var input = fixture('placeholder');
+ assert.equal(input.inputElement.placeholder, input.placeholder, 'inputElement.placeholder equals input.placeholder');
+ assert.equal(input.noLabelFloat, false);
+ var floatingLabel = Polymer.dom(Polymer.dom(input.root).querySelector('paper-input-container').root).querySelector('.label-is-floating');
+ assert.ok(floatingLabel);
+ });
+
+ test('special types autofloat the label', function() {
+ var input = fixture('date');
+ // Browsers that don't support special <input> types like `date` fallback
+ // to `text`, so make sure to only test if type is still preserved after
+ // the element is attached.
+ if (input.inputElement.type === "date") {
+ assert.equal(input.alwaysFloatLabel, true);
+ var floatingLabel = Polymer.dom(Polymer.dom(input.root).querySelector('paper-input-container').root).querySelector('.label-is-floating');
+ assert.ok(floatingLabel);
+ }
+ });
+
+ test('always-float-label attribute works without placeholder', function() {
+ var input = fixture('always-float-label');
+ var container = Polymer.dom(input.root).querySelector('paper-input-container');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ assert.isTrue(inputContent.classList.contains('label-is-floating'), 'label is floating');
+ });
+
+ test('label does not receive pointer events', function() {
+ var input = fixture('always-float-label');
+ var label = Polymer.dom(input.root).querySelector('label');
+ assert.equal(getComputedStyle(label).pointerEvents, 'none');
+ });
+
+ test('error message is displayed', function() {
+ var input = fixture('error');
+ forceXIfStamp(input);
+ var error = Polymer.dom(input.root).querySelector('paper-input-error');
+ assert.ok(error, 'paper-input-error exists');
+ assert.notEqual(getComputedStyle(error).display, 'none', 'error is not display:none');
+ });
+
+ test('empty required input shows error', function() {
+ var input = fixture('required');
+ forceXIfStamp(input);
+ var error = Polymer.dom(input.root).querySelector('paper-input-error');
+ assert.ok(error, 'paper-input-error exists');
+ assert.notEqual(getComputedStyle(error).display, 'none', 'error is not display:none');
+ });
+
+ test('character counter is displayed', function() {
+ var input = fixture('char-counter');
+ forceXIfStamp(input);
+ var counter = Polymer.dom(input.root).querySelector('paper-input-char-counter')
+ assert.ok(counter, 'paper-input-char-counter exists');
+ assert.equal(counter._charCounterStr, input.value.length, 'character counter shows the value length');
+ });
+
+ test('character counter is correct for type=number', function() {
+ var input = fixture('type-number-char-counter');
+ forceXIfStamp(input);
+ var counter = Polymer.dom(input.root).querySelector('paper-input-char-counter')
+ assert.ok(counter, 'paper-input-char-counter exists');
+ assert.equal(counter._charCounterStr, input.value.toString().length, 'character counter shows the value length');
+ });
+
+ test('validator is used', function() {
+ var input = fixture('validator');
+ assert.ok(input.inputElement.invalid, 'input is invalid');
+ });
+
+ test('caret position is preserved', function() {
+ var input = fixture('basic');
+ var ironInput = Polymer.dom(input.root).querySelector('input[is="iron-input"]');
+ input.value = 'nananana';
+ ironInput.selectionStart = 2;
+ ironInput.selectionEnd = 2;
+
+ input.updateValueAndPreserveCaret('nanananabatman');
+
+ assert.equal(ironInput.selectionStart, 2, 'selectionStart is preserved');
+ assert.equal(ironInput.selectionEnd, 2, 'selectionEnd is preserved');
+ });
+
+ test('setting autofocus to true implictly acquires focus', function(done) {
+ var input = fixture('basic');
+ var inputFocusSpy = sinon.spy(input.inputElement, 'focus');
+ window.setTimeout(function() {
+ assert(inputFocusSpy.called);
+ done();
+ }, 50);
+ input.autofocus = true;
+ });
+
+ test('autofocus doesn\'t grab focus if another element already has it', function(done) {
+ var inputs = fixture('multiple-inputs');
+ var inputFocusSpies = inputs.map(function(input) {
+ return sinon.spy(input.inputElement, 'focus');
+ });
+ window.setTimeout(function() {
+ assert(inputFocusSpies[0].called, 'first autofocus input with grabbed focus');
+ assert(!inputFocusSpies[1].called, 'second autofocus input let first input keep focus');
+ done();
+ }, 50);
+ inputs[0].autofocus = true;
+ inputs[1].autofocus = true; // Shouldn't cause focus to change
+ });
+
+ });
+
+ suite('focus/blur events', function() {
+ var input;
+
+ setup(function() {
+ input = fixture('basic');
+ });
+
+ // At the moment, it is very hard to correctly fire exactly
+ // one focus/blur events on a paper-input. This is because
+ // when a paper-input is focused, it needs to focus
+ // its underlying native input, which will also fire a `blur`
+ // event.
+ test('focus events fired on host element', function() {
+ input.addEventListener('focus', function(event) {
+ assert(input.focused, 'input is focused');
+ });
+ MockInteractions.focus(input);
+ });
+
+ test('focus events fired on host element if nested element is focused', function() {
+ input.addEventListener('focus', function(event) {
+ assert(input.focused, 'input is focused');
+ });
+ MockInteractions.focus(input.inputElement);
+ });
+
+ test('blur events fired on host element', function() {
+ MockInteractions.focus(input);
+ input.addEventListener('blur', function(event) {
+ assert(!input.focused, 'input is blurred');
+ });
+ MockInteractions.blur(input);
+ });
+
+ test('blur events fired on host element nested element is blurred', function() {
+ MockInteractions.focus(input);
+ input.addEventListener('blur', function(event) {
+ assert(!input.focused, 'input is blurred');
+ });
+ MockInteractions.blur(input.inputElement);
+ });
+
+ test('focusing then bluring sets the focused attribute correctly', function() {
+ MockInteractions.focus(input);
+ assert(input.focused, 'input is focused');
+ MockInteractions.blur(input);
+ assert(!input.focused, 'input is blurred');
+ MockInteractions.focus(input.inputElement);
+ assert(input.focused, 'input is focused');
+ MockInteractions.blur(input.inputElement);
+ assert(!input.focused, 'input is blurred');
+ });
+
+ test('focusing then bluring with shift-tab removes the focused attribute correctly', function() {
+ MockInteractions.focus(input);
+ assert(input.focused, 'input is focused');
+
+ // Fake a shift-tab induced blur by forcing the flag.
+ input._shiftTabPressed = true;
+ MockInteractions.blur(input.inputElement);
+ assert(!input.focused, 'input is blurred');
+ });
+ });
+
+ suite('focused styling (integration test)', function() {
+
+ test('underline is colored when input is focused', function(done) {
+ var input = fixture('basic');
+ var container = Polymer.dom(input.root).querySelector('paper-input-container');
+ var line = Polymer.dom(container.root).querySelector('.underline');
+ assert.isFalse(line.classList.contains('is-highlighted'), 'line is not highlighted when input is not focused');
+ MockInteractions.focus(input.inputElement);
+ requestAnimationFrame(function() {
+ assert.isTrue(line.classList.contains('is-highlighted'), 'line is highlighted when input is focused');
+ done();
+ });
+ });
+
+ });
+
+ suite('validation', function() {
+
+ test('invalid attribute updated after calling validate()', function() {
+ var input = fixture('required-no-auto-validate');
+ forceXIfStamp(input);
+ input.validate();
+ var error = Polymer.dom(input.root).querySelector('paper-input-error');
+ assert.ok(error, 'paper-input-error exists');
+ assert.notEqual(getComputedStyle(error).display, 'none', 'error is not display:none');
+ assert.isTrue(input.invalid, 'invalid is true');
+ });
+
+ });
+
+ suite('a11y', function() {
+ test('has aria-labelledby, which is monotonically increasing', function() {
+ var inputs = fixture('multiple-inputs');
+
+ // Find the first index of the input in this fixture. Since the label
+ // ids monotonically increase every time a new input is created, and
+ // this fixture isn't the first one in the document, we're going to start
+ // at an ID > 1.
+ var firstLabel = Polymer.dom(inputs[0].root).querySelector('label').id;
+ var index = parseInt(firstLabel.substr(firstLabel.lastIndexOf('-') + 1));
+
+ for (var i = 0; i < inputs.length; i++ ) {
+ var input = inputs[i].inputElement;
+ var label = Polymer.dom(inputs[i].root).querySelector('label').id;
+
+ assert.isTrue(input.hasAttribute('aria-labelledby'));
+ assert.equal(label, 'paper-input-label-' + (index++));
+ assert.equal(input.getAttribute('aria-labelledby'), label);
+ }
+ });
+
+ test('has aria-describedby for error message', function() {
+ var input = fixture('required');
+ forceXIfStamp(input);
+ assert.isTrue(input.inputElement.hasAttribute('aria-describedby'));
+ assert.equal(input.inputElement.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-error').id, 'aria-describedby points to the error message');
+ });
+
+ test('has aria-describedby for character counter', function() {
+ var input = fixture('char-counter');
+ forceXIfStamp(input);
+ assert.isTrue(input.inputElement.hasAttribute('aria-describedby'));
+ assert.equal(input.inputElement.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-char-counter').id, 'aria-describedby points to the character counter');
+ });
+
+ test('has aria-describedby for character counter and error', function() {
+ var input = fixture('required-char-counter');
+ forceXIfStamp(input);
+ assert.isTrue(input.inputElement.hasAttribute('aria-describedby'));
+ assert.equal(input.inputElement.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-error').id + ' ' + Polymer.dom(input.root).querySelector('paper-input-char-counter').id, 'aria-describedby points to the error message and character counter');
+ });
+
+ test('focus an input with tabindex', function(done) {
+ var input = fixture('has-tabindex');
+ flush(function() {
+ MockInteractions.focus(input);
+ flush(function() {
+ assert.equal(input.shadowRoot ? input.shadowRoot.activeElement :
+ document.activeElement, input._focusableElement);
+ done();
+ });
+ });
+ });
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-input/test/paper-textarea.html b/catapult/third_party/polymer/components/paper-input/test/paper-textarea.html
new file mode 100644
index 00000000..f69e4ee7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/test/paper-textarea.html
@@ -0,0 +1,233 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-textarea tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/test-helpers.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../paper-textarea.html">
+</head>
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-textarea></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="label">
+ <template>
+ <paper-textarea label="foo"></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="char-counter">
+ <template>
+ <paper-textarea char-counter></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="required">
+ <template>
+ <paper-textarea auto-validate required error-message="error"></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="required-char-counter">
+ <template>
+ <paper-textarea auto-validate char-counter required error-message="error"></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="always-float-label">
+ <template>
+ <paper-textarea always-float-label label="label"></paper-textarea>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ test('setting value sets the input value', function() {
+ var input = fixture('basic');
+ input.value = 'foobar';
+ assert.equal(input.inputElement.bindValue, input.value, 'inputElement value equals input.value');
+ });
+
+ test('empty required input shows error', function() {
+ var input = fixture('required');
+ forceXIfStamp(input);
+ var error = Polymer.dom(input.root).querySelector('paper-input-error');
+ assert.ok(error, 'paper-input-error exists');
+ assert.notEqual(getComputedStyle(error).display, 'none', 'error is not display:none');
+ });
+
+ test('caret position is preserved', function() {
+ var input = fixture('basic');
+ var ironTextarea = Polymer.dom(input.root).querySelector('iron-autogrow-textarea');
+ input.value = 'nananana';
+ ironTextarea.selectionStart = 2;
+ ironTextarea.selectionEnd = 2;
+
+ input.updateValueAndPreserveCaret('nanananabatman');
+
+ assert.equal(ironTextarea.selectionStart, 2, 'selectionStart is preserved');
+ assert.equal(ironTextarea.selectionEnd, 2, 'selectionEnd is preserved');
+ });
+
+ test('input attributes are bound to textarea', function() {
+ var input = fixture('basic');
+ var attrs = {
+ 'autocomplete': 'true',
+ 'autofocus': true,
+ 'inputmode': 'number',
+ 'name': 'foo',
+ 'placeholder': 'bar',
+ 'readonly': true,
+ 'required': true,
+ 'maxlength': 3
+ };
+ for (var attr in attrs) {
+ input[attr] = attrs[attr];
+ }
+ for (var attr in attrs) {
+ var inputAttr = input.inputElement.getAttribute(attr);
+ if (typeof attrs[attr] === 'boolean') {
+ assert.equal(inputAttr !== null, attrs[attr], 'attribute "' + attr + '" is equal to property (' + attrs[attr] + ', ' + inputAttr !== null + ')');
+ } else {
+ assert.equal(inputAttr, attrs[attr], 'attribute "' + attr + '" is equal to property (' + attrs[attr] + ', ' + inputAttr + ')');
+ }
+ }
+ });
+
+ test('always-float-label attribute works', function() {
+ var input = fixture('always-float-label');
+ var container = Polymer.dom(input.root).querySelector('paper-input-container');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ assert.isTrue(inputContent.classList.contains('label-is-floating'), 'label is floating');
+ });
+
+ test('label does not receive pointer events', function() {
+ var input = fixture('always-float-label');
+ var label = Polymer.dom(input.root).querySelector('label');
+ assert.equal(getComputedStyle(label).pointerEvents, 'none');
+ });
+
+ test('no extra space between input and underline', function() {
+ var input = fixture('label');
+ var container = Polymer.dom(input.root).querySelector('paper-input-container');
+ var inputContent = Polymer.dom(container.root).querySelector('.input-content');
+ var ironTextarea = Polymer.dom(input.root).querySelector('iron-autogrow-textarea');
+ assert.equal(inputContent.clientHeight,ironTextarea.clientHeight, 'container and textarea are same height');
+ });
+ });
+
+ suite('focus/blur events', function() {
+ var input;
+
+ setup(function() {
+ input = fixture('basic');
+ });
+
+ // At the moment, it is very hard to correctly fire exactly
+ // one focus/blur events on a paper-textarea. This is because
+ // when a paper-textarea is focused, it needs to focus
+ // its underlying native textarea, which will also fire a `blur`
+ // event.
+ test('focus events fired on host element', function() {
+ input.addEventListener('focus', function(event) {
+ assert(input.focused, 'input is focused');
+ });
+ MockInteractions.focus(input);
+ });
+
+ test('focus events fired on host element if nested element is focused', function() {
+ input.addEventListener('focus', function(event) {
+ assert(input.focused, 'input is focused');
+ });
+ MockInteractions.focus(input.inputElement.textarea);
+ });
+
+ test('blur events fired on host element', function() {
+ MockInteractions.focus(input);
+ input.addEventListener('blur', function(event) {
+ assert(!input.focused, 'input is blurred');
+ });
+ MockInteractions.blur(input);
+ });
+
+ test('blur events fired on host element nested element is blurred', function() {
+ MockInteractions.focus(input);
+ input.addEventListener('blur', function(event) {
+ assert(!input.focused, 'input is blurred');
+ });
+ MockInteractions.blur(input.inputElement.textarea);
+ });
+
+ test('focusing then bluring sets the focused attribute correctly', function() {
+ MockInteractions.focus(input);
+ assert(input.focused, 'input is focused');
+ MockInteractions.blur(input);
+ assert(!input.focused, 'input is blurred');
+ MockInteractions.focus(input.inputElement.textarea);
+ assert(input.focused, 'input is focused');
+ MockInteractions.blur(input.inputElement.textarea);
+ assert(!input.focused, 'input is blurred');
+ });
+ });
+
+ suite('a11y', function() {
+
+ test('has aria-labelledby', function() {
+ var input = fixture('label');
+ assert.isTrue(input.inputElement.textarea.hasAttribute('aria-labelledby'))
+ assert.equal(input.inputElement.textarea.getAttribute('aria-labelledby'), Polymer.dom(input.root).querySelector('label').id, 'aria-labelledby points to the label');
+ });
+
+ test('has aria-describedby for error message', function() {
+ var input = fixture('required');
+ forceXIfStamp(input);
+ assert.isTrue(input.inputElement.textarea.hasAttribute('aria-describedby'));
+ assert.equal(input.inputElement.textarea.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-error').id, 'aria-describedby points to the error message');
+ });
+
+ test('has aria-describedby for character counter', function() {
+ var input = fixture('char-counter');
+ forceXIfStamp(input);
+ assert.isTrue(input.inputElement.textarea.hasAttribute('aria-describedby'));
+ assert.equal(input.inputElement.textarea.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-char-counter').id, 'aria-describedby points to the character counter');
+ });
+
+ test('has aria-describedby for character counter and error', function() {
+ var input = fixture('required-char-counter');
+ forceXIfStamp(input);
+ assert.isTrue(input.inputElement.textarea.hasAttribute('aria-describedby'));
+ assert.equal(input.inputElement.textarea.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-error').id + ' ' + Polymer.dom(input.root).querySelector('paper-input-char-counter').id, 'aria-describedby points to the error message and character counter');
+ });
+
+ });
+
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-item/.bower.json b/catapult/third_party/polymer/components/paper-item/.bower.json
new file mode 100644
index 00000000..2b408482
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/.bower.json
@@ -0,0 +1,52 @@
+{
+ "name": "paper-item",
+ "version": "1.2.2",
+ "description": "A material-design styled list item",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "item"
+ ],
+ "main": [
+ "paper-item.html",
+ "paper-icon-item.html",
+ "paper-item-body.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-item"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-item",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-behaviors": "polymerelements/iron-behaviors#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0"
+ },
+ "_release": "1.2.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.2",
+ "commit": "d8277ec6bea4a1b1b718737d0e9391f72d278128"
+ },
+ "_source": "https://github.com/PolymerElements/paper-item.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-item"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-item/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-item/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..c38fb3e5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-item/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-item/.gitignore b/catapult/third_party/polymer/components/paper-item/.gitignore
new file mode 100644
index 00000000..fbe05fc9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/.gitignore
@@ -0,0 +1 @@
+bower_components/
diff --git a/catapult/third_party/polymer/components/paper-item/.travis.yml b/catapult/third_party/polymer/components/paper-item/.travis.yml
new file mode 100644
index 00000000..0283b44c
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ L1TRyuZxs3duna5l8uOs3l5OsS0s8hWzTh2J6T7yryOFkwI3giar8O3p9DmQk8+yB45r1J6iJVBqhwaYP9A2ZOh/2J6zfXACCkFvFfDZQA3A3QGQf5SynQyoG7RpvHdm9qMymIKAPbbPzM1f1uyN79R0vfNXW+q3OziWyARI9Zo=
+ - secure: >-
+ IqEh0JXayftVmAwAkyytSmONzK9BuhECFLURSri6wowjkGaYN+76m6hry1tWxoW9cdkgKX5l9WPekBtKtSJsB+jYIJTUtl258j+bktiSFK9laBH+97NOcCC8n0tNdgMO/CCF4cX//eWaelPnCjriJv6evdefVdrkImSZflWYmsc=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-item/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-item/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-item/README.md b/catapult/third_party/polymer/components/paper-item/README.md
new file mode 100644
index 00000000..18deb629
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/README.md
@@ -0,0 +1,163 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-icon-item.html paper-item-behavior.html paper-item-body.html paper-item.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-item.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-item)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-item)_
+
+
+##&lt;paper-item&gt;
+
+Material design: [Lists](https://www.google.com/design/spec/components/lists.html)
+
+`<paper-item>` is an interactive list item. By default, it is a horizontal flexbox.
+
+```html
+<paper-item>Item</paper-item>
+```
+
+Use this element with `<paper-item-body>` to make Material Design styled two-line and three-line
+items.
+
+```html
+<paper-item>
+ <paper-item-body two-line>
+ <div>Show your status</div>
+ <div secondary>Your status is visible to everyone</div>
+ </paper-item-body>
+ <iron-icon icon="warning"></iron-icon>
+</paper-item>
+```
+
+To use `paper-item` as a link, wrap it in an anchor tag. Since `paper-item` will
+already receive focus, you may want to prevent the anchor tag from receiving
+focus as well by setting its tabindex to -1.
+
+```html
+<a href="https://www.polymer-project.org/" tabindex="-1">
+ <paper-item raised>Polymer Project</paper-item>
+</a>
+```
+
+If you are concerned about performance and want to use `paper-item` in a `paper-listbox`
+with many items, you can just use a native `button` with the `paper-item` class
+applied (provided you have correctly included the shared styles):
+
+```html
+<style is="custom-style" include="paper-item-shared-styles"></style>
+
+<paper-listbox>
+ <button class="paper-item" role="option">Inbox</button>
+ <button class="paper-item" role="option">Starred</button>
+ <button class="paper-item" role="option">Sent mail</button>
+</paper-listbox>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-item-min-height` | Minimum height of the item | `48px` |
+| `--paper-item` | Mixin applied to the item | `{}` |
+| `--paper-item-selected-weight` | The font weight of a selected item | `bold` |
+| `--paper-item-selected` | Mixin applied to selected paper-items | `{}` |
+| `--paper-item-disabled-color` | The color for disabled paper-items | `--disabled-text-color` |
+| `--paper-item-disabled` | Mixin applied to disabled paper-items | `{}` |
+| `--paper-item-focused` | Mixin applied to focused paper-items | `{}` |
+| `--paper-item-focused-before` | Mixin applied to :before focused paper-items | `{}` |
+
+### Accessibility
+
+This element has `role="listitem"` by default. Depending on usage, it may be more appropriate to set
+`role="menuitem"`, `role="menuitemcheckbox"` or `role="menuitemradio"`.
+
+```html
+<paper-item role="menuitemcheckbox">
+ <paper-item-body>
+ Show your status
+ </paper-item-body>
+ <paper-checkbox></paper-checkbox>
+</paper-item>
+```
+
+
+
+##&lt;paper-icon-item&gt;
+
+`<paper-icon-item>` is a convenience element to make an item with icon. It is an interactive list
+item with a fixed-width icon area, according to Material Design. This is useful if the icons are of
+varying widths, but you want the item bodies to line up. Use this like a `<paper-item>`. The child
+node with the attribute `item-icon` is placed in the icon area.
+
+```html
+<paper-icon-item>
+ <iron-icon icon="favorite" item-icon></iron-icon>
+ Favorite
+</paper-icon-item>
+<paper-icon-item>
+ <div class="avatar" item-icon></div>
+ Avatar
+</paper-icon-item>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-item-icon-width` | Width of the icon area | `56px` |
+| `--paper-item-icon` | Mixin applied to the icon area | `{}` |
+| `--paper-icon-item` | Mixin applied to the item | `{}` |
+| `--paper-item-selected-weight` | The font weight of a selected item | `bold` |
+| `--paper-item-selected` | Mixin applied to selected paper-items | `{}` |
+| `--paper-item-disabled-color` | The color for disabled paper-items | `--disabled-text-color` |
+| `--paper-item-disabled` | Mixin applied to disabled paper-items | `{}` |
+| `--paper-item-focused` | Mixin applied to focused paper-items | `{}` |
+| `--paper-item-focused-before` | Mixin applied to :before focused paper-items | `{}` |
+
+
+
+##&lt;paper-item-body&gt;
+
+Use `<paper-item-body>` in a `<paper-item>` or `<paper-icon-item>` to make two- or
+three- line items. It is a flex item that is a vertical flexbox.
+
+```html
+<paper-item>
+ <paper-item-body two-line>
+ <div>Show your status</div>
+ <div secondary>Your status is visible to everyone</div>
+ </paper-item-body>
+</paper-item>
+```
+
+The child elements with the `secondary` attribute is given secondary text styling.
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-item-body-two-line-min-height` | Minimum height of a two-line item | `72px` |
+| `--paper-item-body-three-line-min-height` | Minimum height of a three-line item | `88px` |
+| `--paper-item-body-secondary-color` | Foreground color for the `secondary` area | `--secondary-text-color` |
+| `--paper-item-body-secondary` | Mixin applied to the `secondary` area | `{}` |
+
+
+
+<!-- No docs for Polymer.PaperItemBehavior found. -->
diff --git a/catapult/third_party/polymer/components/paper-item/all-imports.html b/catapult/third_party/polymer/components/paper-item/all-imports.html
new file mode 100644
index 00000000..4b1583fe
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/all-imports.html
@@ -0,0 +1,13 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="paper-item.html">
+<link rel="import" href="paper-item-body.html">
+<link rel="import" href="paper-icon-item.html">
diff --git a/catapult/third_party/polymer/components/paper-item/bower.json b/catapult/third_party/polymer/components/paper-item/bower.json
new file mode 100644
index 00000000..5922382d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/bower.json
@@ -0,0 +1,43 @@
+{
+ "name": "paper-item",
+ "version": "1.2.2",
+ "description": "A material-design styled list item",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "item"
+ ],
+ "main": [
+ "paper-item.html",
+ "paper-icon-item.html",
+ "paper-item-body.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-item"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-item",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-behaviors": "polymerelements/iron-behaviors#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-item/demo/index.html b/catapult/third_party/polymer/components/paper-item/demo/index.html
new file mode 100644
index 00000000..92a04d14
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/demo/index.html
@@ -0,0 +1,191 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-item demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-icon/iron-icon.html">
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../iron-icons/communication-icons.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../paper-icon-button/paper-icon-button.html">
+ <link rel="import" href="../paper-icon-item.html">
+ <link rel="import" href="../paper-item.html">
+ <link rel="import" href="../paper-item-body.html">
+ <link rel="import" href="../../paper-styles/color.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ div[role="listbox"] {
+ border: 1px solid #e5e5e5;
+ }
+ .avatar {
+ display: inline-block;
+ box-sizing: border-box;
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: var(--paper-amber-500);
+ }
+
+ .blue {
+ background-color: var(--paper-light-blue-300);
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Paper-items are simple list elements, ideally used in a paper-listbox or
+ an element with <i>role="listbox"</i></h3>
+ <demo-snippet>
+ <template>
+ <div role="listbox">
+ <paper-item>Inbox</paper-item>
+ <paper-item>Starred</paper-item>
+ <paper-item>Sent mail</paper-item>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>They can be styled using custom properties</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ paper-item.fancy {
+ --paper-item-focused: {
+ background: var(--paper-amber-500);
+ font-weight: bold;
+ };
+ --paper-item-focused-before: {
+ opacity: 0;
+ };
+ }
+ </style>
+ <div role="listbox">
+ <paper-item class="fancy">Inbox</paper-item>
+ <paper-item class="fancy">Starred</paper-item>
+ <paper-item class="fancy">Sent mail</paper-item>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>To add a leading element, use a paper-icon-item with an <i>item-icon</i> attribute. This
+ leading image can be an iron-icon, or any other regular element.</h3>
+ <demo-snippet>
+ <template>
+ <div role="listbox">
+ <paper-icon-item>
+ <iron-icon icon="inbox" item-icon></iron-icon>
+ Inbox
+ </paper-icon-item>
+ <paper-icon-item>
+ <iron-icon icon="star" item-icon></iron-icon>
+ Starred
+ </paper-icon-item>
+ <paper-icon-item>
+ <div class="avatar blue" item-icon></div>
+ Alphonso Engelking
+ </paper-icon-item>
+ <paper-icon-item>
+ <div class="avatar" item-icon></div>
+ Angela Decker
+ </paper-icon-item>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>For two-line items, use a paper-item-body inside a paper-item or paper-icon-item</h3>
+ <demo-snippet>
+ <template>
+ <div role="listbox">
+ <paper-item>
+ <paper-item-body two-line>
+ <div>Profile Photo</div>
+ <div secondary>Change your Google+ profile photo</div>
+ </paper-item-body>
+ </paper-item>
+ <paper-icon-item>
+ <iron-icon icon="communication:phone" item-icon>
+ </iron-icon>
+ <paper-item-body two-line>
+ <div>(650) 555-1234</div>
+ <div secondary>Mobile</div>
+ </paper-item-body>
+ </paper-icon-item>
+ <paper-icon-item>
+ <div class="avatar blue" item-icon></div>
+ <paper-item-body two-line>
+ <div>Alphonso Engelking</div>
+ <div secondary>Change photo</div>
+ </paper-item-body>
+ </paper-icon-item>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>Complex layouts are usually a combination of all these elements</h3>
+ <demo-snippet>
+ <template>
+ <div role="listbox">
+ <paper-icon-item>
+ <div class="avatar blue" item-icon></div>
+ <paper-item-body two-line>
+ <div>Photos</div>
+ <div secondary>Jan 9, 2014</div>
+ </paper-item-body>
+ <paper-icon-button icon="star" alt="favourite this!">
+ </paper-icon-button>
+ </paper-icon-item>
+ <paper-icon-item>
+ <div class="avatar" item-icon></div>
+ <paper-item-body two-line>
+ <div>Recipes</div>
+ <div secondary>Jan 17, 2014</div>
+ </paper-item-body>
+ <paper-icon-button icon="star" alt="favourite this!">
+ </paper-icon-button>
+ </paper-icon-item>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>Paper-items can be used as links</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ .paper-item-link {
+ color: inherit;
+ text-decoration: none;
+ }
+ </style>
+ <div role="listbox">
+ <a class="paper-item-link" href="#inbox" tabindex="-1">
+ <paper-item>Inbox</paper-item>
+ </a>
+ <a class="paper-item-link" href="#starred" tabindex="-1">
+ <paper-item>Starred</paper-item>
+ </a>
+ <a class="paper-item-link" href="#sent" tabindex="-1">
+ <paper-item>Sent mail</paper-item>
+ </a>
+ </div>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-item/index.html b/catapult/third_party/polymer/components/paper-item/index.html
new file mode 100644
index 00000000..b409ed12
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/index.html
@@ -0,0 +1,30 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<!doctype html>
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-item</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page src="all-imports.html"></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-item/paper-icon-item.html b/catapult/third_party/polymer/components/paper-item/paper-icon-item.html
new file mode 100644
index 00000000..e8f6d079
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/paper-icon-item.html
@@ -0,0 +1,86 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/typography.html">
+<link rel="import" href="paper-item-behavior.html">
+<link rel="import" href="paper-item-shared-styles.html">
+
+<!--
+`<paper-icon-item>` is a convenience element to make an item with icon. It is an interactive list
+item with a fixed-width icon area, according to Material Design. This is useful if the icons are of
+varying widths, but you want the item bodies to line up. Use this like a `<paper-item>`. The child
+node with the attribute `item-icon` is placed in the icon area.
+
+ <paper-icon-item>
+ <iron-icon icon="favorite" item-icon></iron-icon>
+ Favorite
+ </paper-icon-item>
+ <paper-icon-item>
+ <div class="avatar" item-icon></div>
+ Avatar
+ </paper-icon-item>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+------------------------------|------------------------------------------------|----------
+`--paper-item-icon-width` | Width of the icon area | `56px`
+`--paper-item-icon` | Mixin applied to the icon area | `{}`
+`--paper-icon-item` | Mixin applied to the item | `{}`
+`--paper-item-selected-weight`| The font weight of a selected item | `bold`
+`--paper-item-selected` | Mixin applied to selected paper-items | `{}`
+`--paper-item-disabled-color` | The color for disabled paper-items | `--disabled-text-color`
+`--paper-item-disabled` | Mixin applied to disabled paper-items | `{}`
+`--paper-item-focused` | Mixin applied to focused paper-items | `{}`
+`--paper-item-focused-before` | Mixin applied to :before focused paper-items | `{}`
+-->
+
+<dom-module id="paper-icon-item">
+ <template>
+ <style include="paper-item-shared-styles"></style>
+ <style>
+ :host {
+ @apply(--layout-horizontal);
+ @apply(--layout-center);
+ @apply(--paper-font-subhead);
+
+ @apply(--paper-item);
+ @apply(--paper-icon-item);
+ }
+
+ .content-icon {
+ @apply(--layout-horizontal);
+ @apply(--layout-center);
+
+ width: var(--paper-item-icon-width, 56px);
+ @apply(--paper-item-icon);
+ }
+ </style>
+
+ <div id="contentIcon" class="content-icon">
+ <content select="[item-icon]"></content>
+ </div>
+ <content></content>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-icon-item',
+
+ behaviors: [
+ Polymer.PaperItemBehavior
+ ]
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-item/paper-item-behavior.html b/catapult/third_party/polymer/components/paper-item/paper-item-behavior.html
new file mode 100644
index 00000000..f97b262b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/paper-item-behavior.html
@@ -0,0 +1,36 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-behaviors/iron-button-state.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+
+<!--
+`PaperItemBehavior` is a convenience behavior shared by <paper-item> and
+<paper-icon-item> that manages the shared control states and attributes of
+the items.
+-->
+
+<script>
+ /** @polymerBehavior Polymer.PaperItemBehavior */
+ Polymer.PaperItemBehaviorImpl = {
+ hostAttributes: {
+ role: 'option',
+ tabindex: '0'
+ }
+ };
+
+ /** @polymerBehavior */
+ Polymer.PaperItemBehavior = [
+ Polymer.IronButtonState,
+ Polymer.IronControlState,
+ Polymer.PaperItemBehaviorImpl
+ ];
+</script>
diff --git a/catapult/third_party/polymer/components/paper-item/paper-item-body.html b/catapult/third_party/polymer/components/paper-item/paper-item-body.html
new file mode 100644
index 00000000..5c46cd89
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/paper-item-body.html
@@ -0,0 +1,83 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../paper-styles/typography.html">
+
+<!--
+Use `<paper-item-body>` in a `<paper-item>` or `<paper-icon-item>` to make two- or
+three- line items. It is a flex item that is a vertical flexbox.
+
+ <paper-item>
+ <paper-item-body two-line>
+ <div>Show your status</div>
+ <div secondary>Your status is visible to everyone</div>
+ </paper-item-body>
+ </paper-item>
+
+The child elements with the `secondary` attribute is given secondary text styling.
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-item-body-two-line-min-height` | Minimum height of a two-line item | `72px`
+`--paper-item-body-three-line-min-height` | Minimum height of a three-line item | `88px`
+`--paper-item-body-secondary-color` | Foreground color for the `secondary` area | `--secondary-text-color`
+`--paper-item-body-secondary` | Mixin applied to the `secondary` area | `{}`
+
+-->
+
+<dom-module id="paper-item-body">
+ <template>
+ <style>
+ :host {
+ overflow: hidden; /* needed for text-overflow: ellipsis to work on ff */
+ @apply(--layout-vertical);
+ @apply(--layout-center-justified);
+ @apply(--layout-flex);
+ }
+
+ :host([two-line]) {
+ min-height: var(--paper-item-body-two-line-min-height, 72px);
+ }
+
+ :host([three-line]) {
+ min-height: var(--paper-item-body-three-line-min-height, 88px);
+ }
+
+ :host > ::content > * {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ :host > ::content [secondary] {
+ @apply(--paper-font-body1);
+
+ color: var(--paper-item-body-secondary-color, --secondary-text-color);
+
+ @apply(--paper-item-body-secondary);
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-item-body'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-item/paper-item-shared-styles.html b/catapult/third_party/polymer/components/paper-item/paper-item-shared-styles.html
new file mode 100644
index 00000000..868d4153
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/paper-item-shared-styles.html
@@ -0,0 +1,70 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/color.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../paper-styles/typography.html">
+
+<dom-module id="paper-item-shared-styles">
+ <template>
+ <style>
+ :host, .paper-item {
+ display: block;
+ position: relative;
+ min-height: var(--paper-item-min-height, 48px);
+ padding: 0px 16px;
+ }
+
+ .paper-item {
+ @apply(--paper-font-subhead);
+ border:none;
+ outline: none;
+ background: white;
+ width: 100%;
+ text-align: left;
+ }
+
+ :host([hidden]), .paper-item[hidden] {
+ display: none !important;
+ }
+
+ :host(.iron-selected), .paper-item.iron-selected {
+ font-weight: var(--paper-item-selected-weight, bold);
+
+ @apply(--paper-item-selected);
+ }
+
+ :host([disabled]), .paper-item[disabled] {
+ color: var(--paper-item-disabled-color, --disabled-text-color);
+
+ @apply(--paper-item-disabled);
+ }
+
+ :host(:focus), .paper-item:focus {
+ position: relative;
+ outline: 0;
+
+ @apply(--paper-item-focused);
+ }
+
+ :host(:focus):before, .paper-item:focus:before {
+ @apply(--layout-fit);
+
+ background: currentColor;
+ content: '';
+ opacity: var(--dark-divider-opacity);
+ pointer-events: none;
+
+ @apply(--paper-item-focused-before);
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-item/paper-item.html b/catapult/third_party/polymer/components/paper-item/paper-item.html
new file mode 100644
index 00000000..60b4692d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/paper-item.html
@@ -0,0 +1,111 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="paper-item-behavior.html">
+<link rel="import" href="paper-item-shared-styles.html">
+
+<!--
+Material design: [Lists](https://www.google.com/design/spec/components/lists.html)
+
+`<paper-item>` is an interactive list item. By default, it is a horizontal flexbox.
+
+ <paper-item>Item</paper-item>
+
+Use this element with `<paper-item-body>` to make Material Design styled two-line and three-line
+items.
+
+ <paper-item>
+ <paper-item-body two-line>
+ <div>Show your status</div>
+ <div secondary>Your status is visible to everyone</div>
+ </paper-item-body>
+ <iron-icon icon="warning"></iron-icon>
+ </paper-item>
+
+To use `paper-item` as a link, wrap it in an anchor tag. Since `paper-item` will
+already receive focus, you may want to prevent the anchor tag from receiving
+focus as well by setting its tabindex to -1.
+
+ <a href="https://www.polymer-project.org/" tabindex="-1">
+ <paper-item raised>Polymer Project</paper-item>
+ </a>
+
+If you are concerned about performance and want to use `paper-item` in a `paper-listbox`
+with many items, you can just use a native `button` with the `paper-item` class
+applied (provided you have correctly included the shared styles):
+
+ <style is="custom-style" include="paper-item-shared-styles"></style>
+
+ <paper-listbox>
+ <button class="paper-item" role="option">Inbox</button>
+ <button class="paper-item" role="option">Starred</button>
+ <button class="paper-item" role="option">Sent mail</button>
+ </paper-listbox>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+------------------------------|----------------------------------------------|----------
+`--paper-item-min-height` | Minimum height of the item | `48px`
+`--paper-item` | Mixin applied to the item | `{}`
+`--paper-item-selected-weight`| The font weight of a selected item | `bold`
+`--paper-item-selected` | Mixin applied to selected paper-items | `{}`
+`--paper-item-disabled-color` | The color for disabled paper-items | `--disabled-text-color`
+`--paper-item-disabled` | Mixin applied to disabled paper-items | `{}`
+`--paper-item-focused` | Mixin applied to focused paper-items | `{}`
+`--paper-item-focused-before` | Mixin applied to :before focused paper-items | `{}`
+
+### Accessibility
+
+This element has `role="listitem"` by default. Depending on usage, it may be more appropriate to set
+`role="menuitem"`, `role="menuitemcheckbox"` or `role="menuitemradio"`.
+
+ <paper-item role="menuitemcheckbox">
+ <paper-item-body>
+ Show your status
+ </paper-item-body>
+ <paper-checkbox></paper-checkbox>
+ </paper-item>
+
+@group Paper Elements
+@element paper-item
+@demo demo/index.html
+-->
+
+<dom-module id="paper-item">
+ <template>
+ <style include="paper-item-shared-styles"></style>
+ <style>
+ :host {
+ @apply(--layout-horizontal);
+ @apply(--layout-center);
+ @apply(--paper-font-subhead);
+
+ @apply(--paper-item);
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-item',
+
+ behaviors: [
+ Polymer.PaperItemBehavior
+ ]
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-item/test/index.html b/catapult/third_party/polymer/components/paper-item/test/index.html
new file mode 100644
index 00000000..7029d736
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/test/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>paper-item tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'paper-item.html',
+ 'paper-item.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-item/test/paper-item.html b/catapult/third_party/polymer/components/paper-item/test/paper-item.html
new file mode 100644
index 00000000..a728d6c1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-item/test/paper-item.html
@@ -0,0 +1,218 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<!doctype html>
+<html>
+ <head>
+
+ <title>paper-item tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../paper-input/paper-input.html">
+ <link rel="import" href="../paper-item.html">
+ <link rel="import" href="../paper-icon-item.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="item">
+ <template>
+ <div role="listbox">
+ <paper-item>item</paper-item>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="button">
+ <template>
+ <div role="listbox">
+ <button class="paper-item" role="option">item</button>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="iconItem">
+ <template>
+ <div role="listbox">
+ <paper-icon-item>item</paper-icon-item>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="item-with-input">
+ <template>
+ <div role="list">
+ <paper-item><input></paper-item>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="item-with-paper-input">
+ <template>
+ <div role="list">
+ <paper-item><paper-input></paper-input></paper-item>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="iconItem-with-input">
+ <template>
+ <div role="list">
+ <paper-icon-item><input></paper-icon-item>
+ </div>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('paper-item basic', function() {
+ var item, clickHandler;
+
+ setup(function() {
+ item = fixture('item').querySelector('paper-item');
+ clickHandler = sinon.spy();
+ item.addEventListener('click', clickHandler);
+ });
+
+ test('space triggers a click event', function(done) {
+ MockInteractions.pressSpace(item);
+ Polymer.Base.async(function(){
+ // You need two ticks, one for the MockInteractions event, and one
+ // for the button event.
+ Polymer.Base.async(function(){
+ expect(clickHandler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ }, 1);
+ });
+
+ test('enter triggers a click event', function(done) {
+ MockInteractions.pressEnter(item);
+ Polymer.Base.async(function(){
+ // You need two ticks, one for the MockInteractions event, and one
+ // for the button event.
+ Polymer.Base.async(function(){
+ expect(clickHandler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ }, 1);
+ });
+ });
+
+ suite('paper-icon-item basic', function() {
+ var item, clickHandler;
+
+ setup(function() {
+ item = fixture('iconItem').querySelector('paper-icon-item');
+ clickHandler = sinon.spy();
+ item.addEventListener('click', clickHandler);
+ });
+
+ test('space triggers a click event', function(done) {
+ MockInteractions.pressSpace(item);
+ Polymer.Base.async(function(){
+ // You need two ticks, one for the MockInteractions event, and one
+ // for the button event.
+ Polymer.Base.async(function(){
+ expect(clickHandler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ }, 1);
+ });
+
+ test('click triggers a click event', function(done) {
+ MockInteractions.tap(item);
+ Polymer.Base.async(function(){
+ expect(clickHandler.callCount).to.be.equal(1);
+ done();
+ }, 1);
+ });
+ });
+
+ suite('clickable element inside item', function() {
+ test('paper-item: space in child native input does not trigger a click event', function(done) {
+ var f = fixture('item-with-input');
+ var outerItem = f.querySelector('paper-item');
+ var innerInput = f.querySelector('input');
+
+ var itemClickHandler = sinon.spy();
+ outerItem.addEventListener('click', itemClickHandler);
+
+ innerInput.focus();
+ MockInteractions.pressSpace(innerInput);
+ Polymer.Base.async(function(){
+ expect(itemClickHandler.callCount).to.be.equal(0);
+ done();
+ }, 1);
+ });
+
+ test('paper-item: space in child paper-input does not trigger a click event', function(done) {
+ var f = fixture('item-with-paper-input');
+ var outerItem = f.querySelector('paper-item');
+ var innerInput = f.querySelector('paper-input');
+
+ var itemClickHandler = sinon.spy();
+ outerItem.addEventListener('click', itemClickHandler);
+
+ innerInput.focus();
+ MockInteractions.pressSpace(innerInput);
+ Polymer.Base.async(function(){
+ expect(itemClickHandler.callCount).to.be.equal(0);
+ done();
+ }, 1);
+ });
+
+ test('paper-icon-item: space in child input does not trigger a click event', function(done) {
+ var f = fixture('iconItem-with-input');
+ var outerItem = f.querySelector('paper-icon-item');
+ var innerInput = f.querySelector('input');
+
+ var itemClickHandler = sinon.spy();
+ outerItem.addEventListener('click', itemClickHandler);
+
+ MockInteractions.pressSpace(innerInput);
+ Polymer.Base.async(function(){
+ expect(itemClickHandler.callCount).to.be.equal(0);
+ done();
+ }, 1);
+ });
+ });
+
+ suite('item a11y tests', function() {
+ var item, iconItem;
+
+ setup(function() {
+ item = fixture('item').querySelector('paper-item');
+ iconItem = fixture('iconItem').querySelector('paper-icon-item');
+ });
+
+ test('item has role="listitem"', function() {
+ assert.equal(item.getAttribute('role'), 'option', 'has role="option"');
+ });
+
+ test('icon item has role="listitem"', function() {
+ assert.equal(iconItem.getAttribute('role'), 'option', 'has role="option"');
+ });
+
+ a11ySuite('item');
+ a11ySuite('button');
+ a11ySuite('iconItem');
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-listbox/.bower.json b/catapult/third_party/polymer/components/paper-listbox/.bower.json
new file mode 100644
index 00000000..8c86d3a7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/.bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "paper-listbox",
+ "version": "1.1.3",
+ "description": "Implements an accessible material design listbox",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "listbox"
+ ],
+ "main": "paper-listbox.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-listbox"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-listbox",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.1.3",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.3",
+ "commit": "09a9bc25e8f5324c273fc9afbfe8c9b7071d7d8e"
+ },
+ "_source": "https://github.com/polymerelements/paper-listbox.git",
+ "_target": "^1.1.2",
+ "_originalSource": "polymerelements/paper-listbox"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-listbox/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-listbox/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..f059c9e6
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-listbox/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-listbox/.gitignore b/catapult/third_party/polymer/components/paper-listbox/.gitignore
new file mode 100644
index 00000000..fbe05fc9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/.gitignore
@@ -0,0 +1 @@
+bower_components/
diff --git a/catapult/third_party/polymer/components/paper-listbox/.travis.yml b/catapult/third_party/polymer/components/paper-listbox/.travis.yml
new file mode 100644
index 00000000..d8202477
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/.travis.yml
@@ -0,0 +1,22 @@
+language: node_js
+sudo: required
+before_script:
+- npm install -g bower polymer-cli@next
+- polymer install --variants
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+- xvfb-run polymer test
+- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then polymer test -s 'default'; fi
+dist: trusty
+env:
+ global:
+ - secure: VOiCB6KJPLtfy4TKcsYrjA8gIbveySmK2umUnWIfMiKohgOHpJ6wHMD7cZ9qMxJCHQHDOXkreMhpGKJXnflL0XGOyZt+GyV8xIJ/nqQKu71gI4UCbMki3qzu9WheLJEHSgPpfDN19mKKfsHehsOVJl6q3cac1jZGDoH1uyp015zNAecj18wfnPPOc6NDoFQkYOJu9zAEMQaE9xxTInWXaOC4xjRr+UZrDZ7cMgvmgG8g5vrs4ihVBav5mrF6rrzxqD9CwERDV2tcQMhUfId/kt68X1QkruMpsoqEY/eHy0tD/pBKSNTYYwPUwtNEb+CF7kyi5TkH/UsDW/RHBNc6DmWqdrcipXyOfkWBYV457J24IFhphD31EHUycyu0C/T6UH88P+Hi6Z5QN0xZAbkQM7GEy2ihPWyR0osWsHkplZXfonbL8OfmSametEOeYs/1Lai5dw9kMpLiPt94XzqkDDG63h9WQlsb/eIBi9102rNg9j/DETbUTH1o5JEUO9305Oob0pZKX/3+TBPLXV18AnWzPgEZ6MWD8kJQdCIbKcrfudWlcEP33+5yGNJUkEwYJqcL5bbV2a65fK34X3dU15ZuJy71JkR4ztmqJ1z5sZO+auyqb0SosOf4mb8gpZAQaeIpJPW+golnqFl0dAr9LzomeGiVBYU2M0uTV829IUA=
+ - secure: Chwm3UcQTWbuxtQhBdF68R7Mokpka2FfhSCOVSTrub9ovu6CnzlEa4GBMQOwbrml3gX2ueV0zviqngoVaLpKzyIfd9DDHfx/aY4U6Nmbc0TMqz70JlnKMzdPMcoBtStrdm4X5niBuD55y3Ibmy/IBfpNT0QR8ARr/NMf29dnin7vUGlk/wXwjIdfnG8vvBiHn9QFBLPggeHbQwwILw7eoo6xcjDVWxQKN0C0oXYs8oDhN4C3hz8rdJr4cuFo9ue2NHDOSfKTWI6tnl3XgUBxqMN8elsQfx7E6BtvBmivyaYs3pm/D+3x/eOZuCT+1KtF93BVFwxw22tmEco40w4ewT89a78PXOIXzaADUqMUEiHZPshp71eLi13G7R7IiMT89xaji8qUY6SBwuHOEnz8BFzYTOUFYicfiifqn7sFlUJcI6KeNivYwEOu6LybARdLz0B/pygCZRy5d7csisLjK7Ik45VQdRRoGwr+29eRjz+nxYFBvdwxZO4McTU+4QL9+KgCqkogq1SGHya3j5h0IDu71QrluL7Gn/KK+9Udsmc57sdJ7wsk36xEmxu23PDEFYhTBedo5P58gAfM7ziNytm6hAhMCNhvCNOPRngz2Hk4Oijae7TTVS+4NIs7oDdGVY6O9gizWmnpIiE2o9Eo/BvorveEZZfzFQkGwZrbiWc=
diff --git a/catapult/third_party/polymer/components/paper-listbox/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-listbox/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-listbox/README.md b/catapult/third_party/polymer/components/paper-listbox/README.md
new file mode 100644
index 00000000..247e1bad
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/README.md
@@ -0,0 +1,70 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-listbox.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-listbox.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-listbox)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-listbox)_
+
+
+##&lt;paper-listbox&gt;
+
+Material design: [Menus](https://www.google.com/design/spec/components/menus.html)
+
+`<paper-listbox>` implements an accessible listbox control with Material Design styling. The focused item
+is highlighted, and the selected item has bolded text.
+
+```html
+<paper-listbox>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+</paper-listbox>
+```
+
+An initial selection can be specified with the `selected` attribute.
+
+```html
+<paper-listbox selected="0">
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+</paper-listbox>
+```
+
+Make a multi-select listbox with the `multi` attribute. Items in a multi-select listbox can be deselected,
+and multiple item can be selected.
+
+```html
+<paper-listbox multi>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+</paper-listbox>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-listbox-background-color` | Menu background color | `--primary-background-color` |
+| `--paper-listbox-color` | Menu foreground color | `--primary-text-color` |
+| `--paper-listbox` | Mixin applied to the listbox | `{}` |
+
+### Accessibility
+
+`<paper-listbox>` has `role="listbox"` by default. A multi-select listbox will also have
+`aria-multiselectable` set. It implements key bindings to navigate through the listbox with the up and
+down arrow keys, esc to exit the listbox, and enter to activate a listbox item. Typing the first letter
+of a listbox item will also focus it.
+
+
diff --git a/catapult/third_party/polymer/components/paper-listbox/bower.json b/catapult/third_party/polymer/components/paper-listbox/bower.json
new file mode 100644
index 00000000..6a8be17b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "paper-listbox",
+ "version": "1.1.3",
+ "description": "Implements an accessible material design listbox",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "listbox"
+ ],
+ "main": "paper-listbox.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-listbox"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-listbox",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-listbox/demo/index.html b/catapult/third_party/polymer/components/paper-listbox/demo/index.html
new file mode 100644
index 00000000..c79e63e7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/demo/index.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-listbox demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../iron-collapse/iron-collapse.html">
+ <link rel="import" href="../paper-listbox.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+
+ <style is="custom-style">
+ .horizontal-section {
+ padding: 0 !important;
+ }
+
+ .avatar {
+ display: inline-block;
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ overflow: hidden;
+ background: #ccc;
+ }
+
+ paper-item {
+ --paper-item: {
+ cursor: pointer;
+ };
+ }
+
+ .sublist {
+ padding-left: 20px;
+ padding-right: 20px;
+
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Standard</h4>
+ <div class="horizontal-section">
+ <paper-listbox>
+ <paper-item>Inbox</paper-item>
+ <paper-item>Starred</paper-item>
+ <paper-item>Sent mail</paper-item>
+ <paper-item>Drafts</paper-item>
+ </paper-listbox>
+ </div>
+ </div>
+
+ <div>
+ <h4>Pre-selected</h4>
+ <div class="horizontal-section">
+ <paper-listbox selected="0">
+ <paper-item>Inbox</paper-item>
+ <paper-item disabled>Starred</paper-item>
+ <paper-item>Sent mail</paper-item>
+ <paper-item>Drafts</paper-item>
+ </paper-listbox>
+ </div>
+ </div>
+
+ <div>
+ <h4>Multi-select</h4>
+ <div class="horizontal-section">
+ <paper-listbox multi>
+ <paper-item>Bold</paper-item>
+ <paper-item>Italic</paper-item>
+ <paper-item>Underline</paper-item>
+ <paper-item>Strikethrough</paper-item>
+ </paper-listbox>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-listbox/hero.svg b/catapult/third_party/polymer/components/paper-listbox/hero.svg
new file mode 100755
index 00000000..eaa0fb55
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/hero.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <circle cx="86.5" cy="39" r="4"/>
+ <path d="M138,44c-2,0-3.6-2.4-4.6-4.6c-1.1-2.1-1.7-3.4-3-3.4s-2,1.3-3,3.4c-1.1,2.1-2.2,4.6-4.9,4.6c-2.6,0-3.8-2.4-4.9-4.6
+ c-1.1-2.1-1.8-3.4-3.1-3.4c-1.3,0-2,1.3-3.1,3.4c-1.1,2.1-2.3,4.6-4.9,4.6c-2.6,0-4.1-2.4-5.1-4.6C100.3,37.3,100,36,98,36v-2
+ c3,0,4.1,2.4,5.1,4.6c1.1,2.1,1.9,3.4,3.2,3.4c1.3,0,2.1-1.3,3.2-3.4c1.1-2.1,2.3-4.6,4.9-4.6c2.6,0,3.8,2.4,4.9,4.6
+ c1.1,2.1,1.8,3.4,3.1,3.4c1.3,0,2-1.3,3.1-3.4c1.1-2.1,2.3-4.6,4.9-4.6s3.6,2.4,4.6,4.6c1.1,2.1,1.9,3.4,2.9,3.4V44z"/>
+ <circle cx="86.5" cy="63" r="4"/>
+ <path d="M138,68c-2,0-3.6-2.4-4.6-4.6c-1.1-2.1-1.7-3.4-3-3.4s-2,1.3-3,3.4c-1.1,2.1-2.2,4.6-4.9,4.6c-2.6,0-3.8-2.4-4.9-4.6
+ c-1.1-2.1-1.8-3.4-3.1-3.4c-1.3,0-2,1.3-3.1,3.4c-1.1,2.1-2.3,4.6-4.9,4.6c-2.6,0-4.1-2.4-5.1-4.6C100.3,61.3,100,60,98,60v-2
+ c3,0,4.1,2.4,5.1,4.6c1.1,2.1,1.9,3.4,3.2,3.4c1.3,0,2.1-1.3,3.2-3.4c1.1-2.1,2.3-4.6,4.9-4.6c2.6,0,3.8,2.4,4.9,4.6
+ c1.1,2.1,1.8,3.4,3.1,3.4c1.3,0,2-1.3,3.1-3.4c1.1-2.1,2.3-4.6,4.9-4.6s3.6,2.4,4.6,4.6c1.1,2.1,1.9,3.4,2.9,3.4V68z"/>
+ <circle cx="86.5" cy="88" r="4"/>
+ <path d="M138,93c-2,0-3.6-2.4-4.6-4.6c-1.1-2.1-1.7-3.4-3-3.4s-2,1.3-3,3.4c-1.1,2.1-2.2,4.6-4.9,4.6c-2.6,0-3.8-2.4-4.9-4.6
+ c-1.1-2.1-1.8-3.4-3.1-3.4c-1.3,0-2,1.3-3.1,3.4c-1.1,2.1-2.3,4.6-4.9,4.6c-2.6,0-4.1-2.4-5.1-4.6C100.3,86.3,100,85,98,85v-2
+ c3,0,4.1,2.4,5.1,4.6c1.1,2.1,1.9,3.4,3.2,3.4c1.3,0,2.1-1.3,3.2-3.4c1.1-2.1,2.3-4.6,4.9-4.6c2.6,0,3.8,2.4,4.9,4.6
+ c1.1,2.1,1.8,3.4,3.1,3.4c1.3,0,2-1.3,3.1-3.4c1.1-2.1,2.3-4.6,4.9-4.6s3.6,2.4,4.6,4.6c1.1,2.1,1.9,3.4,2.9,3.4V93z"/>
+ <path d="M151,102H73V24h78V102z M75,100h74V26H75V100z"/>
+ </g>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-listbox/index.html b/catapult/third_party/polymer/components/paper-listbox/index.html
new file mode 100644
index 00000000..b9dad4bd
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-listbox</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-listbox/paper-listbox.html b/catapult/third_party/polymer/components/paper-listbox/paper-listbox.html
new file mode 100644
index 00000000..15d6cf0e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/paper-listbox.html
@@ -0,0 +1,96 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-menu-behavior/iron-menu-behavior.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<!--
+Material design: [Menus](https://www.google.com/design/spec/components/menus.html)
+
+`<paper-listbox>` implements an accessible listbox control with Material Design styling. The focused item
+is highlighted, and the selected item has bolded text.
+
+ <paper-listbox>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+ </paper-listbox>
+
+An initial selection can be specified with the `selected` attribute.
+
+ <paper-listbox selected="0">
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+ </paper-listbox>
+
+Make a multi-select listbox with the `multi` attribute. Items in a multi-select listbox can be deselected,
+and multiple item can be selected.
+
+ <paper-listbox multi>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+ </paper-listbox>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-listbox-background-color` | Menu background color | `--primary-background-color`
+`--paper-listbox-color` | Menu foreground color | `--primary-text-color`
+`--paper-listbox` | Mixin applied to the listbox | `{}`
+
+### Accessibility
+
+`<paper-listbox>` has `role="listbox"` by default. A multi-select listbox will also have
+`aria-multiselectable` set. It implements key bindings to navigate through the listbox with the up and
+down arrow keys, esc to exit the listbox, and enter to activate a listbox item. Typing the first letter
+of a listbox item will also focus it.
+
+@group Paper Elements
+@element paper-listbox
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-listbox">
+ <template>
+ <style>
+ :host {
+ display: block;
+ padding: 8px 0;
+
+ background: var(--paper-listbox-background-color, --primary-background-color);
+ color: var(--paper-listbox-color, --primary-text-color);
+
+ @apply(--paper-listbox);
+ }
+ </style>
+
+ <content></content>
+ </template>
+
+ <script>
+ (function() {
+ Polymer({
+ is: 'paper-listbox',
+
+ behaviors: [
+ Polymer.IronMenuBehavior
+ ],
+
+ hostAttributes: {
+ role: 'listbox'
+ }
+ });
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-listbox/test/index.html b/catapult/third_party/polymer/components/paper-listbox/test/index.html
new file mode 100644
index 00000000..59a5e2b1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/test/index.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>paper-listbox tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+
+ WCT.loadSuites([
+ 'paper-listbox.html'
+ ]);
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-listbox/test/paper-listbox.html b/catapult/third_party/polymer/components/paper-listbox/test/paper-listbox.html
new file mode 100644
index 00000000..58a4fa39
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-listbox/test/paper-listbox.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>paper-listbox tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../paper-listbox.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-listbox selected="0">
+ <div role="option">item 1</div>
+ <div role="option">item 2</div>
+ <div role="option">item 3</div>
+ </paper-listbox>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('<paper-listbox>', function() {
+ var listbox;
+
+ setup(function() {
+ listbox = fixture('basic');
+ });
+
+ test('selected item has an appropriate className', function(done) {
+ Polymer.Base.async(function() {
+ assert(listbox.selectedItem.classList.contains('iron-selected'));
+ done();
+ }, 1);
+ });
+
+ test('has listbox aria role', function() {
+ assert(listbox.getAttribute('role') === 'listbox');
+ });
+
+ a11ySuite('basic');
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-material/.bower.json b/catapult/third_party/polymer/components/paper-material/.bower.json
new file mode 100644
index 00000000..ffeeced7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/.bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "paper-material",
+ "version": "1.0.7",
+ "description": "A material design container that looks like a lifted sheet of paper",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "paper",
+ "container"
+ ],
+ "main": "paper-material.html",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-material"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-material",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0"
+ },
+ "_release": "1.0.7",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.7",
+ "commit": "a125df4f0720b375593b0f2e0174373620f24cde"
+ },
+ "_source": "https://github.com/PolymerElements/paper-material.git",
+ "_target": "^1.0.5",
+ "_originalSource": "PolymerElements/paper-material"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-material/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-material/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..43d220da
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-material/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-material/.gitignore b/catapult/third_party/polymer/components/paper-material/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-material/.travis.yml b/catapult/third_party/polymer/components/paper-material/.travis.yml
new file mode 100644
index 00000000..7205a984
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ R8lCIZgsXsGs5DyUoTfZn0SyVglnf0RKyMQhr11/EtOlhAYA2LmGr9W0EpxsJ/+jyobIcmPMQ/MEpqcE4ZfkJNb3mwKYPfEcbD6zlQ/uDK7JX64r5ErPxynMn7cEUbhPkbmX0Kp5i7NK39cR9STth9gHN66Bfoq1Eoju0GFZ0no=
+ - secure: >-
+ KgR0+L67emjn9JkzZoyHkuY24OzRdX5NmHaKiOI2mpKF3XqTSzVRCLdlw1yL9D7apRAhrxFRqv0+Y0kJb44KZWUYl2kckZI85psSMx42Oj6SXgLwa+SNZ8FA/BIetSEYPtmXZTvcPlZwEWqulkagfYN3nw8ugo5Vno75piPtcLs=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-material/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-material/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-material/README.md b/catapult/third_party/polymer/components/paper-material/README.md
new file mode 100644
index 00000000..d1ad7387
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/README.md
@@ -0,0 +1,35 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-material.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-material.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-material)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-material)_
+
+
+##&lt;paper-material&gt;
+
+Material design: [Cards](https://www.google.com/design/spec/components/cards.html)
+
+`paper-material` is a container that renders two shadows on top of each other to
+create the effect of a lifted piece of paper.
+
+Example:
+
+```html
+<paper-material elevation="1">
+ ... content ...
+</paper-material>
+```
+
+
diff --git a/catapult/third_party/polymer/components/paper-material/bower.json b/catapult/third_party/polymer/components/paper-material/bower.json
new file mode 100644
index 00000000..2abf2f9f
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "paper-material",
+ "version": "1.0.7",
+ "description": "A material design container that looks like a lifted sheet of paper",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "paper",
+ "container"
+ ],
+ "main": "paper-material.html",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-material"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-material",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-material/demo/index.html b/catapult/third_party/polymer/components/paper-material/demo/index.html
new file mode 100644
index 00000000..c0b6d394
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/demo/index.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>paper-material demo</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../paper-material.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-material {
+ display: inline-block;
+ background: white;
+ box-sizing: border-box;
+ margin: 8px;
+ padding: 16px;
+ border-radius: 2px;
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Paper-materials can have different elevations</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-material elevation="0">0</paper-material>
+ <paper-material elevation="1">1</paper-material>
+ <paper-material elevation="2">2</paper-material>
+ <paper-material elevation="3">3</paper-material>
+ <paper-material elevation="4">4</paper-material>
+ <paper-material elevation="5">5</paper-material>
+ </template>
+ </demo-snippet>
+
+ <h3>Changes in elevation can be animated</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style>
+ #a1, #a2 { cursor: pointer; }
+ </style>
+ Tap each of these boxes!
+ <div>
+ <paper-material elevation="0" animated id="a1">animated</paper-material>
+ <paper-material elevation="3" id="a2">not animated</paper-material>
+ </div>
+ <script>
+ document.addEventListener('WebComponentsReady', function() {
+ a1.addEventListener('click', _onTap);
+ a2.addEventListener('click', _onTap);
+ });
+ function _onTap(e) {
+ var target = e.target;
+ if (!target.down) {
+ target.elevation += 1;
+ if (target.elevation === 5) {
+ target.down = true;
+ }
+ } else {
+ target.elevation -= 1;
+ if (target.elevation === 0) {
+ target.down = false;
+ }
+ }
+ };
+ </script>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-material/index.html b/catapult/third_party/polymer/components/paper-material/index.html
new file mode 100644
index 00000000..7209e6d0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-material</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-material/paper-material-shared-styles.html b/catapult/third_party/polymer/components/paper-material/paper-material-shared-styles.html
new file mode 100644
index 00000000..b795f4f4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/paper-material-shared-styles.html
@@ -0,0 +1,42 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../paper-styles/shadow.html">
+
+<dom-module id="paper-material-shared-styles">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ }
+
+ :host([elevation="1"]) {
+ @apply(--shadow-elevation-2dp);
+ }
+
+ :host([elevation="2"]) {
+ @apply(--shadow-elevation-4dp);
+ }
+
+ :host([elevation="3"]) {
+ @apply(--shadow-elevation-6dp);
+ }
+
+ :host([elevation="4"]) {
+ @apply(--shadow-elevation-8dp);
+ }
+
+ :host([elevation="5"]) {
+ @apply(--shadow-elevation-16dp);
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-material/paper-material.html b/catapult/third_party/polymer/components/paper-material/paper-material.html
new file mode 100644
index 00000000..29b34370
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/paper-material.html
@@ -0,0 +1,81 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../paper-styles/shadow.html">
+<link rel="import" href="paper-material-shared-styles.html">
+
+<!--
+Material design: [Cards](https://www.google.com/design/spec/components/cards.html)
+
+`paper-material` is a container that renders two shadows on top of each other to
+create the effect of a lifted piece of paper.
+
+Example:
+
+ <paper-material elevation="1">
+ ... content ...
+ </paper-material>
+
+@group Paper Elements
+@demo demo/index.html
+-->
+
+<dom-module id="paper-material">
+ <template>
+ <style include="paper-material-shared-styles"></style>
+ <style>
+ :host([animated]) {
+ @apply(--shadow-transition);
+ }
+ :host {
+ @apply(--paper-material);
+ }
+ </style>
+
+ <content></content>
+ </template>
+</dom-module>
+<script>
+ Polymer({
+ is: 'paper-material',
+
+ properties: {
+ /**
+ * The z-depth of this element, from 0-5. Setting to 0 will remove the
+ * shadow, and each increasing number greater than 0 will be "deeper"
+ * than the last.
+ *
+ * @attribute elevation
+ * @type number
+ * @default 1
+ */
+ elevation: {
+ type: Number,
+ reflectToAttribute: true,
+ value: 1
+ },
+
+ /**
+ * Set this to true to animate the shadow when setting a new
+ * `elevation` value.
+ *
+ * @attribute animated
+ * @type boolean
+ * @default false
+ */
+ animated: {
+ type: Boolean,
+ reflectToAttribute: true,
+ value: false
+ }
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-material/test/index.html b/catapult/third_party/polymer/components/paper-material/test/index.html
new file mode 100644
index 00000000..08fda477
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-material tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'paper-material.html',
+ 'paper-material.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-material/test/paper-material.html b/catapult/third_party/polymer/components/paper-material/test/paper-material.html
new file mode 100644
index 00000000..7949c112
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-material/test/paper-material.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-material basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <link href="../paper-material.html" rel="import">
+
+</head>
+<body>
+ <test-fixture id="TrivialCard">
+ <template>
+ <paper-material elevation="1"></paper-material>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ProgressiveElevations">
+ <template>
+ <paper-material elevation="1"></paper-material>
+ <paper-material elevation="2"></paper-material>
+ <paper-material elevation="3"></paper-material>
+ <paper-material elevation="4"></paper-material>
+ <paper-material elevation="5"></paper-material>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('<paper-material>', function() {
+ suite('with a non-zero elevation attribute', function() {
+ var style;
+ var card;
+
+ setup(function() {
+ card = fixture('TrivialCard');
+ style = window.getComputedStyle(card);
+ });
+
+ test('has a shadow', function() {
+ expect(style.boxShadow).to.be.ok;
+ expect(style.boxShadow).to.not.be.eql('none');
+ });
+
+ test('loses shadow with elevation value 0', function() {
+ card.elevation = 0;
+ expect(style.boxShadow).to.be.eql('none');
+ });
+ });
+
+ suite('progressively increasing values of elevation', function() {
+ var cards;
+
+ setup(function() {
+ cards = fixture('ProgressiveElevations');
+ });
+
+ test('yield progressively "deeper" cards', function() {
+ var lastStyle;
+ var style;
+
+ expect(cards.length).to.be.eql(5);
+
+ cards.forEach(function (card) {
+ style = window.getComputedStyle(card);
+
+ expect(style.boxShadow).to.be.ok;
+ expect(style.boxShadow).to.not.be.eql('none');
+ expect(style.boxShadow).to.not.be.eql(lastStyle && lastStyle.boxShadow);
+
+ lastStyle = style;
+ });
+ });
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu-button/.bower.json b/catapult/third_party/polymer/components/paper-menu-button/.bower.json
new file mode 100644
index 00000000..343c5dab
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/.bower.json
@@ -0,0 +1,56 @@
+{
+ "name": "paper-menu-button",
+ "version": "1.5.2",
+ "description": "A material design element that composes a trigger and a dropdown menu",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "menu",
+ "button"
+ ],
+ "main": "paper-menu-button.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-menu-button.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-menu-button",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-dropdown": "PolymerElements/iron-dropdown#^1.0.0",
+ "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^1.2.0",
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "iron-image": "PolymerElements/iron-image#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "_release": "1.5.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.2",
+ "commit": "0f43421bc76ca863b16b82b8e4c67efb0267b818"
+ },
+ "_source": "https://github.com/PolymerElements/paper-menu-button.git",
+ "_target": "^1.3.0",
+ "_originalSource": "PolymerElements/paper-menu-button"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-menu-button/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-menu-button/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..19452036
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-menu-button/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-menu-button/.gitignore b/catapult/third_party/polymer/components/paper-menu-button/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-menu-button/.travis.yml b/catapult/third_party/polymer/components/paper-menu-button/.travis.yml
new file mode 100644
index 00000000..ed24d39b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/.travis.yml
@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+before_script:
+- npm install -g bower polylint web-component-tester
+- bower install
+- polylint
+env:
+ global:
+ - secure: aW7z9DGrjDS5YbxO0MSFRykivuQN/XWtuZH1EnKOBOnXv4xEpC51LRQIoe5t292WIuulVL32FdqJ+wS7rKK5aevnH3bMIic+4x9LSydYprPfMnegM9I4Lp9ZpvpE7Ayg12Py9gQGUiDpv7Miwiee8J7PeX6CgHxtbo8+gIwcxEqr2JJYE5X14PiEiB16pxdCJO68UfX+Blxic51c5VrO/8gqNQLbfTVxfXFKZs+QrQIdwFlHuXvEljOLjmHPEPjj0yRTbLWZu1qB5GGE6f7PTgFeTNI6IB7HIbpF9dcxl41fcl8J16Gh2XWVAz2y0DgXqJzWpJufA/B8R5E3yMS+TQW5I+glWMfV1jRLeDeP8LbioQBVBFBwnsPAPsgpZ4vLBB03+twrEkakL+z6TgRmS31e7BHUm8VOlkL/PpDtqiqW4BhWoaEJlXRTdb8PjOc3Vqs4YsKzN//DVwSPRFyO/D42MGV0L8Q6xNeS0OvgcM+DlWivOubpjbMEiTi6sZZvjZgORT80NboWf/GQIW1b9x02i6YwyoB/Tf64gsRBq6z0ZILfbPwZBvaW7Qb2sqHa72Sa8a3vm0PDSmsdIaRIbcLziPrPm7Os50ivNMRx9a6sdyUicW4LBYVYJdAB0EPoSB18Ac5kKgQYmz6id/4w21RhfL9UurhECw3JB/ODqCw=
+ - secure: Sinn/20WmwsOg5UbcHTFIJ+eLsgFk5OobR6n3Eilgwz7as//YU5IEMES9ofsg5AeUp72ikvuolFWdt+SxI/NzokJBqyA3DdxvZ1hpgwi7aH7cnhxtjc0u0g3oObCW6WpKJZYxhEfs3Cj95KH6oK5idTXTDcaa/lnbIVI7OOIxDYrJ0acpr5MyWnghM9MTHJKTgNaRx6cgk3iq5Nue+eSbh3IetcYS3gCJQN4Rr8yLTHH3DK/3mK5LHgEYLAB5loPRBcVwnPeafZPdmt8FIHIHqLn00/m/SsUp2OUC+63SQso3UcqrCrhrt4W2Dfp+ueWuyvgmjU0nIulPYmf8DzLXQZUAIPpUadh03mG1nJXFpkwvW/VUtqwCIjzRyhaX6lalzjypjrzALEkeNRAjE3N7/C5I8oidnTxLa8Dz7SydAQgl9l+gi44TjX5nxWvq1MUswwAJ547j5izhOsZS+U2powlhRoXxNRKeStndqTyq2xGO648rAMwR7jtPIzC3ch7eGtd0twGyFxJ+ZvaUFzF3QcqG/YJO0XIjsQCpdmrEJHcLNSDK/fWeqgXe/X1cEDWQD/AQBi5whT1/E6GXQWhVtHLpd9x1R3u4zkk5VNk0sEkU6IUvGtWDAeLQnLuUzE+ZPw4XP+tWzcMAmpPwKOqBbK/xisMPlu934v21//lX0Q=
+node_js: stable
+addons:
+ firefox: '46.0'
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+- xvfb-run wct
+- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'default'; fi
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-menu-button/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-menu-button/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-menu-button/README.md b/catapult/third_party/polymer/components/paper-menu-button/README.md
new file mode 100644
index 00000000..4e45429b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/README.md
@@ -0,0 +1,69 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-menu-button-animations.html paper-menu-button.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-menu-button.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-menu-button)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-menu-button)_
+
+
+##&lt;paper-menu-button&gt;
+
+Material design: [Dropdown buttons](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)
+
+`paper-menu-button` allows one to compose a designated "trigger" element with
+another element that represents "content", to create a dropdown menu that
+displays the "content" when the "trigger" is clicked.
+
+The child element with the class `dropdown-trigger` will be used as the
+"trigger" element. The child element with the class `dropdown-content` will be
+used as the "content" element.
+
+The `paper-menu-button` is sensitive to its content's `iron-select` events. If
+the "content" element triggers an `iron-select` event, the `paper-menu-button`
+will close automatically.
+
+Example:
+
+```html
+<paper-menu-button>
+ <paper-icon-button icon="menu" class="dropdown-trigger"></paper-icon-button>
+ <paper-menu class="dropdown-content">
+ <paper-item>Share</paper-item>
+ <paper-item>Settings</paper-item>
+ <paper-item>Help</paper-item>
+ </paper-menu>
+</paper-menu-button>
+```
+
+### Styling
+
+The following custom properties and mixins are also available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-menu-button-dropdown-background` | Background color of the paper-menu-button dropdown | `--primary-background-color` |
+| `--paper-menu-button` | Mixin applied to the paper-menu-button | `{}` |
+| `--paper-menu-button-disabled` | Mixin applied to the paper-menu-button when disabled | `{}` |
+| `--paper-menu-button-dropdown` | Mixin applied to the paper-menu-button dropdown | `{}` |
+| `--paper-menu-button-content` | Mixin applied to the paper-menu-button content | `{}` |
+
+
+
+<!-- No docs for <paper-menu-grow-height-animation> found. -->
+
+<!-- No docs for <paper-menu-grow-width-animation> found. -->
+
+<!-- No docs for <paper-menu-shrink-height-animation> found. -->
+
+<!-- No docs for <paper-menu-shrink-width-animation> found. -->
diff --git a/catapult/third_party/polymer/components/paper-menu-button/bower.json b/catapult/third_party/polymer/components/paper-menu-button/bower.json
new file mode 100644
index 00000000..f930aaf8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "paper-menu-button",
+ "version": "1.5.2",
+ "description": "A material design element that composes a trigger and a dropdown menu",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "web-component",
+ "polymer",
+ "menu",
+ "button"
+ ],
+ "main": "paper-menu-button.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-menu-button.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-menu-button",
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-dropdown": "PolymerElements/iron-dropdown#^1.0.0",
+ "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^1.2.0",
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-icons": "PolymerElements/iron-icons#^1.0.0",
+ "iron-image": "PolymerElements/iron-image#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "paper-menu": "PolymerElements/paper-menu#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-menu-button/demo/index.html b/catapult/third_party/polymer/components/paper-menu-button/demo/index.html
new file mode 100644
index 00000000..e4399514
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/demo/index.html
@@ -0,0 +1,203 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-menu-button</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../iron-icon/iron-icon.html">
+ <link rel="import" href="../../iron-image/iron-image.html">
+ <link rel="import" href="../../paper-menu/paper-menu.html">
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../paper-button/paper-button.html">
+ <link rel="import" href="../../paper-icon-button/paper-icon-button.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+
+ <link rel="import" href="../paper-menu-button.html">
+
+ <style is="custom-style">
+ paper-button {
+ display: block;
+ background: #4285f4;
+ color: #fff;
+ }
+
+ paper-menu {
+ display: block;
+ }
+
+ paper-menu-button {
+ margin: auto;
+ }
+
+ iron-image {
+ padding: 1em;
+ }
+
+ .item {
+ max-width: 300px;
+ }
+
+ .horizontal-section {
+ text-align: center;
+ }
+ </style>
+
+</head>
+<body unresolved>
+
+
+ <template id="Demo" is="dom-bind">
+
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Paper Icon Button + Paper Menu</h4>
+ <div class="horizontal-section">
+ <paper-menu-button>
+ <paper-icon-button icon="menu" class="dropdown-trigger" alt="menu"></paper-icon-button>
+ <paper-menu class="dropdown-content">
+ <template is="dom-repeat" items="[[letters]]" as="letter">
+ <paper-item>[[letter]]</paper-item>
+ </template>
+ </paper-menu>
+ </paper-menu-button>
+ </div>
+ </div>
+ </div>
+
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Paper Menu with multi selection</h4>
+ <div class="horizontal-section">
+ <paper-menu-button ignore-select>
+ <paper-icon-button icon="menu" class="dropdown-trigger" alt="multi select"></paper-icon-button>
+ <paper-menu class="dropdown-content" multi>
+ <template is="dom-repeat" items="[[letters]]" as="letter">
+ <paper-item>[[letter]]</paper-item>
+ </template>
+ </paper-menu>
+ </paper-menu-button>
+ </div>
+ </div>
+ </div>
+
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Disabled</h4>
+ <div class="horizontal-section">
+ <paper-menu-button disabled>
+ <paper-icon-button icon="menu" class="dropdown-trigger" alt="menu"></paper-icon-button>
+ <paper-menu class="dropdown-content">
+ <template is="dom-repeat" items="[[letters]]" as="letter">
+ <paper-item>[[letter]]</paper-item>
+ </template>
+ </paper-menu>
+ </paper-menu-button>
+ </div>
+ </div>
+ </div>
+
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Alternate Alignment</h4>
+ <div class="horizontal-section">
+ <paper-menu-button vertical-align="bottom" horizontal-align="right">
+ <paper-icon-button icon="menu" class="dropdown-trigger" alt="bottom align"></paper-icon-button>
+ <paper-menu class="dropdown-content">
+ <template is="dom-repeat" items="[[letters]]" as="letter">
+ <paper-item>[[letter]]</paper-item>
+ </template>
+ </paper-menu>
+ </paper-menu-button>
+ </div>
+ </div>
+ </div>
+
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Alternate Button</h4>
+ <div class="horizontal-section">
+ <paper-menu-button>
+ <paper-button class="dropdown-trigger" raised>
+ <iron-icon icon="check"></iron-icon>
+ <span>Dinosaurs</span>
+ </paper-button>
+ <paper-menu class="dropdown-content">
+ <template is="dom-repeat" items="[[dinosaurs]]" as="dinosaur">
+ <paper-item>[[dinosaur]]</paper-item>
+ </template>
+ </paper-menu>
+ </paper-menu-button>
+ </div>
+ </div>
+ </div>
+
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Alternate Content</h4>
+ <div class="horizontal-section">
+ <paper-menu-button vertical-align="bottom">
+ <paper-icon-button class="dropdown-trigger" icon="polymer" alt="polymer"></paper-icon-button>
+ <iron-image class="dropdown-content" src="../../iron-image/demo/polymer.svg"></iron-image>
+ </paper-menu-button>
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ Demo.letters = [
+ 'alpha',
+ 'beta',
+ 'gamma',
+ 'delta',
+ 'epsilon'
+ ];
+ Demo.dinosaurs = [
+ 'allosaurus',
+ 'brontosaurus',
+ 'carcharodontosaurus',
+ 'diplodocus',
+ 'ekrixinatosaurus',
+ 'fukuiraptor',
+ 'gallimimus',
+ 'hadrosaurus',
+ 'iguanodon',
+ 'jainosaurus',
+ 'kritosaurus',
+ 'liaoceratops',
+ 'megalosaurus',
+ 'nemegtosaurus',
+ 'ornithomimus',
+ 'protoceratops',
+ 'quetecsaurus',
+ 'rajasaurus',
+ 'stegosaurus',
+ 'triceratops',
+ 'utahraptor',
+ 'vulcanodon',
+ 'wannanosaurus',
+ 'xenoceratops',
+ 'yandusaurus',
+ 'zephyrosaurus'
+ ];
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu-button/hero.svg b/catapult/third_party/polymer/components/paper-menu-button/hero.svg
new file mode 100755
index 00000000..b67f8173
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/hero.svg
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <circle cx="109" cy="45" r="4"/>
+ <path d="M165,50c-2.8,0-4.1-2.4-5.3-4.5c-1.2-2.2-2-3.5-3.5-3.5c-1.5,0-2.3,1.3-3.5,3.5c-1.2,2.1-2.5,4.5-5.3,4.5
+ c-2.8,0-4.1-2.4-5.3-4.5c-1.2-2.2-2-3.5-3.5-3.5c-1.5,0-2.3,1.3-3.5,3.5c-1.2,2.1-2.5,4.5-5.3,4.5c-2.8,0-4.1-2.4-5.3-4.5
+ c-1.2-2.2-2-3.5-3.5-3.5v-2c2.8,0,4.1,2.4,5.3,4.5c1.2,2.2,2,3.5,3.5,3.5c1.5,0,2.3-1.3,3.5-3.5c1.2-2.1,2.5-4.5,5.3-4.5
+ c2.8,0,4.1,2.4,5.3,4.5c1.2,2.2,2,3.5,3.5,3.5c1.5,0,2.3-1.3,3.5-3.5c1.2-2.1,2.5-4.5,5.3-4.5s4.1,2.4,5.3,4.5
+ c1.2,2.2,2,3.5,3.5,3.5V50z"/>
+ <circle cx="109" cy="63" r="4"/>
+ <path d="M165,68c-2.8,0-4.1-2.4-5.3-4.5c-1.2-2.2-2-3.5-3.5-3.5c-1.5,0-2.3,1.3-3.5,3.5c-1.2,2.1-2.5,4.5-5.3,4.5
+ c-2.8,0-4.1-2.4-5.3-4.5c-1.2-2.2-2-3.5-3.5-3.5c-1.5,0-2.3,1.3-3.5,3.5c-1.2,2.1-2.5,4.5-5.3,4.5c-2.8,0-4.1-2.4-5.3-4.5
+ c-1.2-2.2-2-3.5-3.5-3.5v-2c2.8,0,4.1,2.4,5.3,4.5c1.2,2.2,2,3.5,3.5,3.5c1.5,0,2.3-1.3,3.5-3.5c1.2-2.1,2.5-4.5,5.3-4.5
+ c2.8,0,4.1,2.4,5.3,4.5c1.2,2.2,2,3.5,3.5,3.5c1.5,0,2.3-1.3,3.5-3.5c1.2-2.1,2.5-4.5,5.3-4.5s4.1,2.4,5.3,4.5
+ c1.2,2.2,2,3.5,3.5,3.5V68z"/>
+ <circle cx="109" cy="81" r="4"/>
+ <path d="M165,86c-2.8,0-4.1-2.4-5.3-4.5c-1.2-2.2-2-3.5-3.5-3.5c-1.5,0-2.3,1.3-3.5,3.5c-1.2,2.1-2.5,4.5-5.3,4.5
+ c-2.8,0-4.1-2.4-5.3-4.5c-1.2-2.2-2-3.5-3.5-3.5c-1.5,0-2.3,1.3-3.5,3.5c-1.2,2.1-2.5,4.5-5.3,4.5c-2.8,0-4.1-2.4-5.3-4.5
+ c-1.2-2.2-2-3.5-3.5-3.5v-2c2.8,0,4.1,2.4,5.3,4.5c1.2,2.2,2,3.5,3.5,3.5c1.5,0,2.3-1.3,3.5-3.5c1.2-2.1,2.5-4.5,5.3-4.5
+ c2.8,0,4.1,2.4,5.3,4.5c1.2,2.2,2,3.5,3.5,3.5c1.5,0,2.3-1.3,3.5-3.5c1.2-2.1,2.5-4.5,5.3-4.5s4.1,2.4,5.3,4.5
+ c1.2,2.2,2,3.5,3.5,3.5V86z"/>
+ <path d="M176,98H94V28h82V98z M96,96h78V30H96V96z"/>
+ <circle cx="65" cy="61" r="8"/>
+ <path d="M82,78H48V44h34V78z M50,76h30V46H50V76z"/>
+ <rect x="81" y="58" width="14" height="2"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-menu-button/index.html b/catapult/third_party/polymer/components/paper-menu-button/index.html
new file mode 100644
index 00000000..040b1952
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+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
+-->
+<html>
+<head>
+
+ <title>paper-menu-button</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu-button/paper-menu-button-animations.html b/catapult/third_party/polymer/components/paper-menu-button/paper-menu-button-animations.html
new file mode 100644
index 00000000..b1339f29
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/paper-menu-button-animations.html
@@ -0,0 +1,109 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../neon-animation/web-animations.html">
+<link rel="import" href="../neon-animation/neon-animation-behavior.html">
+<script>
+ Polymer({
+ is: 'paper-menu-grow-height-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var height = rect.height;
+
+ this._effect = new KeyframeEffect(node, [{
+ height: (height / 2) + 'px'
+ }, {
+ height: height + 'px'
+ }], this.timingFromConfig(config));
+
+ return this._effect;
+ }
+ });
+
+ Polymer({
+ is: 'paper-menu-grow-width-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var width = rect.width;
+
+ this._effect = new KeyframeEffect(node, [{
+ width: (width / 2) + 'px'
+ }, {
+ width: width + 'px'
+ }], this.timingFromConfig(config));
+
+ return this._effect;
+ }
+ });
+
+ Polymer({
+ is: 'paper-menu-shrink-width-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var width = rect.width;
+
+ this._effect = new KeyframeEffect(node, [{
+ width: width + 'px'
+ }, {
+ width: width - (width / 20) + 'px'
+ }], this.timingFromConfig(config));
+
+ return this._effect;
+ }
+ });
+
+ Polymer({
+ is: 'paper-menu-shrink-height-animation',
+
+ behaviors: [
+ Polymer.NeonAnimationBehavior
+ ],
+
+ configure: function(config) {
+ var node = config.node;
+ var rect = node.getBoundingClientRect();
+ var height = rect.height;
+ var top = rect.top;
+
+ this.setPrefixedProperty(node, 'transformOrigin', '0 0');
+
+ this._effect = new KeyframeEffect(node, [{
+ height: height + 'px',
+ transform: 'translateY(0)'
+ }, {
+ height: height / 2 + 'px',
+ transform: 'translateY(-20px)'
+ }], this.timingFromConfig(config));
+
+ return this._effect;
+ }
+ });
+</script>
+
+
diff --git a/catapult/third_party/polymer/components/paper-menu-button/paper-menu-button.html b/catapult/third_party/polymer/components/paper-menu-button/paper-menu-button.html
new file mode 100644
index 00000000..7bb4c744
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/paper-menu-button.html
@@ -0,0 +1,479 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+<link rel="import" href="../iron-dropdown/iron-dropdown.html">
+<link rel="import" href="../neon-animation/animations/fade-in-animation.html">
+<link rel="import" href="../neon-animation/animations/fade-out-animation.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../paper-styles/shadow.html">
+<link rel="import" href="paper-menu-button-animations.html">
+
+<!--
+Material design: [Dropdown buttons](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)
+
+`paper-menu-button` allows one to compose a designated "trigger" element with
+another element that represents "content", to create a dropdown menu that
+displays the "content" when the "trigger" is clicked.
+
+The child element with the class `dropdown-trigger` will be used as the
+"trigger" element. The child element with the class `dropdown-content` will be
+used as the "content" element.
+
+The `paper-menu-button` is sensitive to its content's `iron-select` events. If
+the "content" element triggers an `iron-select` event, the `paper-menu-button`
+will close automatically.
+
+Example:
+
+ <paper-menu-button>
+ <paper-icon-button icon="menu" class="dropdown-trigger"></paper-icon-button>
+ <paper-menu class="dropdown-content">
+ <paper-item>Share</paper-item>
+ <paper-item>Settings</paper-item>
+ <paper-item>Help</paper-item>
+ </paper-menu>
+ </paper-menu-button>
+
+### Styling
+
+The following custom properties and mixins are also available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-menu-button-dropdown-background` | Background color of the paper-menu-button dropdown | `--primary-background-color`
+`--paper-menu-button` | Mixin applied to the paper-menu-button | `{}`
+`--paper-menu-button-disabled` | Mixin applied to the paper-menu-button when disabled | `{}`
+`--paper-menu-button-dropdown` | Mixin applied to the paper-menu-button dropdown | `{}`
+`--paper-menu-button-content` | Mixin applied to the paper-menu-button content | `{}`
+
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-menu-button">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ position: relative;
+ padding: 8px;
+ outline: none;
+
+ @apply(--paper-menu-button);
+ }
+
+ :host([disabled]) {
+ cursor: auto;
+ color: var(--disabled-text-color);
+
+ @apply(--paper-menu-button-disabled);
+ }
+
+ iron-dropdown {
+ @apply(--paper-menu-button-dropdown);
+ }
+
+ .dropdown-content {
+ @apply(--shadow-elevation-2dp);
+
+ position: relative;
+ border-radius: 2px;
+ background-color: var(--paper-menu-button-dropdown-background, --primary-background-color);
+
+ @apply(--paper-menu-button-content);
+ }
+
+ :host([vertical-align="top"]) .dropdown-content {
+ margin-bottom: 20px;
+ margin-top: -10px;
+ top: 10px;
+ }
+
+ :host([vertical-align="bottom"]) .dropdown-content {
+ bottom: 10px;
+ margin-bottom: -10px;
+ margin-top: 20px;
+ }
+
+ #trigger {
+ cursor: pointer;
+ }
+ </style>
+
+ <div id="trigger" on-tap="toggle">
+ <content select=".dropdown-trigger"></content>
+ </div>
+
+ <iron-dropdown
+ id="dropdown"
+ opened="{{opened}}"
+ horizontal-align="[[horizontalAlign]]"
+ vertical-align="[[verticalAlign]]"
+ dynamic-align="[[dynamicAlign]]"
+ horizontal-offset="[[horizontalOffset]]"
+ vertical-offset="[[verticalOffset]]"
+ no-overlap="[[noOverlap]]"
+ open-animation-config="[[openAnimationConfig]]"
+ close-animation-config="[[closeAnimationConfig]]"
+ no-animations="[[noAnimations]]"
+ focus-target="[[_dropdownContent]]"
+ allow-outside-scroll="[[allowOutsideScroll]]"
+ restore-focus-on-close="[[restoreFocusOnClose]]"
+ on-iron-overlay-canceled="__onIronOverlayCanceled">
+ <div class="dropdown-content">
+ <content id="content" select=".dropdown-content"></content>
+ </div>
+ </iron-dropdown>
+ </template>
+
+ <script>
+ (function() {
+ 'use strict';
+
+ var config = {
+ ANIMATION_CUBIC_BEZIER: 'cubic-bezier(.3,.95,.5,1)',
+ MAX_ANIMATION_TIME_MS: 400
+ };
+
+ var PaperMenuButton = Polymer({
+ is: 'paper-menu-button',
+
+ /**
+ * Fired when the dropdown opens.
+ *
+ * @event paper-dropdown-open
+ */
+
+ /**
+ * Fired when the dropdown closes.
+ *
+ * @event paper-dropdown-close
+ */
+
+ behaviors: [
+ Polymer.IronA11yKeysBehavior,
+ Polymer.IronControlState
+ ],
+
+ properties: {
+ /**
+ * True if the content is currently displayed.
+ */
+ opened: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ observer: '_openedChanged'
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * horizontally relative to the dropdown trigger.
+ */
+ horizontalAlign: {
+ type: String,
+ value: 'left',
+ reflectToAttribute: true
+ },
+
+ /**
+ * The orientation against which to align the menu dropdown
+ * vertically relative to the dropdown trigger.
+ */
+ verticalAlign: {
+ type: String,
+ value: 'top',
+ reflectToAttribute: true
+ },
+
+ /**
+ * If true, the `horizontalAlign` and `verticalAlign` properties will
+ * be considered preferences instead of strict requirements when
+ * positioning the dropdown and may be changed if doing so reduces
+ * the area of the dropdown falling outside of `fitInto`.
+ */
+ dynamicAlign: {
+ type: Boolean
+ },
+
+ /**
+ * A pixel value that will be added to the position calculated for the
+ * given `horizontalAlign`. Use a negative value to offset to the
+ * left, or a positive value to offset to the right.
+ */
+ horizontalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
+ * A pixel value that will be added to the position calculated for the
+ * given `verticalAlign`. Use a negative value to offset towards the
+ * top, or a positive value to offset towards the bottom.
+ */
+ verticalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
+ * If true, the dropdown will be positioned so that it doesn't overlap
+ * the button.
+ */
+ noOverlap: {
+ type: Boolean
+ },
+
+ /**
+ * Set to true to disable animations when opening and closing the
+ * dropdown.
+ */
+ noAnimations: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to disable automatically closing the dropdown after
+ * a selection has been made.
+ */
+ ignoreSelect: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Set to true to enable automatically closing the dropdown after an
+ * item has been activated, even if the selection did not change.
+ */
+ closeOnActivate: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * An animation config. If provided, this will be used to animate the
+ * opening of the dropdown.
+ */
+ openAnimationConfig: {
+ type: Object,
+ value: function() {
+ return [{
+ name: 'fade-in-animation',
+ timing: {
+ delay: 100,
+ duration: 200
+ }
+ }, {
+ name: 'paper-menu-grow-width-animation',
+ timing: {
+ delay: 100,
+ duration: 150,
+ easing: config.ANIMATION_CUBIC_BEZIER
+ }
+ }, {
+ name: 'paper-menu-grow-height-animation',
+ timing: {
+ delay: 100,
+ duration: 275,
+ easing: config.ANIMATION_CUBIC_BEZIER
+ }
+ }];
+ }
+ },
+
+ /**
+ * An animation config. If provided, this will be used to animate the
+ * closing of the dropdown.
+ */
+ closeAnimationConfig: {
+ type: Object,
+ value: function() {
+ return [{
+ name: 'fade-out-animation',
+ timing: {
+ duration: 150
+ }
+ }, {
+ name: 'paper-menu-shrink-width-animation',
+ timing: {
+ delay: 100,
+ duration: 50,
+ easing: config.ANIMATION_CUBIC_BEZIER
+ }
+ }, {
+ name: 'paper-menu-shrink-height-animation',
+ timing: {
+ duration: 200,
+ easing: 'ease-in'
+ }
+ }];
+ }
+ },
+
+ /**
+ * By default, the dropdown will constrain scrolling on the page
+ * to itself when opened.
+ * Set to true in order to prevent scroll from being constrained
+ * to the dropdown when it opens.
+ */
+ allowOutsideScroll: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * Whether focus should be restored to the button when the menu closes.
+ */
+ restoreFocusOnClose: {
+ type: Boolean,
+ value: true
+ },
+
+ /**
+ * This is the element intended to be bound as the focus target
+ * for the `iron-dropdown` contained by `paper-menu-button`.
+ */
+ _dropdownContent: {
+ type: Object
+ }
+ },
+
+ hostAttributes: {
+ role: 'group',
+ 'aria-haspopup': 'true'
+ },
+
+ listeners: {
+ 'iron-activate': '_onIronActivate',
+ 'iron-select': '_onIronSelect'
+ },
+
+ /**
+ * The content element that is contained by the menu button, if any.
+ */
+ get contentElement() {
+ return Polymer.dom(this.$.content).getDistributedNodes()[0];
+ },
+
+ /**
+ * Toggles the drowpdown content between opened and closed.
+ */
+ toggle: function() {
+ if (this.opened) {
+ this.close();
+ } else {
+ this.open();
+ }
+ },
+
+ /**
+ * Make the dropdown content appear as an overlay positioned relative
+ * to the dropdown trigger.
+ */
+ open: function() {
+ if (this.disabled) {
+ return;
+ }
+
+ this.$.dropdown.open();
+ },
+
+ /**
+ * Hide the dropdown content.
+ */
+ close: function() {
+ this.$.dropdown.close();
+ },
+
+ /**
+ * When an `iron-select` event is received, the dropdown should
+ * automatically close on the assumption that a value has been chosen.
+ *
+ * @param {CustomEvent} event A CustomEvent instance with type
+ * set to `"iron-select"`.
+ */
+ _onIronSelect: function(event) {
+ if (!this.ignoreSelect) {
+ this.close();
+ }
+ },
+
+ /**
+ * Closes the dropdown when an `iron-activate` event is received if
+ * `closeOnActivate` is true.
+ *
+ * @param {CustomEvent} event A CustomEvent of type 'iron-activate'.
+ */
+ _onIronActivate: function(event) {
+ if (this.closeOnActivate) {
+ this.close();
+ }
+ },
+
+ /**
+ * When the dropdown opens, the `paper-menu-button` fires `paper-open`.
+ * When the dropdown closes, the `paper-menu-button` fires `paper-close`.
+ *
+ * @param {boolean} opened True if the dropdown is opened, otherwise false.
+ * @param {boolean} oldOpened The previous value of `opened`.
+ */
+ _openedChanged: function(opened, oldOpened) {
+ if (opened) {
+ // TODO(cdata): Update this when we can measure changes in distributed
+ // children in an idiomatic way.
+ // We poke this property in case the element has changed. This will
+ // cause the focus target for the `iron-dropdown` to be updated as
+ // necessary:
+ this._dropdownContent = this.contentElement;
+ this.fire('paper-dropdown-open');
+ } else if (oldOpened != null) {
+ this.fire('paper-dropdown-close');
+ }
+ },
+
+ /**
+ * If the dropdown is open when disabled becomes true, close the
+ * dropdown.
+ *
+ * @param {boolean} disabled True if disabled, otherwise false.
+ */
+ _disabledChanged: function(disabled) {
+ Polymer.IronControlState._disabledChanged.apply(this, arguments);
+ if (disabled && this.opened) {
+ this.close();
+ }
+ },
+
+ __onIronOverlayCanceled: function(event) {
+ var uiEvent = event.detail;
+ var target = Polymer.dom(uiEvent).rootTarget;
+ var trigger = this.$.trigger;
+ var path = Polymer.dom(uiEvent).path;
+
+ if (path.indexOf(trigger) > -1) {
+ event.preventDefault();
+ }
+ }
+ });
+
+ Object.keys(config).forEach(function (key) {
+ PaperMenuButton[key] = config[key];
+ });
+
+ Polymer.PaperMenuButton = PaperMenuButton;
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-menu-button/test/index.html b/catapult/third_party/polymer/components/paper-menu-button/test/index.html
new file mode 100644
index 00000000..a1749e8d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-menu-button tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'paper-menu-button.html',
+ 'paper-menu-button.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-menu-button/test/paper-menu-button.html b/catapult/third_party/polymer/components/paper-menu-button/test/paper-menu-button.html
new file mode 100644
index 00000000..bc223a75
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu-button/test/paper-menu-button.html
@@ -0,0 +1,199 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-menu-button basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../paper-menu-button.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+
+</head>
+<body>
+
+ <test-fixture id="TrivialMenuButton">
+ <template>
+ <paper-menu-button no-animations>
+ <span class="dropdown-trigger">trigger</span>
+ <span class="dropdown-content">content</span>
+ </paper-menu-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="TwoMenuButtons">
+ <template>
+ <paper-menu-button no-animations>
+ <span class="dropdown-trigger">trigger</span>
+ <span class="dropdown-content">content</span>
+ </paper-menu-button>
+ <paper-menu-button no-animations>
+ <span class="dropdown-trigger">trigger</span>
+ <span class="dropdown-content">content</span>
+ </paper-menu-button>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('<paper-menu-button>', function() {
+ var menuButton;
+ var trigger;
+ var content;
+
+ setup(function() {
+ menuButton = fixture('TrivialMenuButton');
+ trigger = Polymer.dom(menuButton).querySelector('.dropdown-trigger');
+ content = Polymer.dom(menuButton).querySelector('.dropdown-content');
+ });
+
+ test('opens when trigger is clicked', function(done) {
+ var contentRect;
+
+ contentRect = content.getBoundingClientRect();
+
+ expect(contentRect.width).to.be.equal(0);
+ expect(contentRect.height).to.be.equal(0);
+
+ menuButton.addEventListener('paper-dropdown-open', function() {
+ expect(menuButton.opened).to.be.equal(true);
+ done();
+ });
+
+ MockInteractions.tap(trigger);
+ });
+
+ test('closes when trigger is clicked again', function(done) {
+ menuButton.addEventListener('paper-dropdown-open', function() {
+ menuButton.addEventListener('paper-dropdown-close', function() {
+ expect(menuButton.opened).to.be.equal(false);
+ done();
+ });
+
+ Polymer.Base.async(function() {
+ MockInteractions.tap(trigger);
+ });
+ });
+
+ MockInteractions.tap(trigger);
+ });
+
+ test('closes when disabled while open', function() {
+ var contentRect;
+
+ menuButton.opened = true;
+ menuButton.disabled = true;
+
+ expect(menuButton.opened).to.be.equal(false);
+
+ contentRect = content.getBoundingClientRect();
+ expect(contentRect.width).to.be.equal(0);
+ expect(contentRect.height).to.be.equal(0);
+ });
+
+ test('has aria-haspopup attribute', function() {
+ expect(menuButton.hasAttribute('aria-haspopup')).to.be.equal(true);
+ });
+
+ test('closes on iron-activate if close-on-activate is true', function(done) {
+ menuButton.closeOnActivate = true;
+
+ menuButton.addEventListener('paper-dropdown-open', function() {
+ menuButton.addEventListener('paper-dropdown-close', function() {
+ done();
+ });
+
+ content.dispatchEvent(new CustomEvent('iron-activate', {
+ bubbles: true,
+ cancelable: true
+ }));
+ });
+
+ MockInteractions.tap(trigger);
+ });
+
+ test('allowOutsideScroll propagates to <iron-dropdown>', function() {
+ menuButton.allowOutsideScroll = false;
+ expect(menuButton.$.dropdown.allowOutsideScroll).to.be.equal(false);
+ menuButton.allowOutsideScroll = true;
+ expect(menuButton.$.dropdown.allowOutsideScroll).to.be.equal(true);
+ });
+
+ test('restoreFocusOnClose propagates to <iron-dropdown>', function() {
+ menuButton.restoreFocusOnClose = false;
+ expect(menuButton.$.dropdown.restoreFocusOnClose).to.be.equal(false);
+ menuButton.restoreFocusOnClose = true;
+ expect(menuButton.$.dropdown.restoreFocusOnClose).to.be.equal(true);
+ });
+
+ });
+
+ suite('when there are two buttons', function() {
+ var menuButton;
+ var trigger;
+ var otherButton;
+ var otherTrigger;
+
+ setup(function() {
+ var buttons = fixture('TwoMenuButtons');
+ menuButton = buttons[0];
+ otherButton = buttons[1];
+ trigger = Polymer.dom(menuButton).querySelector('.dropdown-trigger');
+ otherTrigger = Polymer.dom(otherButton).querySelector('.dropdown-trigger');
+ });
+
+ test('closes current and opens other', function(done) {
+ expect(menuButton.opened).to.be.equal(false);
+ expect(otherButton.opened).to.be.equal(false);
+
+ /*
+ NOTE: iron-overlay-behavior adds listeners asynchronously when the
+ overlay opens, so we need to wait for this event which is a
+ more-explicit signal that tells us that the overlay is really opened.
+ */
+ menuButton.addEventListener('iron-overlay-opened', function() {
+ expect(menuButton.opened).to.be.equal(true);
+ expect(otherButton.opened).to.be.equal(false);
+
+ var firstClosed = false;
+ var secondOpened = false;
+
+ menuButton.addEventListener('paper-dropdown-close', function() {
+ firstClosed = true;
+ });
+
+ otherButton.addEventListener('paper-dropdown-open', function() {
+ secondOpened = true;
+ });
+
+ Polymer.Base.async(function() {
+ MockInteractions.tap(otherTrigger);
+ });
+
+
+ Polymer.Base.async(function() {
+ expect(firstClosed).to.be.equal(true);
+ expect(secondOpened).to.be.equal(true);
+
+ done();
+ });
+ });
+
+ MockInteractions.tap(trigger);
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu/.bower.json b/catapult/third_party/polymer/components/paper-menu/.bower.json
new file mode 100644
index 00000000..7169014e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/.bower.json
@@ -0,0 +1,48 @@
+{
+ "name": "paper-menu",
+ "version": "1.3.0",
+ "description": "Implements an accessible material design menu",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "menu"
+ ],
+ "main": [
+ "paper-menu.html",
+ "paper-submenu.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-menu"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-menu",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "_release": "1.3.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.3.0",
+ "commit": "7ca8ebfbdcca22fc98e5115c0ed72bf9d05848fe"
+ },
+ "_source": "https://github.com/PolymerElements/paper-menu.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-menu"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-menu/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-menu/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..22c96d25
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-menu/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-menu/.gitignore b/catapult/third_party/polymer/components/paper-menu/.gitignore
new file mode 100644
index 00000000..fbe05fc9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/.gitignore
@@ -0,0 +1 @@
+bower_components/
diff --git a/catapult/third_party/polymer/components/paper-menu/.travis.yml b/catapult/third_party/polymer/components/paper-menu/.travis.yml
new file mode 100644
index 00000000..7b0de61b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ QrRAESUd7+2wMubE/Ns6jGY7iUl3ffKPgmejKbWHNBMIXM8pAjtHqNVZrzMEmGhFIIJ9BhcUH1BR2+D8/6NEsLgZQNDZMZTWWc6Ny9QGh5WeDMSyP5/3M8KSKZ4EkFt0E/pMu3ZBwtUaHS3oN43qzBZ0F1p+rOg5I0hqDGfATck=
+ - secure: >-
+ UKIrW6iMM38oTlGvL3fzQ/0jdnTgettommG9+oPv+/zFBKfJQeN4Gxndqv3oqlt081SA5uNXHU7WhjPaGmv4h3oK5glX2p825P683ODlNk4DFzOQVFU3HsdBt3AOFz4zeNVaGJaGOFw0m3V4/yvia9ny7VcDuuDubBbp2Y9mEg4=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-menu/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-menu/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-menu/README.md b/catapult/third_party/polymer/components/paper-menu/README.md
new file mode 100644
index 00000000..3afbf6c0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/README.md
@@ -0,0 +1,113 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-menu.html paper-submenu.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-menu.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-menu)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-menu)_
+
+
+##&lt;paper-menu&gt;
+
+Material design: [Menus](https://www.google.com/design/spec/components/menus.html)
+
+`<paper-menu>` implements an accessible menu control with Material Design styling. The focused item
+is highlighted, and the selected item has bolded text.
+
+```html
+<paper-menu>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+</paper-menu>
+```
+
+An initial selection can be specified with the `selected` attribute.
+
+```html
+<paper-menu selected="0">
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+</paper-menu>
+```
+
+Make a multi-select menu with the `multi` attribute. Items in a multi-select menu can be deselected,
+and multiple items can be selected.
+
+```html
+<paper-menu multi>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+</paper-menu>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-menu-background-color` | Menu background color | `--primary-background-color` |
+| `--paper-menu-color` | Menu foreground color | `--primary-text-color` |
+| `--paper-menu-disabled-color` | Foreground color for a disabled item | `--disabled-text-color` |
+| `--paper-menu` | Mixin applied to the menu | `{}` |
+| `--paper-menu-selected-item` | Mixin applied to the selected item | `{}` |
+| `--paper-menu-focused-item` | Mixin applied to the focused item | `{}` |
+| `--paper-menu-focused-item-after` | Mixin applied to the ::after pseudo-element for the focused item | `{}` |
+
+### Accessibility
+
+`<paper-menu>` has `role="menu"` by default. A multi-select menu will also have
+`aria-multiselectable` set. It implements key bindings to navigate through the menu with the up and
+down arrow keys, esc to exit the menu, and enter to activate a menu item. Typing the first letter
+of a menu item will also focus it.
+
+
+
+##&lt;paper-submenu&gt;
+
+`<paper-submenu>` is a nested menu inside of a parent `<paper-menu>`. It
+consists of a trigger that expands or collapses another `<paper-menu>`:
+
+```html
+<paper-menu>
+ <paper-submenu>
+ <paper-item class="menu-trigger">Topics</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>Topic 1</paper-item>
+ <paper-item>Topic 2</paper-item>
+ <paper-item>Topic 3</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu>
+ <paper-item class="menu-trigger">Faves</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>Fave 1</paper-item>
+ <paper-item>Fave 2</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu disabled>
+ <paper-item class="menu-trigger">Unavailable</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>Disabled 1</paper-item>
+ <paper-item>Disabled 2</paper-item>
+ </paper-menu>
+ </paper-submenu>
+</paper-menu>
+```
+
+Just like in `<paper-menu>`, the focused item is highlighted, and the selected
+item has bolded text. Please see the `<paper-menu>` docs for which attributes
+(such as `multi` and `selected`), and styling options are available for the
+`menu-content` menu.
+
+
diff --git a/catapult/third_party/polymer/components/paper-menu/bower.json b/catapult/third_party/polymer/components/paper-menu/bower.json
new file mode 100644
index 00000000..2416c560
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/bower.json
@@ -0,0 +1,39 @@
+{
+ "name": "paper-menu",
+ "version": "1.3.0",
+ "description": "Implements an accessible material design menu",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "menu"
+ ],
+ "main": [
+ "paper-menu.html",
+ "paper-submenu.html"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-menu"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-menu",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-item": "PolymerElements/paper-item#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-menu/demo/index.html b/catapult/third_party/polymer/components/paper-menu/demo/index.html
new file mode 100644
index 00000000..561fab91
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/demo/index.html
@@ -0,0 +1,149 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <title>paper-menu demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../../paper-styles/demo-pages.html">
+ <link rel="import" href="../paper-menu.html">
+ <link rel="import" href="../paper-submenu.html">
+
+ <style is="custom-style">
+ .horizontal-section {
+ padding: 0 !important;
+ }
+
+ .avatar {
+ display: inline-block;
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ overflow: hidden;
+ background: #ccc;
+ }
+
+ paper-item {
+ --paper-item: {
+ cursor: pointer;
+ };
+ }
+
+ .sublist paper-item {
+ padding-left: 30px;
+ }
+
+ .sublist2 paper-item {
+ padding-left: 50px;
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Standard</h4>
+ <div class="horizontal-section">
+ <paper-menu>
+ <paper-item>Inbox</paper-item>
+ <paper-item>Starred</paper-item>
+ <paper-item>Sent mail</paper-item>
+ <paper-item>Drafts</paper-item>
+ </paper-menu>
+ </div>
+ </div>
+
+ <div>
+ <h4>Pre-selected</h4>
+ <div class="horizontal-section">
+ <paper-menu selected="0">
+ <paper-item>Inbox</paper-item>
+ <paper-item disabled>Starred</paper-item>
+ <paper-item>Sent mail</paper-item>
+ <paper-item>Drafts</paper-item>
+ </paper-menu>
+ </div>
+ </div>
+
+ <div>
+ <h4>Multi-select</h4>
+ <div class="horizontal-section">
+ <paper-menu multi>
+ <paper-item>Bold</paper-item>
+ <paper-item>Italic</paper-item>
+ <paper-item>Underline</paper-item>
+ <paper-item>Strikethrough</paper-item>
+ </paper-menu>
+ </div>
+ </div>
+
+ <div>
+ <h4>Sub-menu</h4>
+ <div class="horizontal-section">
+ <paper-menu attr-for-item-title="label" multi>
+ <paper-submenu label="paper-menu">
+ <paper-item class="menu-trigger">paper-menu</paper-item>
+ <paper-menu class="menu-content sublist" multi>
+ <paper-submenu label="Properties">
+ <paper-item class="menu-trigger">Properties</paper-item>
+ <paper-menu class="menu-content sublist2">
+ <paper-item>focusedItem</paper-item>
+ <paper-item>attrForItemTitle</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu label="Methods">
+ <paper-item class="menu-trigger">Methods</paper-item>
+ <paper-menu class="menu-content sublist2">
+ <paper-item>select(value)</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ </paper-menu>
+ </paper-submenu>
+
+ <paper-submenu label="paper-submenu">
+ <paper-item class="menu-trigger">paper-submenu</paper-item>
+ <paper-menu class="menu-content sublist">
+ <paper-submenu label="Properties">
+ <paper-item class="menu-trigger">Properties</paper-item>
+ <paper-menu class="menu-content sublist2">
+ <paper-item>opened</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu label="Methods">
+ <paper-item class="menu-trigger">Methods</paper-item>
+ <paper-menu class="menu-content sublist2">
+ <paper-item>open()</paper-item>
+ <paper-item>close()</paper-item>
+ <paper-item>toggle()</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ </paper-menu>
+ </paper-submenu>
+
+ <paper-submenu label="Unavailable" disabled>
+ <paper-item class="menu-trigger">Unavailable</paper-item>
+ <paper-menu class="menu-content sublist">
+ <paper-item>Unavailable 1</paper-item>
+ <paper-item>Unavailable 2</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ </paper-menu>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu/hero.svg b/catapult/third_party/polymer/components/paper-menu/hero.svg
new file mode 100755
index 00000000..eaa0fb55
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/hero.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <circle cx="86.5" cy="39" r="4"/>
+ <path d="M138,44c-2,0-3.6-2.4-4.6-4.6c-1.1-2.1-1.7-3.4-3-3.4s-2,1.3-3,3.4c-1.1,2.1-2.2,4.6-4.9,4.6c-2.6,0-3.8-2.4-4.9-4.6
+ c-1.1-2.1-1.8-3.4-3.1-3.4c-1.3,0-2,1.3-3.1,3.4c-1.1,2.1-2.3,4.6-4.9,4.6c-2.6,0-4.1-2.4-5.1-4.6C100.3,37.3,100,36,98,36v-2
+ c3,0,4.1,2.4,5.1,4.6c1.1,2.1,1.9,3.4,3.2,3.4c1.3,0,2.1-1.3,3.2-3.4c1.1-2.1,2.3-4.6,4.9-4.6c2.6,0,3.8,2.4,4.9,4.6
+ c1.1,2.1,1.8,3.4,3.1,3.4c1.3,0,2-1.3,3.1-3.4c1.1-2.1,2.3-4.6,4.9-4.6s3.6,2.4,4.6,4.6c1.1,2.1,1.9,3.4,2.9,3.4V44z"/>
+ <circle cx="86.5" cy="63" r="4"/>
+ <path d="M138,68c-2,0-3.6-2.4-4.6-4.6c-1.1-2.1-1.7-3.4-3-3.4s-2,1.3-3,3.4c-1.1,2.1-2.2,4.6-4.9,4.6c-2.6,0-3.8-2.4-4.9-4.6
+ c-1.1-2.1-1.8-3.4-3.1-3.4c-1.3,0-2,1.3-3.1,3.4c-1.1,2.1-2.3,4.6-4.9,4.6c-2.6,0-4.1-2.4-5.1-4.6C100.3,61.3,100,60,98,60v-2
+ c3,0,4.1,2.4,5.1,4.6c1.1,2.1,1.9,3.4,3.2,3.4c1.3,0,2.1-1.3,3.2-3.4c1.1-2.1,2.3-4.6,4.9-4.6c2.6,0,3.8,2.4,4.9,4.6
+ c1.1,2.1,1.8,3.4,3.1,3.4c1.3,0,2-1.3,3.1-3.4c1.1-2.1,2.3-4.6,4.9-4.6s3.6,2.4,4.6,4.6c1.1,2.1,1.9,3.4,2.9,3.4V68z"/>
+ <circle cx="86.5" cy="88" r="4"/>
+ <path d="M138,93c-2,0-3.6-2.4-4.6-4.6c-1.1-2.1-1.7-3.4-3-3.4s-2,1.3-3,3.4c-1.1,2.1-2.2,4.6-4.9,4.6c-2.6,0-3.8-2.4-4.9-4.6
+ c-1.1-2.1-1.8-3.4-3.1-3.4c-1.3,0-2,1.3-3.1,3.4c-1.1,2.1-2.3,4.6-4.9,4.6c-2.6,0-4.1-2.4-5.1-4.6C100.3,86.3,100,85,98,85v-2
+ c3,0,4.1,2.4,5.1,4.6c1.1,2.1,1.9,3.4,3.2,3.4c1.3,0,2.1-1.3,3.2-3.4c1.1-2.1,2.3-4.6,4.9-4.6c2.6,0,3.8,2.4,4.9,4.6
+ c1.1,2.1,1.8,3.4,3.1,3.4c1.3,0,2-1.3,3.1-3.4c1.1-2.1,2.3-4.6,4.9-4.6s3.6,2.4,4.6,4.6c1.1,2.1,1.9,3.4,2.9,3.4V93z"/>
+ <path d="M151,102H73V24h78V102z M75,100h74V26H75V100z"/>
+ </g>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-menu/index.html b/catapult/third_party/polymer/components/paper-menu/index.html
new file mode 100644
index 00000000..fc884114
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-menu</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu/paper-menu-shared-styles.html b/catapult/third_party/polymer/components/paper-menu/paper-menu-shared-styles.html
new file mode 100644
index 00000000..08ccd584
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/paper-menu-shared-styles.html
@@ -0,0 +1,51 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/color.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+
+<dom-module id="paper-menu-shared-styles">
+ <template>
+ <style>
+ /* need a wrapper element to make this higher specificity than the :host rule in paper-item */
+ .selectable-content > ::content > .iron-selected {
+ font-weight: bold;
+
+ @apply(--paper-menu-selected-item);
+ }
+
+ .selectable-content > ::content > [disabled] {
+ color: var(--paper-menu-disabled-color, --disabled-text-color);
+ }
+
+ .selectable-content > ::content > *:focus {
+ position: relative;
+ outline: 0;
+
+ @apply(--paper-menu-focused-item);
+ }
+
+ .selectable-content > ::content > *:focus:after {
+ @apply(--layout-fit);
+ background: currentColor;
+ opacity: var(--dark-divider-opacity);
+ content: '';
+ pointer-events: none;
+
+ @apply(--paper-menu-focused-item-after);
+ }
+
+ .selectable-content > ::content > *[colored]:focus:after {
+ opacity: 0.26;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-menu/paper-menu.html b/catapult/third_party/polymer/components/paper-menu/paper-menu.html
new file mode 100644
index 00000000..b3f68abb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/paper-menu.html
@@ -0,0 +1,100 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-menu-behavior/iron-menu-behavior.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="paper-menu-shared-styles.html">
+
+<!--
+Material design: [Menus](https://www.google.com/design/spec/components/menus.html)
+
+`<paper-menu>` implements an accessible menu control with Material Design styling. The focused item
+is highlighted, and the selected item has bolded text.
+
+ <paper-menu>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+ </paper-menu>
+
+An initial selection can be specified with the `selected` attribute.
+
+ <paper-menu selected="0">
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+ </paper-menu>
+
+Make a multi-select menu with the `multi` attribute. Items in a multi-select menu can be deselected,
+and multiple items can be selected.
+
+ <paper-menu multi>
+ <paper-item>Item 1</paper-item>
+ <paper-item>Item 2</paper-item>
+ </paper-menu>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-menu-background-color` | Menu background color | `--primary-background-color`
+`--paper-menu-color` | Menu foreground color | `--primary-text-color`
+`--paper-menu-disabled-color` | Foreground color for a disabled item | `--disabled-text-color`
+`--paper-menu` | Mixin applied to the menu | `{}`
+`--paper-menu-selected-item` | Mixin applied to the selected item | `{}`
+`--paper-menu-focused-item` | Mixin applied to the focused item | `{}`
+`--paper-menu-focused-item-after` | Mixin applied to the ::after pseudo-element for the focused item | `{}`
+
+### Accessibility
+
+`<paper-menu>` has `role="menu"` by default. A multi-select menu will also have
+`aria-multiselectable` set. It implements key bindings to navigate through the menu with the up and
+down arrow keys, esc to exit the menu, and enter to activate a menu item. Typing the first letter
+of a menu item will also focus it.
+
+@group Paper Elements
+@element paper-menu
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-menu">
+ <template>
+ <style include="paper-menu-shared-styles"></style>
+ <style>
+ :host {
+ display: block;
+ padding: 8px 0;
+
+ background: var(--paper-menu-background-color, --primary-background-color);
+ color: var(--paper-menu-color, --primary-text-color);
+
+ @apply(--paper-menu);
+ }
+ </style>
+
+ <div class="selectable-content">
+ <content></content>
+ </div>
+ </template>
+
+ <script>
+ (function() {
+ Polymer({
+ is: 'paper-menu',
+
+ behaviors: [
+ Polymer.IronMenuBehavior
+ ]
+ });
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-menu/paper-submenu.html b/catapult/third_party/polymer/components/paper-menu/paper-submenu.html
new file mode 100644
index 00000000..4fadb87e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/paper-submenu.html
@@ -0,0 +1,223 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+<link rel="import" href="../iron-collapse/iron-collapse.html">
+<link rel="import" href="paper-menu-shared-styles.html">
+
+<!--
+`<paper-submenu>` is a nested menu inside of a parent `<paper-menu>`. It
+consists of a trigger that expands or collapses another `<paper-menu>`:
+
+ <paper-menu>
+ <paper-submenu>
+ <paper-item class="menu-trigger">Topics</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>Topic 1</paper-item>
+ <paper-item>Topic 2</paper-item>
+ <paper-item>Topic 3</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu>
+ <paper-item class="menu-trigger">Faves</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>Fave 1</paper-item>
+ <paper-item>Fave 2</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu disabled>
+ <paper-item class="menu-trigger">Unavailable</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>Disabled 1</paper-item>
+ <paper-item>Disabled 2</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ </paper-menu>
+
+Just like in `<paper-menu>`, the focused item is highlighted, and the selected
+item has bolded text. Please see the `<paper-menu>` docs for which attributes
+(such as `multi` and `selected`), and styling options are available for the
+`menu-content` menu.
+
+@group Paper Elements
+@element paper-submenu
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-submenu">
+ <template>
+ <style include="paper-menu-shared-styles"></style>
+
+ <div class="selectable-content" on-tap="_onTap">
+ <content id="trigger" select=".menu-trigger"></content>
+ </div>
+ <iron-collapse id="collapse" opened="{{opened}}">
+ <content id="content" select=".menu-content"></content>
+ </iron-collapse>
+ </template>
+
+ <script>
+
+ (function() {
+
+ Polymer({
+
+ is: 'paper-submenu',
+
+ properties: {
+ /**
+ * Fired when the submenu is opened.
+ *
+ * @event paper-submenu-open
+ */
+
+ /**
+ * Fired when the submenu is closed.
+ *
+ * @event paper-submenu-close
+ */
+
+ /**
+ * Set opened to true to show the collapse element and to false to hide it.
+ *
+ * @attribute opened
+ */
+ opened: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ observer: '_openedChanged'
+ }
+ },
+
+ behaviors: [
+ Polymer.IronControlState
+ ],
+
+ listeners: {
+ 'focus': '_onFocus'
+ },
+
+ get __parent() {
+ return Polymer.dom(this).parentNode;
+ },
+
+ get __trigger() {
+ return Polymer.dom(this.$.trigger).getDistributedNodes()[0];
+ },
+
+ get __content() {
+ return Polymer.dom(this.$.content).getDistributedNodes()[0];
+ },
+
+ attached: function() {
+ this.listen(this.__parent, 'iron-activate', '_onParentIronActivate');
+ },
+
+ detached: function() {
+ this.unlisten(this.__parent, 'iron-activate', '_onParentIronActivate');
+ },
+
+ /**
+ * Expand the submenu content.
+ */
+ open: function() {
+ if (!this.disabled) {
+ this.opened = true;
+ }
+ },
+
+ /**
+ * Collapse the submenu content.
+ */
+ close: function() {
+ this.opened = false;
+ },
+
+ /**
+ * Toggle the submenu.
+ */
+ toggle: function() {
+ if (this.opened) {
+ this.close();
+ } else {
+ this.open();
+ }
+ },
+
+ /**
+ * A handler that is called when the trigger is tapped.
+ */
+ _onTap: function(e) {
+ if (!this.disabled) {
+ this.toggle();
+ }
+ },
+
+ /**
+ * Toggles the submenu content when the trigger is tapped.
+ */
+ _openedChanged: function(opened, oldOpened) {
+ if (opened) {
+ this.__trigger && this.__trigger.classList.add('iron-selected');
+ this.__content && this.__content.focus();
+ this.fire('paper-submenu-open');
+ } else if (oldOpened != null) {
+ this.__trigger && this.__trigger.classList.remove('iron-selected');
+ this.fire('paper-submenu-close');
+ }
+ },
+
+ /**
+ * A handler that is called when `iron-activate` is fired.
+ *
+ * @param {CustomEvent} event An `iron-activate` event.
+ */
+ _onParentIronActivate: function(event) {
+ var parent = this.__parent;
+ if (Polymer.dom(event).localTarget === parent) {
+ // The activated item can either be this submenu, in which case it
+ // should be expanded, or any of the other sibling submenus, in which
+ // case this submenu should be collapsed.
+ if (event.detail.item !== this && !parent.multi) {
+ this.close();
+ }
+ }
+ },
+
+ /**
+ * If the dropdown is open when disabled becomes true, close the
+ * dropdown.
+ *
+ * @param {boolean} disabled True if disabled, otherwise false.
+ */
+ _disabledChanged: function(disabled) {
+ Polymer.IronControlState._disabledChanged.apply(this, arguments);
+ if (disabled && this.opened) {
+ this.close();
+ }
+ },
+
+ /**
+ * Handler that is called when the menu receives focus.
+ *
+ * @param {FocusEvent} event A focus event.
+ */
+ _onFocus: function(event) {
+ this.__trigger && this.__trigger.focus();
+ }
+
+ });
+
+ })();
+</script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-menu/test/index.html b/catapult/third_party/polymer/components/paper-menu/test/index.html
new file mode 100644
index 00000000..7956bc1c
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/test/index.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>paper-menu tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+
+ WCT.loadSuites([
+ 'paper-menu.html',
+ 'paper-submenu.html',
+ 'paper-menu.html?dom=shadow',
+ 'paper-submenu.html?dom=shadow'
+ ]);
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu/test/paper-menu.html b/catapult/third_party/polymer/components/paper-menu/test/paper-menu.html
new file mode 100644
index 00000000..d92e6737
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/test/paper-menu.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>paper-menu tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <link rel="import" href="../paper-menu.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-menu>
+ <div>item 1</div>
+ <div>item 2</div>
+ <div>item 3</div>
+ </paper-menu>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('<paper-menu>', function() {
+ var menu;
+
+ setup(function() {
+ menu = fixture('basic');
+ });
+
+ test('selected item is styled', function() {
+
+ var boldDiv = document.createElement('div');
+ boldDiv.style.fontWeight = 'bold';
+ document.body.appendChild(boldDiv);
+
+ menu.selected = 1;
+
+ assert.equal(getComputedStyle(menu.selectedItem).fontWeight,
+ getComputedStyle(boldDiv).fontWeight, 'selected item is bold');
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-menu/test/paper-submenu.html b/catapult/third_party/polymer/components/paper-menu/test/paper-submenu.html
new file mode 100644
index 00000000..13d8ede3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-menu/test/paper-submenu.html
@@ -0,0 +1,285 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <title>paper-submenu tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../paper-item/paper-item.html">
+ <link rel="import" href="../paper-menu.html">
+ <link rel="import" href="../paper-submenu.html">
+
+ </head>
+ <style>
+ paper-item {
+ font-weight: normal;
+ }
+ </style>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-menu>
+ <paper-submenu>
+ <paper-item class="menu-trigger">Topic 1</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>item 1.1</paper-item>
+ <paper-item>item 1.2</paper-item>
+ <paper-item>item 1.3</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu>
+ <paper-item class="menu-trigger">Topic 2</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>item 2.1</paper-item>
+ <paper-item>item 2.2</paper-item>
+ <paper-item>item 2.3</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ <paper-submenu disabled>
+ <paper-item class="menu-trigger">Topic 3</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>item 3.1</paper-item>
+ <paper-item>item 3.2</paper-item>
+ <paper-item>item 3.3</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ </paper-menu>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="opened">
+ <template>
+ <paper-menu>
+ <paper-submenu class="menu-content" opened>
+ <paper-item class="menu-trigger">My submenu is opened to start!</paper-item>
+ <paper-menu class="menu-content">
+ <paper-item>Triggered item</paper-item>
+ </paper-menu>
+ </paper-submenu>
+ </paper-menu>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('<paper-submenu>', function() {
+ var menu,
+ sub1, sub2, sub3,
+ collapse1, collapse2, collapse3,
+ trigger1, trigger2, trigger3;
+
+ setup(function() {
+ menu = fixture('basic');
+
+ sub1 = menu.querySelectorAll('paper-submenu')[0];
+ sub2 = menu.querySelectorAll('paper-submenu')[1];
+ sub3 = menu.querySelectorAll('paper-submenu')[2];
+
+ collapse1 = Polymer.dom(sub1.root).querySelector('iron-collapse');
+ collapse2 = Polymer.dom(sub2.root).querySelector('iron-collapse');
+ collapse3 = Polymer.dom(sub3.root).querySelector('iron-collapse');
+
+ trigger1 = sub1.querySelector('.menu-trigger');
+ trigger2 = sub2.querySelector('.menu-trigger');
+ trigger3 = sub3.querySelector('.menu-trigger');
+ });
+
+ test('selecting an item expands the submenu', function() {
+ assert.isFalse(collapse1.opened);
+ assert.isFalse(collapse2.opened);
+ assert.isFalse(collapse3.opened);
+
+ MockInteractions.tap(trigger1);
+
+ assert.isTrue(collapse1.opened);
+ assert.isFalse(collapse2.opened);
+ assert.isFalse(collapse3.opened);
+ });
+
+ test('selecting a different item closes the previously opened submenu', function() {
+ assert.isFalse(collapse1.opened);
+ assert.isFalse(collapse2.opened);
+ assert.isFalse(collapse3.opened);
+
+ MockInteractions.tap(trigger1);
+
+ assert.isTrue(collapse1.opened);
+ assert.isFalse(collapse2.opened);
+ assert.isFalse(collapse3.opened);
+
+ MockInteractions.tap(trigger2);
+
+ assert.isFalse(collapse1.opened);
+ assert.isTrue(collapse2.opened);
+ assert.isFalse(collapse3.opened);
+ });
+
+ test('cannot open a disabled submenu', function() {
+ assert.isFalse(collapse1.opened);
+ assert.isFalse(collapse2.opened);
+ assert.isFalse(collapse3.opened);
+
+ MockInteractions.tap(trigger3);
+
+ assert.isFalse(collapse1.opened);
+ assert.isFalse(collapse2.opened);
+ assert.isFalse(collapse3.opened);
+ });
+
+ test('selecting an item styles it and the parent', function() {
+ var boldDiv = document.createElement('div');
+ boldDiv.style.fontWeight = 'bold';
+ document.body.appendChild(boldDiv);
+
+ var normalDiv = document.createElement('div');
+ normalDiv.style.fontWeight = 'normal';
+ document.body.appendChild(normalDiv);
+
+ assert.equal(getComputedStyle(trigger1).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger2).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger3).fontWeight, getComputedStyle(normalDiv).fontWeight);
+
+ var item1 = sub1.querySelector('.menu-content').querySelector('paper-item');
+
+ MockInteractions.tap(trigger1);
+ // Nothing is initially selected.
+ assert.equal(getComputedStyle(item1).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ MockInteractions.tap(item1);
+
+ assert.equal(getComputedStyle(item1).fontWeight, getComputedStyle(boldDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger1).fontWeight, getComputedStyle(boldDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger2).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger3).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ });
+
+ test('selecting a new item de-styles the previous one', function() {
+ var boldDiv = document.createElement('div');
+ boldDiv.style.fontWeight = 'bold';
+ document.body.appendChild(boldDiv);
+
+ var normalDiv = document.createElement('div');
+ normalDiv.style.fontWeight = 'normal';
+ document.body.appendChild(normalDiv);
+
+ assert.equal(getComputedStyle(trigger1).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger2).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger3).fontWeight, getComputedStyle(normalDiv).fontWeight);
+
+ var item1 = sub1.querySelector('.menu-content').querySelector('paper-item');
+ var item2 = sub2.querySelector('.menu-content').querySelector('paper-item');
+
+ MockInteractions.tap(trigger1);
+ MockInteractions.tap(item1);
+ MockInteractions.tap(trigger2);
+ MockInteractions.tap(item2);
+
+ // Both children are still selected even though the first one is hidden.
+ assert.equal(getComputedStyle(item1).fontWeight, getComputedStyle(boldDiv).fontWeight);
+ assert.equal(getComputedStyle(item2).fontWeight, getComputedStyle(boldDiv).fontWeight);
+
+ assert.equal(getComputedStyle(trigger1).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger2).fontWeight, getComputedStyle(boldDiv).fontWeight);
+ assert.equal(getComputedStyle(trigger3).fontWeight, getComputedStyle(normalDiv).fontWeight);
+ });
+
+ test('focus a submenu should redirect focus to the trigger', function(done) {
+ MockInteractions.focus(sub1);
+ flush(function() {
+ assert.equal(sub1.shadowRoot ? sub1.shadowRoot.activeElement :
+ document.activeElement, sub1.__trigger);
+ done();
+ });
+ });
+ });
+
+ suite('<paper-submenu opened>', function() {
+ var opened;
+ var submenu;
+ var collapse;
+
+ var fail = function(msg) {
+ return function() {
+ throw new Error(msg);
+ };
+ };
+
+ setup(function() {
+ opened = fixture('opened');
+ submenu = opened.querySelector('paper-submenu');
+ collapse = Polymer.dom(submenu.root).querySelector('iron-collapse');
+ });
+
+ test('opened binding + .menu-trigger tap', function() {
+ assert.isTrue(submenu.opened);
+
+ var trigger = submenu.querySelector('.menu-trigger');
+ MockInteractions.tap(trigger);
+ assert.isFalse(submenu.opened);
+
+ MockInteractions.tap(trigger);
+ assert.isTrue(submenu.opened);
+ });
+
+ test('opened binding + open()/close()', function() {
+ assert.isTrue(submenu.opened);
+
+ submenu.close();
+ assert.isFalse(submenu.opened);
+ assert.isFalse(collapse.opened);
+
+ submenu.open();
+ assert.isTrue(submenu.opened);
+ assert.isTrue(collapse.opened);
+ });
+
+ test('opened binding + toggle()', function() {
+ assert.isTrue(submenu.opened);
+
+ submenu.toggle();
+ assert.isFalse(submenu.opened);
+ assert.isFalse(collapse.opened);
+
+ submenu.toggle();
+ assert.isTrue(submenu.opened);
+ assert.isTrue(collapse.opened);
+ });
+
+ test('opened binding + open() x 2', function() {
+ assert.isTrue(submenu.opened);
+
+ opened.addEventListener('paper-submenu-open', fail('duplicate open'));
+
+ submenu.open(); // Opening when already opened should not fire().
+ });
+
+ test('opened binding + close() x 2', function() {
+ submenu.close();
+ assert.isFalse(submenu.opened);
+
+ opened.addEventListener('paper-submenu-close', fail('duplicate close'));
+
+ submenu.close(); // Closing again when !opened should not fire().
+ });
+ });
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-progress/.bower.json b/catapult/third_party/polymer/components/paper-progress/.bower.json
new file mode 100644
index 00000000..cfdd43f5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/.bower.json
@@ -0,0 +1,43 @@
+{
+ "name": "paper-progress",
+ "version": "1.0.12",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "A material design progress bar",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "progress",
+ "loading"
+ ],
+ "main": "paper-progress.html",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-progress.git"
+ },
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-range-behavior": "PolymerElements/iron-range-behavior#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/paper-progress",
+ "_release": "1.0.12",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.12",
+ "commit": "15b3f289c6dfe365846d5d312ac89f61fefcb1ee"
+ },
+ "_source": "https://github.com/PolymerElements/paper-progress.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-progress"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-progress/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-progress/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..09b5a1e5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-progress/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-progress/.gitignore b/catapult/third_party/polymer/components/paper-progress/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-progress/.travis.yml b/catapult/third_party/polymer/components/paper-progress/.travis.yml
new file mode 100644
index 00000000..e69c63ca
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ H1BFM+lOHi3yosJV4VpeDHO/N33EeAeQqKbWMAiGfbjMmVUa2Y9gQzpIONMtO09KMTZMw0dlE3GYNLnc44JXlnSvmp34Fh8KpxOz4BxlbIj2aw/M99FWxRAJYpV+BWWKhwwuFq6DctATljcdfvYyHxpjCAFjTIMCCAHtdVrowlxMlivu/BSlBlZ4HR0OfHr7l8PAC1hSieWr5Rpk0PMcQ6B0nEhSTRgGdxEwADnkOL3Sx966vNv3qxwIML29UrZ3mFdfrgnfwdNZTivjDaRhY4tUu7bw5vP8c4PeVR7lalhVBse5J4xKo7Xw80To+bm/0Hyo4ZgZPzEbq0EAIszRnwRo/d4YiFGkyIgFHJ8a/v2h3BWxW+9W51nKsQcFijitu2pMhKC9ankRxL23TK0rtYff6hj/bCwtfy/965PzZgiAeBgcQUZ25+unOc11VAyIEGUZeLAqzulMOn2LAcvSrVEVGpb90whvVxfG3T8hkFbIrPtD3TWrtvt4ul/25pe+eLNET2DUTqrPFfizthyaGFfavykA6PPGI58AFD2EnFmaD9eJC/eCP34FAZec8G2yMbXOYxg6B40Z1ezHo8j7jzbekEJBsHVegpart9YSxQwx5b95SsqlPbyhdLLHIZIzT9dSfbS/l0YFUYbejeMM5koi1qdqXVCdCMpbwS61WA8=
+ - secure: >-
+ KZek5eiBlb80rC3C0pgb39C4mWskrGJdxV0Xp9WBAnJQkXN4fvKgB5QOIGVV8yP/aM824zv/nj1Z61MWcjKw5kYJBon0Hw4Qemk7KJh7kde5bhg+3dWcdfv8Hhz9wuVGxqmZ01N8LWat+dRzEqY5ZawFQ+P2M/FXxxwKKsH3GKpjgjN2G8u7nadD2p4hDIBmVVGK0dqDZDOg+YZ+jEdGyWTbjeVOnktvb6dDfcKm52VIpcgAvqa8CCFFwTE3lFrHK4UdiqxFW2YcIB1lgJADJ1B2wAjL0cuf97uu8jaCpCGMeQXz5/XH5SxLA8ah/6GV7C37+WlEPeyallUUbSn05m/xYVFEMvrw/04bFQUyiwt9/rdqA8OLNOUwIEk9hMWC3LkooE2LTLs4l1OKN2x9OuTGPTU2f4LrH4z32zSChxjI/4fqCFwDJs6MBfQFIlpV1e6F1YOvPLPOtBLLTVK1n1KeXeY9HSvk+f5lxRwS4VFZ1oG6+a/l+MoB2HMUmhEn0Wa+KS2qk5Yfea/UIlvi8IuA7zsXoeYkBuwIT7VJVdU0Oebfkam4jWR6qYs/QQF/MchzungKchhwyhsSzQvCuaHJhdA6D1L0oqkXRpPouJwkcvGu9z+wf+zPfh3h065Trg7tC4DYHnXRSTF0ocO3H+EfImRSP5jsgPz0gMl4iI0=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-progress/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-progress/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-progress/README.md b/catapult/third_party/polymer/components/paper-progress/README.md
new file mode 100644
index 00000000..f2278de0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/README.md
@@ -0,0 +1,49 @@
+[![Build status](https://travis-ci.org/PolymerElements/paper-progress.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-progress)
+
+##&lt;paper-progress&gt;
+
+Material design: [Progress & activity](https://www.google.com/design/spec/components/progress-activity.html)
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../paper-styles/color.html">
+ <link rel="import" href="paper-progress.html">
+ <style is="custom-style">
+ paper-progress {
+ display: block;
+ width: 100%;
+ margin: 20px 0;
+ }
+ paper-progress.slow {
+ --paper-progress-indeterminate-cycle-duration: 5s;
+ }
+ paper-progress.blue {
+ --paper-progress-active-color: var(--paper-light-blue-500);
+ --paper-progress-secondary-color: var(--paper-light-blue-100);
+ }
+ paper-progress.red {
+ --paper-progress-active-color: var(--paper-red-500);
+ --paper-progress-secondary-color: var(--paper-red-100);
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-progress indeterminate class="blue"></paper-progress>
+<paper-progress indeterminate class="slow red"></paper-progress>
+<paper-progress value="40" secondary-progress="80"></paper-progress>
+```
+
+The progress bars are for situations where the percentage completed can be
+determined. They give users a quick sense of how much longer an operation
+will take.
+
+There is also a secondary progress which is useful for displaying intermediate
+progress, such as the buffer level during a streaming playback progress bar.
+
diff --git a/catapult/third_party/polymer/components/paper-progress/bower.json b/catapult/third_party/polymer/components/paper-progress/bower.json
new file mode 100644
index 00000000..f488a191
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "paper-progress",
+ "version": "1.0.12",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "A material design progress bar",
+ "authors": "The Polymer Authors",
+ "keywords": [
+ "web-components",
+ "polymer",
+ "progress",
+ "loading"
+ ],
+ "main": "paper-progress.html",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-progress.git"
+ },
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-range-behavior": "PolymerElements/iron-range-behavior#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-progress/demo/index.html b/catapult/third_party/polymer/components/paper-progress/demo/index.html
new file mode 100644
index 00000000..6f7b02b1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/demo/index.html
@@ -0,0 +1,127 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<!doctype html>
+<html>
+<head>
+ <title>paper-progress demo</title>
+
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../paper-progress.html">
+ <link rel="import" href="../../paper-button/paper-button.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-progress {
+ display: block;
+ width: 100%;
+ margin: 20px 0;
+ }
+ paper-button {
+ display: inline-block;
+ padding: 5px;
+ }
+ </style>
+
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>paper-progress can be imperatively controlled</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <p>Once started, loops 5 times before stopping.
+ <!-- View the source code to see the contents of startProgress() -->
+ <paper-button raised onclick="startProgress();" id="start">Start</paper-button>
+ </p>
+ <paper-progress id="progress"></paper-progress>
+ </template>
+ </demo-snippet>
+
+ <h3>paper-progress can be indeterminate with a custom duration</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-progress.slow {
+ --paper-progress-indeterminate-cycle-duration: 20s;
+ }
+ </style>
+ <paper-progress indeterminate></paper-progress>
+ <paper-progress indeterminate class="slow"></paper-progress>
+ </template>
+ </demo-snippet>
+
+ <h3>It can be styled using custom properties</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-progress.blue {
+ --paper-progress-active-color: var(--paper-light-blue-500);
+ --paper-progress-secondary-color: var(--paper-light-blue-100);
+ }
+
+ paper-progress.red {
+ --paper-progress-active-color: var(--paper-red-500);
+ --paper-progress-secondary-color: var(--paper-red-100);
+ }
+
+ paper-progress.green {
+ --paper-progress-active-color: var(--paper-light-green-500);
+ --paper-progress-secondary-color: var(--paper-light-green-100);
+ }
+ </style>
+ <paper-progress value="800" min="100" max="1000" class="red"></paper-progress>
+ <paper-progress value="60" class="green"></paper-progress>
+ <paper-progress value="40" secondary-progress="80" class="blue"></paper-progress>
+ </template>
+ </demo-snippet>
+ </div>
+
+ <script>
+ var progress, button;
+ var repeat, maxRepeat = 5, animating = false;
+
+ function nextProgress() {
+ animating = true;
+ if (progress.value < progress.max) {
+ progress.value += (progress.step || 1);
+ } else {
+ if (++repeat >= maxRepeat) {
+ animating = false;
+ button.disabled = false;
+ return;
+ }
+ progress.value = progress.min;
+ }
+ requestAnimationFrame(nextProgress);
+ }
+
+ function startProgress() {
+ repeat = 0;
+ progress.value = progress.min;
+ button.disabled = true;
+ if (!animating) {
+ nextProgress();
+ }
+ }
+
+ window.addEventListener('WebComponentsReady', function() {
+ progress = document.querySelector('paper-progress');
+ button = document.querySelector('paper-button');
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-progress/hero.svg b/catapult/third_party/polymer/components/paper-progress/hero.svg
new file mode 100755
index 00000000..0f569e62
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/hero.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <rect x="57" y="59" width="20" height="2"/>
+ <rect x="38" y="59" width="11" height="2"/>
+ <rect x="84" y="59" width="40" height="2"/>
+ <rect x="133" y="59" width="54" height="2"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-progress/index.html b/catapult/third_party/polymer/components/paper-progress/index.html
new file mode 100644
index 00000000..225e3dd9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <title>paper-progress</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-progress/paper-progress.html b/catapult/third_party/polymer/components/paper-progress/paper-progress.html
new file mode 100644
index 00000000..f0334b42
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/paper-progress.html
@@ -0,0 +1,354 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../iron-range-behavior/iron-range-behavior.html">
+<link rel="import" href="../paper-styles/color.html">
+
+<!--
+Material design: [Progress & activity](https://www.google.com/design/spec/components/progress-activity.html)
+
+The progress bars are for situations where the percentage completed can be
+determined. They give users a quick sense of how much longer an operation
+will take.
+
+Example:
+
+ <paper-progress value="10"></paper-progress>
+
+There is also a secondary progress which is useful for displaying intermediate
+progress, such as the buffer level during a streaming playback progress bar.
+
+Example:
+
+ <paper-progress value="10" secondary-progress="30"></paper-progress>
+
+### Styling progress bar:
+
+To change the active progress bar color:
+
+ paper-progress {
+ --paper-progress-active-color: #e91e63;
+ }
+
+To change the secondary progress bar color:
+
+ paper-progress {
+ --paper-progress-secondary-color: #f8bbd0;
+ }
+
+To change the progress bar background color:
+
+ paper-progress {
+ --paper-progress-container-color: #64ffda;
+ }
+
+Add the class `transiting` to a paper-progress to animate the progress bar when
+the value changed. You can also customize the transition:
+
+ paper-progress {
+ --paper-progress-transition-duration: 0.08s;
+ --paper-progress-transition-timing-function: ease;
+ --paper-progress-transition-transition-delay: 0s;
+ }
+
+To change the duration of the indeterminate cycle:
+
+ paper-progress {
+ --paper-progress-indeterminate-cycle-duration: 2s;
+ }
+
+The following mixins are available for styling:
+
+Custom property | Description | Default
+-------------------------------------------------|---------------------------------------------|--------------
+`--paper-progress-container` | Mixin applied to container | `{}`
+`--paper-progress-transition-duration` | Duration of the transition | `0.008s`
+`--paper-progress-transition-timing-function` | The timing function for the transition | `ease`
+`--paper-progress-transition-delay` | delay for the transition | `0s`
+`--paper-progress-container-color` | Color of the container | `--google-grey-300`
+`--paper-progress-active-color` | The color of the active bar | `--google-green-500`
+`--paper-progress-secondary-color` | The color of the secondary bar | `--google-green-100`
+`--paper-progress-disabled-active-color` | The color of the active bar if disabled | `--google-grey-500`
+`--paper-progress-disabled-secondary-color` | The color of the secondary bar if disabled | `--google-grey-300`
+`--paper-progress-height` | The height of the progress bar | `4px`
+`--paper-progress-indeterminate-cycle-duration` | Duration of an indeterminate cycle | `2s`
+
+@group Paper Elements
+@element paper-progress
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-progress">
+ <template>
+ <style>
+ :host {
+ display: block;
+ width: 200px;
+ position: relative;
+ overflow: hidden;
+ }
+
+ :host([hidden]) {
+ display: none !important;
+ }
+
+ #progressContainer {
+ @apply(--paper-progress-container);
+ position: relative;
+ }
+
+ #progressContainer,
+ /* the stripe for the indeterminate animation*/
+ .indeterminate::after {
+ height: var(--paper-progress-height, 4px);
+ }
+
+ #primaryProgress,
+ #secondaryProgress,
+ .indeterminate::after {
+ @apply(--layout-fit);
+ }
+
+ #progressContainer,
+ .indeterminate::after {
+ background: var(--paper-progress-container-color, --google-grey-300);
+ }
+
+ :host(.transiting) #primaryProgress,
+ :host(.transiting) #secondaryProgress {
+ -webkit-transition-property: -webkit-transform;
+ transition-property: transform;
+
+ /* Duration */
+ -webkit-transition-duration: var(--paper-progress-transition-duration, 0.08s);
+ transition-duration: var(--paper-progress-transition-duration, 0.08s);
+
+ /* Timing function */
+ -webkit-transition-timing-function: var(--paper-progress-transition-timing-function, ease);
+ transition-timing-function: var(--paper-progress-transition-timing-function, ease);
+
+ /* Delay */
+ -webkit-transition-delay: var(--paper-progress-transition-delay, 0s);
+ transition-delay: var(--paper-progress-transition-delay, 0s);
+ }
+
+ #primaryProgress,
+ #secondaryProgress {
+ @apply(--layout-fit);
+ -webkit-transform-origin: left center;
+ transform-origin: left center;
+ -webkit-transform: scaleX(0);
+ transform: scaleX(0);
+ will-change: transform;
+ }
+
+ #primaryProgress {
+ background: var(--paper-progress-active-color, --google-green-500);
+ }
+
+ #secondaryProgress {
+ background: var(--paper-progress-secondary-color, --google-green-100);
+ }
+
+ :host([disabled]) #primaryProgress {
+ background: var(--paper-progress-disabled-active-color, --google-grey-500);
+ }
+
+ :host([disabled]) #secondaryProgress {
+ background: var(--paper-progress-disabled-secondary-color, --google-grey-300);
+ }
+
+ :host(:not([disabled])) #primaryProgress.indeterminate {
+ -webkit-transform-origin: right center;
+ transform-origin: right center;
+ -webkit-animation: indeterminate-bar var(--paper-progress-indeterminate-cycle-duration, 2s) linear infinite;
+ animation: indeterminate-bar var(--paper-progress-indeterminate-cycle-duration, 2s) linear infinite;
+ }
+
+ :host(:not([disabled])) #primaryProgress.indeterminate::after {
+ content: "";
+ -webkit-transform-origin: center center;
+ transform-origin: center center;
+
+ -webkit-animation: indeterminate-splitter var(--paper-progress-indeterminate-cycle-duration, 2s) linear infinite;
+ animation: indeterminate-splitter var(--paper-progress-indeterminate-cycle-duration, 2s) linear infinite;
+ }
+
+ @-webkit-keyframes indeterminate-bar {
+ 0% {
+ -webkit-transform: scaleX(1) translateX(-100%);
+ }
+ 50% {
+ -webkit-transform: scaleX(1) translateX(0%);
+ }
+ 75% {
+ -webkit-transform: scaleX(1) translateX(0%);
+ -webkit-animation-timing-function: cubic-bezier(.28,.62,.37,.91);
+ }
+ 100% {
+ -webkit-transform: scaleX(0) translateX(0%);
+ }
+ }
+
+ @-webkit-keyframes indeterminate-splitter {
+ 0% {
+ -webkit-transform: scaleX(.75) translateX(-125%);
+ }
+ 30% {
+ -webkit-transform: scaleX(.75) translateX(-125%);
+ -webkit-animation-timing-function: cubic-bezier(.42,0,.6,.8);
+ }
+ 90% {
+ -webkit-transform: scaleX(.75) translateX(125%);
+ }
+ 100% {
+ -webkit-transform: scaleX(.75) translateX(125%);
+ }
+ }
+
+ @keyframes indeterminate-bar {
+ 0% {
+ transform: scaleX(1) translateX(-100%);
+ }
+ 50% {
+ transform: scaleX(1) translateX(0%);
+ }
+ 75% {
+ transform: scaleX(1) translateX(0%);
+ animation-timing-function: cubic-bezier(.28,.62,.37,.91);
+ }
+ 100% {
+ transform: scaleX(0) translateX(0%);
+ }
+ }
+
+ @keyframes indeterminate-splitter {
+ 0% {
+ transform: scaleX(.75) translateX(-125%);
+ }
+ 30% {
+ transform: scaleX(.75) translateX(-125%);
+ animation-timing-function: cubic-bezier(.42,0,.6,.8);
+ }
+ 90% {
+ transform: scaleX(.75) translateX(125%);
+ }
+ 100% {
+ transform: scaleX(.75) translateX(125%);
+ }
+ }
+ </style>
+
+ <div id="progressContainer">
+ <div id="secondaryProgress" hidden$="[[_hideSecondaryProgress(secondaryRatio)]]"></div>
+ <div id="primaryProgress"></div>
+ </div>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'paper-progress',
+
+ behaviors: [
+ Polymer.IronRangeBehavior
+ ],
+
+ properties: {
+ /**
+ * The number that represents the current secondary progress.
+ */
+ secondaryProgress: {
+ type: Number,
+ value: 0
+ },
+
+ /**
+ * The secondary ratio
+ */
+ secondaryRatio: {
+ type: Number,
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Use an indeterminate progress indicator.
+ */
+ indeterminate: {
+ type: Boolean,
+ value: false,
+ observer: '_toggleIndeterminate'
+ },
+
+ /**
+ * True if the progress is disabled.
+ */
+ disabled: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ observer: '_disabledChanged'
+ }
+ },
+
+ observers: [
+ '_progressChanged(secondaryProgress, value, min, max)'
+ ],
+
+ hostAttributes: {
+ role: 'progressbar'
+ },
+
+ _toggleIndeterminate: function(indeterminate) {
+ // If we use attribute/class binding, the animation sometimes doesn't translate properly
+ // on Safari 7.1. So instead, we toggle the class here in the update method.
+ this.toggleClass('indeterminate', indeterminate, this.$.primaryProgress);
+ },
+
+ _transformProgress: function(progress, ratio) {
+ var transform = 'scaleX(' + (ratio / 100) + ')';
+ progress.style.transform = progress.style.webkitTransform = transform;
+ },
+
+ _mainRatioChanged: function(ratio) {
+ this._transformProgress(this.$.primaryProgress, ratio);
+ },
+
+ _progressChanged: function(secondaryProgress, value, min, max) {
+ secondaryProgress = this._clampValue(secondaryProgress);
+ value = this._clampValue(value);
+
+ var secondaryRatio = this._calcRatio(secondaryProgress) * 100;
+ var mainRatio = this._calcRatio(value) * 100;
+
+ this._setSecondaryRatio(secondaryRatio);
+ this._transformProgress(this.$.secondaryProgress, secondaryRatio);
+ this._transformProgress(this.$.primaryProgress, mainRatio);
+
+ this.secondaryProgress = secondaryProgress;
+
+ this.setAttribute('aria-valuenow', value);
+ this.setAttribute('aria-valuemin', min);
+ this.setAttribute('aria-valuemax', max);
+ },
+
+ _disabledChanged: function(disabled) {
+ this.setAttribute('aria-disabled', disabled ? 'true' : 'false');
+ },
+
+ _hideSecondaryProgress: function(secondaryRatio) {
+ return secondaryRatio === 0;
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-progress/test/basic.html b/catapult/third_party/polymer/components/paper-progress/test/basic.html
new file mode 100644
index 00000000..73547501
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/test/basic.html
@@ -0,0 +1,148 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-progress test</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link rel="import" href="../paper-progress.html">
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+</head>
+<body>
+
+
+ <test-fixture id="trivialProgress">
+ <template>
+ <paper-progress></paper-progress>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="transitingProgress">
+ <template>
+ <paper-progress class="transiting"></paper-progress>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('basic features', function() {
+ var progress;
+
+ setup(function() {
+ progress = fixture('trivialProgress');
+ });
+
+ test('check default', function() {
+ assert.equal(progress.min, 0);
+ assert.equal(progress.max, 100);
+ assert.equal(progress.value, 0);
+ });
+
+ test('set value', function(done) {
+ progress.value = 50;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.value, 50);
+ // test clamp value
+ progress.value = 60.1;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.value, 60);
+ done();
+ });
+ });
+ });
+
+ test('set max', function(done) {
+ progress.max = 10;
+ progress.value = 11;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.value, progress.max);
+ done();
+ });
+ });
+
+ test('test ratio', function(done) {
+ progress.max = 10;
+ progress.value = 5;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.ratio, 50);
+ done();
+ });
+ });
+
+ test('test secondary ratio', function(done) {
+ progress.max = 10;
+ progress.secondaryProgress = 5;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.secondaryRatio, 50);
+ done();
+ });
+ });
+
+ test('set min', function(done) {
+ progress.min = 10
+ progress.max = 50;
+ progress.value = 30;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.ratio, 50);
+ progress.value = 0;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.value, progress.min);
+ done();
+ });
+ });
+ });
+
+ test('set step', function(done) {
+ progress.min = 0;
+ progress.max = 10;
+ progress.value = 5.1;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.value, 5);
+ progress.step = 0.1;
+ progress.value = 5.1;
+ asyncPlatformFlush(function() {
+ assert.equal(progress.value, 5.1);
+ done();
+ });
+ });
+ });
+ });
+
+ suite('transiting class', function() {
+ var progress;
+
+ setup(function() {
+ progress = fixture('transitingProgress');
+ });
+
+ test('progress bars', function() {
+ var stylesForPrimaryProgress = window.getComputedStyle(progress.$.primaryProgress);
+ var stylesForSecondaryProgress = window.getComputedStyle(progress.$.secondaryProgress);
+ var transitionProp = stylesForPrimaryProgress['transition-property'];
+
+ assert.isTrue(transitionProp === 'transform' || transitionProp === '-webkit-transform');
+ assert.equal(stylesForPrimaryProgress['transition-duration'], '0.08s');
+
+ transitionProp = stylesForSecondaryProgress['transition-property'];
+
+ assert.isTrue(transitionProp === 'transform' || transitionProp === '-webkit-transform');
+ assert.equal(stylesForSecondaryProgress['transition-duration'], '0.08s');
+ });
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-progress/test/index.html b/catapult/third_party/polymer/components/paper-progress/test/index.html
new file mode 100644
index 00000000..b66f1def
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-progress/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-radio-button/.bower.json b/catapult/third_party/polymer/components/paper-radio-button/.bower.json
new file mode 100644
index 00000000..8d2b70e5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/.bower.json
@@ -0,0 +1,48 @@
+{
+ "name": "paper-radio-button",
+ "version": "1.4.0",
+ "description": "A material design radio button",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "radio",
+ "control"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-radio-button"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-radio-button",
+ "ignore": [],
+ "dependencies": {
+ "iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#~1.3.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "paper-radio-button.html",
+ "_release": "1.4.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.4.0",
+ "commit": "853490b37537ed1675e14c3f68a40db0be45fe9a"
+ },
+ "_source": "https://github.com/PolymerElements/paper-radio-button.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-radio-button",
+ "_direct": true
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-radio-button/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-radio-button/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..0a8739ed
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-radio-button/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-radio-button/.gitignore b/catapult/third_party/polymer/components/paper-radio-button/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-radio-button/.travis.yml b/catapult/third_party/polymer/components/paper-radio-button/.travis.yml
new file mode 100644
index 00000000..ff46d558
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ GigwuEaV52dS0ZJk4iaUTWOW7zZTtD/j3KqPny9XS71GxDFve3KKAXvhPCHHKuZwsOKWJ3JnBIXDgEVxHwkS6jUVAEVPDrmF6OZxs5rkRTDFeGKVfoYzCkssj6NE+/XnDMOF5gFdRuI4ZBS/kMB9OASeNsH84wgkMazh9GPu9Lk=
+ - secure: >-
+ EURQh/CaC+RBsHTw5xbyZAFPc6cgQQWFFvp35A8ZHTLvHrhAIybnBb/wisGpPyulSZUd72wdIMNxaUAmP9bniwLs94tzmqbDuBtgZ4a1IPHIm9qGzn7q4JMU2mUYriP/HDIc9Liu6nfccrf7YgYrYKVupu72WU5FktvyWedUj7w=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-radio-button/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-radio-button/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-radio-button/README.md b/catapult/third_party/polymer/components/paper-radio-button/README.md
new file mode 100644
index 00000000..a99fda07
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/README.md
@@ -0,0 +1,56 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-radio-button.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-radio-button.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-radio-button)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-radio-button)_
+
+
+##&lt;paper-radio-button&gt;
+
+Material design: [Radio button](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-radio-button)
+
+`paper-radio-button` is a button that can be either checked or unchecked.
+User can tap the radio button to check or uncheck it.
+
+Use a `<paper-radio-group>` to group a set of radio buttons. When radio buttons
+are inside a radio group, exactly one radio button in the group can be checked
+at any time.
+
+Example:
+
+```html
+<paper-radio-button></paper-radio-button>
+<paper-radio-button>Item label</paper-radio-button>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-radio-button-unchecked-background-color` | Radio button background color when the input is not checked | `transparent` |
+| `--paper-radio-button-unchecked-color` | Radio button color when the input is not checked | `--primary-text-color` |
+| `--paper-radio-button-unchecked-ink-color` | Selected/focus ripple color when the input is not checked | `--primary-text-color` |
+| `--paper-radio-button-checked-color` | Radio button color when the input is checked | `--primary-color` |
+| `--paper-radio-button-checked-ink-color` | Selected/focus ripple color when the input is checked | `--primary-color` |
+| `--paper-radio-button-size` | Size of the radio button | `16px` |
+| `--paper-radio-button-label-color` | Label color | `--primary-text-color` |
+| `--paper-radio-button-label-spacing` | Spacing between the label and the button | `10px` |
+
+This element applies the mixin `--paper-font-common-base` but does not import `paper-styles/typography.html`.
+In order to apply the `Roboto` font to this element, make sure you've imported `paper-styles/typography.html`.
+
+
diff --git a/catapult/third_party/polymer/components/paper-radio-button/bower.json b/catapult/third_party/polymer/components/paper-radio-button/bower.json
new file mode 100644
index 00000000..90ec58cb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/bower.json
@@ -0,0 +1,38 @@
+{
+ "name": "paper-radio-button",
+ "version": "1.4.0",
+ "description": "A material design radio button",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "radio",
+ "control"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-radio-button"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-radio-button",
+ "ignore": [],
+ "dependencies": {
+ "iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#~1.3.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.1.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "paper-radio-button.html"
+}
diff --git a/catapult/third_party/polymer/components/paper-radio-button/demo/index.html b/catapult/third_party/polymer/components/paper-radio-button/demo/index.html
new file mode 100644
index 00000000..930e399e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/demo/index.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <title>paper-radio-button demo</title>
+
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../paper-radio-button.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ .vertical-section-container {
+ max-width: 630px;
+ }
+
+ paper-radio-button {
+ margin-right: 24px;
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Radio buttons can be checked or unchecked, or disabled entirely</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-radio-button>Radio</paper-radio-button>
+ <paper-radio-button checked>Radio</paper-radio-button>
+ <paper-radio-button disabled>Disabled</paper-radio-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Radio buttons can hide the ripple effect using the <i>noink</i> attribute</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-radio-button noink>Radio</paper-radio-button>
+ </template>
+ </demo-snippet>
+
+ <h3>Radio buttons can be styled using custom properties</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-radio-button.red {
+ --paper-radio-button-checked-color: var(--paper-red-500);
+ --paper-radio-button-checked-ink-color: var(--paper-red-500);
+ --paper-radio-button-unchecked-color: var(--paper-red-900);
+ --paper-radio-button-unchecked-ink-color: var(--paper-red-900);
+ --paper-radio-button-label-color: var(--paper-red-500);
+ }
+ paper-radio-button.green {
+ --paper-radio-button-checked-color: var(--paper-green-500);
+ --paper-radio-button-checked-ink-color: var(--paper-green-500);
+ --paper-radio-button-unchecked-color: var(--paper-green-900);
+ --paper-radio-button-unchecked-ink-color: var(--paper-green-900);
+ --paper-radio-button-label-color: var(--paper-green-500);
+ }
+ </style>
+
+ <paper-radio-button class="red">Radio</paper-radio-button>
+ <paper-radio-button checked class="green">Radio</paper-radio-button>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-radio-button/hero.svg b/catapult/third_party/polymer/components/paper-radio-button/hero.svg
new file mode 100755
index 00000000..7fbac94d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/hero.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <circle cx="112.5" cy="63" r="8"/>
+ <path d="M112.5,80c-9.4,0-17-7.6-17-17s7.6-17,17-17s17,7.6,17,17S121.9,80,112.5,80z M112.5,48c-8.3,0-15,6.7-15,15s6.7,15,15,15
+ s15-6.7,15-15S120.8,48,112.5,48z"/>
+ </g>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-radio-button/index.html b/catapult/third_party/polymer/components/paper-radio-button/index.html
new file mode 100644
index 00000000..04b0c661
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-radio-button</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-radio-button/paper-radio-button.html b/catapult/third_party/polymer/components/paper-radio-button/paper-radio-button.html
new file mode 100644
index 00000000..1e630900
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/paper-radio-button.html
@@ -0,0 +1,247 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../paper-behaviors/paper-checked-element-behavior.html">
+<link rel="import" href="../paper-styles/default-theme.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+
+<!--
+Material design: [Radio button](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-radio-button)
+
+`paper-radio-button` is a button that can be either checked or unchecked.
+User can tap the radio button to check or uncheck it.
+
+Use a `<paper-radio-group>` to group a set of radio buttons. When radio buttons
+are inside a radio group, exactly one radio button in the group can be checked
+at any time.
+
+Example:
+
+ <paper-radio-button></paper-radio-button>
+ <paper-radio-button>Item label</paper-radio-button>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-radio-button-unchecked-background-color` | Radio button background color when the input is not checked | `transparent`
+`--paper-radio-button-unchecked-color` | Radio button color when the input is not checked | `--primary-text-color`
+`--paper-radio-button-unchecked-ink-color` | Selected/focus ripple color when the input is not checked | `--primary-text-color`
+`--paper-radio-button-checked-color` | Radio button color when the input is checked | `--primary-color`
+`--paper-radio-button-checked-ink-color` | Selected/focus ripple color when the input is checked | `--primary-color`
+`--paper-radio-button-size` | Size of the radio button | `16px`
+`--paper-radio-button-ink-size` | Size of the ripple | `48px`
+`--paper-radio-button-label-color` | Label color | `--primary-text-color`
+`--paper-radio-button-label-spacing` | Spacing between the label and the button | `10px`
+`--paper-radio-button-radio-container` | A mixin applied to the internal radio container | `{}`
+`--paper-radio-button-label` | A mixin applied to the internal label | `{}`
+`--paper-radio-button-label-checked` | A mixin applied to the internal label when the radio button is checked | `{}`
+
+This element applies the mixin `--paper-font-common-base` but does not import `paper-styles/typography.html`.
+In order to apply the `Roboto` font to this element, make sure you've imported `paper-styles/typography.html`.
+
+@group Paper Elements
+@element paper-radio-button
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-radio-button">
+ <template strip-whitespace>
+ <style>
+ :host {
+ display: inline-block;
+ line-height: 0;
+ white-space: nowrap;
+ cursor: pointer;
+ @apply(--paper-font-common-base);
+ --calculated-paper-radio-button-size: var(--paper-radio-button-size, 16px);
+ /* -1px is a sentinel for the default and is replace in `attached`. */
+ --calculated-paper-radio-button-ink-size: var(--paper-radio-button-ink-size, -1px);
+ }
+
+ :host(:focus) {
+ outline: none;
+ }
+
+ #radioContainer {
+ @apply(--layout-inline);
+ @apply(--layout-center-center);
+ position: relative;
+ width: var(--calculated-paper-radio-button-size);
+ height: var(--calculated-paper-radio-button-size);
+ vertical-align: middle;
+
+ @apply(--paper-radio-button-radio-container);
+ }
+
+ #ink {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ right: auto;
+ width: var(--calculated-paper-radio-button-ink-size);
+ height: var(--calculated-paper-radio-button-ink-size);
+ color: var(--paper-radio-button-unchecked-ink-color, --primary-text-color);
+ opacity: 0.6;
+ pointer-events: none;
+ -webkit-transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%);
+ }
+
+ #ink[checked] {
+ color: var(--paper-radio-button-checked-ink-color, --primary-color);
+ }
+
+ #offRadio, #onRadio {
+ position: absolute;
+ box-sizing: border-box;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ }
+
+ #offRadio {
+ border: 2px solid var(--paper-radio-button-unchecked-color, --primary-text-color);
+ background-color: var(--paper-radio-button-unchecked-background-color, transparent);
+ transition: border-color 0.28s;
+ }
+
+ #onRadio {
+ background-color: var(--paper-radio-button-checked-color, --primary-color);
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ transition: -webkit-transform ease 0.28s;
+ transition: transform ease 0.28s;
+ will-change: transform;
+ }
+
+ :host([checked]) #offRadio {
+ border-color: var(--paper-radio-button-checked-color, --primary-color);
+ }
+
+ :host([checked]) #onRadio {
+ -webkit-transform: scale(0.5);
+ transform: scale(0.5);
+ }
+
+ #radioLabel {
+ line-height: normal;
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ margin-left: var(--paper-radio-button-label-spacing, 10px);
+ white-space: normal;
+ color: var(--paper-radio-button-label-color, --primary-text-color);
+
+ @apply(--paper-radio-button-label);
+ }
+
+ :host([checked]) #radioLabel {
+ @apply(--paper-radio-button-label-checked);
+ }
+
+ :host-context([dir="rtl"]) #radioLabel {
+ margin-left: 0;
+ margin-right: var(--paper-radio-button-label-spacing, 10px);
+ }
+
+ #radioLabel[hidden] {
+ display: none;
+ }
+
+ /* disabled state */
+
+ :host([disabled]) #offRadio {
+ border-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
+ opacity: 0.5;
+ }
+
+ :host([disabled][checked]) #onRadio {
+ background-color: var(--paper-radio-button-unchecked-color, --primary-text-color);
+ opacity: 0.5;
+ }
+
+ :host([disabled]) #radioLabel {
+ /* slightly darker than the button, so that it's readable */
+ opacity: 0.65;
+ }
+ </style>
+
+ <div id="radioContainer">
+ <div id="offRadio"></div>
+ <div id="onRadio"></div>
+ </div>
+
+ <div id="radioLabel"><content></content></div>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-radio-button',
+
+ behaviors: [
+ Polymer.PaperCheckedElementBehavior
+ ],
+
+ hostAttributes: {
+ role: 'radio',
+ 'aria-checked': false,
+ tabindex: 0
+ },
+
+ properties: {
+ /**
+ * Fired when the checked state changes due to user interaction.
+ *
+ * @event change
+ */
+
+ /**
+ * Fired when the checked state changes.
+ *
+ * @event iron-change
+ */
+
+ ariaActiveAttribute: {
+ type: String,
+ value: 'aria-checked'
+ }
+ },
+
+ ready: function() {
+ this._rippleContainer = this.$.radioContainer;
+ },
+
+ attached: function() {
+ var inkSize = this.getComputedStyleValue('--calculated-paper-radio-button-ink-size').trim();
+ // If unset, compute and set the default `--paper-radio-button-ink-size`.
+ if (inkSize === '-1px') {
+ var size = parseFloat(this.getComputedStyleValue('--calculated-paper-radio-button-size').trim());
+ var defaultInkSize = Math.floor(3 * size);
+
+ // The button and ripple need to have the same parity so that their
+ // centers align.
+ if (defaultInkSize % 2 !== size % 2) {
+ defaultInkSize++;
+ }
+
+ this.customStyle['--paper-radio-button-ink-size'] = defaultInkSize + 'px';
+ this.updateStyles();
+ }
+ },
+ })
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-radio-button/test/basic.html b/catapult/third_party/polymer/components/paper-radio-button/test/basic.html
new file mode 100644
index 00000000..288f1b2d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/test/basic.html
@@ -0,0 +1,207 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-radio-button basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../paper-radio-button.html">
+
+ <style is="custom-style">
+ paper-radio-button.tiny {
+ --paper-radio-button-size: 5px;
+ }
+ paper-radio-button.medium {
+ --paper-radio-button-size: 37px;
+ }
+ paper-radio-button.giant {
+ --paper-radio-button-size: 50px;
+ }
+ paper-radio-button.enormous {
+ --paper-radio-button-size: 71px;
+ }
+
+ paper-radio-button.custom-ink-size {
+ --paper-radio-button-size: 25px;
+ --paper-radio-button-ink-size: 30px;
+ }
+ </style>
+</head>
+<body>
+ <test-fixture id="NoLabel">
+ <template>
+ <paper-radio-button id="radio1"></paper-radio-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithLabel">
+ <template>
+ <paper-radio-button id="radio2">Batman</paper-radio-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="AriaLabel">
+ <template>
+ <paper-radio-button aria-label="Batman">Robin</paper-radio-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithDifferentSizes">
+ <template>
+ <paper-radio-button class="tiny"></paper-radio-button>
+ <paper-radio-button></paper-radio-button>
+ <paper-radio-button class="medium"></paper-radio-button>
+ <paper-radio-button class="giant"></paper-radio-button>
+ <paper-radio-button class="enormous"></paper-radio-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="CustomInkSize">
+ <template>
+ <paper-radio-button class="custom-ink-size"></paper-radio-button>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('defaults', function() {
+ var r1;
+
+ setup(function() {
+ r1 = fixture('NoLabel');
+ });
+
+ test('check button via click', function(done) {
+ r1.addEventListener('click', function() {
+ assert.isTrue(r1.getAttribute('aria-checked') == 'true');
+ assert.isTrue(r1.checked);
+ done();
+ });
+ MockInteractions.tap(r1);
+ });
+
+ test('toggle button via click', function(done) {
+ r1.checked = true;
+ r1.addEventListener('click', function() {
+ assert.isFalse(r1.getAttribute('aria-checked') == 'true');
+ assert.isFalse(r1.checked);
+ done();
+ });
+ MockInteractions.tap(r1);
+ });
+
+ test('disabled button cannot be clicked', function(done) {
+ r1.disabled = true;
+ r1.checked = true;
+ MockInteractions.tap(r1);
+
+ setTimeout(function() {
+ assert.isTrue(r1.getAttribute('aria-checked') == 'true');
+ assert.isTrue(r1.checked);
+ done();
+ }, 1);
+ });
+
+ test('can be styled with different sizes', function() {
+ var r2 = fixture('WithDifferentSizes');
+ var small = r2[0].getBoundingClientRect();
+ var medium = r2[1].getBoundingClientRect();
+ var large = r2[2].getBoundingClientRect();
+
+ console.log(small.width, medium.width, large.width);
+
+ assert.isTrue(4 < small.height);
+ assert.isTrue(small.height < medium.height);
+ assert.isTrue(medium.height < large.height);
+ assert.isTrue(large.height < 72);
+
+ assert.isTrue(4 < small.width);
+ assert.isTrue(small.width < medium.width);
+ assert.isTrue(medium.width < large.width);
+ assert.isTrue(large.width < 72);
+ });
+ });
+
+ suite('ink size', function() {
+ var radioButtons;
+
+ setup(function() {
+ radioButtons = fixture('WithDifferentSizes');
+ });
+
+ test('`--paper-radio-button-ink-size` sets the ink size', function() {
+ var radioButton = fixture('CustomInkSize');
+ assert.equal(radioButton.getComputedStyleValue('--calculated-paper-radio-button-size').trim(), '25px');
+ assert.equal(radioButton.getComputedStyleValue('--calculated-paper-radio-button-ink-size').trim(), '30px');
+ });
+
+ test('ink sizes are near (3 * radio button size) by default', function() {
+ radioButtons.forEach(function(radioButton) {
+ var size = parseFloat(radioButton.getComputedStyleValue('--calculated-paper-radio-button-size'), 10);
+ var inkSize = parseFloat(radioButton.getComputedStyleValue('--calculated-paper-radio-button-ink-size'), 10);
+ assert.approximately(inkSize / size, 3, 0.1);
+ });
+ });
+
+ test('ink sizes are integers', function() {
+ radioButtons.forEach(function(radioButton) {
+ var unparsedInkSize = radioButton.getComputedStyleValue('--calculated-paper-radio-button-ink-size');
+ var floatInkSize = parseFloat(unparsedInkSize, 10);
+ var intInkSize = parseInt(unparsedInkSize, 10);
+ assert.equal(floatInkSize, intInkSize);
+ });
+ });
+
+ test('ink size parity matches radio button size parity (centers are aligned)', function() {
+ radioButtons.forEach(function(radioButton) {
+ var size = parseInt(radioButton.getComputedStyleValue('--calculated-paper-radio-button-size'), 10);
+ var inkSize = parseInt(radioButton.getComputedStyleValue('--calculated-paper-radio-button-ink-size'), 10);
+ assert.equal(size % 2, inkSize % 2);
+ });
+ });
+ });
+
+ suite('a11y', function() {
+ var r1;
+ var r2;
+
+ setup(function() {
+ r1 = fixture('NoLabel');
+ r2 = fixture('WithLabel');
+ });
+
+ test('has aria role "radio"', function() {
+ assert.isTrue(r1.getAttribute('role') == 'radio');
+ assert.isTrue(r2.getAttribute('role') == 'radio');
+ });
+
+ test('button with no label has no aria label', function() {
+ assert.isTrue(!r1.getAttribute('aria-label'));
+ });
+
+ test('button respects the user set aria-label', function() {
+ var c = fixture('AriaLabel');
+ assert.isTrue(c.getAttribute('aria-label') == "Batman");
+ });
+
+ a11ySuite('NoLabel');
+ a11ySuite('WithLabel');
+ a11ySuite('AriaLabel');
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-radio-button/test/index.html b/catapult/third_party/polymer/components/paper-radio-button/test/index.html
new file mode 100644
index 00000000..fdf44bfb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-button/test/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-radio-button tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow',
+ // To enable `useNativeCSSProperties`, the native Shadow DOM and lazy
+ // registration flags must also be enabled.
+ // https://github.com/Polymer/polymer/blob/ff6e884ef4f309d41491333860a8bc9c2f178696/src/lib/settings.html#L55
+ 'basic.html?dom=shadow&lazyRegister=true&useNativeCSSProperties=true'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-radio-group/.bower.json b/catapult/third_party/polymer/components/paper-radio-group/.bower.json
new file mode 100644
index 00000000..afc443ec
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/.bower.json
@@ -0,0 +1,48 @@
+{
+ "name": "paper-radio-group",
+ "version": "1.2.2",
+ "description": "A group of material design radio buttons",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "radio",
+ "control"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-radio-group.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-radio-group",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.1.7",
+ "paper-radio-button": "PolymerElements/paper-radio-button#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "paper-radio-group.html",
+ "_release": "1.2.2",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.2",
+ "commit": "1305bd0c7158a9d4675ccd7c93b75aa47f923678"
+ },
+ "_source": "https://github.com/PolymerElements/paper-radio-group.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-radio-group",
+ "_direct": true
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-radio-group/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-radio-group/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..c30f8cf1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-radio-group/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-radio-group/.gitignore b/catapult/third_party/polymer/components/paper-radio-group/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-radio-group/.travis.yml b/catapult/third_party/polymer/components/paper-radio-group/.travis.yml
new file mode 100644
index 00000000..27e783f7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ MERTTM+t9rMsIccJ4em6ocmNYm1w1r2Zy9qRxhdM1ya0xT2dAlV7UZY7t1jZ4MEdvqtTro1oEzfUr0zgD/mwBJUxLAB0RLRIbYAJEAfngaUUMgbbIgj3yLUZ/bZCvPtfhDPA1W/VsyldhUGCxojzgJthWgAoqWRj6jFX6X+QeWw=
+ - secure: >-
+ FvMxnNJsGlUg31KGuGjb0PpWHYFlMf9UPFzmLcFQAbc0kiWVoOl5adCfnv3yySf9EAKnolO02zR3K8KcCBQJ0SpWBlQ1pIgEdOxTXtWQRhlKHttKkj5L2X+qYAw4q5z0sy7lvb0VIjIOceabhvTTcnYmaF+OcrrVxd90ZpIckvk=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-radio-group/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-radio-group/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-radio-group/README.md b/catapult/third_party/polymer/components/paper-radio-group/README.md
new file mode 100644
index 00000000..2f258bb9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/README.md
@@ -0,0 +1,59 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-radio-group.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-radio-group.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-radio-group)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-radio-group)_
+
+
+##&lt;paper-radio-group&gt;
+
+Material design: [Radio button](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-radio-button)
+
+`paper-radio-group` allows user to select at most one radio button from a set.
+Checking one radio button that belongs to a radio group unchecks any
+previously checked radio button within the same group. Use
+`selected` to get or set the selected radio button.
+
+The <paper-radio-buttons> inside the group must have the `name` attribute
+set.
+
+Example:
+
+```html
+<paper-radio-group selected="small">
+ <paper-radio-button name="small">Small</paper-radio-button>
+ <paper-radio-button name="medium">Medium</paper-radio-button>
+ <paper-radio-button name="large">Large</paper-radio-button>
+</paper-radio-group>
+```
+
+Radio-button-groups can be made optional, and allow zero buttons to be selected:
+
+```html
+<paper-radio-group selected="small" allow-empty-selection>
+ <paper-radio-button name="small">Small</paper-radio-button>
+ <paper-radio-button name="medium">Medium</paper-radio-button>
+ <paper-radio-button name="large">Large</paper-radio-button>
+</paper-radio-group>
+```
+
+See <a href="paper-radio-button">paper-radio-button</a> for more
+information about `paper-radio-button`.
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-radio-group-item-padding` | The padding of the item | `12px` |
+
+
diff --git a/catapult/third_party/polymer/components/paper-radio-group/bower.json b/catapult/third_party/polymer/components/paper-radio-group/bower.json
new file mode 100644
index 00000000..2b948161
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/bower.json
@@ -0,0 +1,38 @@
+{
+ "name": "paper-radio-group",
+ "version": "1.2.2",
+ "description": "A group of material design radio buttons",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "radio",
+ "control"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-radio-group.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-radio-group",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.1.7",
+ "paper-radio-button": "PolymerElements/paper-radio-button#^1.0.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": "paper-radio-group.html"
+}
diff --git a/catapult/third_party/polymer/components/paper-radio-group/demo/index.html b/catapult/third_party/polymer/components/paper-radio-group/demo/index.html
new file mode 100644
index 00000000..cfb613d2
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/demo/index.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <title>paper-radio-group demo</title>
+
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../paper-radio-button/paper-radio-button.html">
+ <link rel="import" href="../paper-radio-group.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ .vertical-section-container {
+ max-width: 500px;
+ }
+
+ label {
+ align-self: center;
+ }
+ </style>
+ </head>
+
+ <body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>A paper-radio-group allows only one item to be selected</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <label id="label1">Dinosaurs:</label>
+ <paper-radio-group aria-labelledby="label1">
+ <paper-radio-button name="a">allosaurus</paper-radio-button>
+ <paper-radio-button name="b">brontosaurus</paper-radio-button>
+ <paper-radio-button name="d" disabled>diplodocus</paper-radio-button>
+ </paper-radio-group>
+ </template>
+ </demo-snippet>
+
+ <h3>It can have an initial selection</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <label id="label2">Dinosaurs:</label>
+ <paper-radio-group selected="b" aria-labelledby="label2">
+ <paper-radio-button name="a">allosaurus</paper-radio-button>
+ <paper-radio-button name="b">brontosaurus</paper-radio-button>
+ <paper-radio-button name="d" disabled>diplodocus</paper-radio-button>
+ </paper-radio-group>
+ </template>
+ </demo-snippet>
+
+ <h3>It can optionally allow items to be deselected</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <label id="label3">Dinosaurs:</label>
+ <paper-radio-group selected="b" allow-empty-selection aria-labelledby="label3">
+ <paper-radio-button name="a">allosaurus</paper-radio-button>
+ <paper-radio-button name="b">brontosaurus</paper-radio-button>
+ <paper-radio-button name="d" disabled>diplodocus</paper-radio-button>
+ </paper-radio-group>
+ </template>
+ </demo-snippet>
+
+ </div>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-radio-group/hero.svg b/catapult/third_party/polymer/components/paper-radio-group/hero.svg
new file mode 100755
index 00000000..fc78ba76
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/hero.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <circle cx="112.5" cy="41" r="8"/>
+ <path d="M112.5,58c-9.4,0-17-7.6-17-17s7.6-17,17-17s17,7.6,17,17S121.9,58,112.5,58z M112.5,26c-8.3,0-15,6.7-15,15s6.7,15,15,15
+ s15-6.7,15-15S120.8,26,112.5,26z"/>
+ <circle cx="112.5" cy="85" r="8"/>
+ <path d="M112.5,102c-9.4,0-17-7.6-17-17s7.6-17,17-17s17,7.6,17,17S121.9,102,112.5,102z M112.5,70c-8.3,0-15,6.7-15,15
+ s6.7,15,15,15s15-6.7,15-15S120.8,70,112.5,70z"/>
+ </g>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-radio-group/index.html b/catapult/third_party/polymer/components/paper-radio-group/index.html
new file mode 100644
index 00000000..966c7172
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-radio-group</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+ </head>
+ <body>
+
+ <iron-component-page></iron-component-page>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-radio-group/paper-radio-group.html b/catapult/third_party/polymer/components/paper-radio-group/paper-radio-group.html
new file mode 100644
index 00000000..5027e11d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/paper-radio-group.html
@@ -0,0 +1,185 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+<link rel="import" href="../iron-menu-behavior/iron-menubar-behavior.html">
+<link rel="import" href="../paper-radio-button/paper-radio-button.html">
+
+<!--
+Material design: [Radio button](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-radio-button)
+
+`paper-radio-group` allows user to select at most one radio button from a set.
+Checking one radio button that belongs to a radio group unchecks any
+previously checked radio button within the same group. Use
+`selected` to get or set the selected radio button.
+
+The <paper-radio-buttons> inside the group must have the `name` attribute
+set.
+
+Example:
+
+ <paper-radio-group selected="small">
+ <paper-radio-button name="small">Small</paper-radio-button>
+ <paper-radio-button name="medium">Medium</paper-radio-button>
+ <paper-radio-button name="large">Large</paper-radio-button>
+ </paper-radio-group>
+
+Radio-button-groups can be made optional, and allow zero buttons to be selected:
+
+ <paper-radio-group selected="small" allow-empty-selection>
+ <paper-radio-button name="small">Small</paper-radio-button>
+ <paper-radio-button name="medium">Medium</paper-radio-button>
+ <paper-radio-button name="large">Large</paper-radio-button>
+ </paper-radio-group>
+
+See <a href="paper-radio-button">paper-radio-button</a> for more
+information about `paper-radio-button`.
+
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-radio-group-item-padding` | The padding of the item | `12px`
+
+@group Paper Elements
+@element paper-radio-group
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-radio-group">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ }
+
+ :host ::content > * {
+ padding: var(--paper-radio-group-item-padding, 12px);
+ }
+ </style>
+
+ <content id="items" select="*"></content>
+ </template>
+</dom-module>
+
+<script>
+ Polymer({
+ is: 'paper-radio-group',
+
+ behaviors: [
+ Polymer.IronMenubarBehavior
+ ],
+
+ hostAttributes: {
+ role: 'radiogroup',
+ tabindex: 0
+ },
+
+ properties: {
+ /**
+ * Fired when the radio group selection changes.
+ *
+ * @event paper-radio-group-changed
+ */
+
+ /**
+ * Overriden from Polymer.IronSelectableBehavior
+ */
+ attrForSelected: {
+ type: String,
+ value: 'name'
+ },
+
+ /**
+ * Overriden from Polymer.IronSelectableBehavior
+ */
+ selectedAttribute: {
+ type: String,
+ value: 'checked'
+ },
+
+ /**
+ * Overriden from Polymer.IronSelectableBehavior
+ */
+ selectable: {
+ type: String,
+ value: 'paper-radio-button'
+ },
+
+ /**
+ * If true, radio-buttons can be deselected
+ */
+ allowEmptySelection: {
+ type: Boolean,
+ value: false
+ }
+ },
+
+ /**
+ * Selects the given value.
+ */
+ select: function(value) {
+ var newItem = this._valueToItem(value);
+ if (newItem && newItem.hasAttribute('disabled')) {
+ return;
+ }
+
+ if (this.selected) {
+ var oldItem = this._valueToItem(this.selected);
+
+ if (this.selected == value) {
+ // If deselecting is allowed we'll have to apply an empty selection.
+ // Otherwise, we should force the selection to stay and make this
+ // action a no-op.
+ if (this.allowEmptySelection) {
+ value = '';
+ } else {
+ if (oldItem)
+ oldItem.checked = true;
+ return;
+ }
+ }
+
+ if (oldItem)
+ oldItem.checked = false;
+ }
+
+ Polymer.IronSelectableBehavior.select.apply(this, [value]);
+ this.fire('paper-radio-group-changed');
+ },
+
+ _activateFocusedItem: function() {
+ this._itemActivate(this._valueForItem(this.focusedItem), this.focusedItem);
+ },
+
+ _onUpKey: function(event) {
+ this._focusPrevious();
+ event.preventDefault();
+ this._activateFocusedItem();
+ },
+
+ _onDownKey: function(event) {
+ this._focusNext();
+ event.preventDefault();
+ this._activateFocusedItem();
+ },
+
+ _onLeftKey: function(event) {
+ Polymer.IronMenubarBehaviorImpl._onLeftKey.apply(this, arguments);
+ this._activateFocusedItem();
+ },
+
+ _onRightKey: function(event) {
+ Polymer.IronMenubarBehaviorImpl._onRightKey.apply(this, arguments);
+ this._activateFocusedItem();
+ }
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/paper-radio-group/test/basic.html b/catapult/third_party/polymer/components/paper-radio-group/test/basic.html
new file mode 100644
index 00000000..d123c7fd
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/test/basic.html
@@ -0,0 +1,249 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>paper-radio-group basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+ <link rel="import" href="../paper-radio-group.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="NoSelection">
+ <template>
+ <paper-radio-group>
+ <paper-radio-button name="r1">r1</paper-radio-button>
+ <paper-radio-button name="r2">r2</paper-radio-button>
+ <paper-radio-button name="r3">r3</paper-radio-button>
+ </paper-radio-group>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithSelection">
+ <template>
+ <paper-radio-group selected="r1">
+ <paper-radio-button name="r1">r1</paper-radio-button>
+ <paper-radio-button name="r2">r2</paper-radio-button>
+ <paper-radio-button name="r3">r3</paper-radio-button>
+ </paper-radio-group>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="WithDisabled">
+ <template>
+ <paper-radio-group selected="r1">
+ <paper-radio-button name="r1">r1</paper-radio-button>
+ <paper-radio-button name="r2" disabled>r2</paper-radio-button>
+ <paper-radio-button name="r3">r3</paper-radio-button>
+ </paper-radio-group>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('defaults', function() {
+ var LEFT_ARROW = 37;
+ var RIGHT_ARROW = 39;
+
+ test('group can have no selection', function (done) {
+ var g = fixture('NoSelection');
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ expect(g.selected).to.not.be.ok;
+ var items = g.items;
+ expect(items.length).to.be.equal(3);
+
+ expect(items[0].checked).to.be.equal(false);
+ expect(items[1].checked).to.be.equal(false);
+ expect(items[2].checked).to.be.equal(false);
+
+ done();
+ }, 1);
+
+ });
+
+ test('group can have a selection', function (done) {
+ var g = fixture('WithSelection');
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ expect(g.selected).to.be.ok;
+ var items = g.items;
+ expect(items.length).to.be.equal(3);
+
+ expect(items[0].checked).to.be.equal(true);
+ expect(items[1].checked).to.be.equal(false);
+ expect(items[2].checked).to.be.equal(false);
+ expect(items[0].getAttribute('name')).to.be.equal(g.selected);
+
+ done();
+ }, 1);
+ });
+
+ test('right arrow advances the selection', function (done) {
+ var g = fixture('WithSelection');
+ MockInteractions.focus(g);
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ var items = g.items;
+ expect(items[0].checked).to.be.equal(true);
+
+ g.addEventListener('paper-radio-group-changed', function () {
+ expect(items[0].checked).to.be.equal(false);
+ expect(items[1].checked).to.be.equal(true);
+ expect(items[2].checked).to.be.equal(false);
+ done();
+ });
+ MockInteractions.keyDownOn(g, RIGHT_ARROW);
+ }, 1);
+ });
+
+ test('left arrow reverses the selection', function (done) {
+ var g = fixture('WithSelection');
+ MockInteractions.focus(g);
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ var items = g.items;
+ expect(items[0].checked).to.be.equal(true);
+
+ g.addEventListener('paper-radio-group-changed', function () {
+ expect(items[0].checked).to.be.equal(false);
+ expect(items[1].checked).to.be.equal(false);
+ expect(items[2].checked).to.be.equal(true);
+ done();
+ });
+ MockInteractions.keyDownOn(g, LEFT_ARROW);
+ }, 1);
+ });
+
+ test('selection should skip disabled items', function (done) {
+ var g = fixture('WithDisabled');
+ MockInteractions.focus(g);
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ var items = g.items;
+ expect(items[0].checked).to.be.equal(true);
+
+ g.addEventListener('paper-radio-group-changed', function () {
+ expect(items[0].checked).to.be.equal(false);
+ expect(items[1].checked).to.be.equal(false);
+ expect(items[2].checked).to.be.equal(true);
+ done();
+ });
+ MockInteractions.keyDownOn(g, RIGHT_ARROW);
+ }, 1);
+ });
+
+ test('clicking should change the selection', function (done) {
+ var g = fixture('WithSelection');
+ MockInteractions.focus(g);
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ var items = g.items;
+ expect(items[0].checked).to.be.equal(true);
+
+ g.addEventListener('paper-radio-group-changed', function () {
+ expect(items[0].checked).to.be.equal(false);
+ expect(items[1].checked).to.be.equal(true);
+ expect(items[2].checked).to.be.equal(false);
+ done();
+ });
+ MockInteractions.tap(items[1]);
+ }, 1);
+ });
+
+ test('clicking the selected item should not deselect', function (done) {
+ var g = fixture('WithSelection');
+ MockInteractions.focus(g);
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ var items = g.items;
+ expect(items[0].checked).to.be.equal(true);
+
+ // The selection should not change, but wait for a little bit just
+ // in case it would and an event would be fired.
+ setTimeout(function() {
+ expect(items[0].checked).to.be.equal(true);
+ expect(items[1].checked).to.be.equal(false);
+ expect(items[2].checked).to.be.equal(false);
+ done();
+ }, 1);
+ MockInteractions.tap(items[0]);
+ }, 1);
+ });
+
+ test('clicking the selected item should deselect if allow-empty-selection is set', function (done) {
+ var g = fixture('WithSelection');
+ g.allowEmptySelection = true;
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ var items = g.items;
+ expect(items[0].checked).to.be.equal(true);
+
+ // The selection should not change, but wait for a little bit just
+ // in case it would and an event would be fired.
+ setTimeout(function() {
+ expect(items[0].checked).to.be.equal(false);
+ expect(items[1].checked).to.be.equal(false);
+ expect(items[2].checked).to.be.equal(false);
+ done();
+ }, 1);
+ MockInteractions.tap(items[0]);
+ }, 1);
+ });
+
+ test('arrow keys cause iron-activate events', function(done) {
+ var g = fixture('WithSelection');
+ MockInteractions.focus(g);
+
+ // Needs to be async since the underlying iron-selector uses observeNodes.
+ Polymer.Base.async(function() {
+ g.items[0].focus();
+
+ var i = 0;
+ g.addEventListener('iron-activate', function() {
+ switch (i++) {
+ case 0:
+ MockInteractions.pressAndReleaseKeyOn(g, 38);
+ break;
+
+ case 1:
+ MockInteractions.pressAndReleaseKeyOn(g, 39);
+ break;
+
+ case 2:
+ MockInteractions.pressAndReleaseKeyOn(g, 40);
+ break;
+
+ default:
+ done();
+ }
+ });
+
+ MockInteractions.pressAndReleaseKeyOn(g, 37);
+ }, 1);
+ });
+
+ });
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-radio-group/test/index.html b/catapult/third_party/polymer/components/paper-radio-group/test/index.html
new file mode 100644
index 00000000..cf387c5d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-radio-group/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-radio-group tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+ </head>
+ <body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-ripple/.bower.json b/catapult/third_party/polymer/components/paper-ripple/.bower.json
new file mode 100644
index 00000000..3d26c367
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/.bower.json
@@ -0,0 +1,45 @@
+{
+ "name": "paper-ripple",
+ "version": "1.0.10",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Adds a material design ripple to any container",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "ripple"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-ripple.git"
+ },
+ "main": "paper-ripple.html",
+ "dependencies": {
+ "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.1.5",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "iron-icons": "polymerelements/iron-icons#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/paper-ripple",
+ "_release": "1.0.10",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.10",
+ "commit": "6fef0ee16db4b7eec81f270efaf6b55b6ea53aa2"
+ },
+ "_source": "https://github.com/PolymerElements/paper-ripple.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-ripple"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-ripple/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-ripple/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..689423da
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-ripple/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-ripple/.gitignore b/catapult/third_party/polymer/components/paper-ripple/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-ripple/.travis.yml b/catapult/third_party/polymer/components/paper-ripple/.travis.yml
new file mode 100644
index 00000000..787deed9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/.travis.yml
@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+before_script:
+- npm install -g bower polylint web-component-tester
+- bower install
+- polylint
+env:
+ global:
+ - secure: eLNXQngbn6nHUirBVQAroarZUJHGZAj8sqSJaxRBqaL08//nzaOpnzuavYUeNSJ1fOnbL95jS5yZJy8+oCUW0a6BCXZUHJCrj/N6ywG4KpgPYQnUZVnpvsLdXZJuq4L4l5jYL1GsPydMePfAlrDpSu+QzEc1+C0q7sprxsbHbbQ=
+ - secure: YLIZR4/tqr25Ty+daEdTInLVXoV/lSwagZCGMfT84SgHj94UYHr9u9Te3tNDeI8I83Kq1PSUn1kKE6ptT+EjuGhTDsyLx2IboVDtNlngDIo5GTSO1RBBzIlHRIb2eMS6om9cfLiOEWM3gFS/Mos/VYO3/A3ZSLE5BcNFagij724=
+node_js: stable
+addons:
+ firefox: '46.0'
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+- xvfb-run wct
+- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'default'; fi
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-ripple/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-ripple/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-ripple/README.md b/catapult/third_party/polymer/components/paper-ripple/README.md
new file mode 100644
index 00000000..6d3fa5a4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/README.md
@@ -0,0 +1,40 @@
+[![Build status](https://travis-ci.org/PolymerElements/paper-ripple.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-ripple)
+
+##&lt;paper-ripple&gt;
+
+Material design: [Surface reaction](https://www.google.com/design/spec/animation/responsive-interaction.html#responsive-interaction-surface-reaction)
+
+`paper-ripple` provides a visual effect that other paper elements can
+use to simulate a rippling effect emanating from the point of contact. The
+effect can be visualized as a concentric circle with motion.
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-ripple.html">
+ <style is="custom-style">
+ div {
+ height: 100px;
+ width: 100%;
+ box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24);
+ }
+
+ paper-ripple {
+ color: #4285f4;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<div style="position: relative">
+ <paper-ripple></paper-ripple>
+</div
+```
+
+
+
diff --git a/catapult/third_party/polymer/components/paper-ripple/bower.json b/catapult/third_party/polymer/components/paper-ripple/bower.json
new file mode 100644
index 00000000..f5e3dceb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "paper-ripple",
+ "version": "1.0.9",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Adds a material design ripple to any container",
+ "private": true,
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "ripple"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-ripple.git"
+ },
+ "main": "paper-ripple.html",
+ "dependencies": {
+ "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.1.5",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "iron-icon": "polymerelements/iron-icon#^1.0.0",
+ "iron-icons": "polymerelements/iron-icons#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-styles": "polymerelements/paper-styles#^1.0.0",
+ "test-fixture": "polymerelements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-ripple/demo/index.html b/catapult/third_party/polymer/components/paper-ripple/demo/index.html
new file mode 100644
index 00000000..e12cde5c
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/demo/index.html
@@ -0,0 +1,415 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<!doctype html>
+<html>
+<head>
+ <title>paper-ripple demo</title>
+
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../paper-ripple.html">
+ <link rel="import" href="../../paper-styles/typography.html">
+ <link rel="import" href="../../iron-icon/iron-icon.html">
+
+ <style>
+
+ body {
+ background-color: #f9f9f9;
+ font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ -webkit-touch-callout: none;
+ }
+
+ section {
+ padding: 30px 25px;
+ }
+
+ section > * {
+ margin: 10px
+ }
+
+ /* Button */
+ .button {
+ display: inline-block;
+ position: relative;
+ width: 120px;
+ height: 32px;
+ line-height: 32px;
+ border-radius: 2px;
+ font-size: 0.9em;
+ background-color: #fff;
+ color: #646464;
+ }
+
+ .button > paper-ripple {
+ border-radius: 2px;
+ overflow: hidden;
+ }
+
+ .button.narrow {
+ width: 60px;
+ }
+
+ .button.grey {
+ background-color: #eee;
+ }
+
+ .button.blue {
+ background-color: #4285f4;
+ color: #fff;
+ }
+
+ .button.green {
+ background-color: #0f9d58;
+ color: #fff;
+ }
+
+ .button.raised {
+ transition: box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ transition-delay: 0.2s;
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
+ }
+
+ .button.raised:active {
+ box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2);
+ transition-delay: 0s;
+ }
+
+ /* Icon Button */
+ .icon-button {
+ position: relative;
+ display: inline-block;
+ width: 56px;
+ height: 56px;
+ }
+
+ .icon-button > iron-icon {
+ margin: 16px;
+ transition: -webkit-transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ }
+
+ .icon-button:hover > iron-icon {
+ -webkit-transform: scale(1.2);
+ transform: scale(1.2);
+ }
+
+ .icon-button > paper-ripple {
+ overflow: hidden;
+ color: #646464;
+ }
+
+ .icon-button.red > iron-icon::shadow path {
+ fill: #db4437;
+ }
+
+ .icon-button.red > paper-ripple {
+ color: #db4437;
+ }
+
+ .icon-button.blue > iron-icon::shadow path {
+ fill: #4285f4;
+ }
+
+ .icon-button.blue > paper-ripple {
+ color: #4285f4;
+ }
+
+ /* FAB */
+ .fab {
+ position: relative;
+ display: inline-block;
+ width: 56px;
+ height: 56px;
+ border-radius: 50%;
+ color: #fff;
+ overflow: hidden;
+ transition: box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ transition-delay: 0.2s;
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
+ }
+
+ .fab.red {
+ background-color: #d23f31;
+ }
+
+ .fab.blue {
+ background-color: #4285f4;
+ }
+
+ .fab.green {
+ background-color: #0f9d58;
+ }
+
+ .fab:active {
+ box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2);
+ transition-delay: 0s;
+ }
+
+ .fab > iron-icon {
+ margin: 16px;
+ }
+
+ .fab > iron-icon::shadow path {
+ fill: #fff;
+ }
+
+ /* Menu */
+ .menu {
+ display: inline-block;
+ width: 180px;
+ background-color: #fff;
+ box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2);
+ }
+
+ .item {
+ position: relative;
+ height: 48px;
+ line-height: 48px;
+ color: #646464;
+ font-size: 0.9em;
+ }
+
+ .menu.blue > .item {
+ color: #4285f4;
+ }
+
+ /* Card, Dialog */
+ .card, .dialog {
+ position: relative;
+ display: inline-block;
+ width: 300px;
+ height: 240px;
+ vertical-align: top;
+ background-color: #fff;
+ box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24);
+ }
+
+ .dialog {
+ box-sizing: border-box;
+ padding: 16px;
+ }
+
+ .dialog > .content {
+ height: 170px;
+ font-size: 0.9em;
+ }
+
+ .dialog > .content > .title {
+ font-size: 1.3em;
+ }
+
+ .dialog > .button {
+ width: 90px;
+ float: right;
+ }
+
+ .card.image {
+ background: url(http://lorempixel.com/300/240/nature/);
+ color: #fff;
+ }
+
+ /* Misc */
+ .center {
+ text-align: center;
+ }
+
+ .label {
+ padding: 0 16px;
+ }
+
+ .label-blue {
+ color: #4285f4;
+ }
+
+ .label-red {
+ color: #d23f31;
+ }
+
+ </style>
+
+</head>
+<body>
+
+ <section>
+
+ <div class="button raised">
+ <div class="center" fit tabindex="1">SUBMIT</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ <div class="button raised" noink>
+ <div class="center" fit tabindex="1">NO INK</div>
+ <paper-ripple noink></paper-ripple>
+ </div>
+
+ <div class="button raised grey">
+ <div class="center" fit tabindex="1">CANCEL</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ <div class="button raised blue">
+ <div class="center" fit tabindex="1">COMPOSE</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ <div class="button raised green">
+ <div class="center" fit tabindex="1">OK</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ </section>
+
+ <section>
+
+ <div class="button raised grey narrow">
+ <div class="center" fit tabindex="1">+1</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ <div class="button raised grey narrow label-blue">
+ <div class="center" fit tabindex="1">+1</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ <div class="button raised grey narrow label-red">
+ <div class="center" fit tabindex="1">+1</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ </section>
+
+ <section>
+
+ <div class="icon-button">
+ <iron-icon icon="menu" tabindex="1"></iron-icon>
+ <paper-ripple class="circle" recenters></paper-ripple>
+ </div>
+
+ <div class="icon-button">
+ <iron-icon icon="more-vert" tabindex="1"></iron-icon>
+ <paper-ripple class="circle" recenters></paper-ripple>
+ </div>
+
+ <div class="icon-button red">
+ <iron-icon icon="delete" tabindex="1"></iron-icon>
+ <paper-ripple class="circle" recenters></paper-ripple>
+ </div>
+
+ <div class="icon-button blue">
+ <iron-icon icon="account-box" tabindex="1"></iron-icon>
+ <paper-ripple class="circle" recenters></paper-ripple>
+ </div>
+
+ </section>
+
+ <section>
+
+ <div class="fab red">
+ <iron-icon icon="add" tabindex="1"></iron-icon>
+ <paper-ripple class="circle" recenters></paper-ripple>
+ </div>
+
+ <div class="fab blue">
+ <iron-icon icon="mail" tabindex="1"></iron-icon>
+ <paper-ripple class="circle" recenters></paper-ripple>
+ </div>
+
+ <div class="fab green">
+ <iron-icon icon="create" tabindex="1"></iron-icon>
+ <paper-ripple class="circle" recenters></paper-ripple>
+ </div>
+
+ </section>
+
+ <section>
+
+ <div class="menu">
+
+ <div class="item">
+ <div class="label" fit tabindex="1">Mark as unread</div>
+ <paper-ripple></paper-ripple>
+ </div>
+ <div class="item">
+ <div class="label" fit tabindex="1">Mark as important</div>
+ <paper-ripple></paper-ripple>
+ </div>
+ <div class="item">
+ <div class="label" fit tabindex="1">Add to Tasks</div>
+ <paper-ripple></paper-ripple>
+ </div>
+ <div class="item">
+ <div class="label" fit tabindex="1">Create event</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ </div>
+
+ <div class="menu blue">
+
+ <div class="item">
+ <div class="label" fit tabindex="1">Import</div>
+ <paper-ripple></paper-ripple>
+ </div>
+ <div class="item">
+ <div class="label" fit tabindex="1">Export</div>
+ <paper-ripple></paper-ripple>
+ </div>
+ <div class="item">
+ <div class="label" fit tabindex="1">Print</div>
+ <paper-ripple></paper-ripple>
+ </div>
+ <div class="item">
+ <div class="label" fit tabindex="1">Restore contacts</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ </div>
+
+ </section>
+
+ <section>
+
+ <div class="dialog">
+
+ <div class="content">
+ <div class="title">Permission</div><br>
+ <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.</div>
+ </div>
+
+ <div class="button label-blue">
+ <div class="center" fit tabindex="1">ACCEPT</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ <div class="button">
+ <div class="center" fit tabindex="1">DECLINE</div>
+ <paper-ripple></paper-ripple>
+ </div>
+
+ </div>
+
+ <div class="card" tabindex="1">
+ <paper-ripple recenters></paper-ripple>
+ </div>
+
+ <div class="card image" tabindex="1">
+ <paper-ripple recenters></paper-ripple>
+ </div>
+
+ </section>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-ripple/hero.svg b/catapult/third_party/polymer/components/paper-ripple/hero.svg
new file mode 100755
index 00000000..96f0b4c7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/hero.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <path d="M175,81H49V45h126V81z M51,79h122V47H51V79z"/>
+ <g>
+ <defs>
+ <rect id="SVGID_5_" x="50" y="46" width="124" height="34"/>
+ </defs>
+ <clipPath id="SVGID_2_">
+ <use xlink:href="#SVGID_5_" overflow="visible"/>
+ </clipPath>
+ <circle opacity="0.5" clip-path="url(#SVGID_2_)" cx="84.4" cy="62.7" r="41.9"/>
+ <circle opacity="0.6" clip-path="url(#SVGID_2_)" cx="84.4" cy="62.7" r="26.3"/>
+ <circle opacity="0.6" clip-path="url(#SVGID_2_)" cx="66.4" cy="62.7" r="26.3"/>
+ </g>
+ <circle cx="50" cy="80" r="4"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-ripple/index.html b/catapult/third_party/polymer/components/paper-ripple/index.html
new file mode 100644
index 00000000..e552b0bb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/index.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-ripple</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-ripple/paper-ripple.html b/catapult/third_party/polymer/components/paper-ripple/paper-ripple.html
new file mode 100644
index 00000000..3fb18a37
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/paper-ripple.html
@@ -0,0 +1,763 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
+
+<!--
+Material design: [Surface reaction](https://www.google.com/design/spec/animation/responsive-interaction.html#responsive-interaction-surface-reaction)
+
+`paper-ripple` provides a visual effect that other paper elements can
+use to simulate a rippling effect emanating from the point of contact. The
+effect can be visualized as a concentric circle with motion.
+
+Example:
+
+ <div style="position:relative">
+ <paper-ripple></paper-ripple>
+ </div>
+
+Note, it's important that the parent container of the ripple be relative position, otherwise
+the ripple will emanate outside of the desired container.
+
+`paper-ripple` listens to "mousedown" and "mouseup" events so it would display ripple
+effect when touches on it. You can also defeat the default behavior and
+manually route the down and up actions to the ripple element. Note that it is
+important if you call `downAction()` you will have to make sure to call
+`upAction()` so that `paper-ripple` would end the animation loop.
+
+Example:
+
+ <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple>
+ ...
+ downAction: function(e) {
+ this.$.ripple.downAction({detail: {x: e.x, y: e.y}});
+ },
+ upAction: function(e) {
+ this.$.ripple.upAction();
+ }
+
+Styling ripple effect:
+
+ Use CSS color property to style the ripple:
+
+ paper-ripple {
+ color: #4285f4;
+ }
+
+ Note that CSS color property is inherited so it is not required to set it on
+ the `paper-ripple` element directly.
+
+By default, the ripple is centered on the point of contact. Apply the `recenters`
+attribute to have the ripple grow toward the center of its container.
+
+ <paper-ripple recenters></paper-ripple>
+
+You can also center the ripple inside its container from the start.
+
+ <paper-ripple center></paper-ripple>
+
+Apply `circle` class to make the rippling effect within a circle.
+
+ <paper-ripple class="circle"></paper-ripple>
+
+@group Paper Elements
+@element paper-ripple
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-ripple">
+
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: absolute;
+ border-radius: inherit;
+ overflow: hidden;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ /* See PolymerElements/paper-behaviors/issues/34. On non-Chrome browsers,
+ * creating a node (with a position:absolute) in the middle of an event
+ * handler "interrupts" that event handler (which happens when the
+ * ripple is created on demand) */
+ pointer-events: none;
+ }
+
+ :host([animating]) {
+ /* This resolves a rendering issue in Chrome (as of 40) where the
+ ripple is not properly clipped by its parent (which may have
+ rounded corners). See: http://jsbin.com/temexa/4
+
+ Note: We only apply this style conditionally. Otherwise, the browser
+ will create a new compositing layer for every ripple element on the
+ page, and that would be bad. */
+ -webkit-transform: translate(0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ #background,
+ #waves,
+ .wave-container,
+ .wave {
+ pointer-events: none;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+
+ #background,
+ .wave {
+ opacity: 0;
+ }
+
+ #waves,
+ .wave {
+ overflow: hidden;
+ }
+
+ .wave-container,
+ .wave {
+ border-radius: 50%;
+ }
+
+ :host(.circle) #background,
+ :host(.circle) #waves {
+ border-radius: 50%;
+ }
+
+ :host(.circle) .wave-container {
+ overflow: hidden;
+ }
+ </style>
+
+ <div id="background"></div>
+ <div id="waves"></div>
+ </template>
+</dom-module>
+<script>
+ (function() {
+ var Utility = {
+ distance: function(x1, y1, x2, y2) {
+ var xDelta = (x1 - x2);
+ var yDelta = (y1 - y2);
+
+ return Math.sqrt(xDelta * xDelta + yDelta * yDelta);
+ },
+
+ now: window.performance && window.performance.now ?
+ window.performance.now.bind(window.performance) : Date.now
+ };
+
+ /**
+ * @param {HTMLElement} element
+ * @constructor
+ */
+ function ElementMetrics(element) {
+ this.element = element;
+ this.width = this.boundingRect.width;
+ this.height = this.boundingRect.height;
+
+ this.size = Math.max(this.width, this.height);
+ }
+
+ ElementMetrics.prototype = {
+ get boundingRect () {
+ return this.element.getBoundingClientRect();
+ },
+
+ furthestCornerDistanceFrom: function(x, y) {
+ var topLeft = Utility.distance(x, y, 0, 0);
+ var topRight = Utility.distance(x, y, this.width, 0);
+ var bottomLeft = Utility.distance(x, y, 0, this.height);
+ var bottomRight = Utility.distance(x, y, this.width, this.height);
+
+ return Math.max(topLeft, topRight, bottomLeft, bottomRight);
+ }
+ };
+
+ /**
+ * @param {HTMLElement} element
+ * @constructor
+ */
+ function Ripple(element) {
+ this.element = element;
+ this.color = window.getComputedStyle(element).color;
+
+ this.wave = document.createElement('div');
+ this.waveContainer = document.createElement('div');
+ this.wave.style.backgroundColor = this.color;
+ this.wave.classList.add('wave');
+ this.waveContainer.classList.add('wave-container');
+ Polymer.dom(this.waveContainer).appendChild(this.wave);
+
+ this.resetInteractionState();
+ }
+
+ Ripple.MAX_RADIUS = 300;
+
+ Ripple.prototype = {
+ get recenters() {
+ return this.element.recenters;
+ },
+
+ get center() {
+ return this.element.center;
+ },
+
+ get mouseDownElapsed() {
+ var elapsed;
+
+ if (!this.mouseDownStart) {
+ return 0;
+ }
+
+ elapsed = Utility.now() - this.mouseDownStart;
+
+ if (this.mouseUpStart) {
+ elapsed -= this.mouseUpElapsed;
+ }
+
+ return elapsed;
+ },
+
+ get mouseUpElapsed() {
+ return this.mouseUpStart ?
+ Utility.now () - this.mouseUpStart : 0;
+ },
+
+ get mouseDownElapsedSeconds() {
+ return this.mouseDownElapsed / 1000;
+ },
+
+ get mouseUpElapsedSeconds() {
+ return this.mouseUpElapsed / 1000;
+ },
+
+ get mouseInteractionSeconds() {
+ return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds;
+ },
+
+ get initialOpacity() {
+ return this.element.initialOpacity;
+ },
+
+ get opacityDecayVelocity() {
+ return this.element.opacityDecayVelocity;
+ },
+
+ get radius() {
+ var width2 = this.containerMetrics.width * this.containerMetrics.width;
+ var height2 = this.containerMetrics.height * this.containerMetrics.height;
+ var waveRadius = Math.min(
+ Math.sqrt(width2 + height2),
+ Ripple.MAX_RADIUS
+ ) * 1.1 + 5;
+
+ var duration = 1.1 - 0.2 * (waveRadius / Ripple.MAX_RADIUS);
+ var timeNow = this.mouseInteractionSeconds / duration;
+ var size = waveRadius * (1 - Math.pow(80, -timeNow));
+
+ return Math.abs(size);
+ },
+
+ get opacity() {
+ if (!this.mouseUpStart) {
+ return this.initialOpacity;
+ }
+
+ return Math.max(
+ 0,
+ this.initialOpacity - this.mouseUpElapsedSeconds * this.opacityDecayVelocity
+ );
+ },
+
+ get outerOpacity() {
+ // Linear increase in background opacity, capped at the opacity
+ // of the wavefront (waveOpacity).
+ var outerOpacity = this.mouseUpElapsedSeconds * 0.3;
+ var waveOpacity = this.opacity;
+
+ return Math.max(
+ 0,
+ Math.min(outerOpacity, waveOpacity)
+ );
+ },
+
+ get isOpacityFullyDecayed() {
+ return this.opacity < 0.01 &&
+ this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS);
+ },
+
+ get isRestingAtMaxRadius() {
+ return this.opacity >= this.initialOpacity &&
+ this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS);
+ },
+
+ get isAnimationComplete() {
+ return this.mouseUpStart ?
+ this.isOpacityFullyDecayed : this.isRestingAtMaxRadius;
+ },
+
+ get translationFraction() {
+ return Math.min(
+ 1,
+ this.radius / this.containerMetrics.size * 2 / Math.sqrt(2)
+ );
+ },
+
+ get xNow() {
+ if (this.xEnd) {
+ return this.xStart + this.translationFraction * (this.xEnd - this.xStart);
+ }
+
+ return this.xStart;
+ },
+
+ get yNow() {
+ if (this.yEnd) {
+ return this.yStart + this.translationFraction * (this.yEnd - this.yStart);
+ }
+
+ return this.yStart;
+ },
+
+ get isMouseDown() {
+ return this.mouseDownStart && !this.mouseUpStart;
+ },
+
+ resetInteractionState: function() {
+ this.maxRadius = 0;
+ this.mouseDownStart = 0;
+ this.mouseUpStart = 0;
+
+ this.xStart = 0;
+ this.yStart = 0;
+ this.xEnd = 0;
+ this.yEnd = 0;
+ this.slideDistance = 0;
+
+ this.containerMetrics = new ElementMetrics(this.element);
+ },
+
+ draw: function() {
+ var scale;
+ var translateString;
+ var dx;
+ var dy;
+
+ this.wave.style.opacity = this.opacity;
+
+ scale = this.radius / (this.containerMetrics.size / 2);
+ dx = this.xNow - (this.containerMetrics.width / 2);
+ dy = this.yNow - (this.containerMetrics.height / 2);
+
+
+ // 2d transform for safari because of border-radius and overflow:hidden clipping bug.
+ // https://bugs.webkit.org/show_bug.cgi?id=98538
+ this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' + dy + 'px)';
+ this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy + 'px, 0)';
+ this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')';
+ this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)';
+ },
+
+ /** @param {Event=} event */
+ downAction: function(event) {
+ var xCenter = this.containerMetrics.width / 2;
+ var yCenter = this.containerMetrics.height / 2;
+
+ this.resetInteractionState();
+ this.mouseDownStart = Utility.now();
+
+ if (this.center) {
+ this.xStart = xCenter;
+ this.yStart = yCenter;
+ this.slideDistance = Utility.distance(
+ this.xStart, this.yStart, this.xEnd, this.yEnd
+ );
+ } else {
+ this.xStart = event ?
+ event.detail.x - this.containerMetrics.boundingRect.left :
+ this.containerMetrics.width / 2;
+ this.yStart = event ?
+ event.detail.y - this.containerMetrics.boundingRect.top :
+ this.containerMetrics.height / 2;
+ }
+
+ if (this.recenters) {
+ this.xEnd = xCenter;
+ this.yEnd = yCenter;
+ this.slideDistance = Utility.distance(
+ this.xStart, this.yStart, this.xEnd, this.yEnd
+ );
+ }
+
+ this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(
+ this.xStart,
+ this.yStart
+ );
+
+ this.waveContainer.style.top =
+ (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px';
+ this.waveContainer.style.left =
+ (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px';
+
+ this.waveContainer.style.width = this.containerMetrics.size + 'px';
+ this.waveContainer.style.height = this.containerMetrics.size + 'px';
+ },
+
+ /** @param {Event=} event */
+ upAction: function(event) {
+ if (!this.isMouseDown) {
+ return;
+ }
+
+ this.mouseUpStart = Utility.now();
+ },
+
+ remove: function() {
+ Polymer.dom(this.waveContainer.parentNode).removeChild(
+ this.waveContainer
+ );
+ }
+ };
+
+ Polymer({
+ is: 'paper-ripple',
+
+ behaviors: [
+ Polymer.IronA11yKeysBehavior
+ ],
+
+ properties: {
+ /**
+ * The initial opacity set on the wave.
+ *
+ * @attribute initialOpacity
+ * @type number
+ * @default 0.25
+ */
+ initialOpacity: {
+ type: Number,
+ value: 0.25
+ },
+
+ /**
+ * How fast (opacity per second) the wave fades out.
+ *
+ * @attribute opacityDecayVelocity
+ * @type number
+ * @default 0.8
+ */
+ opacityDecayVelocity: {
+ type: Number,
+ value: 0.8
+ },
+
+ /**
+ * If true, ripples will exhibit a gravitational pull towards
+ * the center of their container as they fade away.
+ *
+ * @attribute recenters
+ * @type boolean
+ * @default false
+ */
+ recenters: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, ripples will center inside its container
+ *
+ * @attribute recenters
+ * @type boolean
+ * @default false
+ */
+ center: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * A list of the visual ripples.
+ *
+ * @attribute ripples
+ * @type Array
+ * @default []
+ */
+ ripples: {
+ type: Array,
+ value: function() {
+ return [];
+ }
+ },
+
+ /**
+ * True when there are visible ripples animating within the
+ * element.
+ */
+ animating: {
+ type: Boolean,
+ readOnly: true,
+ reflectToAttribute: true,
+ value: false
+ },
+
+ /**
+ * If true, the ripple will remain in the "down" state until `holdDown`
+ * is set to false again.
+ */
+ holdDown: {
+ type: Boolean,
+ value: false,
+ observer: '_holdDownChanged'
+ },
+
+ /**
+ * If true, the ripple will not generate a ripple effect
+ * via pointer interaction.
+ * Calling ripple's imperative api like `simulatedRipple` will
+ * still generate the ripple effect.
+ */
+ noink: {
+ type: Boolean,
+ value: false
+ },
+
+ _animating: {
+ type: Boolean
+ },
+
+ _boundAnimate: {
+ type: Function,
+ value: function() {
+ return this.animate.bind(this);
+ }
+ }
+ },
+
+ get target () {
+ return this.keyEventTarget;
+ },
+
+ keyBindings: {
+ 'enter:keydown': '_onEnterKeydown',
+ 'space:keydown': '_onSpaceKeydown',
+ 'space:keyup': '_onSpaceKeyup'
+ },
+
+ attached: function() {
+ // Set up a11yKeysBehavior to listen to key events on the target,
+ // so that space and enter activate the ripple even if the target doesn't
+ // handle key events. The key handlers deal with `noink` themselves.
+ if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE
+ this.keyEventTarget = Polymer.dom(this).getOwnerRoot().host;
+ } else {
+ this.keyEventTarget = this.parentNode;
+ }
+ var keyEventTarget = /** @type {!EventTarget} */ (this.keyEventTarget);
+ this.listen(keyEventTarget, 'up', 'uiUpAction');
+ this.listen(keyEventTarget, 'down', 'uiDownAction');
+ },
+
+ detached: function() {
+ this.unlisten(this.keyEventTarget, 'up', 'uiUpAction');
+ this.unlisten(this.keyEventTarget, 'down', 'uiDownAction');
+ this.keyEventTarget = null;
+ },
+
+ get shouldKeepAnimating () {
+ for (var index = 0; index < this.ripples.length; ++index) {
+ if (!this.ripples[index].isAnimationComplete) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ simulatedRipple: function() {
+ this.downAction(null);
+
+ // Please see polymer/polymer#1305
+ this.async(function() {
+ this.upAction();
+ }, 1);
+ },
+
+ /**
+ * Provokes a ripple down effect via a UI event,
+ * respecting the `noink` property.
+ * @param {Event=} event
+ */
+ uiDownAction: function(event) {
+ if (!this.noink) {
+ this.downAction(event);
+ }
+ },
+
+ /**
+ * Provokes a ripple down effect via a UI event,
+ * *not* respecting the `noink` property.
+ * @param {Event=} event
+ */
+ downAction: function(event) {
+ if (this.holdDown && this.ripples.length > 0) {
+ return;
+ }
+
+ var ripple = this.addRipple();
+
+ ripple.downAction(event);
+
+ if (!this._animating) {
+ this._animating = true;
+ this.animate();
+ }
+ },
+
+ /**
+ * Provokes a ripple up effect via a UI event,
+ * respecting the `noink` property.
+ * @param {Event=} event
+ */
+ uiUpAction: function(event) {
+ if (!this.noink) {
+ this.upAction(event);
+ }
+ },
+
+ /**
+ * Provokes a ripple up effect via a UI event,
+ * *not* respecting the `noink` property.
+ * @param {Event=} event
+ */
+ upAction: function(event) {
+ if (this.holdDown) {
+ return;
+ }
+
+ this.ripples.forEach(function(ripple) {
+ ripple.upAction(event);
+ });
+
+ this._animating = true;
+ this.animate();
+ },
+
+ onAnimationComplete: function() {
+ this._animating = false;
+ this.$.background.style.backgroundColor = null;
+ this.fire('transitionend');
+ },
+
+ addRipple: function() {
+ var ripple = new Ripple(this);
+
+ Polymer.dom(this.$.waves).appendChild(ripple.waveContainer);
+ this.$.background.style.backgroundColor = ripple.color;
+ this.ripples.push(ripple);
+
+ this._setAnimating(true);
+
+ return ripple;
+ },
+
+ removeRipple: function(ripple) {
+ var rippleIndex = this.ripples.indexOf(ripple);
+
+ if (rippleIndex < 0) {
+ return;
+ }
+
+ this.ripples.splice(rippleIndex, 1);
+
+ ripple.remove();
+
+ if (!this.ripples.length) {
+ this._setAnimating(false);
+ }
+ },
+
+ /**
+ * This conflicts with Element#antimate().
+ * https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
+ * @suppress {checkTypes}
+ */
+ animate: function() {
+ if (!this._animating) {
+ return;
+ }
+ var index;
+ var ripple;
+
+ for (index = 0; index < this.ripples.length; ++index) {
+ ripple = this.ripples[index];
+
+ ripple.draw();
+
+ this.$.background.style.opacity = ripple.outerOpacity;
+
+ if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) {
+ this.removeRipple(ripple);
+ }
+ }
+
+ if (!this.shouldKeepAnimating && this.ripples.length === 0) {
+ this.onAnimationComplete();
+ } else {
+ window.requestAnimationFrame(this._boundAnimate);
+ }
+ },
+
+ _onEnterKeydown: function() {
+ this.uiDownAction();
+ this.async(this.uiUpAction, 1);
+ },
+
+ _onSpaceKeydown: function() {
+ this.uiDownAction();
+ },
+
+ _onSpaceKeyup: function() {
+ this.uiUpAction();
+ },
+
+ // note: holdDown does not respect noink since it can be a focus based
+ // effect.
+ _holdDownChanged: function(newVal, oldVal) {
+ if (oldVal === undefined) {
+ return;
+ }
+ if (newVal) {
+ this.downAction();
+ } else {
+ this.upAction();
+ }
+ }
+
+ /**
+ Fired when the animation finishes.
+ This is useful if you want to wait until
+ the ripple animation finishes to perform some action.
+
+ @event transitionend
+ @param {{node: Object}} detail Contains the animated node.
+ */
+ });
+ })();
+</script>
diff --git a/catapult/third_party/polymer/components/paper-ripple/test/index.html b/catapult/third_party/polymer/components/paper-ripple/test/index.html
new file mode 100644
index 00000000..865c40eb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'paper-ripple.html',
+ 'paper-ripple.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-ripple/test/paper-ripple.html b/catapult/third_party/polymer/components/paper-ripple/test/paper-ripple.html
new file mode 100644
index 00000000..559f9cd3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-ripple/test/paper-ripple.html
@@ -0,0 +1,253 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="UTF-8">
+ <title>paper-ripple</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../paper-ripple.html">
+
+ <style>
+ #RippleContainer {
+ display: block;
+ position: relative;
+ width: 100px;
+ height: 50px;
+ }
+ </style>
+</head>
+<body>
+ <test-fixture id="TrivialRipple">
+ <template>
+ <div id="RippleContainer">
+ <paper-ripple></paper-ripple>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="CenteringRipple">
+ <template>
+ <div id="RippleContainer">
+ <paper-ripple center></paper-ripple>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="RecenteringRipple">
+ <template>
+ <div id="RippleContainer">
+ <paper-ripple recenters></paper-ripple>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="NoinkTarget">
+ <template>
+ <div id="RippleContainer">
+ <paper-ripple noink></paper-ripple>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="NoRipple">
+ <template>
+ <div id="RippleContainer">
+ </div>
+ </template>
+ </test-fixture>
+
+ <script>
+ suite('<paper-ripple>', function () {
+ var mouseEvent;
+ var rippleContainer;
+ var ripple;
+
+ suite('when tapped', function () {
+ setup(function () {
+ rippleContainer = fixture('TrivialRipple');
+ ripple = rippleContainer.firstElementChild;
+ });
+
+ test('creates a ripple', function () {
+ expect(ripple.ripples.length).to.be.eql(0);
+ MockInteractions.down(ripple);
+ expect(ripple.ripples.length).to.be.eql(1);
+ });
+
+ test('may create multiple ripples that overlap', function () {
+ expect(ripple.ripples.length).to.be.eql(0);
+
+ for (var i = 0; i < 3; ++i) {
+ MockInteractions.down(ripple);
+ expect(ripple.ripples.length).to.be.eql(i + 1);
+ }
+ });
+ });
+
+ suite('when holdDown is togggled', function() {
+ setup(function () {
+ rippleContainer = fixture('TrivialRipple');
+ ripple = rippleContainer.firstElementChild;
+ });
+
+ test('generates a ripple', function() {
+ ripple.holdDown = true;
+ expect(ripple.ripples.length).to.be.eql(1);
+ });
+
+ test('generates a ripple when noink', function() {
+ ripple.noink = true;
+ ripple.holdDown = true;
+ expect(ripple.ripples.length).to.be.eql(1);
+
+ });
+
+ });
+
+ suite('when target is noink', function () {
+ setup(function () {
+ rippleContainer = fixture('NoinkTarget');
+ ripple = rippleContainer.firstElementChild;
+ });
+
+ test('tapping does not create a ripple', function () {
+ expect(ripple.ripples.length).to.be.eql(0);
+ MockInteractions.down(ripple);
+ expect(ripple.ripples.length).to.be.eql(0);
+ });
+
+ test('ripples can be manually created', function () {
+ expect(ripple.ripples.length).to.be.eql(0);
+ ripple.simulatedRipple()
+ expect(ripple.ripples.length).to.be.eql(1);
+ });
+ });
+
+ suite('with the `center` attribute set to true', function () {
+ setup(function () {
+ rippleContainer = fixture('CenteringRipple');
+ ripple = rippleContainer.firstElementChild;
+ });
+
+ test('ripples will center', function (done) {
+ var waveContainerElement;
+ // let's ask the browser what `translate3d(0px, 0px, 0)` will actually look like
+ var div = document.createElement('div');
+ div.style.webkitTransform = 'translate3d(0px, 0px, 0px)';
+ div.style.transform = 'translate3d(0px, 0px, 0)';
+
+ MockInteractions.down(ripple);
+
+ waveContainerElement = ripple.ripples[0].waveContainer;
+
+ MockInteractions.up(ripple);
+
+ window.requestAnimationFrame(function () {
+ var currentTransform = waveContainerElement.style.transform;
+ try {
+ expect(div.style.transform).to.be.ok;
+ expect(currentTransform).to.be.ok;
+ expect(currentTransform).to.be.eql(div.style.transform);
+
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ });
+ });
+
+ suite('with the `recenters` attribute set to true', function () {
+ setup(function () {
+ rippleContainer = fixture('RecenteringRipple');
+ ripple = rippleContainer.firstElementChild;
+ });
+ test('ripples will gravitate towards the center', function (done) {
+ var waveContainerElement;
+ var waveTranslateString;
+ MockInteractions.down(ripple, {x: 10, y: 10});
+ waveContainerElement = ripple.ripples[0].waveContainer;
+ waveTranslateString = waveContainerElement.style.transform;
+ MockInteractions.up(ripple);
+ window.requestAnimationFrame(function () {
+ try {
+ expect(waveTranslateString).to.be.ok;
+ expect(waveContainerElement.style.transform).to.be.ok;
+ expect(waveContainerElement.style.transform).to.not.be.eql(waveTranslateString);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ });
+ });
+
+ suite('remove a paper ripple', function () {
+ setup(function () {
+ rippleContainer = fixture('NoRipple');
+ });
+ test('add and remove a paper-ripple', function (done) {
+ var ripple = document.createElement('paper-ripple');
+ ripple.addEventListener('transitionend', function() {
+ expect(ripple.parentNode).to.be.ok;
+ Polymer.dom(rippleContainer).removeChild(ripple);
+ done();
+ });
+ Polymer.dom(rippleContainer).appendChild(ripple);
+ ripple.downAction();
+ ripple.upAction();
+ });
+ test('reuse a paper-ripple', function (done) {
+ var ripple = document.createElement('paper-ripple');
+ Polymer.dom(rippleContainer).appendChild(ripple);
+ Polymer.dom(rippleContainer).removeChild(ripple);
+
+ ripple.addEventListener('transitionend', function() {
+ expect(ripple.parentNode).to.be.ok;
+ Polymer.dom(document.body).removeChild(ripple);
+ done();
+ });
+ Polymer.dom(document.body).appendChild(ripple);
+ ripple.downAction();
+ ripple.upAction();
+ });
+ });
+
+ suite('avoid double transitionend event', function () {
+ setup(function () {
+ rippleContainer = fixture('NoRipple');
+ });
+ test('the transitionend event should only fire once', function (done) {
+ var ripple = document.createElement('paper-ripple');
+ var transitionedEventCount = 0;
+ ripple.addEventListener('transitionend', function() {
+ ++transitionedEventCount;
+ expect(transitionedEventCount).to.be.eql(1);
+ Polymer.dom(rippleContainer).removeChild(ripple);
+ setTimeout(function() { done(); });
+ });
+ Polymer.dom(rippleContainer).appendChild(ripple);
+ ripple.downAction();
+ ripple.upAction();
+ });
+ });
+
+ });
+ </script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-spinner/.bower.json b/catapult/third_party/polymer/components/paper-spinner/.bower.json
new file mode 100644
index 00000000..2eb41f40
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/.bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "paper-spinner",
+ "version": "1.2.1",
+ "description": "A material design spinner",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "spinner",
+ "loading"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-spinner"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-spinner",
+ "ignore": [],
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": [
+ "paper-spinner.html",
+ "paper-spinner-lite.html"
+ ],
+ "_release": "1.2.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.2.1",
+ "commit": "b858c85867bb6e2f9fa00ab335ea9ee496fa14b8"
+ },
+ "_source": "https://github.com/PolymerElements/paper-spinner.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-spinner"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-spinner/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-spinner/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..f1a728a5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-spinner/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-spinner/.gitignore b/catapult/third_party/polymer/components/paper-spinner/.gitignore
new file mode 100644
index 00000000..fbe05fc9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/.gitignore
@@ -0,0 +1 @@
+bower_components/
diff --git a/catapult/third_party/polymer/components/paper-spinner/.travis.yml b/catapult/third_party/polymer/components/paper-spinner/.travis.yml
new file mode 100644
index 00000000..ff38c72d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/.travis.yml
@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+before_script:
+- npm install -g bower polylint web-component-tester
+- bower install
+- polylint
+env:
+ global:
+ - secure: dIA8M55rHJN07lbp0VCU9RkN4CWlbkVdU6cP4wFZabuJJusISThMZVrZeGtbdErvQ8oiSexrE8iCZ7A/OcLnVNCrVBZX5YwJlfbex4I4uG6L8zw1E3oOX1MmdcTx2sI8MffDyG1pnXzwP5lzPItKiscEpepGY9+V0JP1j5z9qVg=
+ - secure: KvttUgmPIlCz4WU2WIpse5s/1SVXHoS+snGDkNqYLOVXscRjJoncXYbdvLltf7SPrU7gK4HuEuEVRthhDGtuvgrXUlIOS/gaK8dWI3kuVYPppOU1DnlXgAtj/3quGZG1dNw07IGzOEW4Taq/5KdU8LRqb9clvK+jyoBQZKIXbtg=
+node_js: stable
+addons:
+ firefox: '46.0'
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+ sauce_connect: true
+script:
+- xvfb-run wct
+- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'default'; fi
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-spinner/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-spinner/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-spinner/README.md b/catapult/third_party/polymer/components/paper-spinner/README.md
new file mode 100644
index 00000000..e913ae5b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/README.md
@@ -0,0 +1,49 @@
+[![Build status](https://travis-ci.org/PolymerElements/paper-spinner.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-spinner)
+
+##&lt;paper-spinner&gt;
+
+Material design: [Progress & activity](https://www.google.com/design/spec/components/progress-activity.html)
+
+Element providing a multiple color material design circular spinner.
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-spinner.html">
+ <link rel="import" href="paper-spinner-lite.html">
+ <style is="custom-style">
+ paper-spinner, paper-spinner-lite {
+ margin: 8px 8px 8px 0;
+ }
+ paper-spinner-lite.orange {
+ --paper-spinner-color: var(--google-yellow-500);
+ }
+ paper-spinner-lite.green {
+ --paper-spinner-color: var(--google-green-500);
+ }
+ paper-spinner-lite.thin {
+ --paper-spinner-stroke-width: 1px;
+ }
+ paper-spinner-lite.thick {
+ --paper-spinner-stroke-width: 6px;
+ }
+ #container {
+ display: flex;
+ }
+ </style>
+ <div id="container">
+ <next-code-block></next-code-block>
+ </div>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-spinner active>...</paper-spinner>
+<paper-spinner-lite active class="orange"></paper-spinner-lite>
+<paper-spinner-lite active class="green"></paper-spinner-lite>
+<paper-spinner-lite active class="thin"></paper-spinner-lite>
+<paper-spinner-lite active class="thick"></paper-spinner-lite>
+```
diff --git a/catapult/third_party/polymer/components/paper-spinner/bower.json b/catapult/third_party/polymer/components/paper-spinner/bower.json
new file mode 100644
index 00000000..7f8f09a9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/bower.json
@@ -0,0 +1,38 @@
+{
+ "name": "paper-spinner",
+ "version": "1.2.0",
+ "description": "A material design spinner",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "spinner",
+ "loading"
+ ],
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-spinner"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-spinner",
+ "ignore": [],
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "main": [
+ "paper-spinner.html",
+ "paper-spinner-lite.html"
+ ]
+}
diff --git a/catapult/third_party/polymer/components/paper-spinner/demo/index.html b/catapult/third_party/polymer/components/paper-spinner/demo/index.html
new file mode 100644
index 00000000..d4c56f94
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/demo/index.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html lang="en">
+ <head>
+ <title>paper-spinner demo</title>
+
+ <meta charset="UTF-8">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../paper-spinner-lite.html">
+ <link rel="import" href="../paper-spinner.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ .vertical-section-container {
+ max-width: 550px;
+ }
+
+ paper-spinner, paper-spinner-lite {
+ margin-right: 24px;
+ }
+ </style>
+ </head>
+ <body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Spinners can be deactivated or activated</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-spinner></paper-spinner>
+ <paper-spinner active></paper-spinner>
+
+ <paper-spinner-lite></paper-spinner-lite>
+ <paper-spinner-lite active></paper-spinner-lite>
+
+ <button onclick="toggle(event)">Toggle</button>
+ </template>
+ </demo-snippet>
+
+ <h3>Spinners can be styled using custom properties</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-spinner.multi {
+ --paper-spinner-layer-1-color: var(--paper-purple-500);
+ --paper-spinner-layer-2-color: var(--paper-cyan-500);
+ --paper-spinner-layer-3-color: var(--paper-blue-grey-500);
+ --paper-spinner-layer-4-color: var(--paper-amber-500);
+ }
+ paper-spinner-lite.orange {
+ --paper-spinner-color: var(--google-yellow-500);
+ }
+ paper-spinner-lite.green {
+ --paper-spinner-color: var(--google-green-500);
+ }
+ paper-spinner-lite.thin {
+ --paper-spinner-stroke-width: 1px;
+ }
+ paper-spinner-lite.thick {
+ --paper-spinner-stroke-width: 6px;
+ }
+ </style>
+
+ <paper-spinner class="multi" active></paper-spinner>
+
+ <paper-spinner-lite class="orange" active></paper-spinner-lite>
+ <paper-spinner-lite class="green" active></paper-spinner-lite>
+
+ <paper-spinner-lite class="thin" active></paper-spinner-lite>
+ <paper-spinner-lite class="thick" active></paper-spinner-lite>
+ </template>
+ </demo-snippet>
+ </div>
+
+ <script>
+ function toggle(event) {
+ var spinners = event.target.parentElement.querySelectorAll('paper-spinner, paper-spinner-lite');
+ Array.prototype.forEach.call(spinners, function(spinner) {
+ spinner.active = !spinner.active;
+ });
+ };
+ </script>
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-spinner/hero.svg b/catapult/third_party/polymer/components/paper-spinner/hero.svg
new file mode 100755
index 00000000..d35a53b0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/hero.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <g>
+ <path d="M112.1,92.3c-13.4,0-25.1-9.1-28.4-22l1.9-0.5c3.1,12.1,13.9,20.5,26.4,20.5c15,0,27.3-12.2,27.3-27.3
+ s-12.2-27.3-27.3-27.3c-8.1,0-15.8,3.6-21,9.8l-1.5-1.3c5.6-6.7,13.8-10.6,22.5-10.6c16.1,0,29.3,13.1,29.3,29.3
+ S128.2,92.3,112.1,92.3z"/>
+ <path d="M112.7,87.3c-6.6,0-12.7-2.5-17.3-7.2c-4.6-4.6-7.2-10.8-7.2-17.3c0-6.6,2.5-12.7,7.2-17.3c7.9-7.9,20.2-9.5,29.8-3.8
+ l-1,1.7c-8.8-5.3-20.1-3.8-27.4,3.5c-4.2,4.2-6.6,9.9-6.6,15.9s2.3,11.7,6.6,15.9s9.9,6.6,15.9,6.6c6,0,11.7-2.3,15.9-6.6
+ c4.7-4.7,7.1-11.3,6.5-18l2-0.2c0.7,7.3-1.9,14.4-7.1,19.6C125.4,84.7,119.2,87.3,112.7,87.3z"/>
+ <path d="M112.5,43.5C101.8,43.5,93,52.3,93,63s8.7,19.5,19.5,19.5S132,73.7,132,63S123.2,43.5,112.5,43.5z M119,64h-6v6h-2v-6h-6
+ v-2h6v-6h2v6h6V64z"/>
+ </g>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-spinner/index.html b/catapult/third_party/polymer/components/paper-spinner/index.html
new file mode 100644
index 00000000..98743346
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-spinner</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../polymer/polymer.html">
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+ </head>
+ <body>
+
+ <iron-component-page></iron-component-page>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-spinner/paper-spinner-behavior.html b/catapult/third_party/polymer/components/paper-spinner/paper-spinner-behavior.html
new file mode 100644
index 00000000..ce3594b0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/paper-spinner-behavior.html
@@ -0,0 +1,87 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<script>
+
+ /** @polymerBehavior */
+ Polymer.PaperSpinnerBehavior = {
+
+ listeners: {
+ 'animationend': '__reset',
+ 'webkitAnimationEnd': '__reset'
+ },
+
+ properties: {
+ /**
+ * Displays the spinner.
+ */
+ active: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ observer: '__activeChanged'
+ },
+
+ /**
+ * Alternative text content for accessibility support.
+ * If alt is present, it will add an aria-label whose content matches alt when active.
+ * If alt is not present, it will default to 'loading' as the alt value.
+ */
+ alt: {
+ type: String,
+ value: 'loading',
+ observer: '__altChanged'
+ },
+
+ __coolingDown: {
+ type: Boolean,
+ value: false
+ }
+ },
+
+ __computeContainerClasses: function(active, coolingDown) {
+ return [
+ active || coolingDown ? 'active' : '',
+ coolingDown ? 'cooldown' : ''
+ ].join(' ');
+ },
+
+ __activeChanged: function(active, old) {
+ this.__setAriaHidden(!active);
+ this.__coolingDown = !active && old;
+ },
+
+ __altChanged: function(alt) {
+ // user-provided `aria-label` takes precedence over prototype default
+ if (alt === this.getPropertyInfo('alt').value) {
+ this.alt = this.getAttribute('aria-label') || alt;
+ } else {
+ this.__setAriaHidden(alt==='');
+ this.setAttribute('aria-label', alt);
+ }
+ },
+
+ __setAriaHidden: function(hidden) {
+ var attr = 'aria-hidden';
+ if (hidden) {
+ this.setAttribute(attr, 'true');
+ } else {
+ this.removeAttribute(attr);
+ }
+ },
+
+ __reset: function() {
+ this.active = false;
+ this.__coolingDown = false;
+ }
+ };
+</script>
diff --git a/catapult/third_party/polymer/components/paper-spinner/paper-spinner-lite.html b/catapult/third_party/polymer/components/paper-spinner/paper-spinner-lite.html
new file mode 100644
index 00000000..5d123a75
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/paper-spinner-lite.html
@@ -0,0 +1,71 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/color.html">
+<link rel="import" href="paper-spinner-behavior.html">
+<link rel="import" href="paper-spinner-styles.html">
+
+<!--
+Material design: [Progress & activity](https://www.google.com/design/spec/components/progress-activity.html)
+
+Element providing a single color material design circular spinner.
+
+ <paper-spinner-lite active></paper-spinner-lite>
+
+The default spinner is blue. It can be customized to be a different color.
+
+### Accessibility
+
+Alt attribute should be set to provide adequate context for accessibility. If not provided,
+it defaults to 'loading'.
+Empty alt can be provided to mark the element as decorative if alternative content is provided
+in another form (e.g. a text block following the spinner).
+
+ <paper-spinner-lite alt="Loading contacts list" active></paper-spinner-lite>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-spinner-color` | Color of the spinner | `--google-blue-500`
+`--paper-spinner-stroke-width` | The width of the spinner stroke | 3px
+
+@group Paper Elements
+@element paper-spinner-lite
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-spinner-lite">
+ <template strip-whitespace>
+ <style include="paper-spinner-styles"></style>
+
+ <div id="spinnerContainer" class-name="[[__computeContainerClasses(active, __coolingDown)]]">
+ <div class="spinner-layer">
+ <div class="circle-clipper left"></div>
+ <div class="circle-clipper right"></div>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-spinner-lite',
+
+ behaviors: [
+ Polymer.PaperSpinnerBehavior
+ ]
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-spinner/paper-spinner-styles.html b/catapult/third_party/polymer/components/paper-spinner/paper-spinner-styles.html
new file mode 100644
index 00000000..08329293
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/paper-spinner-styles.html
@@ -0,0 +1,341 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<dom-module id="paper-spinner-styles">
+ <template>
+ <style>
+ /*
+ /**************************/
+ /* STYLES FOR THE SPINNER */
+ /**************************/
+
+ /*
+ * Constants:
+ * ARCSIZE = 270 degrees (amount of circle the arc takes up)
+ * ARCTIME = 1333ms (time it takes to expand and contract arc)
+ * ARCSTARTROT = 216 degrees (how much the start location of the arc
+ * should rotate each time, 216 gives us a
+ * 5 pointed star shape (it's 360/5 * 3).
+ * For a 7 pointed star, we might do
+ * 360/7 * 3 = 154.286)
+ * SHRINK_TIME = 400ms
+ */
+
+ :host {
+ display: inline-block;
+ position: relative;
+ width: 28px;
+ height: 28px;
+
+ /* 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */
+ --paper-spinner-container-rotation-duration: 1568ms;
+
+ /* ARCTIME */
+ --paper-spinner-expand-contract-duration: 1333ms;
+
+ /* 4 * ARCTIME */
+ --paper-spinner-full-cycle-duration: 5332ms;
+
+ /* SHRINK_TIME */
+ --paper-spinner-cooldown-duration: 400ms;
+ }
+
+ #spinnerContainer {
+ width: 100%;
+ height: 100%;
+
+ /* The spinner does not have any contents that would have to be
+ * flipped if the direction changes. Always use ltr so that the
+ * style works out correctly in both cases. */
+ direction: ltr;
+ }
+
+ #spinnerContainer.active {
+ -webkit-animation: container-rotate var(--paper-spinner-container-rotation-duration) linear infinite;
+ animation: container-rotate var(--paper-spinner-container-rotation-duration) linear infinite;
+ }
+
+ @-webkit-keyframes container-rotate {
+ to { -webkit-transform: rotate(360deg) }
+ }
+
+ @keyframes container-rotate {
+ to { transform: rotate(360deg) }
+ }
+
+ .spinner-layer {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ white-space: nowrap;
+ border-color: var(--paper-spinner-color, --google-blue-500);
+ }
+
+ .layer-1 {
+ border-color: var(--paper-spinner-layer-1-color, --google-blue-500);
+ }
+
+ .layer-2 {
+ border-color: var(--paper-spinner-layer-2-color, --google-red-500);
+ }
+
+ .layer-3 {
+ border-color: var(--paper-spinner-layer-3-color, --google-yellow-500);
+ }
+
+ .layer-4 {
+ border-color: var(--paper-spinner-layer-4-color, --google-green-500);
+ }
+
+ /**
+ * IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
+ *
+ * iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
+ * guarantee that the animation will start _exactly_ after that value. So we avoid using
+ * animation-delay and instead set custom keyframes for each color (as layer-2undant as it
+ * seems).
+ */
+ .active .spinner-layer {
+ -webkit-animation-name: fill-unfill-rotate;
+ -webkit-animation-duration: var(--paper-spinner-full-cycle-duration);
+ -webkit-animation-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1);
+ -webkit-animation-iteration-count: infinite;
+ animation-name: fill-unfill-rotate;
+ animation-duration: var(--paper-spinner-full-cycle-duration);
+ animation-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1);
+ animation-iteration-count: infinite;
+ opacity: 1;
+ }
+
+ .active .spinner-layer.layer-1 {
+ -webkit-animation-name: fill-unfill-rotate, layer-1-fade-in-out;
+ animation-name: fill-unfill-rotate, layer-1-fade-in-out;
+ }
+
+ .active .spinner-layer.layer-2 {
+ -webkit-animation-name: fill-unfill-rotate, layer-2-fade-in-out;
+ animation-name: fill-unfill-rotate, layer-2-fade-in-out;
+ }
+
+ .active .spinner-layer.layer-3 {
+ -webkit-animation-name: fill-unfill-rotate, layer-3-fade-in-out;
+ animation-name: fill-unfill-rotate, layer-3-fade-in-out;
+ }
+
+ .active .spinner-layer.layer-4 {
+ -webkit-animation-name: fill-unfill-rotate, layer-4-fade-in-out;
+ animation-name: fill-unfill-rotate, layer-4-fade-in-out;
+ }
+
+ @-webkit-keyframes fill-unfill-rotate {
+ 12.5% { -webkit-transform: rotate(135deg) } /* 0.5 * ARCSIZE */
+ 25% { -webkit-transform: rotate(270deg) } /* 1 * ARCSIZE */
+ 37.5% { -webkit-transform: rotate(405deg) } /* 1.5 * ARCSIZE */
+ 50% { -webkit-transform: rotate(540deg) } /* 2 * ARCSIZE */
+ 62.5% { -webkit-transform: rotate(675deg) } /* 2.5 * ARCSIZE */
+ 75% { -webkit-transform: rotate(810deg) } /* 3 * ARCSIZE */
+ 87.5% { -webkit-transform: rotate(945deg) } /* 3.5 * ARCSIZE */
+ to { -webkit-transform: rotate(1080deg) } /* 4 * ARCSIZE */
+ }
+
+ @keyframes fill-unfill-rotate {
+ 12.5% { transform: rotate(135deg) } /* 0.5 * ARCSIZE */
+ 25% { transform: rotate(270deg) } /* 1 * ARCSIZE */
+ 37.5% { transform: rotate(405deg) } /* 1.5 * ARCSIZE */
+ 50% { transform: rotate(540deg) } /* 2 * ARCSIZE */
+ 62.5% { transform: rotate(675deg) } /* 2.5 * ARCSIZE */
+ 75% { transform: rotate(810deg) } /* 3 * ARCSIZE */
+ 87.5% { transform: rotate(945deg) } /* 3.5 * ARCSIZE */
+ to { transform: rotate(1080deg) } /* 4 * ARCSIZE */
+ }
+
+ @-webkit-keyframes layer-1-fade-in-out {
+ 0% { opacity: 1 }
+ 25% { opacity: 1 }
+ 26% { opacity: 0 }
+ 89% { opacity: 0 }
+ 90% { opacity: 1 }
+ to { opacity: 1 }
+ }
+
+ @keyframes layer-1-fade-in-out {
+ 0% { opacity: 1 }
+ 25% { opacity: 1 }
+ 26% { opacity: 0 }
+ 89% { opacity: 0 }
+ 90% { opacity: 1 }
+ to { opacity: 1 }
+ }
+
+ @-webkit-keyframes layer-2-fade-in-out {
+ 0% { opacity: 0 }
+ 15% { opacity: 0 }
+ 25% { opacity: 1 }
+ 50% { opacity: 1 }
+ 51% { opacity: 0 }
+ to { opacity: 0 }
+ }
+
+ @keyframes layer-2-fade-in-out {
+ 0% { opacity: 0 }
+ 15% { opacity: 0 }
+ 25% { opacity: 1 }
+ 50% { opacity: 1 }
+ 51% { opacity: 0 }
+ to { opacity: 0 }
+ }
+
+ @-webkit-keyframes layer-3-fade-in-out {
+ 0% { opacity: 0 }
+ 40% { opacity: 0 }
+ 50% { opacity: 1 }
+ 75% { opacity: 1 }
+ 76% { opacity: 0 }
+ to { opacity: 0 }
+ }
+
+ @keyframes layer-3-fade-in-out {
+ 0% { opacity: 0 }
+ 40% { opacity: 0 }
+ 50% { opacity: 1 }
+ 75% { opacity: 1 }
+ 76% { opacity: 0 }
+ to { opacity: 0 }
+ }
+
+ @-webkit-keyframes layer-4-fade-in-out {
+ 0% { opacity: 0 }
+ 65% { opacity: 0 }
+ 75% { opacity: 1 }
+ 90% { opacity: 1 }
+ to { opacity: 0 }
+ }
+
+ @keyframes layer-4-fade-in-out {
+ 0% { opacity: 0 }
+ 65% { opacity: 0 }
+ 75% { opacity: 1 }
+ 90% { opacity: 1 }
+ to { opacity: 0 }
+ }
+
+ .circle-clipper {
+ display: inline-block;
+ position: relative;
+ width: 50%;
+ height: 100%;
+ overflow: hidden;
+ border-color: inherit;
+ }
+
+ /**
+ * Patch the gap that appear between the two adjacent div.circle-clipper while the
+ * spinner is rotating (appears on Chrome 50, Safari 9.1.1, and Edge).
+ */
+ .spinner-layer::after {
+ left: 45%;
+ width: 10%;
+ border-top-style: solid;
+ }
+
+ .spinner-layer::after,
+ .circle-clipper::after {
+ content: '';
+ box-sizing: border-box;
+ position: absolute;
+ top: 0;
+ border-width: var(--paper-spinner-stroke-width, 3px);
+ border-color: inherit;
+ border-radius: 50%;
+ }
+
+ .circle-clipper::after {
+ bottom: 0;
+ width: 200%;
+ border-style: solid;
+ border-bottom-color: transparent !important;
+ }
+
+ .circle-clipper.left::after {
+ left: 0;
+ border-right-color: transparent !important;
+ -webkit-transform: rotate(129deg);
+ transform: rotate(129deg);
+ }
+
+ .circle-clipper.right::after {
+ left: -100%;
+ border-left-color: transparent !important;
+ -webkit-transform: rotate(-129deg);
+ transform: rotate(-129deg);
+ }
+
+ .active .gap-patch::after,
+ .active .circle-clipper::after {
+ -webkit-animation-duration: var(--paper-spinner-expand-contract-duration);
+ -webkit-animation-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1);
+ -webkit-animation-iteration-count: infinite;
+ animation-duration: var(--paper-spinner-expand-contract-duration);
+ animation-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1);
+ animation-iteration-count: infinite;
+ }
+
+ .active .circle-clipper.left::after {
+ -webkit-animation-name: left-spin;
+ animation-name: left-spin;
+ }
+
+ .active .circle-clipper.right::after {
+ -webkit-animation-name: right-spin;
+ animation-name: right-spin;
+ }
+
+ @-webkit-keyframes left-spin {
+ 0% { -webkit-transform: rotate(130deg) }
+ 50% { -webkit-transform: rotate(-5deg) }
+ to { -webkit-transform: rotate(130deg) }
+ }
+
+ @keyframes left-spin {
+ 0% { transform: rotate(130deg) }
+ 50% { transform: rotate(-5deg) }
+ to { transform: rotate(130deg) }
+ }
+
+ @-webkit-keyframes right-spin {
+ 0% { -webkit-transform: rotate(-130deg) }
+ 50% { -webkit-transform: rotate(5deg) }
+ to { -webkit-transform: rotate(-130deg) }
+ }
+
+ @keyframes right-spin {
+ 0% { transform: rotate(-130deg) }
+ 50% { transform: rotate(5deg) }
+ to { transform: rotate(-130deg) }
+ }
+
+ #spinnerContainer.cooldown {
+ -webkit-animation: container-rotate var(--paper-spinner-container-rotation-duration) linear infinite, fade-out var(--paper-spinner-cooldown-duration) cubic-bezier(0.4, 0.0, 0.2, 1);
+ animation: container-rotate var(--paper-spinner-container-rotation-duration) linear infinite, fade-out var(--paper-spinner-cooldown-duration) cubic-bezier(0.4, 0.0, 0.2, 1);
+ }
+
+ @-webkit-keyframes fade-out {
+ 0% { opacity: 1 }
+ to { opacity: 0 }
+ }
+
+ @keyframes fade-out {
+ 0% { opacity: 1 }
+ to { opacity: 0 }
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-spinner/paper-spinner.html b/catapult/third_party/polymer/components/paper-spinner/paper-spinner.html
new file mode 100644
index 00000000..54ff8bc0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/paper-spinner.html
@@ -0,0 +1,91 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-styles/color.html">
+<link rel="import" href="paper-spinner-behavior.html">
+<link rel="import" href="paper-spinner-styles.html">
+
+<!--
+Material design: [Progress & activity](https://www.google.com/design/spec/components/progress-activity.html)
+
+Element providing a multiple color material design circular spinner.
+
+ <paper-spinner active></paper-spinner>
+
+The default spinner cycles between four layers of colors; by default they are
+blue, red, yellow and green. It can be customized to cycle between four different
+colors. Use <paper-spinner-lite> for single color spinners.
+
+### Accessibility
+
+Alt attribute should be set to provide adequate context for accessibility. If not provided,
+it defaults to 'loading'.
+Empty alt can be provided to mark the element as decorative if alternative content is provided
+in another form (e.g. a text block following the spinner).
+
+ <paper-spinner alt="Loading contacts list" active></paper-spinner>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-spinner-layer-1-color` | Color of the first spinner rotation | `--google-blue-500`
+`--paper-spinner-layer-2-color` | Color of the second spinner rotation | `--google-red-500`
+`--paper-spinner-layer-3-color` | Color of the third spinner rotation | `--google-yellow-500`
+`--paper-spinner-layer-4-color` | Color of the fourth spinner rotation | `--google-green-500`
+`--paper-spinner-stroke-width` | The width of the spinner stroke | 3px
+
+@group Paper Elements
+@element paper-spinner
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-spinner">
+ <template strip-whitespace>
+ <style include="paper-spinner-styles"></style>
+
+ <div id="spinnerContainer" class-name="[[__computeContainerClasses(active, __coolingDown)]]">
+ <div class="spinner-layer layer-1">
+ <div class="circle-clipper left"></div>
+ <div class="circle-clipper right"></div>
+ </div>
+
+ <div class="spinner-layer layer-2">
+ <div class="circle-clipper left"></div>
+ <div class="circle-clipper right"></div>
+ </div>
+
+ <div class="spinner-layer layer-3">
+ <div class="circle-clipper left"></div>
+ <div class="circle-clipper right"></div>
+ </div>
+
+ <div class="spinner-layer layer-4">
+ <div class="circle-clipper left"></div>
+ <div class="circle-clipper right"></div>
+ </div>
+ </div>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-spinner',
+
+ behaviors: [
+ Polymer.PaperSpinnerBehavior
+ ]
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-spinner/test/index.html b/catapult/third_party/polymer/components/paper-spinner/test/index.html
new file mode 100644
index 00000000..e32c223a
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/test/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <title>paper-spinner tests</title>
+
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'paper-spinner.html',
+ 'paper-spinner.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-spinner/test/paper-spinner.html b/catapult/third_party/polymer/components/paper-spinner/test/paper-spinner.html
new file mode 100644
index 00000000..6e05f4ce
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-spinner/test/paper-spinner.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+ <head>
+
+ <meta charset="UTF-8">
+ <title>paper-spinner basic tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+
+ <link href="../../test-fixture/test-fixture.html" rel="import">
+ <link href="../paper-spinner.html" rel="import">
+
+ </head>
+ <body>
+
+ <test-fixture id="PaperSpinner">
+ <template>
+ <paper-spinner></paper-spinner>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="ActivePaperSpinner">
+ <template>
+ <paper-spinner active></paper-spinner>
+ </template>
+ </test-fixture>
+
+ <script>
+ 'use strict';
+
+ suite('<paper-spinner>', function() {
+
+ suite('an accessible paper spinner', function() {
+ var spinner;
+ var activeSpinner;
+
+ setup(function() {
+ spinner = fixture('PaperSpinner');
+ activeSpinner = fixture('ActivePaperSpinner');
+ });
+
+ test('adds an ARIA label when `alt` is supplied', function() {
+ var ALT_TEXT = 'Loading the next gif...';
+
+ spinner.alt = ALT_TEXT;
+ expect(spinner.getAttribute('aria-label')).to.be.eql(ALT_TEXT);
+ });
+
+ test('hides from ARIA when inactive', function() {
+ spinner.active = false;
+ expect(spinner.getAttribute('aria-hidden')).to.be.eql('true');
+ });
+
+ test('toggle during cooldown', function(done) {
+ activeSpinner.active = false;
+
+ // Set active to true before cooldown animation completes.
+ setTimeout(function() {
+ activeSpinner.active = true;
+
+ // Wait for cooldown animation to complete.
+ setTimeout(function() {
+ expect(activeSpinner.active).to.equal(true);
+ done();
+ }, 500);
+ }, 100);
+ });
+ });
+
+ });
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-styles/.bower.json b/catapult/third_party/polymer/components/paper-styles/.bower.json
new file mode 100644
index 00000000..24b398d2
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/.bower.json
@@ -0,0 +1,42 @@
+{
+ "name": "paper-styles",
+ "version": "1.3.1",
+ "description": "Common (global) styles for Material Design elements.",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-component",
+ "polymer",
+ "style"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-styles.git"
+ },
+ "main": "paper-styles.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/polymerelements/paper-styles/",
+ "ignore": [
+ "/.*"
+ ],
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "font-roboto": "PolymerElements/font-roboto#^1.0.1",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "web-component-tester": "^4.0.0"
+ },
+ "_release": "1.3.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.3.1",
+ "commit": "1ed324102daa36458d153dd20eac80be9758b00e"
+ },
+ "_source": "https://github.com/PolymerElements/paper-styles.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-styles"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-styles/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-styles/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-styles/README.md b/catapult/third_party/polymer/components/paper-styles/README.md
new file mode 100644
index 00000000..ca555bdb
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/README.md
@@ -0,0 +1,49 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-styles.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-styles.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-styles)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-styles)_
+
+
+##&lt;paper-styles&gt;
+
+The `<paper-styles>` component provides simple ways to use Material Design CSS styles
+in your application. The following imports are available:
+
+1. [color.html](https://github.com/PolymerElements/paper-styles/blob/master/color.html):
+a complete list of the colors defined in the Material Design [palette](https://www.google.com/design/spec/style/color.html)
+
+
+1. [default-theme.html](https://github.com/PolymerElements/paper-styles/blob/master/default-theme.html): text,
+background and accent colors that match the default Material Design theme
+
+
+1. [shadow.html](https://github.com/PolymerElements/paper-styles/blob/master/shadow.html): Material Design
+[elevation](https://www.google.com/design/spec/what-is-material/elevation-shadows.html) and shadow styles
+
+
+1. [typography.html](https://github.com/PolymerElements/paper-styles/blob/master/typography.html):
+Material Design [font](http://www.google.com/design/spec/style/typography.html#typography-styles) styles and sizes
+
+
+1. [demo-pages.html](https://github.com/PolymerElements/paper-styles/blob/master/demo-pages.html): generic styles
+used in the PolymerElements demo pages
+
+
+
+We recommend importing each of these individual files, and using the style mixins
+available in each ones, rather than the aggregated `paper-styles.html` as a whole.
+
+
diff --git a/catapult/third_party/polymer/components/paper-styles/bower.json b/catapult/third_party/polymer/components/paper-styles/bower.json
new file mode 100644
index 00000000..daaab796
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "paper-styles",
+ "version": "1.3.1",
+ "description": "Common (global) styles for Material Design elements.",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-component",
+ "polymer",
+ "style"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-styles.git"
+ },
+ "main": "paper-styles.html",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/polymerelements/paper-styles/",
+ "ignore": [
+ "/.*"
+ ],
+ "dependencies": {
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "font-roboto": "PolymerElements/font-roboto#^1.0.1",
+ "polymer": "Polymer/polymer#^1.0.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+ "web-component-tester": "^4.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-styles/classes/global.html b/catapult/third_party/polymer/components/paper-styles/classes/global.html
new file mode 100644
index 00000000..6f0d5dde
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/classes/global.html
@@ -0,0 +1,96 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../paper-styles-classes.html">
+
+<!--
+A set of base styles that are applied to the document and standard elements that
+match the Material Design spec.
+-->
+<style>
+/*
+Note that there is a lot of style duplication here. The hope is that the Polymer
+0.8 styling solution will allow for inheritance of properties so that we can
+eventually avoid it.
+*/
+
+/* Mixins */
+
+/* [paper-font] */
+body {
+ font-family: 'Roboto', 'Noto', sans-serif;
+ -webkit-font-smoothing: antialiased; /* OS X subpixel AA bleed bug */
+}
+
+/* [paper-font=display2] */
+h1 {
+ font-size: 45px;
+ font-weight: 400;
+ letter-spacing: -.018em;
+ line-height: 48px;
+}
+
+/* [paper-font=display1] */
+h2 {
+ font-size: 34px;
+ font-weight: 400;
+ letter-spacing: -.01em;
+ line-height: 40px;
+}
+
+/* [paper-font=headline] */
+h3 {
+ font-size: 24px;
+ font-weight: 400;
+ letter-spacing: -.012em;
+ line-height: 32px;
+}
+
+/* [paper-font=subhead] */
+h4 {
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 24px;
+}
+
+/* [paper-font=body2] */
+h5, h6 {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 24px;
+}
+
+/* [paper-font=button] */
+a {
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.018em;
+ line-height: 24px;
+ text-transform: uppercase;
+}
+
+/* Overrides */
+
+body, a {
+ color: #212121;
+}
+
+h1, h2, h3, h4, h5, h6, p {
+ margin: 0 0 20px 0;
+}
+
+h1, h2, h3, h4, h5, h6, a {
+ text-rendering: optimizeLegibility;
+}
+
+a {
+ text-decoration: none;
+}
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/classes/shadow-layout.html b/catapult/third_party/polymer/components/paper-styles/classes/shadow-layout.html
new file mode 100644
index 00000000..50708be7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/classes/shadow-layout.html
@@ -0,0 +1,307 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<script>
+ console.warn('This file is deprecated. Please use `iron-flex-layout/iron-flex-layout-classes.html`, and one of the specific dom-modules instead');
+</script>
+
+<style>
+
+ /*******************************
+ Flex Layout
+ *******************************/
+
+ html /deep/ .layout.horizontal,
+ html /deep/ .layout.horizontal-reverse,
+ html /deep/ .layout.vertical,
+ html /deep/ .layout.vertical-reverse {
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ }
+
+ html /deep/ .layout.inline {
+ display: -ms-inline-flexbox;
+ display: -webkit-inline-flex;
+ display: inline-flex;
+ }
+
+ html /deep/ .layout.horizontal {
+ -ms-flex-direction: row;
+ -webkit-flex-direction: row;
+ flex-direction: row;
+ }
+
+ html /deep/ .layout.horizontal-reverse {
+ -ms-flex-direction: row-reverse;
+ -webkit-flex-direction: row-reverse;
+ flex-direction: row-reverse;
+ }
+
+ html /deep/ .layout.vertical {
+ -ms-flex-direction: column;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ }
+
+ html /deep/ .layout.vertical-reverse {
+ -ms-flex-direction: column-reverse;
+ -webkit-flex-direction: column-reverse;
+ flex-direction: column-reverse;
+ }
+
+ html /deep/ .layout.wrap {
+ -ms-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+ }
+
+ html /deep/ .layout.wrap-reverse {
+ -ms-flex-wrap: wrap-reverse;
+ -webkit-flex-wrap: wrap-reverse;
+ flex-wrap: wrap-reverse;
+ }
+
+ html /deep/ .flex-auto {
+ -ms-flex: 1 1 auto;
+ -webkit-flex: 1 1 auto;
+ flex: 1 1 auto;
+ }
+
+ html /deep/ .flex-none {
+ -ms-flex: none;
+ -webkit-flex: none;
+ flex: none;
+ }
+
+ html /deep/ .flex,
+ html /deep/ .flex-1 {
+ -ms-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ }
+
+ html /deep/ .flex-2 {
+ -ms-flex: 2;
+ -webkit-flex: 2;
+ flex: 2;
+ }
+
+ html /deep/ .flex-3 {
+ -ms-flex: 3;
+ -webkit-flex: 3;
+ flex: 3;
+ }
+
+ html /deep/ .flex-4 {
+ -ms-flex: 4;
+ -webkit-flex: 4;
+ flex: 4;
+ }
+
+ html /deep/ .flex-5 {
+ -ms-flex: 5;
+ -webkit-flex: 5;
+ flex: 5;
+ }
+
+ html /deep/ .flex-6 {
+ -ms-flex: 6;
+ -webkit-flex: 6;
+ flex: 6;
+ }
+
+ html /deep/ .flex-7 {
+ -ms-flex: 7;
+ -webkit-flex: 7;
+ flex: 7;
+ }
+
+ html /deep/ .flex-8 {
+ -ms-flex: 8;
+ -webkit-flex: 8;
+ flex: 8;
+ }
+
+ html /deep/ .flex-9 {
+ -ms-flex: 9;
+ -webkit-flex: 9;
+ flex: 9;
+ }
+
+ html /deep/ .flex-10 {
+ -ms-flex: 10;
+ -webkit-flex: 10;
+ flex: 10;
+ }
+
+ html /deep/ .flex-11 {
+ -ms-flex: 11;
+ -webkit-flex: 11;
+ flex: 11;
+ }
+
+ html /deep/ .flex-12 {
+ -ms-flex: 12;
+ -webkit-flex: 12;
+ flex: 12;
+ }
+
+ /* alignment in cross axis */
+
+ html /deep/ .layout.start {
+ -ms-flex-align: start;
+ -webkit-align-items: flex-start;
+ align-items: flex-start;
+ }
+
+ html /deep/ .layout.center,
+ html /deep/ .layout.center-center {
+ -ms-flex-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ html /deep/ .layout.end {
+ -ms-flex-align: end;
+ -webkit-align-items: flex-end;
+ align-items: flex-end;
+ }
+
+ /* alignment in main axis */
+
+ html /deep/ .layout.start-justified {
+ -ms-flex-pack: start;
+ -webkit-justify-content: flex-start;
+ justify-content: flex-start;
+ }
+
+ html /deep/ .layout.center-justified,
+ html /deep/ .layout.center-center {
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ }
+
+ html /deep/ .layout.end-justified {
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ }
+
+ html /deep/ .layout.around-justified {
+ -ms-flex-pack: around;
+ -webkit-justify-content: space-around;
+ justify-content: space-around;
+ }
+
+ html /deep/ .layout.justified {
+ -ms-flex-pack: justify;
+ -webkit-justify-content: space-between;
+ justify-content: space-between;
+ }
+
+ /* self alignment */
+
+ html /deep/ .self-start {
+ -ms-align-self: flex-start;
+ -webkit-align-self: flex-start;
+ align-self: flex-start;
+ }
+
+ html /deep/ .self-center {
+ -ms-align-self: center;
+ -webkit-align-self: center;
+ align-self: center;
+ }
+
+ html /deep/ .self-end {
+ -ms-align-self: flex-end;
+ -webkit-align-self: flex-end;
+ align-self: flex-end;
+ }
+
+ html /deep/ .self-stretch {
+ -ms-align-self: stretch;
+ -webkit-align-self: stretch;
+ align-self: stretch;
+ }
+
+ /*******************************
+ Other Layout
+ *******************************/
+
+ html /deep/ .block {
+ display: block;
+ }
+
+ /* IE 10 support for HTML5 hidden attr */
+ html /deep/ [hidden] {
+ display: none !important;
+ }
+
+ html /deep/ .invisible {
+ visibility: hidden !important;
+ }
+
+ html /deep/ .relative {
+ position: relative;
+ }
+
+ html /deep/ .fit {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ body.fullbleed {
+ margin: 0;
+ height: 100vh;
+ }
+
+ html /deep/ .scroll {
+ -webkit-overflow-scrolling: touch;
+ overflow: auto;
+ }
+
+ .fixed-bottom,
+ .fixed-left,
+ .fixed-right,
+ .fixed-top {
+ position: fixed;
+ }
+
+ html /deep/ .fixed-top {
+ top: 0;
+ left: 0;
+ right: 0;
+ }
+
+ html /deep/ .fixed-right {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ html /deep/ .fixed-bottom {
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+ html /deep/ .fixed-left {
+ top: 0;
+ bottom: 0;
+ left: 0;
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/classes/shadow.html b/catapult/third_party/polymer/components/paper-styles/classes/shadow.html
new file mode 100644
index 00000000..4c40a147
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/classes/shadow.html
@@ -0,0 +1,52 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<style>
+.shadow-transition {
+ transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.shadow-elevation-2dp {
+ 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);
+}
+
+.shadow-elevation-3dp {
+ box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 8px 0 rgba(0, 0, 0, 0.12),
+ 0 3px 3px -2px rgba(0, 0, 0, 0.4);
+}
+
+.shadow-elevation-4dp {
+ box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 10px 0 rgba(0, 0, 0, 0.12),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.4);
+}
+
+.shadow-elevation-6dp {
+ box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 18px 0 rgba(0, 0, 0, 0.12),
+ 0 3px 5px -1px rgba(0, 0, 0, 0.4);
+}
+
+.shadow-elevation-8dp {
+ box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
+ 0 3px 14px 2px rgba(0, 0, 0, 0.12),
+ 0 5px 5px -3px rgba(0, 0, 0, 0.4);
+}
+
+.shadow-elevation-16dp {
+ box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
+ 0 6px 30px 5px rgba(0, 0, 0, 0.12),
+ 0 8px 10px -5px rgba(0, 0, 0, 0.4);
+}
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/classes/typography.html b/catapult/third_party/polymer/components/paper-styles/classes/typography.html
new file mode 100644
index 00000000..b399395e
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/classes/typography.html
@@ -0,0 +1,169 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<link rel="import" href="../../font-roboto/roboto.html">
+
+<!--
+Typographic styles are provided matching the Material Design standard styles:
+http://www.google.com/design/spec/style/typography.html#typography-standard-styles
+
+To make use of them, apply a `paper-font-<style>` class to elements, matching
+the font style you wish it to inherit.
+
+ <h1 class="paper-font-display2">Hey there!</h1>
+
+Note that these are English/Latin centric styles. You may need to further adjust
+line heights and weights for CJK typesetting. See the notes in the Material
+Design typography section.
+-->
+<style>
+
+.paper-font-display4,
+.paper-font-display3,
+.paper-font-display2,
+.paper-font-display1,
+.paper-font-headline,
+.paper-font-title,
+.paper-font-subhead,
+.paper-font-body2,
+.paper-font-body1,
+.paper-font-caption,
+.paper-font-menu,
+.paper-font-button {
+ font-family: 'Roboto', 'Noto', sans-serif;
+ -webkit-font-smoothing: antialiased; /* OS X subpixel AA bleed bug */
+}
+
+.paper-font-code2,
+.paper-font-code1 {
+ font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
+ -webkit-font-smoothing: antialiased; /* OS X subpixel AA bleed bug */
+}
+
+/* Opt for better kerning for headers & other short labels. */
+.paper-font-display4,
+.paper-font-display3,
+.paper-font-display2,
+.paper-font-display1,
+.paper-font-headline,
+.paper-font-title,
+.paper-font-subhead,
+.paper-font-menu,
+.paper-font-button {
+ text-rendering: optimizeLegibility;
+}
+
+/*
+"Line wrapping only applies to Body, Subhead, Headline, and the smaller Display
+styles. All other styles should exist as single lines."
+*/
+.paper-font-display4,
+.paper-font-display3,
+.paper-font-title,
+.paper-font-caption,
+.paper-font-menu,
+.paper-font-button {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.paper-font-display4 {
+ font-size: 112px;
+ font-weight: 300;
+ letter-spacing: -.044em;
+ line-height: 120px;
+}
+
+.paper-font-display3 {
+ font-size: 56px;
+ font-weight: 400;
+ letter-spacing: -.026em;
+ line-height: 60px;
+}
+
+.paper-font-display2 {
+ font-size: 45px;
+ font-weight: 400;
+ letter-spacing: -.018em;
+ line-height: 48px;
+}
+
+.paper-font-display1 {
+ font-size: 34px;
+ font-weight: 400;
+ letter-spacing: -.01em;
+ line-height: 40px;
+}
+
+.paper-font-headline {
+ font-size: 24px;
+ font-weight: 400;
+ letter-spacing: -.012em;
+ line-height: 32px;
+}
+
+.paper-font-title {
+ font-size: 20px;
+ font-weight: 500;
+ line-height: 28px;
+}
+
+.paper-font-subhead {
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 24px;
+}
+
+.paper-font-body2 {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 24px;
+}
+
+.paper-font-body1 {
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+}
+
+.paper-font-caption {
+ font-size: 12px;
+ font-weight: 400;
+ letter-spacing: 0.011em;
+ line-height: 20px;
+}
+
+.paper-font-menu {
+ font-size: 13px;
+ font-weight: 500;
+ line-height: 24px;
+}
+
+.paper-font-button {
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.018em;
+ line-height: 24px;
+ text-transform: uppercase;
+}
+
+.paper-font-code2 {
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+}
+
+.paper-font-code1 {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+}
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/color.html b/catapult/third_party/polymer/components/paper-styles/color.html
new file mode 100644
index 00000000..51887901
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/color.html
@@ -0,0 +1,333 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<style is="custom-style">
+
+ :root {
+
+ /* Material Design color palette for Google products */
+
+ --google-red-100: #f4c7c3;
+ --google-red-300: #e67c73;
+ --google-red-500: #db4437;
+ --google-red-700: #c53929;
+
+ --google-blue-100: #c6dafc;
+ --google-blue-300: #7baaf7;
+ --google-blue-500: #4285f4;
+ --google-blue-700: #3367d6;
+
+ --google-green-100: #b7e1cd;
+ --google-green-300: #57bb8a;
+ --google-green-500: #0f9d58;
+ --google-green-700: #0b8043;
+
+ --google-yellow-100: #fce8b2;
+ --google-yellow-300: #f7cb4d;
+ --google-yellow-500: #f4b400;
+ --google-yellow-700: #f09300;
+
+ --google-grey-100: #f5f5f5;
+ --google-grey-300: #e0e0e0;
+ --google-grey-500: #9e9e9e;
+ --google-grey-700: #616161;
+
+ /* Material Design color palette from online spec document */
+
+ --paper-red-50: #ffebee;
+ --paper-red-100: #ffcdd2;
+ --paper-red-200: #ef9a9a;
+ --paper-red-300: #e57373;
+ --paper-red-400: #ef5350;
+ --paper-red-500: #f44336;
+ --paper-red-600: #e53935;
+ --paper-red-700: #d32f2f;
+ --paper-red-800: #c62828;
+ --paper-red-900: #b71c1c;
+ --paper-red-a100: #ff8a80;
+ --paper-red-a200: #ff5252;
+ --paper-red-a400: #ff1744;
+ --paper-red-a700: #d50000;
+
+ --paper-pink-50: #fce4ec;
+ --paper-pink-100: #f8bbd0;
+ --paper-pink-200: #f48fb1;
+ --paper-pink-300: #f06292;
+ --paper-pink-400: #ec407a;
+ --paper-pink-500: #e91e63;
+ --paper-pink-600: #d81b60;
+ --paper-pink-700: #c2185b;
+ --paper-pink-800: #ad1457;
+ --paper-pink-900: #880e4f;
+ --paper-pink-a100: #ff80ab;
+ --paper-pink-a200: #ff4081;
+ --paper-pink-a400: #f50057;
+ --paper-pink-a700: #c51162;
+
+ --paper-purple-50: #f3e5f5;
+ --paper-purple-100: #e1bee7;
+ --paper-purple-200: #ce93d8;
+ --paper-purple-300: #ba68c8;
+ --paper-purple-400: #ab47bc;
+ --paper-purple-500: #9c27b0;
+ --paper-purple-600: #8e24aa;
+ --paper-purple-700: #7b1fa2;
+ --paper-purple-800: #6a1b9a;
+ --paper-purple-900: #4a148c;
+ --paper-purple-a100: #ea80fc;
+ --paper-purple-a200: #e040fb;
+ --paper-purple-a400: #d500f9;
+ --paper-purple-a700: #aa00ff;
+
+ --paper-deep-purple-50: #ede7f6;
+ --paper-deep-purple-100: #d1c4e9;
+ --paper-deep-purple-200: #b39ddb;
+ --paper-deep-purple-300: #9575cd;
+ --paper-deep-purple-400: #7e57c2;
+ --paper-deep-purple-500: #673ab7;
+ --paper-deep-purple-600: #5e35b1;
+ --paper-deep-purple-700: #512da8;
+ --paper-deep-purple-800: #4527a0;
+ --paper-deep-purple-900: #311b92;
+ --paper-deep-purple-a100: #b388ff;
+ --paper-deep-purple-a200: #7c4dff;
+ --paper-deep-purple-a400: #651fff;
+ --paper-deep-purple-a700: #6200ea;
+
+ --paper-indigo-50: #e8eaf6;
+ --paper-indigo-100: #c5cae9;
+ --paper-indigo-200: #9fa8da;
+ --paper-indigo-300: #7986cb;
+ --paper-indigo-400: #5c6bc0;
+ --paper-indigo-500: #3f51b5;
+ --paper-indigo-600: #3949ab;
+ --paper-indigo-700: #303f9f;
+ --paper-indigo-800: #283593;
+ --paper-indigo-900: #1a237e;
+ --paper-indigo-a100: #8c9eff;
+ --paper-indigo-a200: #536dfe;
+ --paper-indigo-a400: #3d5afe;
+ --paper-indigo-a700: #304ffe;
+
+ --paper-blue-50: #e3f2fd;
+ --paper-blue-100: #bbdefb;
+ --paper-blue-200: #90caf9;
+ --paper-blue-300: #64b5f6;
+ --paper-blue-400: #42a5f5;
+ --paper-blue-500: #2196f3;
+ --paper-blue-600: #1e88e5;
+ --paper-blue-700: #1976d2;
+ --paper-blue-800: #1565c0;
+ --paper-blue-900: #0d47a1;
+ --paper-blue-a100: #82b1ff;
+ --paper-blue-a200: #448aff;
+ --paper-blue-a400: #2979ff;
+ --paper-blue-a700: #2962ff;
+
+ --paper-light-blue-50: #e1f5fe;
+ --paper-light-blue-100: #b3e5fc;
+ --paper-light-blue-200: #81d4fa;
+ --paper-light-blue-300: #4fc3f7;
+ --paper-light-blue-400: #29b6f6;
+ --paper-light-blue-500: #03a9f4;
+ --paper-light-blue-600: #039be5;
+ --paper-light-blue-700: #0288d1;
+ --paper-light-blue-800: #0277bd;
+ --paper-light-blue-900: #01579b;
+ --paper-light-blue-a100: #80d8ff;
+ --paper-light-blue-a200: #40c4ff;
+ --paper-light-blue-a400: #00b0ff;
+ --paper-light-blue-a700: #0091ea;
+
+ --paper-cyan-50: #e0f7fa;
+ --paper-cyan-100: #b2ebf2;
+ --paper-cyan-200: #80deea;
+ --paper-cyan-300: #4dd0e1;
+ --paper-cyan-400: #26c6da;
+ --paper-cyan-500: #00bcd4;
+ --paper-cyan-600: #00acc1;
+ --paper-cyan-700: #0097a7;
+ --paper-cyan-800: #00838f;
+ --paper-cyan-900: #006064;
+ --paper-cyan-a100: #84ffff;
+ --paper-cyan-a200: #18ffff;
+ --paper-cyan-a400: #00e5ff;
+ --paper-cyan-a700: #00b8d4;
+
+ --paper-teal-50: #e0f2f1;
+ --paper-teal-100: #b2dfdb;
+ --paper-teal-200: #80cbc4;
+ --paper-teal-300: #4db6ac;
+ --paper-teal-400: #26a69a;
+ --paper-teal-500: #009688;
+ --paper-teal-600: #00897b;
+ --paper-teal-700: #00796b;
+ --paper-teal-800: #00695c;
+ --paper-teal-900: #004d40;
+ --paper-teal-a100: #a7ffeb;
+ --paper-teal-a200: #64ffda;
+ --paper-teal-a400: #1de9b6;
+ --paper-teal-a700: #00bfa5;
+
+ --paper-green-50: #e8f5e9;
+ --paper-green-100: #c8e6c9;
+ --paper-green-200: #a5d6a7;
+ --paper-green-300: #81c784;
+ --paper-green-400: #66bb6a;
+ --paper-green-500: #4caf50;
+ --paper-green-600: #43a047;
+ --paper-green-700: #388e3c;
+ --paper-green-800: #2e7d32;
+ --paper-green-900: #1b5e20;
+ --paper-green-a100: #b9f6ca;
+ --paper-green-a200: #69f0ae;
+ --paper-green-a400: #00e676;
+ --paper-green-a700: #00c853;
+
+ --paper-light-green-50: #f1f8e9;
+ --paper-light-green-100: #dcedc8;
+ --paper-light-green-200: #c5e1a5;
+ --paper-light-green-300: #aed581;
+ --paper-light-green-400: #9ccc65;
+ --paper-light-green-500: #8bc34a;
+ --paper-light-green-600: #7cb342;
+ --paper-light-green-700: #689f38;
+ --paper-light-green-800: #558b2f;
+ --paper-light-green-900: #33691e;
+ --paper-light-green-a100: #ccff90;
+ --paper-light-green-a200: #b2ff59;
+ --paper-light-green-a400: #76ff03;
+ --paper-light-green-a700: #64dd17;
+
+ --paper-lime-50: #f9fbe7;
+ --paper-lime-100: #f0f4c3;
+ --paper-lime-200: #e6ee9c;
+ --paper-lime-300: #dce775;
+ --paper-lime-400: #d4e157;
+ --paper-lime-500: #cddc39;
+ --paper-lime-600: #c0ca33;
+ --paper-lime-700: #afb42b;
+ --paper-lime-800: #9e9d24;
+ --paper-lime-900: #827717;
+ --paper-lime-a100: #f4ff81;
+ --paper-lime-a200: #eeff41;
+ --paper-lime-a400: #c6ff00;
+ --paper-lime-a700: #aeea00;
+
+ --paper-yellow-50: #fffde7;
+ --paper-yellow-100: #fff9c4;
+ --paper-yellow-200: #fff59d;
+ --paper-yellow-300: #fff176;
+ --paper-yellow-400: #ffee58;
+ --paper-yellow-500: #ffeb3b;
+ --paper-yellow-600: #fdd835;
+ --paper-yellow-700: #fbc02d;
+ --paper-yellow-800: #f9a825;
+ --paper-yellow-900: #f57f17;
+ --paper-yellow-a100: #ffff8d;
+ --paper-yellow-a200: #ffff00;
+ --paper-yellow-a400: #ffea00;
+ --paper-yellow-a700: #ffd600;
+
+ --paper-amber-50: #fff8e1;
+ --paper-amber-100: #ffecb3;
+ --paper-amber-200: #ffe082;
+ --paper-amber-300: #ffd54f;
+ --paper-amber-400: #ffca28;
+ --paper-amber-500: #ffc107;
+ --paper-amber-600: #ffb300;
+ --paper-amber-700: #ffa000;
+ --paper-amber-800: #ff8f00;
+ --paper-amber-900: #ff6f00;
+ --paper-amber-a100: #ffe57f;
+ --paper-amber-a200: #ffd740;
+ --paper-amber-a400: #ffc400;
+ --paper-amber-a700: #ffab00;
+
+ --paper-orange-50: #fff3e0;
+ --paper-orange-100: #ffe0b2;
+ --paper-orange-200: #ffcc80;
+ --paper-orange-300: #ffb74d;
+ --paper-orange-400: #ffa726;
+ --paper-orange-500: #ff9800;
+ --paper-orange-600: #fb8c00;
+ --paper-orange-700: #f57c00;
+ --paper-orange-800: #ef6c00;
+ --paper-orange-900: #e65100;
+ --paper-orange-a100: #ffd180;
+ --paper-orange-a200: #ffab40;
+ --paper-orange-a400: #ff9100;
+ --paper-orange-a700: #ff6500;
+
+ --paper-deep-orange-50: #fbe9e7;
+ --paper-deep-orange-100: #ffccbc;
+ --paper-deep-orange-200: #ffab91;
+ --paper-deep-orange-300: #ff8a65;
+ --paper-deep-orange-400: #ff7043;
+ --paper-deep-orange-500: #ff5722;
+ --paper-deep-orange-600: #f4511e;
+ --paper-deep-orange-700: #e64a19;
+ --paper-deep-orange-800: #d84315;
+ --paper-deep-orange-900: #bf360c;
+ --paper-deep-orange-a100: #ff9e80;
+ --paper-deep-orange-a200: #ff6e40;
+ --paper-deep-orange-a400: #ff3d00;
+ --paper-deep-orange-a700: #dd2c00;
+
+ --paper-brown-50: #efebe9;
+ --paper-brown-100: #d7ccc8;
+ --paper-brown-200: #bcaaa4;
+ --paper-brown-300: #a1887f;
+ --paper-brown-400: #8d6e63;
+ --paper-brown-500: #795548;
+ --paper-brown-600: #6d4c41;
+ --paper-brown-700: #5d4037;
+ --paper-brown-800: #4e342e;
+ --paper-brown-900: #3e2723;
+
+ --paper-grey-50: #fafafa;
+ --paper-grey-100: #f5f5f5;
+ --paper-grey-200: #eeeeee;
+ --paper-grey-300: #e0e0e0;
+ --paper-grey-400: #bdbdbd;
+ --paper-grey-500: #9e9e9e;
+ --paper-grey-600: #757575;
+ --paper-grey-700: #616161;
+ --paper-grey-800: #424242;
+ --paper-grey-900: #212121;
+
+ --paper-blue-grey-50: #eceff1;
+ --paper-blue-grey-100: #cfd8dc;
+ --paper-blue-grey-200: #b0bec5;
+ --paper-blue-grey-300: #90a4ae;
+ --paper-blue-grey-400: #78909c;
+ --paper-blue-grey-500: #607d8b;
+ --paper-blue-grey-600: #546e7a;
+ --paper-blue-grey-700: #455a64;
+ --paper-blue-grey-800: #37474f;
+ --paper-blue-grey-900: #263238;
+
+ /* opacity for dark text on a light background */
+ --dark-divider-opacity: 0.12;
+ --dark-disabled-opacity: 0.38; /* or hint text or icon */
+ --dark-secondary-opacity: 0.54;
+ --dark-primary-opacity: 0.87;
+
+ /* opacity for light text on a dark background */
+ --light-divider-opacity: 0.12;
+ --light-disabled-opacity: 0.3; /* or hint text or icon */
+ --light-secondary-opacity: 0.7;
+ --light-primary-opacity: 1.0;
+
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/default-theme.html b/catapult/third_party/polymer/components/paper-styles/default-theme.html
new file mode 100644
index 00000000..cc697281
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/default-theme.html
@@ -0,0 +1,72 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="color.html">
+
+<!-- Taken from https://www.google.com/design/spec/style/color.html#color-ui-color-application -->
+
+<style is="custom-style">
+
+ :root {
+ /*
+ * You can use these generic variables in your elements for easy theming.
+ * For example, if all your elements use `--primary-text-color` as its main
+ * color, then switching from a light to a dark theme is just a matter of
+ * changing the value of `--primary-text-color` in your application.
+ */
+ --primary-text-color: var(--light-theme-text-color);
+ --primary-background-color: var(--light-theme-background-color);
+ --secondary-text-color: var(--light-theme-secondary-color);
+ --disabled-text-color: var(--light-theme-disabled-color);
+ --divider-color: var(--light-theme-divider-color);
+ --error-color: var(--paper-deep-orange-a700);
+
+ /*
+ * Primary and accent colors. Also see color.html for more colors.
+ */
+ --primary-color: var(--paper-indigo-500);
+ --light-primary-color: var(--paper-indigo-100);
+ --dark-primary-color: var(--paper-indigo-700);
+
+ --accent-color: var(--paper-pink-a200);
+ --light-accent-color: var(--paper-pink-a100);
+ --dark-accent-color: var(--paper-pink-a400);
+
+
+ /*
+ * Material Design Light background theme
+ */
+ --light-theme-background-color: #ffffff;
+ --light-theme-base-color: #000000;
+ --light-theme-text-color: var(--paper-grey-900);
+ --light-theme-secondary-color: #737373; /* for secondary text and icons */
+ --light-theme-disabled-color: #9b9b9b; /* disabled/hint text */
+ --light-theme-divider-color: #dbdbdb;
+
+ /*
+ * Material Design Dark background theme
+ */
+ --dark-theme-background-color: var(--paper-grey-900);
+ --dark-theme-base-color: #ffffff;
+ --dark-theme-text-color: #ffffff;
+ --dark-theme-secondary-color: #bcbcbc; /* for secondary text and icons */
+ --dark-theme-disabled-color: #646464; /* disabled/hint text */
+ --dark-theme-divider-color: #3c3c3c;
+
+ /*
+ * Deprecated values because of their confusing names.
+ */
+ --text-primary-color: var(--dark-theme-text-color);
+ --default-primary-color: var(--primary-color);
+
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/demo-pages.html b/catapult/third_party/polymer/components/paper-styles/demo-pages.html
new file mode 100644
index 00000000..6e900ad1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/demo-pages.html
@@ -0,0 +1,72 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+
+<link rel="import" href="color.html">
+<link rel="import" href="typography.html">
+<link rel="import" href="shadow.html">
+
+<style is="custom-style">
+
+ body {
+ @apply(--paper-font-common-base);
+ font-size: 14px;
+ margin: 0;
+ padding: 24px;
+ background-color: var(--paper-grey-50);
+ }
+
+ .horizontal-section-container {
+ @apply(--layout-horizontal);
+ @apply(--layout-center-justified);
+ @apply(--layout-wrap);
+ }
+
+ .vertical-section-container {
+ @apply(--layout-vertical);
+ @apply(--center-justified);
+ }
+
+ .horizontal-section {
+ background-color: white;
+ padding: 24px;
+ margin-right: 24px;
+ min-width: 200px;
+
+ @apply(--shadow-elevation-2dp);
+ }
+
+ .vertical-section {
+ background-color: white;
+ padding: 24px;
+ margin: 0 24px 24px 24px;
+
+ @apply(--shadow-elevation-2dp);
+ }
+
+ .centered {
+ max-width: 400px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ code {
+ color: var(--google-grey-700);
+ }
+
+ /* TODO: remove this hack and use horizontal-section-container instead */
+ body > div.layout.horizontal.center-justified {
+ @apply(--layout-wrap);
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/demo.css b/catapult/third_party/polymer/components/paper-styles/demo.css
new file mode 100644
index 00000000..efd8b471
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/demo.css
@@ -0,0 +1,25 @@
+/**
+@license
+Copyright (c) 2015 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
+
+*/
+body {
+ font-family: 'Roboto', 'Noto', sans-serif;
+ font-size: 14px;
+ margin: 0;
+ padding: 24px;
+}
+
+section {
+ padding: 20px 0;
+}
+
+section > div {
+ padding: 14px;
+ font-size: 16px;
+}
diff --git a/catapult/third_party/polymer/components/paper-styles/demo/index.html b/catapult/third_party/polymer/components/paper-styles/demo/index.html
new file mode 100644
index 00000000..1080cc66
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/demo/index.html
@@ -0,0 +1,339 @@
+<!doctype html>
+
+<!--
+ @license
+ Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+ <title>paper-styles demo</title>
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../color.html">
+ <link rel="import" href="../typography.html">
+ <link rel="import" href="../default-theme.html">
+ <link rel="import" href="../demo-pages.html">
+ </head>
+
+ <style>
+ .redlines {
+ background: linear-gradient(0deg, transparent, transparent 3.5px, rgba(255,0,0,0.2) 3.5px, rgba(255,0,0,0.2) 4px);
+ background-size: 100% 4px;
+ }
+
+ .paragraph {
+ margin-bottom: 20px;
+ }
+ </style>
+ <style is="custom-style">
+ .paper-font-display4 {
+ @apply(--paper-font-display4);
+ }
+
+ .paper-font-display3 {
+ @apply(--paper-font-display3);
+ }
+
+ .paper-font-display2 {
+ @apply(--paper-font-display2);
+ }
+
+ .paper-font-display1 {
+ @apply(--paper-font-display1);
+ }
+
+ .paper-font-headline {
+ @apply(--paper-font-headline);
+ }
+
+ .paper-font-title {
+ @apply(--paper-font-title);
+ }
+
+ .paper-font-subhead {
+ @apply(--paper-font-subhead);
+ }
+
+ .paper-font-body2 {
+ @apply(--paper-font-body2);
+ }
+
+ .paper-font-body1 {
+ @apply(--paper-font-body1);
+ }
+
+ .paper-font-caption {
+ @apply(--paper-font-caption);
+ }
+
+ .paper-font-menu {
+ @apply(--paper-font-menu);
+ }
+
+ .paper-font-button {
+ @apply(--paper-font-button);
+ }
+
+ .mobile-app {
+ max-width: 320px;
+ }
+
+ .toolbar {
+ height: 144px;
+ padding: 16px;
+
+ background: var(--default-primary-color);
+ color: var(--text-primary-color);
+ @apply(--paper-font-display1);
+ }
+
+ .item, .disabled-item {
+ position: relative;
+ padding: 8px;
+ border: 1px solid;
+ border-color: var(--divider-color);
+ border-top: 0;
+ }
+
+ .item .primary {
+ color: var(--primary-text-color);
+
+ @apply(--paper-font-body2);
+ }
+
+ .item .secondary {
+ color: var(--secondary-text-color);
+
+ @apply(--paper-font-body1);
+ }
+
+ .disabled-item {
+ color: var(--disabled-text-color);
+
+ @apply(--paper-font-body2);
+ }
+
+ .fab {
+ position: absolute;
+ box-sizing: border-box;
+ padding: 8px;
+ width: 56px;
+ height: 56px;
+ right: 16px;
+ top: -28px;
+ border-radius: 50%;
+ text-align: center;
+
+ background: var(--accent-color);
+ color: var(--text-primary-color);
+ @apply(--paper-font-display1);
+ }
+
+ .shadow {
+ display: inline-block;
+ padding: 8px;
+ margin: 16px;
+ height: 50px;
+ width: 50px;
+ }
+
+ .shadow-2dp {
+ @apply(--shadow-elevation-2dp);
+ }
+
+ .shadow-3dp {
+ @apply(--shadow-elevation-3dp);
+ }
+
+ .shadow-4dp {
+ @apply(--shadow-elevation-4dp);
+ }
+
+ .shadow-6dp {
+ @apply(--shadow-elevation-6dp);
+ }
+
+ .shadow-8dp {
+ @apply(--shadow-elevation-8dp);
+ }
+
+ .shadow-12dp {
+ @apply(--shadow-elevation-12dp);
+ }
+
+ .shadow-16dp {
+ @apply(--shadow-elevation-16dp);
+ }
+ </style>
+
+ <body unresolved>
+ <h1>paper-styles</h1>
+
+ <section id="default-theme">
+ <h2>default-theme.html</h2>
+
+ <section class="mobile-app">
+ <div class="toolbar">
+ Title
+ </div>
+ <div class="item">
+ <div class="fab">+</div>
+ <div class="primary">Primary text</div>
+ <div class="secondary">Secondary text</div>
+ </div>
+ <div class="disabled-item">
+ Disabled
+ </div>
+ </section>
+ </section>
+
+ <section id="typography">
+ <h2>typography.html</h2>
+ <p>
+ Grumpy wizards make toxic brew for the evil Queen and Jack.
+ </p>
+ <section class="redlines paragraph">
+ <div class="paper-font-display4">Display 4</div>
+ <div class="paper-font-display3">Display 3</div>
+ <div class="paper-font-display2">Display 2</div>
+ <div class="paper-font-display1">Display 1</div>
+ <div class="paper-font-headline">Headline</div>
+ <div class="paper-font-title">Title</div>
+ <div class="paper-font-subhead">Subhead</div>
+ <div class="paper-font-body2">Body 2</div>
+ <div class="paper-font-body1">Body 1</div>
+ <div class="paper-font-caption">Caption</div>
+ <div class="paper-font-menu">Menu</div>
+ <div class="paper-font-button">Button</div>
+ </section>
+
+ <h3>Paragraphs</h3>
+
+ <h4>body2</h4>
+ <section class="paper-font-body2 redlines">
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi
+ tincidunt dui sit amet mi auctor, ac gravida magna aliquam. Fusce quis
+ purus elementum, tempus nisi vel, volutpat nulla. Vestibulum mollis
+ dictum tellus, vulputate porttitor arcu. Curabitur imperdiet risus id
+ egestas accumsan. Donec lectus felis, dignissim id iaculis sit amet,
+ faucibus in leo.
+ </p>
+ <p>
+ Mauris id urna ac ante ultrices commodo a imperdiet elit. Vivamus
+ interdum neque magna, eget dapibus est auctor et. Donec accumsan
+ libero nec augue scelerisque, ac egestas ante tincidunt. Proin
+ sollicitudin, mi eget sagittis mollis, arcu orci scelerisque turpis, a
+ sollicitudin tellus quam non sapien.
+ </p>
+ </section>
+
+ <h4>body1</h4>
+ <section class="paper-font-body1 redlines">
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi
+ tincidunt dui sit amet mi auctor, ac gravida magna aliquam. Fusce quis
+ purus elementum, tempus nisi vel, volutpat nulla. Vestibulum mollis
+ dictum tellus, vulputate porttitor arcu. Curabitur imperdiet risus id
+ egestas accumsan. Donec lectus felis, dignissim id iaculis sit amet,
+ faucibus in leo.
+ </p>
+ <p>
+ Mauris id urna ac ante ultrices commodo a imperdiet elit. Vivamus
+ interdum neque magna, eget dapibus est auctor et. Donec accumsan
+ libero nec augue scelerisque, ac egestas ante tincidunt. Proin
+ sollicitudin, mi eget sagittis mollis, arcu orci scelerisque turpis, a
+ sollicitudin tellus quam non sapien.
+ </p>
+ </section>
+ </section>
+
+ <section id="shadow">
+ <h2>shadow.html</h2>
+ <div class="shadow shadow-2dp">2dp</div>
+ <div class="shadow shadow-3dp">3dp</div>
+ <div class="shadow shadow-4dp">4dp</div>
+ <div class="shadow shadow-6dp">6dp</div>
+ <div class="shadow shadow-8dp">8dp</div>
+ <div class="shadow shadow-12dp">12dp</div>
+ <div class="shadow shadow-16dp">16dp</div>
+ </section>
+
+ <section id="demo-page">
+ <h2>demo-pages.html</h2>
+
+ <h3>Horizontal sections</h3>
+ <div class="horizontal-section-container">
+ <div>
+ <h4>Column 1</h4>
+ <div class="horizontal-section">
+ <div>Oxygen</div>
+ <div>Carbon</div>
+ <div>Hydrogen</div>
+ <div>Nitrogen</div>
+ <div>Calcium</div>
+ </div>
+ </div>
+
+ <div>
+ <h4>Column 2</h4>
+ <div class="horizontal-section">
+ <div>Oxygen</div>
+ <div>Carbon</div>
+ <div>Hydrogen</div>
+ <div>Nitrogen</div>
+ <div>Calcium</div>
+ </div>
+ </div>
+
+ <div>
+ <h4>Column 3</h4>
+ <div class="horizontal-section">
+ <div>Oxygen</div>
+ <div>Carbon</div>
+ <div>Hydrogen</div>
+ <div>Nitrogen</div>
+ <div>Calcium</div>
+ </div>
+ </div>
+ </div>
+
+ <h3>Vertical sections</h3>
+ <div class="vertical-section-container">
+ <div>
+ <h4>Section 1</h4>
+ <div class="vertical-section">
+ <div>Oxygen</div>
+ <div>Carbon</div>
+ <div>Hydrogen</div>
+ <div>Nitrogen</div>
+ <div>Calcium</div>
+ </div>
+ </div>
+ </div>
+
+ <div class="vertical-section-container centered">
+ <h4>Section 2 (centered)</h4>
+ <div class="vertical-section">
+ <div>Oxygen</div>
+ <div>Carbon</div>
+ <div>Hydrogen</div>
+ <div>Nitrogen</div>
+ <div>Calcium</div>
+ </div>
+ </div>
+ </section>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-styles/element-styles/paper-item-styles.html b/catapult/third_party/polymer/components/paper-styles/element-styles/paper-item-styles.html
new file mode 100644
index 00000000..796cd24b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/element-styles/paper-item-styles.html
@@ -0,0 +1,90 @@
+<!--
+@license
+Copyright (c) 2017 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../color.html">
+<link rel="import" href="../default-theme.html">
+<link rel="import" href="../typography.html">
+
+<!--
+Material design: [Lists](https://www.google.com/design/spec/components/lists.html)
+
+Shared styles for a native `button` to be used as an item in a `paper-listbox` element:
+
+ <custom-style>
+ <style is="custom-style" include="paper-item-styles"></style>
+ </custom-style>
+
+ <paper-listbox>
+ <button class="paper-item" role="option">Inbox</button>
+ <button class="paper-item" role="option">Starred</button>
+ <button class="paper-item" role="option">Sent mail</button>
+ </paper-listbox>
+
+@group Paper Elements
+@demo demo/index.html
+-->
+
+<dom-module id="paper-item-styles">
+ <template>
+ <style>
+ :host, html {
+ --paper-item: {
+ display: block;
+ position: relative;
+ min-height: var(--paper-item-min-height, 48px);
+ padding: 0px 16px;
+ @apply --paper-font-subhead;
+ border:none;
+ outline: none;
+ background: white;
+ width: 100%;
+ text-align: left;
+ };
+ }
+ .paper-item {
+ @apply --paper-item;
+ }
+
+ .paper-item[hidden] {
+ display: none !important;
+ }
+
+ .paper-item.iron-selected {
+ font-weight: var(--paper-item-selected-weight, bold);
+ @apply --paper-item-selected;
+ }
+
+ .paper-item[disabled] {
+ color: var(--paper-item-disabled-color, var(--disabled-text-color));
+ @apply --paper-item-disabled;
+ }
+
+ .paper-item:focus {
+ position: relative;
+ outline: 0;
+ @apply --paper-item-focused;
+ }
+
+ .paper-item:focus:before {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: currentColor;
+ content: '';
+ opacity: var(--dark-divider-opacity);
+ pointer-events: none;
+ @apply --paper-item-focused-before;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-styles/element-styles/paper-material-styles.html b/catapult/third_party/polymer/components/paper-styles/element-styles/paper-material-styles.html
new file mode 100644
index 00000000..83aee8d5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/element-styles/paper-material-styles.html
@@ -0,0 +1,78 @@
+<!--
+@license
+Copyright (c) 2017 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../shadow.html">
+
+<!--
+Material design: [Cards](https://www.google.com/design/spec/components/cards.html)
+
+Shared styles that you can apply to an element to renders two shadows on top
+of each other,that create the effect of a lifted piece of paper.
+
+Example:
+
+ <custom-style>
+ <style is="custom-style" include="paper-material-styles"></style>
+ </custom-style>
+
+ <div class="paper-material elevation-1">
+ ... content ...
+ </div>
+
+@group Paper Elements
+@demo demo/index.html
+-->
+
+<dom-module id="paper-material-styles">
+ <template>
+ <style>
+ :host, html {
+ --paper-material: {
+ display: block;
+ position: relative;
+ };
+ --paper-material-elevation-1: {
+ @apply --shadow-elevation-2dp;
+ };
+ --paper-material-elevation-2: {
+ @apply --shadow-elevation-4dp;
+ };
+ --paper-material-elevation-3: {
+ @apply --shadow-elevation-6dp;
+ };
+ --paper-material-elevation-4: {
+ @apply --shadow-elevation-8dp;
+ };
+ --paper-material-elevation-5: {
+ @apply --shadow-elevation-16dp;
+ };
+ }
+ :host(.paper-material), .paper-material {
+ @apply --paper-material;
+ }
+ :host(.paper-material[elevation="1"]), .paper-material[elevation="1"] {
+ @apply --paper-material-elevation-1;
+ }
+ :host(.paper-material[elevation="2"]), .paper-material[elevation="2"] {
+ @apply --paper-material-elevation-2;
+ }
+ :host(.paper-material[elevation="3"]), .paper-material[elevation="3"] {
+ @apply --paper-material-elevation-3;
+ }
+ :host(.paper-material[elevation="4"]), .paper-material[elevation="4"] {
+ @apply --paper-material-elevation-4;
+ }
+ :host(.paper-material[elevation="5"]), .paper-material[elevation="5"] {
+ @apply --paper-material-elevation-5;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-styles/index.html b/catapult/third_party/polymer/components/paper-styles/index.html
new file mode 100644
index 00000000..8e96ebe0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-styles</title>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-styles/paper-styles-classes.html b/catapult/third_party/polymer/components/paper-styles/paper-styles-classes.html
new file mode 100644
index 00000000..ae315a57
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/paper-styles-classes.html
@@ -0,0 +1,14 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-flex-layout/classes/iron-flex-layout.html">
+
+<link rel="import" href="classes/typography.html">
+<link rel="import" href="classes/shadow.html">
diff --git a/catapult/third_party/polymer/components/paper-styles/paper-styles.html b/catapult/third_party/polymer/components/paper-styles/paper-styles.html
new file mode 100644
index 00000000..9eca03a7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/paper-styles.html
@@ -0,0 +1,44 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../iron-flex-layout/classes/iron-flex-layout.html">
+
+<!--
+The `<paper-styles>` component provides simple ways to use Material Design CSS styles
+in your application. The following imports are available:
+
+1. [color.html](https://github.com/PolymerElements/paper-styles/blob/master/color.html):
+a complete list of the colors defined in the Material Design [palette](https://www.google.com/design/spec/style/color.html)
+
+2. [default-theme.html](https://github.com/PolymerElements/paper-styles/blob/master/default-theme.html): text,
+background and accent colors that match the default Material Design theme
+
+3. [shadow.html](https://github.com/PolymerElements/paper-styles/blob/master/shadow.html): Material Design
+[elevation](https://www.google.com/design/spec/what-is-material/elevation-shadows.html) and shadow styles
+
+4. [typography.html](https://github.com/PolymerElements/paper-styles/blob/master/typography.html):
+Material Design [font](http://www.google.com/design/spec/style/typography.html#typography-styles) styles and sizes
+
+5. [demo-pages.html](https://github.com/PolymerElements/paper-styles/blob/master/demo-pages.html): generic styles
+used in the PolymerElements demo pages
+
+We recommend importing each of these individual files, and using the style mixins
+available in each ones, rather than the aggregated `paper-styles.html` as a whole.
+
+@group Paper Elements
+@pseudoElement paper-styles
+@demo demo/index.html
+-->
+
+<link rel="import" href="color.html">
+<link rel="import" href="default-theme.html">
+<link rel="import" href="shadow.html">
+<link rel="import" href="typography.html">
diff --git a/catapult/third_party/polymer/components/paper-styles/shadow.html b/catapult/third_party/polymer/components/paper-styles/shadow.html
new file mode 100644
index 00000000..7e0546ee
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/shadow.html
@@ -0,0 +1,76 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+
+<style is="custom-style">
+
+ :root {
+
+ --shadow-transition: {
+ transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
+ };
+
+ --shadow-none: {
+ box-shadow: none;
+ };
+
+ /* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */
+
+ --shadow-elevation-2dp: {
+ 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);
+ };
+
+ --shadow-elevation-3dp: {
+ box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 8px 0 rgba(0, 0, 0, 0.12),
+ 0 3px 3px -2px rgba(0, 0, 0, 0.4);
+ };
+
+ --shadow-elevation-4dp: {
+ box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 10px 0 rgba(0, 0, 0, 0.12),
+ 0 2px 4px -1px rgba(0, 0, 0, 0.4);
+ };
+
+ --shadow-elevation-6dp: {
+ box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 18px 0 rgba(0, 0, 0, 0.12),
+ 0 3px 5px -1px rgba(0, 0, 0, 0.4);
+ };
+
+ --shadow-elevation-8dp: {
+ box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
+ 0 3px 14px 2px rgba(0, 0, 0, 0.12),
+ 0 5px 5px -3px rgba(0, 0, 0, 0.4);
+ };
+
+ --shadow-elevation-12dp: {
+ box-shadow: 0 12px 16px 1px rgba(0, 0, 0, 0.14),
+ 0 4px 22px 3px rgba(0, 0, 0, 0.12),
+ 0 6px 7px -4px rgba(0, 0, 0, 0.4);
+ };
+
+ --shadow-elevation-16dp: {
+ box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
+ 0 6px 30px 5px rgba(0, 0, 0, 0.12),
+ 0 8px 10px -5px rgba(0, 0, 0, 0.4);
+ };
+
+ --shadow-elevation-24dp: {
+ box-shadow: 0 24px 38px 3px rgba(0, 0, 0, 0.14),
+ 0 9px 46px 8px rgba(0, 0, 0, 0.12),
+ 0 11px 15px -7px rgba(0, 0, 0, 0.4);
+ };
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-styles/typography.html b/catapult/third_party/polymer/components/paper-styles/typography.html
new file mode 100644
index 00000000..055b5f99
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-styles/typography.html
@@ -0,0 +1,169 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../font-roboto/roboto.html">
+
+<style is="custom-style">
+
+ :root {
+
+ /* Shared Styles */
+ --paper-font-common-base: {
+ font-family: 'Roboto', 'Noto', sans-serif;
+ -webkit-font-smoothing: antialiased;
+ };
+
+ --paper-font-common-code: {
+ font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
+ -webkit-font-smoothing: antialiased;
+ };
+
+ --paper-font-common-expensive-kerning: {
+ text-rendering: optimizeLegibility;
+ };
+
+ --paper-font-common-nowrap: {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ };
+
+ /* Material Font Styles */
+
+ --paper-font-display4: {
+ @apply(--paper-font-common-base);
+ @apply(--paper-font-common-nowrap);
+
+ font-size: 112px;
+ font-weight: 300;
+ letter-spacing: -.044em;
+ line-height: 120px;
+ };
+
+ --paper-font-display3: {
+ @apply(--paper-font-common-base);
+ @apply(--paper-font-common-nowrap);
+
+ font-size: 56px;
+ font-weight: 400;
+ letter-spacing: -.026em;
+ line-height: 60px;
+ };
+
+ --paper-font-display2: {
+ @apply(--paper-font-common-base);
+
+ font-size: 45px;
+ font-weight: 400;
+ letter-spacing: -.018em;
+ line-height: 48px;
+ };
+
+ --paper-font-display1: {
+ @apply(--paper-font-common-base);
+
+ font-size: 34px;
+ font-weight: 400;
+ letter-spacing: -.01em;
+ line-height: 40px;
+ };
+
+ --paper-font-headline: {
+ @apply(--paper-font-common-base);
+
+ font-size: 24px;
+ font-weight: 400;
+ letter-spacing: -.012em;
+ line-height: 32px;
+ };
+
+ --paper-font-title: {
+ @apply(--paper-font-common-base);
+ @apply(--paper-font-common-nowrap);
+
+ font-size: 20px;
+ font-weight: 500;
+ line-height: 28px;
+ };
+
+ --paper-font-subhead: {
+ @apply(--paper-font-common-base);
+
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 24px;
+ };
+
+ --paper-font-body2: {
+ @apply(--paper-font-common-base);
+
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 24px;
+ };
+
+ --paper-font-body1: {
+ @apply(--paper-font-common-base);
+
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+ };
+
+ --paper-font-caption: {
+ @apply(--paper-font-common-base);
+ @apply(--paper-font-common-nowrap);
+
+ font-size: 12px;
+ font-weight: 400;
+ letter-spacing: 0.011em;
+ line-height: 20px;
+ };
+
+ --paper-font-menu: {
+ @apply(--paper-font-common-base);
+ @apply(--paper-font-common-nowrap);
+
+ font-size: 13px;
+ font-weight: 500;
+ line-height: 24px;
+ };
+
+ --paper-font-button: {
+ @apply(--paper-font-common-base);
+ @apply(--paper-font-common-nowrap);
+
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.018em;
+ line-height: 24px;
+ text-transform: uppercase;
+ };
+
+ --paper-font-code2: {
+ @apply(--paper-font-common-code);
+
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ };
+
+ --paper-font-code1: {
+ @apply(--paper-font-common-code);
+
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ };
+
+ }
+
+</style>
diff --git a/catapult/third_party/polymer/components/paper-tabs/.bower.json b/catapult/third_party/polymer/components/paper-tabs/.bower.json
new file mode 100644
index 00000000..856d1086
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/.bower.json
@@ -0,0 +1,54 @@
+{
+ "name": "paper-tabs",
+ "version": "1.8.0",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Material design tabs",
+ "private": true,
+ "main": "paper-tabs.html",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "tabs",
+ "control"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-tabs.git"
+ },
+ "dependencies": {
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.1.0",
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-pages": "PolymerElements/iron-pages#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-toolbar": "PolymerElements/paper-toolbar#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/paper-tabs",
+ "_release": "1.8.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.8.0",
+ "commit": "0476ca9d9b9a1d4702da4f3405ea2bb2801cfbff"
+ },
+ "_source": "https://github.com/PolymerElements/paper-tabs.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-tabs"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-tabs/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-tabs/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..655302e8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-tabs/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-tabs/.gitignore b/catapult/third_party/polymer/components/paper-tabs/.gitignore
new file mode 100644
index 00000000..fbe05fc9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/.gitignore
@@ -0,0 +1 @@
+bower_components/
diff --git a/catapult/third_party/polymer/components/paper-tabs/.travis.yml b/catapult/third_party/polymer/components/paper-tabs/.travis.yml
new file mode 100644
index 00000000..6b98b2c8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/.travis.yml
@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+before_script:
+- npm install -g bower polylint web-component-tester
+- bower install
+- polylint
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+- xvfb-run wct -l chrome
+- xvfb-run wct -l firefox
+- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'default'; fi
+dist: trusty
+env:
+ global:
+ - secure: KEzThhoQ3Aq9kSZIuCzyi6MtYbzxB67JSs2t9CebYoXgW49k3q7Uh/KMUHz8RM04hSa7k2gDGf1Spr4jkXpItHn037aW/Bd+95l49my8pSJvHLEQR8KO/b+FSH/Tu+wKXAm4xk4Mp+vESOxIWap1JXHxSJnoa0EqKyYCeKVVkMI=
+ - secure: dhfUH2Sx4rC089kHVtjggaGRMdEa0UHa9ksC5buMk5XBI2GriRE+D3/1N7tBxg4f2HcVbi1+7jTIRHz+xYvypuXCr/9Bh7zh19KhBjrMdFYPZwy3fK3O9xfrypwpldFq1Q3mFcJ3p5sfqHjKvvWlGf/lffqQY5rVr8UI86Lklac=
diff --git a/catapult/third_party/polymer/components/paper-tabs/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-tabs/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-tabs/README.md b/catapult/third_party/polymer/components/paper-tabs/README.md
new file mode 100644
index 00000000..050979f8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/README.md
@@ -0,0 +1,46 @@
+[![Build status](https://travis-ci.org/PolymerElements/paper-tabs.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-tabs)
+[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://beta.webcomponents.org/element/PolymerElements/paper-tabs)
+
+##&lt;paper-tabs&gt;
+
+Material design: [Tabs](https://www.google.com/design/spec/components/tabs.html)
+
+`paper-tabs` makes it easy to explore and switch between different views or functional aspects of
+an app, or to browse categorized data sets.
+
+<!---
+```
+<custom-element-demo>
+ <template>
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="paper-tabs.html">
+ <style>
+ body {
+ margin: 0;
+ display: flex;
+ align-items: flex-end;
+ height: 80px;
+ background: #009688;
+ color: white;
+ }
+ paper-tabs {
+ font-family: 'Roboto', 'Noto', sans-serif;
+ -webkit-font-smoothing: antialiased;
+ width: 100%;
+ text-transform: uppercase;
+ margin-bottom: 1px;
+ }
+ </style>
+ <next-code-block></next-code-block>
+ </template>
+</custom-element-demo>
+```
+-->
+```html
+<paper-tabs selected="0" scrollable>
+ <paper-tab>The first tab</paper-tab>
+ <paper-tab>Tab two</paper-tab>
+ <paper-tab>The third tab</paper-tab>
+ <paper-tab>Fourth tab</paper-tab>
+</paper-tabs>
+```
diff --git a/catapult/third_party/polymer/components/paper-tabs/bower.json b/catapult/third_party/polymer/components/paper-tabs/bower.json
new file mode 100644
index 00000000..01ed2846
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/bower.json
@@ -0,0 +1,44 @@
+{
+ "name": "paper-tabs",
+ "version": "1.8.0",
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "description": "Material design tabs",
+ "private": true,
+ "main": "paper-tabs.html",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "tabs",
+ "control"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-tabs.git"
+ },
+ "dependencies": {
+ "iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+ "iron-icon": "PolymerElements/iron-icon#^1.0.0",
+ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#^1.0.0",
+ "iron-menu-behavior": "PolymerElements/iron-menu-behavior#^1.1.0",
+ "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
+ "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "polymer": "Polymer/polymer#^1.1.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "iron-pages": "PolymerElements/iron-pages#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-toolbar": "PolymerElements/paper-toolbar#^1.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-tabs/demo/index.html b/catapult/third_party/polymer/components/paper-tabs/demo/index.html
new file mode 100644
index 00000000..74e20df7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/demo/index.html
@@ -0,0 +1,338 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>paper-tabs demo</title>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-flex-layout/iron-flex-layout.html">
+ <link rel="import" href="../../iron-pages/iron-pages.html">
+ <link rel="import" href="../../paper-toolbar/paper-toolbar.html">
+ <link rel="import" href="../paper-tab.html">
+ <link rel="import" href="../paper-tabs.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ #container {
+ max-width: 600px;
+ }
+
+ paper-tabs, paper-toolbar {
+ background-color: var(--paper-blue-900);
+ color: #fff;
+ }
+ </style>
+
+ </head>
+ <body unresolved>
+
+ <div id="container" class="vertical-section-container centered">
+
+ <h3>Plain <code>paper-tabs</code>.</h3>
+ <demo-snippet>
+ <template>
+ <paper-tabs selected="0">
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>The <code>no-bar</code> attribute disables the selection bar.</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ paper-tabs[no-bar] paper-tab.iron-selected {
+ color: #ffff8d;
+ }
+ </style>
+
+ <paper-tabs id="plain-tabs" selected="0" no-bar>
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ The <code>no-slide</code> attribute prevents the selection bar from
+ animating when the selection changes.
+ </h3>
+ <demo-snippet>
+ <template>
+ <paper-tabs selected="0" no-slide>
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ The <code>noink</code> attribute disables the ink ripple animation seen
+ when the user activates a tab (by clicking, tapping, etc.).
+ </h3>
+ <demo-snippet>
+ <template>
+ <paper-tabs selected="0" noink>
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ The <code>scrollable</code> attribute causes the tabs to scroll, rather
+ than compress, when there is not enough space. Arrow buttons appear when
+ any of the tabs are not completely visible.
+ </h3>
+ <demo-snippet>
+ <template>
+ <paper-tabs selected="0" scrollable>
+ <paper-tab>NUMBER ONE ITEM</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>THE THIRD ITEM</paper-tab>
+ <paper-tab>THE ITEM FOUR</paper-tab>
+ <paper-tab>FIFTH</paper-tab>
+ <paper-tab>THE SIXTH TAB</paper-tab>
+ <paper-tab>NUMBER SEVEN</paper-tab>
+ <paper-tab>EIGHT</paper-tab>
+ <paper-tab>NUMBER NINE</paper-tab>
+ <paper-tab>THE TENTH</paper-tab>
+ <paper-tab>THE ITEM ELEVEN</paper-tab>
+ <paper-tab>TWELFTH ITEM</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ The <code>fit-container</code> attribute causes scrollable tabs to
+ stretch to fit their container if the tabs don't need to scroll.
+ </h3>
+ <demo-snippet>
+ <template>
+ <paper-tabs selected="0" scrollable fit-container>
+ <paper-tab>NUMBER ONE ITEM</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>THE THIRD ITEM</paper-tab>
+ </paper-tabs>
+ <br>
+ <paper-tabs selected="0" scrollable fit-container style="width: 50%;">
+ <paper-tab>NUMBER ONE ITEM</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>THE THIRD ITEM</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Tab layout is affected by the writing direction of the surrounding area.
+ </h3>
+ <demo-snippet>
+ <template>
+ <div dir="rtl">
+ <paper-tabs selected="0">
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ &nbsp;
+ <paper-tabs selected="0" scrollable>
+ <paper-tab>NUMBER ONE ITEM</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>THE THIRD ITEM</paper-tab>
+ <paper-tab>THE ITEM FOUR</paper-tab>
+ <paper-tab>FIFTH</paper-tab>
+ <paper-tab>THE SIXTH TAB</paper-tab>
+ <paper-tab>NUMBER SEVEN</paper-tab>
+ <paper-tab>EIGHT</paper-tab>
+ <paper-tab>NUMBER NINE</paper-tab>
+ <paper-tab>THE TENTH</paper-tab>
+ <paper-tab>THE ITEM ELEVEN</paper-tab>
+ <paper-tab>TWELFTH ITEM</paper-tab>
+ </paper-tabs>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Use the <code>align-bottom</code> attribute when your tabs are
+ positioned below the content they control. The selection bar will be
+ shown at the top of the tabs.
+ </h3>
+ <demo-snippet>
+ <template>
+ <paper-tabs selected="0" align-bottom>
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Use the <code>link</code> attribute when a <code>paper-tab</code>
+ contains a link. The link will fill the entire tab. <code>paper-tabs</code>
+ implements its own tabindexing and keyboard focus patterns so an anchor
+ placed inside should set <code>tabindex="-1"</code>.
+ </h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ paper-tab[link] a {
+ /* These mixins (from iron-flex-layout) center the link text. */
+ @apply(--layout-horizontal);
+ @apply(--layout-center-center);
+ color: #fff;
+ text-decoration: none;
+ }
+ </style>
+
+ <paper-tabs selected="0">
+ <paper-tab link>
+ <a href="#item1" tabindex="-1">ITEM ONE</a>
+ </paper-tab>
+ <paper-tab link>
+ <a href="#item2" tabindex="-1">ITEM TWO</a>
+ </paper-tab>
+ <paper-tab link>
+ <a href="#item3" tabindex="-1">ITEM THREE</a>
+ </paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Tabs can be used in a <code>paper-toolbar</code>.
+ </h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ paper-toolbar {
+ --paper-toolbar-background: var(--paper-blue-900);
+ }
+
+ .self-end {
+ /* This mixin (from iron-flex-layout) aligns the tabs to the
+ bottom of the toolbar. */
+ @apply(--layout-self-end);
+ }
+ </style>
+
+ <paper-toolbar class="tall">
+ <paper-tabs selected="0" class="bottom self-end" style="width: 300px;">
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ </paper-tabs>
+ </paper-toolbar>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Use <code>autoselect</code> to enable automatic tab selection.
+ </h3>
+ <demo-snippet>
+ <template>
+ <paper-tabs selected="0" autoselect>
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <h3>
+ Use <code>autoselect-delay</code> to adjust the delay between the last
+ keyup event and when the tab is automatically selected (when
+ <code>autoselect</code> is true).
+ </h3>
+ <demo-snippet>
+ <template>
+ <h4>
+ <code>autoselect-delay="0"</code>
+ </h4>
+ <paper-tabs selected="0" no-slide autoselect autoselect-delay="0">
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+
+ <h4>
+ <code>autoselect-delay="1000"</code>
+ </h4>
+ <paper-tabs selected="0" autoselect autoselect-delay="1000">
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </demo-snippet>
+
+ <!--
+ Nested templates are not supported in IE, meaning they can't be used in a
+ demo-snippet. See Polymer/polymer#2495 for more details.
+ -->
+ <!--
+ <h3>J. Bound Selection</h3>
+ <demo-snippet>
+ <template>
+ <template is="dom-bind">
+ <h2>Current Tab: <span>[[selected]]</span></h2>
+ <paper-tabs selected="{{selected}}">
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </template>
+ </demo-snippet>
+
+ <h3>K. Controlling content</h3>
+ <demo-snippet>
+ <template>
+ <style is="custom-style">
+ iron-pages {
+ border: 1px solid #ccc;
+ border-top: none;
+ padding: 8px;
+ }
+ </style>
+
+ <template is="dom-bind">
+ <paper-tabs selected="{{selected}}">
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ <iron-pages selected="{{selected}}">
+ <div>CONTENT ONE</div>
+ <div>CONTENT TWO</div>
+ <div>CONTENT THREE</div>
+ </iron-pages>
+ </template>
+ </template>
+ </demo-snippet>
+ -->
+
+ </div>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tabs/hero.svg b/catapult/third_party/polymer/components/paper-tabs/hero.svg
new file mode 100755
index 00000000..e29eaf4b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/hero.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <path d="M188,78H37V44h151V78z M39,76h147V46H39V76z"/>
+ <polygon points="66,64.8 60.6,56.8 55.3,64.8 49.2,55.6 50.8,54.4 55.3,61.2 60.6,53.2 66,61.2 71.3,53.2 77.4,62.4 75.8,63.6
+ 71.3,56.8 "/>
+ <rect x="149" y="58" width="26" height="2"/>
+ <rect x="99" y="58" width="26" height="2"/>
+ <rect x="38" y="72" width="50" height="4"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-tabs/index.html b/catapult/third_party/polymer/components/paper-tabs/index.html
new file mode 100644
index 00000000..f618702f
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/index.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <title>paper-tabs</title>
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page sources='["paper-tabs.html", "paper-tab.html"]'></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tabs/paper-tab.html b/catapult/third_party/polymer/components/paper-tabs/paper-tab.html
new file mode 100644
index 00000000..b2dd2c05
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/paper-tab.html
@@ -0,0 +1,175 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-behaviors/iron-button-state.html">
+<link rel="import" href="../iron-behaviors/iron-control-state.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../paper-behaviors/paper-ripple-behavior.html">
+
+<!--
+`paper-tab` is styled to look like a tab. It should be used in conjunction with
+`paper-tabs`.
+
+Example:
+
+ <paper-tabs selected="0">
+ <paper-tab>TAB 1</paper-tab>
+ <paper-tab>TAB 2</paper-tab>
+ <paper-tab>TAB 3</paper-tab>
+ </paper-tabs>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-tab-ink` | Ink color | `--paper-yellow-a100`
+`--paper-tab` | Mixin applied to the tab | `{}`
+`--paper-tab-content` | Mixin applied to the tab content | `{}`
+`--paper-tab-content-unselected` | Mixin applied to the tab content when the tab is not selected | `{}`
+
+This element applies the mixin `--paper-font-common-base` but does not import `paper-styles/typography.html`.
+In order to apply the `Roboto` font to this element, make sure you've imported `paper-styles/typography.html`.
+
+-->
+
+<dom-module id="paper-tab">
+ <template>
+ <style>
+ :host {
+ @apply(--layout-inline);
+ @apply(--layout-center);
+ @apply(--layout-center-justified);
+ @apply(--layout-flex-auto);
+
+ position: relative;
+ padding: 0 12px;
+ overflow: hidden;
+ cursor: pointer;
+ vertical-align: middle;
+
+ @apply(--paper-font-common-base);
+ @apply(--paper-tab);
+ }
+
+ :host(:focus) {
+ outline: none;
+ }
+
+ :host([link]) {
+ padding: 0;
+ }
+
+ .tab-content {
+ height: 100%;
+ transform: translateZ(0);
+ -webkit-transform: translateZ(0);
+ transition: opacity 0.1s cubic-bezier(0.4, 0.0, 1, 1);
+ @apply(--layout-horizontal);
+ @apply(--layout-center-center);
+ @apply(--layout-flex-auto);
+ @apply(--paper-tab-content);
+ }
+
+ :host(:not(.iron-selected)) > .tab-content {
+ opacity: 0.8;
+
+ @apply(--paper-tab-content-unselected);
+ }
+
+ :host(:focus) .tab-content {
+ opacity: 1;
+ font-weight: 700;
+ }
+
+ paper-ripple {
+ color: var(--paper-tab-ink, --paper-yellow-a100);
+ }
+
+ .tab-content > ::content > a {
+ @apply(--layout-flex-auto);
+
+ height: 100%;
+ }
+ </style>
+
+ <div class="tab-content">
+ <content></content>
+ </div>
+ </template>
+</dom-module>
+<script>
+Polymer({
+ is: 'paper-tab',
+
+ behaviors: [
+ Polymer.IronControlState,
+ Polymer.IronButtonState,
+ Polymer.PaperRippleBehavior
+ ],
+
+ properties: {
+
+ /**
+ * If true, the tab will forward keyboard clicks (enter/space) to
+ * the first anchor element found in its descendants
+ */
+ link: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true
+ }
+
+ },
+
+ hostAttributes: {
+ role: 'tab'
+ },
+
+ listeners: {
+ down: '_updateNoink',
+ tap: '_onTap'
+ },
+
+ attached: function() {
+ this._updateNoink();
+ },
+
+ get _parentNoink () {
+ var parent = Polymer.dom(this).parentNode;
+ return !!parent && !!parent.noink;
+ },
+
+ _updateNoink: function() {
+ this.noink = !!this.noink || !!this._parentNoink;
+ },
+
+ _onTap: function(event) {
+ if (this.link) {
+ var anchor = this.queryEffectiveChildren('a');
+
+ if (!anchor) {
+ return;
+ }
+
+ // Don't get stuck in a loop delegating
+ // the listener from the child anchor
+ if (event.target === anchor) {
+ return;
+ }
+
+ anchor.click();
+ }
+ }
+
+});
+</script>
diff --git a/catapult/third_party/polymer/components/paper-tabs/paper-tabs-icons.html b/catapult/third_party/polymer/components/paper-tabs/paper-tabs-icons.html
new file mode 100644
index 00000000..c299046a
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/paper-tabs-icons.html
@@ -0,0 +1,18 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html">
+
+<iron-iconset-svg name="paper-tabs" size="24">
+<svg><defs>
+<g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></g>
+<g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></g>
+</defs></svg>
+</iron-iconset-svg>
diff --git a/catapult/third_party/polymer/components/paper-tabs/paper-tabs.html b/catapult/third_party/polymer/components/paper-tabs/paper-tabs.html
new file mode 100644
index 00000000..bc504e1a
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/paper-tabs.html
@@ -0,0 +1,661 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
+<link rel="import" href="../iron-icon/iron-icon.html">
+<link rel="import" href="../iron-menu-behavior/iron-menubar-behavior.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="../paper-icon-button/paper-icon-button.html">
+<link rel="import" href="../paper-styles/color.html">
+<link rel="import" href="paper-tabs-icons.html">
+<link rel="import" href="paper-tab.html">
+
+<!--
+Material design: [Tabs](https://www.google.com/design/spec/components/tabs.html)
+
+`paper-tabs` makes it easy to explore and switch between different views or functional aspects of
+an app, or to browse categorized data sets.
+
+Use `selected` property to get or set the selected tab.
+
+Example:
+
+ <paper-tabs selected="0">
+ <paper-tab>TAB 1</paper-tab>
+ <paper-tab>TAB 2</paper-tab>
+ <paper-tab>TAB 3</paper-tab>
+ </paper-tabs>
+
+See <a href="?active=paper-tab">paper-tab</a> for more information about
+`paper-tab`.
+
+A common usage for `paper-tabs` is to use it along with `iron-pages` to switch
+between different views.
+
+ <paper-tabs selected="{{selected}}">
+ <paper-tab>Tab 1</paper-tab>
+ <paper-tab>Tab 2</paper-tab>
+ <paper-tab>Tab 3</paper-tab>
+ </paper-tabs>
+
+ <iron-pages selected="{{selected}}">
+ <div>Page 1</div>
+ <div>Page 2</div>
+ <div>Page 3</div>
+ </iron-pages>
+
+
+To use links in tabs, add `link` attribute to `paper-tab` and put an `<a>`
+element in `paper-tab` with a `tabindex` of -1.
+
+Example:
+
+<pre><code>
+&lt;style is="custom-style">
+ .link {
+ &#64;apply(--layout-horizontal);
+ &#64;apply(--layout-center-center);
+ }
+&lt;/style>
+
+&lt;paper-tabs selected="0">
+ &lt;paper-tab link>
+ &lt;a href="#link1" class="link" tabindex="-1">TAB ONE&lt;/a>
+ &lt;/paper-tab>
+ &lt;paper-tab link>
+ &lt;a href="#link2" class="link" tabindex="-1">TAB TWO&lt;/a>
+ &lt;/paper-tab>
+ &lt;paper-tab link>
+ &lt;a href="#link3" class="link" tabindex="-1">TAB THREE&lt;/a>
+ &lt;/paper-tab>
+&lt;/paper-tabs>
+</code></pre>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-tabs-selection-bar-color` | Color for the selection bar | `--paper-yellow-a100`
+`--paper-tabs-selection-bar` | Mixin applied to the selection bar | `{}`
+`--paper-tabs` | Mixin applied to the tabs | `{}`
+`--paper-tabs-content` | Mixin applied to the content container of tabs | `{}`
+`--paper-tabs-container` | Mixin applied to the layout container of tabs | `{}`
+
+@hero hero.svg
+@demo demo/index.html
+-->
+
+<dom-module id="paper-tabs">
+ <template>
+ <style>
+ :host {
+ @apply(--layout);
+ @apply(--layout-center);
+
+ height: 48px;
+ font-size: 14px;
+ font-weight: 500;
+ overflow: hidden;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+
+ /* NOTE: Both values are needed, since some phones require the value to be `transparent`. */
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+
+ @apply(--paper-tabs);
+ }
+
+ :host-context([dir=rtl]) {
+ @apply(--layout-horizontal-reverse);
+ }
+
+ #tabsContainer {
+ position: relative;
+ height: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ @apply(--layout-flex-auto);
+ @apply(--paper-tabs-container);
+ }
+
+ #tabsContent {
+ height: 100%;
+ -moz-flex-basis: auto;
+ -ms-flex-basis: auto;
+ flex-basis: auto;
+ @apply(--paper-tabs-content);
+ }
+
+ #tabsContent.scrollable {
+ position: absolute;
+ white-space: nowrap;
+ }
+
+ #tabsContent:not(.scrollable),
+ #tabsContent.scrollable.fit-container {
+ @apply(--layout-horizontal);
+ }
+
+ #tabsContent.scrollable.fit-container {
+ min-width: 100%;
+ }
+
+ #tabsContent.scrollable.fit-container > ::content > * {
+ /* IE - prevent tabs from compressing when they should scroll. */
+ -ms-flex: 1 0 auto;
+ -webkit-flex: 1 0 auto;
+ flex: 1 0 auto;
+ }
+
+ .hidden {
+ display: none;
+ }
+
+ .not-visible {
+ opacity: 0;
+ cursor: default;
+ }
+
+ paper-icon-button {
+ width: 48px;
+ height: 48px;
+ padding: 12px;
+ margin: 0 4px;
+ }
+
+ #selectionBar {
+ position: absolute;
+ height: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ border-bottom: 2px solid var(--paper-tabs-selection-bar-color, --paper-yellow-a100);
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ -webkit-transform-origin: left center;
+ transform-origin: left center;
+ transition: -webkit-transform;
+ transition: transform;
+
+ @apply(--paper-tabs-selection-bar);
+ }
+
+ #selectionBar.align-bottom {
+ top: 0;
+ bottom: auto;
+ }
+
+ #selectionBar.expand {
+ transition-duration: 0.15s;
+ transition-timing-function: cubic-bezier(0.4, 0.0, 1, 1);
+ }
+
+ #selectionBar.contract {
+ transition-duration: 0.18s;
+ transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1);
+ }
+
+ #tabsContent > ::content > *:not(#selectionBar) {
+ height: 100%;
+ }
+ </style>
+
+ <paper-icon-button icon="paper-tabs:chevron-left" class$="[[_computeScrollButtonClass(_leftHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onLeftScrollButtonDown" tabindex="-1"></paper-icon-button>
+
+ <div id="tabsContainer" on-track="_scroll" on-down="_down">
+ <div id="tabsContent" class$="[[_computeTabsContentClass(scrollable, fitContainer)]]">
+ <div id="selectionBar" class$="[[_computeSelectionBarClass(noBar, alignBottom)]]"
+ on-transitionend="_onBarTransitionEnd"></div>
+ <content select="*"></content>
+ </div>
+ </div>
+
+ <paper-icon-button icon="paper-tabs:chevron-right" class$="[[_computeScrollButtonClass(_rightHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onRightScrollButtonDown" tabindex="-1"></paper-icon-button>
+
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-tabs',
+
+ behaviors: [
+ Polymer.IronResizableBehavior,
+ Polymer.IronMenubarBehavior
+ ],
+
+ properties: {
+ /**
+ * If true, ink ripple effect is disabled. When this property is changed,
+ * all descendant `<paper-tab>` elements have their `noink` property
+ * changed to the new value as well.
+ */
+ noink: {
+ type: Boolean,
+ value: false,
+ observer: '_noinkChanged'
+ },
+
+ /**
+ * If true, the bottom bar to indicate the selected tab will not be shown.
+ */
+ noBar: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, the slide effect for the bottom bar is disabled.
+ */
+ noSlide: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, tabs are scrollable and the tab width is based on the label width.
+ */
+ scrollable: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, tabs expand to fit their container. This currently only applies when
+ * scrollable is true.
+ */
+ fitContainer: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, dragging on the tabs to scroll is disabled.
+ */
+ disableDrag: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, scroll buttons (left/right arrow) will be hidden for scrollable tabs.
+ */
+ hideScrollButtons: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * If true, the tabs are aligned to bottom (the selection bar appears at the top).
+ */
+ alignBottom: {
+ type: Boolean,
+ value: false
+ },
+
+ selectable: {
+ type: String,
+ value: 'paper-tab'
+ },
+
+ /**
+ * If true, tabs are automatically selected when focused using the
+ * keyboard.
+ */
+ autoselect: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The delay (in milliseconds) between when the user stops interacting
+ * with the tabs through the keyboard and when the focused item is
+ * automatically selected (if `autoselect` is true).
+ */
+ autoselectDelay: {
+ type: Number,
+ value: 0
+ },
+
+ _step: {
+ type: Number,
+ value: 10
+ },
+
+ _holdDelay: {
+ type: Number,
+ value: 1
+ },
+
+ _leftHidden: {
+ type: Boolean,
+ value: false
+ },
+
+ _rightHidden: {
+ type: Boolean,
+ value: false
+ },
+
+ _previousTab: {
+ type: Object
+ }
+ },
+
+ hostAttributes: {
+ role: 'tablist'
+ },
+
+ listeners: {
+ 'iron-resize': '_onTabSizingChanged',
+ 'iron-items-changed': '_onTabSizingChanged',
+ 'iron-select': '_onIronSelect',
+ 'iron-deselect': '_onIronDeselect'
+ },
+
+ keyBindings: {
+ 'left:keyup right:keyup': '_onArrowKeyup'
+ },
+
+ created: function() {
+ this._holdJob = null;
+ this._pendingActivationItem = undefined;
+ this._pendingActivationTimeout = undefined;
+ this._bindDelayedActivationHandler = this._delayedActivationHandler.bind(this);
+ this.addEventListener('blur', this._onBlurCapture.bind(this), true);
+ },
+
+ ready: function() {
+ this.setScrollDirection('y', this.$.tabsContainer);
+ },
+
+ detached: function() {
+ this._cancelPendingActivation();
+ },
+
+ _noinkChanged: function(noink) {
+ var childTabs = Polymer.dom(this).querySelectorAll('paper-tab');
+ childTabs.forEach(noink ? this._setNoinkAttribute : this._removeNoinkAttribute);
+ },
+
+ _setNoinkAttribute: function(element) {
+ element.setAttribute('noink', '');
+ },
+
+ _removeNoinkAttribute: function(element) {
+ element.removeAttribute('noink');
+ },
+
+ _computeScrollButtonClass: function(hideThisButton, scrollable, hideScrollButtons) {
+ if (!scrollable || hideScrollButtons) {
+ return 'hidden';
+ }
+
+ if (hideThisButton) {
+ return 'not-visible';
+ }
+
+ return '';
+ },
+
+ _computeTabsContentClass: function(scrollable, fitContainer) {
+ return scrollable ? 'scrollable' + (fitContainer ? ' fit-container' : '') : ' fit-container';
+ },
+
+ _computeSelectionBarClass: function(noBar, alignBottom) {
+ if (noBar) {
+ return 'hidden';
+ } else if (alignBottom) {
+ return 'align-bottom';
+ }
+
+ return '';
+ },
+
+ // TODO(cdata): Add `track` response back in when gesture lands.
+
+ _onTabSizingChanged: function() {
+ this.debounce('_onTabSizingChanged', function() {
+ this._scroll();
+ this._tabChanged(this.selectedItem);
+ }, 10);
+ },
+
+ _onIronSelect: function(event) {
+ this._tabChanged(event.detail.item, this._previousTab);
+ this._previousTab = event.detail.item;
+ this.cancelDebouncer('tab-changed');
+ },
+
+ _onIronDeselect: function(event) {
+ this.debounce('tab-changed', function() {
+ this._tabChanged(null, this._previousTab);
+ this._previousTab = null;
+ // See polymer/polymer#1305
+ }, 1);
+ },
+
+ _activateHandler: function() {
+ // Cancel item activations scheduled by keyboard events when any other
+ // action causes an item to be activated (e.g. clicks).
+ this._cancelPendingActivation();
+
+ Polymer.IronMenuBehaviorImpl._activateHandler.apply(this, arguments);
+ },
+
+ /**
+ * Activates an item after a delay (in milliseconds).
+ */
+ _scheduleActivation: function(item, delay) {
+ this._pendingActivationItem = item;
+ this._pendingActivationTimeout = this.async(
+ this._bindDelayedActivationHandler, delay);
+ },
+
+ /**
+ * Activates the last item given to `_scheduleActivation`.
+ */
+ _delayedActivationHandler: function() {
+ var item = this._pendingActivationItem;
+ this._pendingActivationItem = undefined;
+ this._pendingActivationTimeout = undefined;
+ item.fire(this.activateEvent, null, {
+ bubbles: true,
+ cancelable: true
+ });
+ },
+
+ /**
+ * Cancels a previously scheduled item activation made with
+ * `_scheduleActivation`.
+ */
+ _cancelPendingActivation: function() {
+ if (this._pendingActivationTimeout !== undefined) {
+ this.cancelAsync(this._pendingActivationTimeout);
+ this._pendingActivationItem = undefined;
+ this._pendingActivationTimeout = undefined;
+ }
+ },
+
+ _onArrowKeyup: function(event) {
+ if (this.autoselect) {
+ this._scheduleActivation(this.focusedItem, this.autoselectDelay);
+ }
+ },
+
+ _onBlurCapture: function(event) {
+ // Cancel a scheduled item activation (if any) when that item is
+ // blurred.
+ if (event.target === this._pendingActivationItem) {
+ this._cancelPendingActivation();
+ }
+ },
+
+ get _tabContainerScrollSize () {
+ return Math.max(
+ 0,
+ this.$.tabsContainer.scrollWidth -
+ this.$.tabsContainer.offsetWidth
+ );
+ },
+
+ _scroll: function(e, detail) {
+ if (!this.scrollable) {
+ return;
+ }
+
+ var ddx = (detail && -detail.ddx) || 0;
+ this._affectScroll(ddx);
+ },
+
+ _down: function(e) {
+ // go one beat async to defeat IronMenuBehavior
+ // autorefocus-on-no-selection timeout
+ this.async(function() {
+ if (this._defaultFocusAsync) {
+ this.cancelAsync(this._defaultFocusAsync);
+ this._defaultFocusAsync = null;
+ }
+ }, 1);
+ },
+
+ _affectScroll: function(dx) {
+ this.$.tabsContainer.scrollLeft += dx;
+
+ var scrollLeft = this.$.tabsContainer.scrollLeft;
+
+ this._leftHidden = scrollLeft === 0;
+ this._rightHidden = scrollLeft === this._tabContainerScrollSize;
+ },
+
+ _onLeftScrollButtonDown: function() {
+ this._scrollToLeft();
+ this._holdJob = setInterval(this._scrollToLeft.bind(this), this._holdDelay);
+ },
+
+ _onRightScrollButtonDown: function() {
+ this._scrollToRight();
+ this._holdJob = setInterval(this._scrollToRight.bind(this), this._holdDelay);
+ },
+
+ _onScrollButtonUp: function() {
+ clearInterval(this._holdJob);
+ this._holdJob = null;
+ },
+
+ _scrollToLeft: function() {
+ this._affectScroll(-this._step);
+ },
+
+ _scrollToRight: function() {
+ this._affectScroll(this._step);
+ },
+
+ _tabChanged: function(tab, old) {
+ if (!tab) {
+ // Remove the bar without animation.
+ this.$.selectionBar.classList.remove('expand');
+ this.$.selectionBar.classList.remove('contract');
+ this._positionBar(0, 0);
+ return;
+ }
+
+ var r = this.$.tabsContent.getBoundingClientRect();
+ var w = r.width;
+ var tabRect = tab.getBoundingClientRect();
+ var tabOffsetLeft = tabRect.left - r.left;
+
+ this._pos = {
+ width: this._calcPercent(tabRect.width, w),
+ left: this._calcPercent(tabOffsetLeft, w)
+ };
+
+ if (this.noSlide || old == null) {
+ // Position the bar without animation.
+ this.$.selectionBar.classList.remove('expand');
+ this.$.selectionBar.classList.remove('contract');
+ this._positionBar(this._pos.width, this._pos.left);
+ return;
+ }
+
+ var oldRect = old.getBoundingClientRect();
+ var oldIndex = this.items.indexOf(old);
+ var index = this.items.indexOf(tab);
+ var m = 5;
+
+ // bar animation: expand
+ this.$.selectionBar.classList.add('expand');
+
+ var moveRight = oldIndex < index;
+ var isRTL = this._isRTL;
+ if (isRTL) {
+ moveRight = !moveRight;
+ }
+
+ if (moveRight) {
+ this._positionBar(this._calcPercent(tabRect.left + tabRect.width - oldRect.left, w) - m,
+ this._left);
+ } else {
+ this._positionBar(this._calcPercent(oldRect.left + oldRect.width - tabRect.left, w) - m,
+ this._calcPercent(tabOffsetLeft, w) + m);
+ }
+
+ if (this.scrollable) {
+ this._scrollToSelectedIfNeeded(tabRect.width, tabOffsetLeft);
+ }
+ },
+
+ _scrollToSelectedIfNeeded: function(tabWidth, tabOffsetLeft) {
+ var l = tabOffsetLeft - this.$.tabsContainer.scrollLeft;
+ if (l < 0) {
+ this.$.tabsContainer.scrollLeft += l;
+ } else {
+ l += (tabWidth - this.$.tabsContainer.offsetWidth);
+ if (l > 0) {
+ this.$.tabsContainer.scrollLeft += l;
+ }
+ }
+ },
+
+ _calcPercent: function(w, w0) {
+ return 100 * w / w0;
+ },
+
+ _positionBar: function(width, left) {
+ width = width || 0;
+ left = left || 0;
+
+ this._width = width;
+ this._left = left;
+ this.transform(
+ 'translateX(' + left + '%) scaleX(' + (width / 100) + ')',
+ this.$.selectionBar);
+ },
+
+ _onBarTransitionEnd: function(e) {
+ var cl = this.$.selectionBar.classList;
+ // bar animation: expand -> contract
+ if (cl.contains('expand')) {
+ cl.remove('expand');
+ cl.add('contract');
+ this._positionBar(this._pos.width, this._pos.left);
+ // bar animation done
+ } else if (cl.contains('contract')) {
+ cl.remove('contract');
+ }
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-tabs/test/attr-for-selected.html b/catapult/third_party/polymer/components/paper-tabs/test/attr-for-selected.html
new file mode 100644
index 00000000..cc1198e6
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/test/attr-for-selected.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>paper-tabs-attr-for-selected</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../paper-tabs.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-tabs attr-for-selected="name" selected="bar">
+ <paper-tab name="foo">ITEM FOO</paper-tab>
+ <paper-tab name="bar">ITEM BAR</paper-tab>
+ <paper-tab name="zot">ITEM ZOT</paper-tab>
+ </paper-tabs>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('set the selected attribute', function() {
+
+ var tabs;
+
+ setup(function () {
+ tabs = fixture('basic');
+ });
+
+ test('selected value', function() {
+ assert.equal(tabs.selected, 'bar');
+ });
+
+ test('selected tab has iron-selected class', function() {
+ Polymer.dom.flush();
+ assert.isTrue(tabs.querySelector('[name=bar]').classList.contains('iron-selected'));
+ });
+
+ });
+
+ suite('select tab via click', function() {
+
+ var tabs, tab;
+
+ setup(function () {
+ tabs = fixture('basic');
+ tab = tabs.querySelector('[name=zot]');
+ tab.dispatchEvent(new CustomEvent('click', {bubbles: true}));
+ });
+
+ test('selected value', function() {
+ assert.equal(tabs.selected, 'zot');
+ });
+
+ test('selected tab has iron-selected class', function() {
+ Polymer.dom.flush();
+ assert.isTrue(tab.classList.contains('iron-selected'));
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tabs/test/basic.html b/catapult/third_party/polymer/components/paper-tabs/test/basic.html
new file mode 100644
index 00000000..dd74854d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/test/basic.html
@@ -0,0 +1,394 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>paper-tabs-basic</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../paper-tabs.html">
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-tabs>
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ <paper-tab>ITEM THREE</paper-tab>
+ </paper-tabs>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="HiddenTabs">
+ <template>
+ <paper-tabs hidden>
+ <paper-tab>ITEM ONE</paper-tab>
+ <paper-tab>ITEM TWO</paper-tab>
+ </paper-tabs>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ function ensureDocumentHasFocus() {
+ window.top && window.top.focus();
+ }
+
+ function checkSelectionBar(tabs, tab) {
+ var tabRect = tab.getBoundingClientRect();
+ var rect = Polymer.dom(tabs.root).querySelector('#selectionBar').getBoundingClientRect();
+ assert.equal(Math.round(tabRect.left), Math.round(rect.left));
+ }
+
+ suite('defaults', function() {
+
+ var tabs;
+
+ setup(function () {
+ tabs = fixture('basic');
+ });
+
+ test('to nothing selected', function() {
+ assert.equal(tabs.selected, undefined);
+ });
+
+ test('no tabs have iron-selected class', function() {
+ Array.prototype.forEach.call(tabs.querySelectorAll('paper-tab'), function(tab) {
+ assert.isFalse(tab.classList.contains('iron-selected'));
+ });
+ });
+
+ test('to false as noink', function() {
+ assert.equal(tabs.noink, false);
+ });
+
+ test('to false as noBar', function() {
+ assert.equal(tabs.noBar, false);
+ });
+
+ test('to false as noSlide', function() {
+ assert.equal(tabs.noSlide, false);
+ });
+
+ test('to false as scrollable', function() {
+ assert.equal(tabs.scrollable, false);
+ });
+
+ test('to false as disableDrag', function() {
+ assert.equal(tabs.disableDrag, false);
+ });
+
+ test('to false as hideScrollButtons', function() {
+ assert.equal(tabs.hideScrollButtons, false);
+ });
+
+ test('to false as alignBottom', function() {
+ assert.equal(tabs.alignBottom, false);
+ });
+ });
+
+ suite('hidden tabs', function() {
+ var tabs;
+
+ setup(function() {
+ tabs = fixture('HiddenTabs');
+ });
+
+ test('choose the correct bar position once made visible', function() {
+ tabs.removeAttribute('hidden');
+ tabs.selected = 0;
+ expect(tabs._width).to.be.greaterThan(0);
+ expect(tabs._left).to.be.equal(0);
+ });
+ });
+
+ suite('set the selected attribute', function() {
+
+ var tabs, index = 0;
+
+ setup(function () {
+ tabs = fixture('basic');
+ tabs.selected = index;
+ });
+
+ test('selected value', function() {
+ assert.equal(tabs.selected, index);
+ });
+
+ test('selected tab has iron-selected class', function() {
+ var tab = tabs.querySelectorAll('paper-tab')[index];
+ assert.isTrue(tab.classList.contains('iron-selected'));
+ });
+
+ test('selected tab has selection bar position at the bottom of the tab', function(done) {
+ setTimeout(function() {
+ checkSelectionBar(tabs, tabs.querySelectorAll('paper-tab')[index]);
+ done();
+ }, 1000);
+ });
+
+ });
+
+ suite('select tab via click', function() {
+
+ var tabs, index = 1;
+ var tab;
+
+ setup(function () {
+ tabs = fixture('basic');
+ tab = tabs.querySelectorAll('paper-tab')[index];
+ tab.dispatchEvent(new CustomEvent('click', {bubbles: true}));
+ });
+
+ test('selected value', function() {
+ assert.equal(tabs.selected, index);
+ });
+
+ test('selected tab has iron-selected class', function() {
+ var tab = tabs.querySelectorAll('paper-tab')[index];
+ assert.isTrue(tab.classList.contains('iron-selected'));
+ });
+
+ test('selected tab has selection bar position at the bottom of the tab', function(done) {
+ setTimeout(function() {
+ checkSelectionBar(tabs, tabs.querySelectorAll('paper-tab')[index]);
+ done();
+ }, 1000);
+ });
+
+ test('pressing enter on tab causes a click', function(done) {
+ var clickCount = 0;
+ tab.addEventListener('click', function onTabClick() {
+ clickCount++;
+ tab.removeEventListener('click', onTabClick);
+
+ expect(clickCount).to.be.equal(1);
+ done();
+ });
+
+ MockInteractions.pressEnter(tab);
+ });
+ });
+
+ suite('noink attribute', function() {
+ var tabs;
+
+ setup(function () {
+ tabs = fixture('basic');
+ });
+
+ test('noink attribute propagates to all descendant tabs', function() {
+ tabs.noink = true;
+ Array.prototype.slice.apply(tabs.querySelectorAll('paper-tab')).forEach(function(tab) {
+ assert.isTrue(tab.noink);
+ });
+
+ tabs.noink = false;
+ Array.prototype.slice.apply(tabs.querySelectorAll('paper-tab')).forEach(function(tab) {
+ assert.isFalse(tab.noink);
+ });
+ });
+ });
+
+ suite('accessibility', function() {
+ var LEFT = 37;
+ var RIGHT = 39;
+ var tabs;
+
+ setup(function () {
+ tabs = fixture('basic');
+ Polymer.dom.flush();
+ });
+
+ test('paper-tabs has role tablist', function() {
+ assert.equal(tabs.getAttribute('role'), 'tablist');
+ });
+
+ test('paper-tab has role tab', function() {
+ tabs.items.forEach(function(tab) {
+ assert.equal(tab.getAttribute('role'), 'tab');
+ });
+ });
+
+ test('without autoselect, tabs are not automatically selected',
+ function(done) {
+ ensureDocumentHasFocus();
+ Polymer.Base.async(function() {
+ tabs.select(0);
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 0);
+
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, LEFT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 0);
+
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, LEFT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 0);
+ done();
+ }, 100);
+ }, 100);
+ }, 100);
+ });
+ });
+
+ test('with autoselect, tabs are selected when moved to using arrow ' +
+ 'keys', function(done) {
+ ensureDocumentHasFocus();
+ Polymer.Base.async(function() {
+ tabs.autoselect = true;
+ tabs.select(0);
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 1);
+
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 2);
+
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, LEFT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 1);
+ done();
+ }, 100);
+ }, 100);
+ }, 100);
+ });
+ });
+
+ test('with autoselect, tabs are selected when moved to using arrow ' +
+ 'keys (RTL)',function(done) {
+ ensureDocumentHasFocus();
+ Polymer.Base.async(function() {
+ tabs.setAttribute('dir', 'rtl');
+
+ tabs.autoselect = true;
+ tabs.select(0);
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, LEFT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 1);
+
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, LEFT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 2);
+
+ MockInteractions.pressAndReleaseKeyOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 1);
+ done();
+ }, 100);
+ }, 100);
+ }, 100);
+ });
+ });
+
+ test('with autoselect-delay zero, tabs are selected with ' +
+ 'microtask timing after the keyup', function(done) {
+ ensureDocumentHasFocus();
+ Polymer.Base.async(function() {
+ tabs.autoselect = true;
+ tabs.autoselectDelay = 0;
+ tabs.select(0);
+
+ MockInteractions.keyDownOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 0);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 1);
+
+ // No keyup between keydown events: the key is being held.
+ MockInteractions.keyDownOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 0);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 2);
+
+ MockInteractions.keyUpOn(tabs.selectedItem, RIGHT);
+ assert.equal(tabs.selected, 0);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 2);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 2);
+
+ MockInteractions.keyDownOn(tabs.selectedItem, LEFT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 2);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 1);
+
+ MockInteractions.keyUpOn(tabs.selectedItem, LEFT);
+ assert.equal(tabs.selected, 2);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 1);
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+
+ test('with autoselect-delay positive, tabs are selected with ' +
+ 'microtask timing after the keyup and delay', function(done) {
+ ensureDocumentHasFocus();
+ Polymer.Base.async(function() {
+ var DELAY = 100;
+
+ tabs.autoselect = true;
+ tabs.autoselectDelay = DELAY;
+ tabs.select(0);
+
+ MockInteractions.keyDownOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 0);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 1);
+
+ // No keyup between keydown events: the key is being held.
+ MockInteractions.keyDownOn(tabs.selectedItem, RIGHT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 0);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 2);
+
+ MockInteractions.keyUpOn(tabs.selectedItem, RIGHT);
+ assert.equal(tabs.selected, 0);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 2);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 2);
+
+ MockInteractions.keyDownOn(tabs.selectedItem, LEFT);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 2);
+ assert.equal(tabs.items.indexOf(tabs.focusedItem), 1);
+
+ MockInteractions.keyUpOn(tabs.selectedItem, LEFT);
+ assert.equal(tabs.selected, 2);
+ Polymer.Base.async(function() {
+ assert.equal(tabs.selected, 1);
+ done();
+ }, DELAY + 100);
+ });
+ }, DELAY + 100);
+ });
+ });
+ });
+ });
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tabs/test/index.html b/catapult/third_party/polymer/components/paper-tabs/test/index.html
new file mode 100644
index 00000000..9b58696b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/test/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'attr-for-selected.html',
+ 'links.html',
+ 'basic.html?dom=shadow',
+ 'attr-for-selected.html?dom=shadow',
+ 'links.html?dom=shadow'
+ ]);
+ </script>
+
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-tabs/test/links.html b/catapult/third_party/polymer/components/paper-tabs/test/links.html
new file mode 100644
index 00000000..5d43e0ca
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tabs/test/links.html
@@ -0,0 +1,163 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <title>paper-tabs-links</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../paper-tabs.html">
+ <link rel="import" href="../../iron-test-helpers/iron-test-helpers.html">
+
+ </head>
+ <body>
+
+ <test-fixture id="links">
+ <template>
+ <paper-tabs>
+ <paper-tab link><a href="#one" tabindex="-1">ONE</a></paper-tab>
+ <paper-tab link><a href="#two" tabindex="-1">TWO</a></paper-tab>
+ <paper-tab link><a href="#three" tabindex="-1">THREE</a></paper-tab>
+ </paper-tabs>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="not-links">
+ <template>
+ <paper-tabs>
+ <paper-tab><a href="#one" tabindex="-1">ONE</a></paper-tab>
+ <paper-tab><a href="#two" tabindex="-1">TWO</a></paper-tab>
+ <paper-tab><a href="#three" tabindex="-1">THREE</a></paper-tab>
+ </paper-tabs>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="not-first-child">
+ <template>
+ <paper-tabs>
+ <paper-tab>
+ <div>
+ <a href="#one" tabindex="-1">ONE</a>
+ </div>
+ </paper-tab>
+ <paper-tab>
+ <div>
+ <a href="#two" tabindex="-1">TWO</a>
+ </div>
+ </paper-tab>
+ <paper-tab>
+ <div>
+ <a href="#three" tabindex="-1">THREE</a>
+ </div>
+ </paper-tab>
+ </paper-tabs>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('links', function() {
+
+ suite('has link attribute', function() {
+
+ var tabs;
+ var tab, anchor;
+
+ setup(function () {
+ tabs = fixture('links');
+ tab = tabs.querySelectorAll('paper-tab')[1];
+ anchor = tab.querySelector('a');
+ });
+
+ test('pressing enter on tab causes anchor click', function(done) {
+ tab.addEventListener('click', function onTabClick(event) {
+ tab.removeEventListener('click', onTabClick);
+
+ expect(event.target).to.be.equal(anchor);
+ done();
+ });
+
+ MockInteractions.pressEnter(tab);
+ });
+
+ test('pressing space on tab causes anchor click', function(done) {
+ tab.addEventListener('click', function onTabClick(event) {
+ tab.removeEventListener('click', onTabClick);
+
+ expect(event.target).to.be.equal(anchor);
+ done();
+ });
+
+ MockInteractions.pressSpace(tab);
+ });
+
+ });
+
+ suite('does not have link attribute', function() {
+
+ var tabs;
+ var tab, anchor;
+
+ setup(function () {
+ tabs = fixture('not-links');
+ tab = tabs.querySelectorAll('paper-tab')[1];
+ anchor = tab.querySelector('a');
+ });
+
+ test('pressing enter on tab does not cause anchor click', function(done) {
+ tab.addEventListener('click', function onTabClick(event) {
+ tab.removeEventListener('click', onTabClick);
+
+ expect(event.target).to.not.equal(anchor);
+ expect(event.target).to.be.equal(tab);
+ done();
+ });
+
+ MockInteractions.pressEnter(tab);
+ });
+
+ });
+
+ suite('not first child', function() {
+
+ var tabs;
+ var tab, anchor;
+
+ setup(function () {
+ tabs = fixture('links');
+ tab = tabs.querySelectorAll('paper-tab')[1];
+ anchor = tab.querySelector('a');
+ });
+
+ test('pressing enter on tab causes anchor click', function(done) {
+ tab.addEventListener('click', function onTabClick(event) {
+ tab.removeEventListener('click', onTabClick);
+
+ expect(event.target).to.be.equal(anchor);
+ done();
+ });
+
+ MockInteractions.pressEnter(tab);
+ });
+
+ });
+
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-toast/.bower.json b/catapult/third_party/polymer/components/paper-toast/.bower.json
new file mode 100644
index 00000000..9b1fe158
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/.bower.json
@@ -0,0 +1,45 @@
+{
+ "name": "paper-toast",
+ "version": "1.3.1",
+ "description": "A material design notification toast",
+ "private": true,
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "toast",
+ "notification"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-toast.git"
+ },
+ "main": "paper-toast.html",
+ "dependencies": {
+ "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#^1.0.0",
+ "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^1.0.9",
+ "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^1.1.0",
+ "polymer": "Polymer/polymer#^1.5.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": [],
+ "homepage": "https://github.com/PolymerElements/paper-toast",
+ "_release": "1.3.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.3.1",
+ "commit": "832ba27b6446711bc703717afdae2ac6485568ef"
+ },
+ "_source": "https://github.com/PolymerElements/paper-toast.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-toast"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-toast/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-toast/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..9251e7f2
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-toast/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-toast/.gitignore b/catapult/third_party/polymer/components/paper-toast/.gitignore
new file mode 100644
index 00000000..8d4ae253
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/.gitignore
@@ -0,0 +1 @@
+bower_components
diff --git a/catapult/third_party/polymer/components/paper-toast/.travis.yml b/catapult/third_party/polymer/components/paper-toast/.travis.yml
new file mode 100644
index 00000000..f2409740
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ fg+1RZ1aJQfxiM42iXvK8hj8L9RN8zX6IEjhuxFgNcgBY786DCyy9CIn1Gz4esv+4ZXyJfLyCbzGi8NMrzc6j3nbmewRKf3/zkX5NI4at82EvlkkN8Bcp7z+AV871LyLeV5q94waiR6+9/2LHega+F96pyl/4SAKxk2Gmh3BuPk=
+ - secure: >-
+ VkX13Tu1Ojr/2ZRXRORize8w4tlNo2bBUYEbEDF/RtcoxQeTffjAigmZ1q4Z/eZaA41JI/Fh90HUzZRg9V0W+TGT3t6htK9zxZq9zygM6sHVpOayxGInEW1TcMSM3QlcRI+3y8xUeCgpCmBmLLLI8K+HENRi1trg4C2HJk4JCN8=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-toast/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-toast/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-toast/README.md b/catapult/third_party/polymer/components/paper-toast/README.md
new file mode 100644
index 00000000..3c9287dd
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/README.md
@@ -0,0 +1,72 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-toast.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-toast.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-toast)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-toast)_
+
+
+##&lt;paper-toast&gt;
+
+Material design: [Snackbars & toasts](https://www.google.com/design/spec/components/snackbars-toasts.html)
+
+`paper-toast` provides a subtle notification toast. Only one `paper-toast` will
+be visible on screen.
+
+Use `opened` to show the toast:
+
+Example:
+
+```html
+<paper-toast text="Hello world!" opened></paper-toast>
+```
+
+Also `open()` or `show()` can be used to show the toast:
+
+Example:
+
+```html
+<paper-button on-click="openToast">Open Toast</paper-button>
+<paper-toast id="toast" text="Hello world!"></paper-toast>
+
+...
+
+openToast: function() {
+ this.$.toast.open();
+}
+```
+
+Set `duration` to 0, a negative number or Infinity to persist the toast on screen:
+
+Example:
+
+```html
+<paper-toast text="Terms and conditions" opened duration="0">
+ <a href="#">Show more</a>
+</paper-toast>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-toast-background-color` | The paper-toast background-color | `#323232` |
+| `--paper-toast-color` | The paper-toast color | `#f1f1f1` |
+
+This element applies the mixin `--paper-font-common-base` but does not import `paper-styles/typography.html`.
+In order to apply the `Roboto` font to this element, make sure you've imported `paper-styles/typography.html`.
+
+
diff --git a/catapult/third_party/polymer/components/paper-toast/bower.json b/catapult/third_party/polymer/components/paper-toast/bower.json
new file mode 100644
index 00000000..2a2b34b5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "paper-toast",
+ "version": "1.3.1",
+ "description": "A material design notification toast",
+ "private": true,
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "toast",
+ "notification"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-toast.git"
+ },
+ "main": "paper-toast.html",
+ "dependencies": {
+ "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#^1.0.0",
+ "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^1.0.9",
+ "iron-fit-behavior": "PolymerElements/iron-fit-behavior#^1.1.0",
+ "polymer": "Polymer/polymer#^1.5.0"
+ },
+ "devDependencies": {
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+ "paper-button": "PolymerElements/paper-button#^1.0.0",
+ "web-component-tester": "^4.0.0",
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+ },
+ "ignore": []
+}
diff --git a/catapult/third_party/polymer/components/paper-toast/demo/index.html b/catapult/third_party/polymer/components/paper-toast/demo/index.html
new file mode 100644
index 00000000..12638834
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/demo/index.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+
+<head>
+ <title>paper-toast</title>
+
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+
+ <link rel="import" href="../paper-toast.html">
+ <link rel="import" href="../../paper-button/paper-button.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles"></style>
+
+</head>
+
+<body unresolved class="centered">
+ <h3>Toast auto-closes after 3 seconds. Only one toast per time will be visible</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-button raised onclick="toast0.open()">Default toast</paper-button>
+ <paper-toast id="toast0" text="This toast auto-closes after 3 seconds"></paper-toast>
+ </template>
+ </demo-snippet>
+
+ <h3>Toast does not auto-close when <code>duration</code> is negative, <code>0</code>, or <code>Infinity</code></h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style>
+ .yellow-button {
+ text-transform: none;
+ color: #eeff41;
+ }
+ </style>
+
+ <paper-button raised onclick="toast1.open()">Persistent toast</paper-button>
+
+ <paper-toast id="toast1" duration="0" text="This toast will stay opened until you close it, or open another toast.">
+ <paper-button onclick="toast1.toggle()" class="yellow-button">Close now!</paper-button>
+ </paper-toast>
+ </template>
+ </demo-snippet>
+
+ <h3>Toast can be styled</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ #toast2 {
+ --paper-toast-background-color: red;
+ --paper-toast-color: white;
+ }
+ </style>
+
+ <paper-button raised onclick="toast2.open()">Styled toast</paper-button>
+
+ <paper-toast id="toast2" class="fit-bottom" text="This toast is red and fits bottom!"></paper-toast>
+ </template>
+ </demo-snippet>
+
+ <h3>Toast can fit into any element</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style>
+ #container {
+ padding: 100px;
+ border: 1px solid gray;
+ }
+ </style>
+ <div id="container">
+ <paper-button raised onclick="toast3.open()">Open toast</paper-button>
+ </div>
+ <paper-toast id="toast3" class="fit-bottom" text="This toast fits into the container."></paper-toast>
+
+ <script>
+ toast3.fitInto = container;
+ </script>
+
+ </template>
+ </demo-snippet>
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/paper-toast/hero.svg b/catapult/third_party/polymer/components/paper-toast/hero.svg
new file mode 100755
index 00000000..c82a7075
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/hero.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
+<g id="background" display="none">
+ <rect display="inline" fill="#B0BEC5" width="225" height="126"/>
+</g>
+<g id="label">
+</g>
+<g id="art">
+ <path d="M164,114H13V88h151V114z M15,112h147V90H15V112z"/>
+ <rect x="26" y="100" width="79" height="2"/>
+ <rect x="135" y="100" width="16" height="2"/>
+ <g id="ic_x5F_add_x0D_">
+ </g>
+</g>
+<g id="Guides">
+</g>
+</svg>
diff --git a/catapult/third_party/polymer/components/paper-toast/index.html b/catapult/third_party/polymer/components/paper-toast/index.html
new file mode 100644
index 00000000..487bb5c3
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/index.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+ <iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-toast/paper-toast.html b/catapult/third_party/polymer/components/paper-toast/paper-toast.html
new file mode 100644
index 00000000..5a7d7058
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/paper-toast.html
@@ -0,0 +1,323 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-a11y-announcer/iron-a11y-announcer.html">
+<link rel="import" href="../iron-overlay-behavior/iron-overlay-behavior.html">
+
+<!--
+Material design: [Snackbars & toasts](https://www.google.com/design/spec/components/snackbars-toasts.html)
+
+`paper-toast` provides a subtle notification toast. Only one `paper-toast` will
+be visible on screen.
+
+Use `opened` to show the toast:
+
+Example:
+
+ <paper-toast text="Hello world!" opened></paper-toast>
+
+Also `open()` or `show()` can be used to show the toast:
+
+Example:
+
+ <paper-button on-click="openToast">Open Toast</paper-button>
+ <paper-toast id="toast" text="Hello world!"></paper-toast>
+
+ ...
+
+ openToast: function() {
+ this.$.toast.open();
+ }
+
+Set `duration` to 0, a negative number or Infinity to persist the toast on screen:
+
+Example:
+
+ <paper-toast text="Terms and conditions" opened duration="0">
+ <a href="#">Show more</a>
+ </paper-toast>
+
+
+### Styling
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-toast-background-color` | The paper-toast background-color | `#323232`
+`--paper-toast-color` | The paper-toast color | `#f1f1f1`
+
+This element applies the mixin `--paper-font-common-base` but does not import `paper-styles/typography.html`.
+In order to apply the `Roboto` font to this element, make sure you've imported `paper-styles/typography.html`.
+
+@group Paper Elements
+@element paper-toast
+@demo demo/index.html
+@hero hero.svg
+-->
+
+<dom-module id="paper-toast">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: fixed;
+ background-color: var(--paper-toast-background-color, #323232);
+ color: var(--paper-toast-color, #f1f1f1);
+ min-height: 48px;
+ min-width: 288px;
+ padding: 16px 24px;
+ box-sizing: border-box;
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
+ border-radius: 2px;
+ margin: 12px;
+ font-size: 14px;
+ cursor: default;
+ -webkit-transition: -webkit-transform 0.3s, opacity 0.3s;
+ transition: transform 0.3s, opacity 0.3s;
+ opacity: 0;
+ -webkit-transform: translateY(100px);
+ transform: translateY(100px);
+ @apply(--paper-font-common-base);
+ }
+
+ :host(.capsule) {
+ border-radius: 24px;
+ }
+
+ :host(.fit-bottom) {
+ width: 100%;
+ min-width: 0;
+ border-radius: 0;
+ margin: 0;
+ }
+
+ :host(.paper-toast-open) {
+ opacity: 1;
+ -webkit-transform: translateY(0px);
+ transform: translateY(0px);
+ }
+ </style>
+
+ <span id="label">{{text}}</span>
+ <content></content>
+ </template>
+
+ <script>
+ (function() {
+ // Keeps track of the toast currently opened.
+ var currentToast = null;
+
+ Polymer({
+ is: 'paper-toast',
+
+ behaviors: [
+ Polymer.IronOverlayBehavior
+ ],
+
+ properties: {
+ /**
+ * The element to fit `this` into.
+ * Overridden from `Polymer.IronFitBehavior`.
+ */
+ fitInto: {
+ type: Object,
+ value: window,
+ observer: '_onFitIntoChanged'
+ },
+
+ /**
+ * The orientation against which to align the dropdown content
+ * horizontally relative to `positionTarget`.
+ * Overridden from `Polymer.IronFitBehavior`.
+ */
+ horizontalAlign: {
+ type: String,
+ value: 'left'
+ },
+
+ /**
+ * The orientation against which to align the dropdown content
+ * vertically relative to `positionTarget`.
+ * Overridden from `Polymer.IronFitBehavior`.
+ */
+ verticalAlign: {
+ type: String,
+ value: 'bottom'
+ },
+
+ /**
+ * The duration in milliseconds to show the toast.
+ * Set to `0`, a negative number, or `Infinity`, to disable the
+ * toast auto-closing.
+ */
+ duration: {
+ type: Number,
+ value: 3000
+ },
+
+ /**
+ * The text to display in the toast.
+ */
+ text: {
+ type: String,
+ value: ''
+ },
+
+ /**
+ * Overridden from `IronOverlayBehavior`.
+ * Set to false to enable closing of the toast by clicking outside it.
+ */
+ noCancelOnOutsideClick: {
+ type: Boolean,
+ value: true
+ },
+
+ /**
+ * Overridden from `IronOverlayBehavior`.
+ * Set to true to disable auto-focusing the toast or child nodes with
+ * the `autofocus` attribute` when the overlay is opened.
+ */
+ noAutoFocus: {
+ type: Boolean,
+ value: true
+ }
+ },
+
+ listeners: {
+ 'transitionend': '__onTransitionEnd'
+ },
+
+ /**
+ * Read-only. Deprecated. Use `opened` from `IronOverlayBehavior`.
+ * @property visible
+ * @deprecated
+ */
+ get visible() {
+ Polymer.Base._warn('`visible` is deprecated, use `opened` instead');
+ return this.opened;
+ },
+
+ /**
+ * Read-only. Can auto-close if duration is a positive finite number.
+ * @property _canAutoClose
+ */
+ get _canAutoClose() {
+ return this.duration > 0 && this.duration !== Infinity;
+ },
+
+ created: function() {
+ this._autoClose = null;
+ Polymer.IronA11yAnnouncer.requestAvailability();
+ },
+
+ /**
+ * Show the toast. Without arguments, this is the same as `open()` from `IronOverlayBehavior`.
+ * @param {(Object|string)=} properties Properties to be set before opening the toast.
+ * e.g. `toast.show('hello')` or `toast.show({text: 'hello', duration: 3000})`
+ */
+ show: function(properties) {
+ if (typeof properties == 'string') {
+ properties = { text: properties };
+ }
+ for (var property in properties) {
+ if (property.indexOf('_') === 0) {
+ Polymer.Base._warn('The property "' + property + '" is private and was not set.');
+ } else if (property in this) {
+ this[property] = properties[property];
+ } else {
+ Polymer.Base._warn('The property "' + property + '" is not valid.');
+ }
+ }
+ this.open();
+ },
+
+ /**
+ * Hide the toast. Same as `close()` from `IronOverlayBehavior`.
+ */
+ hide: function() {
+ this.close();
+ },
+
+ /**
+ * Called on transitions of the toast, indicating a finished animation
+ * @private
+ */
+ __onTransitionEnd: function(e) {
+ // there are different transitions that are happening when opening and
+ // closing the toast. The last one so far is for `opacity`.
+ // This marks the end of the transition, so we check for this to determine if this
+ // is the correct event.
+ if (e && e.target === this && e.propertyName === 'opacity') {
+ if (this.opened) {
+ this._finishRenderOpened();
+ } else {
+ this._finishRenderClosed();
+ }
+ }
+ },
+
+ /**
+ * Overridden from `IronOverlayBehavior`.
+ * Called when the value of `opened` changes.
+ */
+ _openedChanged: function() {
+ if (this._autoClose !== null) {
+ this.cancelAsync(this._autoClose);
+ this._autoClose = null;
+ }
+ if (this.opened) {
+ if (currentToast && currentToast !== this) {
+ currentToast.close();
+ }
+ currentToast = this;
+ this.fire('iron-announce', {
+ text: this.text
+ });
+ if (this._canAutoClose) {
+ this._autoClose = this.async(this.close, this.duration);
+ }
+ } else if (currentToast === this) {
+ currentToast = null;
+ }
+ Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments);
+ },
+
+ /**
+ * Overridden from `IronOverlayBehavior`.
+ */
+ _renderOpened: function() {
+ this.classList.add('paper-toast-open');
+ },
+
+ /**
+ * Overridden from `IronOverlayBehavior`.
+ */
+ _renderClosed: function() {
+ this.classList.remove('paper-toast-open');
+ },
+
+ /**
+ * @private
+ */
+ _onFitIntoChanged: function(fitInto) {
+ this.positionTarget = fitInto;
+ }
+
+ /**
+ * Fired when `paper-toast` is opened.
+ *
+ * @event 'iron-announce'
+ * @param {{text: string}} detail Contains text that will be announced.
+ */
+ });
+ })();
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-toast/test/basic.html b/catapult/third_party/polymer/components/paper-toast/test/basic.html
new file mode 100644
index 00000000..154015d7
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/test/basic.html
@@ -0,0 +1,243 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+
+<head>
+
+ <title>paper-toast-basic</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ <link rel="import" href="../paper-toast.html">
+
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ </style>
+</head>
+
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <paper-toast></paper-toast>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="show">
+ <template>
+ <paper-toast opened></paper-toast>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="contained">
+ <template>
+ <paper-toast class="fit-bottom"></paper-toast>
+ <div style="margin: 50px; width: 100px; height: 100px; background-color: orange;"></div>
+ </template>
+ </test-fixture>
+
+ <script>
+
+ suite('basic', function() {
+
+ var toast;
+
+ test('is hidden', function() {
+ toast = fixture('basic');
+ assert.isFalse(toast.opened, '`opened` is false');
+ });
+
+ test('is visible', function() {
+ toast = fixture('show');
+ assert.isTrue(toast.opened, '`opened` is true');
+ });
+
+ test('show() will open toast', function() {
+ toast = fixture('basic');
+ toast.show();
+ assert.isTrue(toast.opened, '`opened` is true');
+ });
+
+ test('hide() will close toast', function() {
+ toast = fixture('show');
+ toast.hide();
+ assert.isFalse(toast.opened, '`opened` is false');
+ });
+
+ test('toast auto-close after 10ms', function(done) {
+ toast = fixture('basic');
+ toast.duration = 10;
+ toast.show();
+ setTimeout(function() {
+ assert.isFalse(toast.opened, '`opened` is false');
+ done();
+ }, 12);
+ });
+
+ test('toast fires opened event', function(done) {
+ toast = fixture('show');
+ toast.addEventListener('iron-overlay-opened', function() {
+ done();
+ });
+ });
+
+ test('toast does not get focused', function(done) {
+ toast = fixture('show');
+ var spy = sinon.spy(toast, 'focus');
+ assert.isTrue(toast.noAutoFocus, 'no-auto-focus is true');
+ toast.addEventListener('iron-overlay-opened', function() {
+ assert.isFalse(spy.called, 'toast is not focused');
+ done();
+ });
+ });
+
+ test('toast fires closed event', function(done) {
+ toast = fixture('basic');
+ toast.show({duration: 350});
+ toast.addEventListener('iron-overlay-closed', function() {
+ done();
+ });
+ });
+
+ test('show() accepts valid properties', function() {
+ toast = fixture('basic');
+ toast.show({text: 'hello world', duration: 20});
+ assert.isTrue(toast.opened, '`opened` is true');
+ assert.equal(toast.text, 'hello world', '`text` is correct');
+ assert.equal(toast.duration, 20, '`duration` is correct');
+ });
+
+ test('show() does not accept invalid properties', function() {
+ toast = fixture('basic');
+ toast.show({foo: 'bar'});
+ assert.isUndefined(toast.foo, '`foo` is not a valid property and will not be set');
+ assert.isTrue(toast.opened, '`opened` is true');
+ });
+
+ test('show() does not accept private properties', function() {
+ toast = fixture('basic');
+ var temp = toast._manager;
+ toast.show({_manager: 'bar'});
+ assert.equal(toast._manager, temp, '`_manager` is a private property and will not be set');
+ assert.isTrue(toast.opened, '`opened` is true');
+ });
+
+ test('show() accepts a string argument as the text parameter', function() {
+ toast = fixture('basic');
+ toast.show('hello world 2');
+ assert.equal(toast.text, 'hello world 2', '`text is correct`');
+ });
+
+ suite('disable auto-close', function() {
+ var spy;
+ setup(function() {
+ toast = fixture('basic');
+ spy = sinon.spy(toast, 'async');
+ });
+ test('duration = Infinity', function() {
+ toast.duration = Infinity;
+ toast.show();
+ assert.isFalse(spy.calledWith(toast.close), '`async` was not called with `close()`');
+ assert.isFalse(spy.calledWith(toast.hide), '`async` was not called with `hide()`');
+ });
+
+ test('duration = 0', function() {
+ toast.duration = 0;
+ toast.show();
+ assert.isFalse(spy.calledWith(toast.close), '`async` was not called with `close()`');
+ assert.isFalse(spy.calledWith(toast.hide), '`async` was not called with `hide()`');
+ });
+
+ test('duration = -10', function() {
+ toast.duration = -10;
+ toast.show();
+ assert.isFalse(spy.calledWith(toast.close), '`async` was not called with `close()`');
+ assert.isFalse(spy.calledWith(toast.hide), '`async` was not called with `hide()`');
+ });
+ });
+
+ test('there is only 1 toast opened', function() {
+ var toast1 = fixture('basic');
+ var toast2 = fixture('show');
+ toast2.open();
+ toast1.open();
+ assert.isTrue(toast1.opened, 'toast1 is opened');
+ assert.isFalse(toast2.opened, 'toast2 is not opened');
+ toast2.open();
+ assert.isFalse(toast1.opened, 'toast1 is now not opened');
+ assert.isTrue(toast2.opened, 'toast2 is now opened');
+ });
+
+ test('auto-close is correctly reset', function(done) {
+ toast = fixture('basic');
+ toast.duration = 10;
+ toast.show();
+ // a bit later (before the auto-close), toast is reset
+ setTimeout(function() {
+ toast.hide();
+ // keep toast opened
+ toast.duration = 0;
+ toast.show();
+ setTimeout(function() {
+ assert.isTrue(toast.opened, 'toast is still open');
+ done();
+ }, 10);
+ }, 5);
+ });
+
+ test('toast is positioned according at the bottom left of its fitInto', function(done) {
+ var f = fixture('contained');
+ var toast = f[0];
+ var container = f[1];
+ toast.fitInto = container;
+ toast.open();
+ // Wait for it to be opened, so it will be sized correctly.
+ toast.addEventListener('iron-overlay-opened', function() {
+ var rect = toast.getBoundingClientRect();
+ assert.equal(rect.left, 50, 'left ok');
+ // 150px from top, (100px of height + 50px of margin-top)
+ assert.equal(rect.bottom, 150, 'bottom');
+ done();
+ });
+ });
+
+ suite('a11y', function() {
+ test('show() will announce text', function() {
+ toast = fixture('basic');
+ var spy = sinon.spy(toast, 'fire');
+ toast.text = 'announce!';
+ toast.show();
+ assert.isTrue(spy.calledWith('iron-announce', {
+ text: 'announce!'
+ }), 'text announced');
+ });
+
+ test('hide() will not announce text', function() {
+ toast = fixture('show');
+ var spy = sinon.spy(toast, 'fire');
+ toast.hide();
+ assert.isFalse(spy.calledWith('iron-announce'), 'text not announced');
+ });
+ });
+
+ });
+ </script>
+
+</body>
+
+</html>
diff --git a/catapult/third_party/polymer/components/paper-toast/test/index.html b/catapult/third_party/polymer/components/paper-toast/test/index.html
new file mode 100644
index 00000000..c058372b
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-toast/test/index.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<html>
+ <head>
+
+ <meta charset="utf-8">
+ <title>Tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+
+ </head>
+ <body>
+
+ <script>
+
+ WCT.loadSuites([
+ 'basic.html'
+ ]);
+
+ </script>
+
+ </body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/.bower.json b/catapult/third_party/polymer/components/paper-tooltip/.bower.json
new file mode 100644
index 00000000..8a542dd4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/.bower.json
@@ -0,0 +1,45 @@
+{
+ "name": "paper-tooltip",
+ "version": "1.1.4",
+ "description": "Material design tooltip popup for content",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "tooltip"
+ ],
+ "main": "paper-tooltip.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-tooltip.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-tooltip",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "polymerelements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0"
+ },
+ "_release": "1.1.4",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.1.4",
+ "commit": "c7c0a4d47e6ce30578f948986e9274d06fa2b94c"
+ },
+ "_source": "https://github.com/PolymerElements/paper-tooltip.git",
+ "_target": "^1.0.0",
+ "_originalSource": "PolymerElements/paper-tooltip"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/paper-tooltip/.github/ISSUE_TEMPLATE.md b/catapult/third_party/polymer/components/paper-tooltip/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..61136cf8
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,33 @@
+<!-- Instructions: https://github.com/PolymerElements/paper-tooltip/CONTRIBUTING.md#filing-issues -->
+### Description
+<!-- Example: The `paper-foo` element causes the page to turn pink when clicked. -->
+
+### Expected outcome
+
+<!-- Example: The page stays the same color. -->
+
+### Actual outcome
+
+<!-- Example: The page turns pink. -->
+
+### Live Demo
+<!-- Example: https://jsbin.com/cagaye/edit?html,output -->
+
+### Steps to reproduce
+
+<!-- Example
+1. Put a `paper-foo` element in the page.
+2. Open the page in a web browser.
+3. Click the `paper-foo` element.
+-->
+
+### Browsers Affected
+<!-- Check all that apply -->
+- [ ] Chrome
+- [ ] Firefox
+- [ ] Safari 9
+- [ ] Safari 8
+- [ ] Safari 7
+- [ ] Edge
+- [ ] IE 11
+- [ ] IE 10
diff --git a/catapult/third_party/polymer/components/paper-tooltip/.gitignore b/catapult/third_party/polymer/components/paper-tooltip/.gitignore
new file mode 100644
index 00000000..fbe05fc9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/.gitignore
@@ -0,0 +1 @@
+bower_components/
diff --git a/catapult/third_party/polymer/components/paper-tooltip/.travis.yml b/catapult/third_party/polymer/components/paper-tooltip/.travis.yml
new file mode 100644
index 00000000..14b4e4b5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/.travis.yml
@@ -0,0 +1,24 @@
+language: node_js
+sudo: required
+before_script:
+ - npm install -g bower polylint web-component-tester
+ - bower install
+ - polylint
+env:
+ global:
+ - secure: >-
+ gfWhTNPGzDs8BHzRxw6W2h/Uw8VIs0/ZWHhBkSS3XrpUx3A1ffZguK4PpiuAWMms5TaNCG/0N5f9YKnWVsKV6YAq78DdPh36wSNHZL3AKZaaHoLhcIOZUYLQ7DXlgVpJkGwvooPNUhVVy+NX33eLw4yAnn5IfQ9lAq+WOZH9xSPubzEA1iqB88EO5kPYkZblmmIwghEFP5bVXseb2Rc5VQHbOJPKJr20NXZCLvHvLxJYLO1wQRBJatQm/o5PTsN+Ey/2szsRlwWKSiTnIHMHIRqcvarxxsI5rm8nT7GOXxjzRc+nzZ8cwaWlFGwrDwKhgsjQytBQNPqctDnUwk+CQpTkJtYfxZTk0MfTpmTjtnpgErCiFBPjX+8igZC3L8xChZTAnTGbWHyyMSzs4N0eYJAEAdGksPMlhZ56q+EiP160NFAV8eJcGnpyfyqmrvSVcq5XqclgSLpeirmO49old1RzfdMvjDPbZQVoUievSwgUZDBodilynES+rOswMojQWwpu6bK2stM1qwxNs9JY22ssiWPg2ZN+98BpaLFZ/43yhuoyThOXGaaUVNrWpyGtgvUMtPBTI2J0zEDWAhMhZL4Uh2JWnZpfolLtGESLSMQptU8dz8jTLItEdBzotiCNJniGwiwPDMQrROfjekQaevVSJpqBfr38+1VEzb6ev6g=
+ - secure: >-
+ QjFHuOpEtakzECoxcIYl8TeZ6NIcrji6tN/5aWPEZaIUtsMIH7OwM6N7pEsdODEyanU4ay5PQsLjNmupsvVb515mk7sxTK3ZJBm8lk4T61WCQc74slV+WCjZ+jBs9SRwZM5SKECfqVIOfnDn0ZIMomVI4BgIexOAXi8pkc+9+zn6wVxYJGT2ORNu4fRJXHBfs5497Xp6qEtngXRvZHhtvElL796WiVVsMuePEXE/2BT0FoXL3FzhtCnY2xy/+Z8A+XzVVOftpSbMEPkkJJA7t+K6zsJnu5p4BBkf7afGaR1rEacZeoSEywQZ4iMX+BZt/fSkO1vJLXW+4YiCR6xivhpbMpHkvECFhB4g08ydRngdVqIcBOX1M94DeqvWodzHxjUo4fOd8loWEI8Y6eigLf/ex/YVN9q2kLYxySFINsA2vKFthTPjA6RdQZqiAAYZn7HvkGsMbULB2LvtgYksT+oayLQwS+YxgzqAWgfMkaD2kS8ISFbwW5YKa0lxiQf1ZafD/OZDhLBMn+UTnLH6BtRrWHzWgzFQSOoE+CS1OH3dDlah6lLc18U+dhF9WynjiT2F41jGAQuZ5Wqld18Ge5dhJ5e3nqHylS+GZ6pHQs9jFQStZlAT+Pv1PCi9PikaIv8/OPE338buZK3b3WFrWTQATWazCgck+u8230kMTaQ=
+node_js: '6'
+addons:
+ firefox: latest
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
+script:
+ - xvfb-run wct
+ - 'if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s ''default''; fi'
+dist: trusty
diff --git a/catapult/third_party/polymer/components/paper-tooltip/CONTRIBUTING.md b/catapult/third_party/polymer/components/paper-tooltip/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/paper-tooltip/README.md b/catapult/third_party/polymer/components/paper-tooltip/README.md
new file mode 100644
index 00000000..b20a34b5
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/README.md
@@ -0,0 +1,62 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+paper-tooltip.html
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerElements/paper-tooltip.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-tooltip)
+
+_[Demo and API docs](https://elements.polymer-project.org/elements/paper-tooltip)_
+
+
+##&lt;paper-tooltip&gt;
+
+Material design: [Tooltips](https://www.google.com/design/spec/components/tooltips.html)
+
+`<paper-tooltip>` is a label that appears on hover and focus when the user
+hovers over an element with the cursor or with the keyboard. It will be centered
+to an anchor element specified in the `for` attribute, or, if that doesn't exist,
+centered to the parent node containing it.
+
+Example:
+
+```html
+<div style="display:inline-block">
+ <button>Click me!</button>
+ <paper-tooltip>Tooltip text</paper-tooltip>
+</div>
+
+<div>
+ <button id="btn">Click me!</button>
+ <paper-tooltip for="btn">Tooltip text</paper-tooltip>
+</div>
+```
+
+The tooltip can be positioned on the top|bottom|left|right of the anchor using
+the `position` attribute. The default position is bottom.
+
+```html
+<paper-tooltip for="btn" position="left">Tooltip text</paper-tooltip>
+<paper-tooltip for="btn" position="top">Tooltip text</paper-tooltip>
+```
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+| Custom property | Description | Default |
+| --- | --- | --- |
+| `--paper-tooltip-background` | The background color of the tooltip | `#616161` |
+| `--paper-tooltip-opacity` | The opacity of the tooltip | `0.9` |
+| `--paper-tooltip-text-color` | The text color of the tooltip | `white` |
+| `--paper-tooltip` | Mixin applied to the tooltip | `{}` |
+
+
diff --git a/catapult/third_party/polymer/components/paper-tooltip/bower.json b/catapult/third_party/polymer/components/paper-tooltip/bower.json
new file mode 100644
index 00000000..f7a687d9
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/bower.json
@@ -0,0 +1,36 @@
+{
+ "name": "paper-tooltip",
+ "version": "1.1.4",
+ "description": "Material design tooltip popup for content",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "keywords": [
+ "web-components",
+ "polymer",
+ "tooltip"
+ ],
+ "main": "paper-tooltip.html",
+ "private": true,
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/PolymerElements/paper-tooltip.git"
+ },
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "homepage": "https://github.com/PolymerElements/paper-tooltip",
+ "ignore": [],
+ "dependencies": {
+ "polymer": "Polymer/polymer#^1.1.0",
+ "paper-styles": "PolymerElements/paper-styles#^1.0.0",
+ "neon-animation": "PolymerElements/neon-animation#^1.0.0"
+ },
+ "devDependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+ "web-component-tester": "^4.0.0",
+ "test-fixture": "PolymerElements/test-fixture#^1.0.0",
+ "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+ "iron-demo-helpers": "polymerelements/iron-demo-helpers#^1.0.0",
+ "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
+ "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/paper-tooltip/demo/index.html b/catapult/third_party/polymer/components/paper-tooltip/demo/index.html
new file mode 100644
index 00000000..c0bad5e4
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/demo/index.html
@@ -0,0 +1,133 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html lang="en">
+<head>
+ <title>paper-tooltip demo</title>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
+ <link rel="import" href="../../iron-icons/iron-icons.html">
+ <link rel="import" href="../../paper-icon-button/paper-icon-button.html">
+ <link rel="import" href="../../paper-styles/color.html">
+ <link rel="import" href="../paper-tooltip.html">
+ <link rel="import" href="test-button.html">
+
+ <style is="custom-style" include="demo-pages-shared-styles">
+ paper-icon-button, input, .avatar {
+ margin: 0 10px;
+ }
+
+ .avatar {
+ box-sizing: border-box;
+ width: 40px;
+ height: 40px;
+ padding: 8px;
+ border-radius: 50%;
+ cursor: pointer;
+ }
+
+ .blue {
+ background-color: var(--paper-light-blue-300);
+ }
+ .orange {
+ background-color: var(--paper-amber-500);
+ }
+ .green {
+ background-color: var(--paper-green-500);
+ }
+ .red {
+ background-color: var(--paper-pink-500);
+ }
+ </style>
+</head>
+<body unresolved>
+ <div class="vertical-section-container centered">
+ <h3>Tooltips can be anchored to elements using their ID</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <paper-icon-button id="id_1" icon="favorite" alt="heart"></paper-icon-button>
+ <paper-icon-button id="id_2" icon="alarm-on" alt="go back"></paper-icon-button>
+ <div id="id_3" class="avatar blue" tabindex="0"></div>
+ <div id="id_4" class="avatar orange" tabindex="0"></div>
+
+ <!-- paper-icon-buttons have an inherent padding that will push the tooltip down. offset undoes it -->
+ <paper-tooltip for="id_1" offset="0">&lt;3 &lt;3 &lt;3 </paper-tooltip>
+ <paper-tooltip for="id_2" offset="0">wake up!</paper-tooltip>
+ <paper-tooltip for="id_3" offset="0">halp I am trapped in a tooltip</paper-tooltip>
+ <paper-tooltip for="id_4" offset="0">meow!</paper-tooltip>
+ </template>
+ </demo-snippet>
+
+ <h3>Tooltips can be anchored to elements relative to their parent</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <!-- Adding a tabindex so that we can show the tooltip when the whole box is tabbed to -->
+ <div tabindex="0">
+ <input type="checkbox">allosaurus
+ <paper-tooltip>the name means "different lizard"</paper-tooltip>
+ </div>
+ <div tabindex="0">
+ <input type="checkbox">brontosaurus
+ <paper-tooltip>the name means "thunder lizard"</paper-tooltip>
+ </div>
+ <div tabindex="0">
+ <input type="checkbox">megalosaurus
+ <paper-tooltip>the name means "roof lizard"</paper-tooltip>
+ </div>
+ </div>
+ </template>
+ </demo-snippet>
+
+ <h3>Tooltips can open in different directions</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <div id="dir_1" class="avatar red" tabindex="0"></div>
+ <div id="dir_2" class="avatar blue" tabindex="0"></div>
+ <div id="dir_3" class="avatar green" tabindex="0"></div>
+ <div id="dir_4" class="avatar orange" tabindex="0"></div>
+
+ <paper-tooltip for="dir_1" position="left" animation-delay="0">👈</paper-tooltip>
+ <paper-tooltip for="dir_2" position="right" animation-delay="0">👉</paper-tooltip>
+ <paper-tooltip for="dir_3" position="top" animation-delay="0">👍</paper-tooltip>
+ <paper-tooltip for="dir_4" position="bottom" animation-delay="0">👎</paper-tooltip>
+ </template>
+ </demo-snippet>
+
+ <h3>Tooltips can contain rich text (though against the Material Design spec)</h3>
+ <demo-snippet class="centered-demo">
+ <template>
+ <style is="custom-style">
+ paper-tooltip.custom img {
+ width: 40px;
+ padding-right: 10px;
+ padding-bottom: 10px;
+ float: left;
+ }
+ .custom {
+ --paper-tooltip-background: black;
+ --paper-tooltip-text-color: var(--paper-pink-100);
+ width: 160px;
+ }
+ </style>
+ <paper-icon-button id="demo4_icon1" icon="favorite" alt="heart"></paper-icon-button>
+ <paper-tooltip for="demo4_icon1" class="custom" animation-delay="0">
+ <img src="https://placekitten.com/50/50">
+ Rich-text tooltips are doable but against the Material Design spec.
+ </paper-tooltip>
+ </template>
+ </demo-snippet>
+ </div>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/demo/test-button.html b/catapult/third_party/polymer/components/paper-tooltip/demo/test-button.html
new file mode 100644
index 00000000..0942f58c
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/demo/test-button.html
@@ -0,0 +1,37 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../../paper-icon-button/paper-icon-button.html">
+<link rel="import" href="../../iron-icons/iron-icons.html">
+<link rel="import" href="../paper-tooltip.html">
+
+<dom-module id="test-button">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ }
+
+ paper-icon-button {
+ padding: 0;
+ }
+ </style>
+
+ <paper-icon-button id="m" icon="menu" alt="menu"></paper-icon-button>
+ <paper-tooltip for="m" offset="8">hot dogs</paper-tooltip>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'test-button'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/index.html b/catapult/third_party/polymer/components/paper-tooltip/index.html
new file mode 100644
index 00000000..848f0426
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <title>paper-tooltip</title>
+
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+
+</head>
+<body>
+
+<iron-component-page></iron-component-page>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/paper-tooltip.html b/catapult/third_party/polymer/components/paper-tooltip/paper-tooltip.html
new file mode 100644
index 00000000..670280b1
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/paper-tooltip.html
@@ -0,0 +1,407 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../neon-animation/neon-animation-runner-behavior.html">
+<link rel="import" href="../neon-animation/animations/fade-in-animation.html">
+<link rel="import" href="../neon-animation/animations/fade-out-animation.html">
+
+<!--
+Material design: [Tooltips](https://www.google.com/design/spec/components/tooltips.html)
+
+`<paper-tooltip>` is a label that appears on hover and focus when the user
+hovers over an element with the cursor or with the keyboard. It will be centered
+to an anchor element specified in the `for` attribute, or, if that doesn't exist,
+centered to the parent node containing it.
+
+Example:
+
+ <div style="display:inline-block">
+ <button>Click me!</button>
+ <paper-tooltip>Tooltip text</paper-tooltip>
+ </div>
+
+ <div>
+ <button id="btn">Click me!</button>
+ <paper-tooltip for="btn">Tooltip text</paper-tooltip>
+ </div>
+
+The tooltip can be positioned on the top|bottom|left|right of the anchor using
+the `position` attribute. The default position is bottom.
+
+ <paper-tooltip for="btn" position="left">Tooltip text</paper-tooltip>
+ <paper-tooltip for="btn" position="top">Tooltip text</paper-tooltip>
+
+### Styling
+
+The following custom properties and mixins are available for styling:
+
+Custom property | Description | Default
+----------------|-------------|----------
+`--paper-tooltip-background` | The background color of the tooltip | `#616161`
+`--paper-tooltip-opacity` | The opacity of the tooltip | `0.9`
+`--paper-tooltip-text-color` | The text color of the tooltip | `white`
+`--paper-tooltip` | Mixin applied to the tooltip | `{}`
+
+@group Paper Elements
+@element paper-tooltip
+@demo demo/index.html
+-->
+
+<dom-module id="paper-tooltip">
+ <template>
+ <style>
+ :host {
+ display: block;
+ position: absolute;
+ outline: none;
+ z-index: 1002;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: default;
+ }
+
+ #tooltip {
+ display: block;
+ outline: none;
+ @apply(--paper-font-common-base);
+ font-size: 10px;
+ line-height: 1;
+
+ background-color: var(--paper-tooltip-background, #616161);
+ opacity: var(--paper-tooltip-opacity, 0.9);
+ color: var(--paper-tooltip-text-color, white);
+
+ padding: 8px;
+ border-radius: 2px;
+
+ @apply(--paper-tooltip);
+ }
+
+ /* Thanks IE 10. */
+ .hidden {
+ display: none !important;
+ }
+ </style>
+
+ <div id="tooltip" class="hidden">
+ <content></content>
+ </div>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'paper-tooltip',
+
+ hostAttributes: {
+ role: 'tooltip',
+ tabindex: -1
+ },
+
+ behaviors: [
+ Polymer.NeonAnimationRunnerBehavior
+ ],
+
+ properties: {
+ /**
+ * The id of the element that the tooltip is anchored to. This element
+ * must be a sibling of the tooltip.
+ */
+ for: {
+ type: String,
+ observer: '_findTarget'
+ },
+
+ /**
+ * Set this to true if you want to manually control when the tooltip
+ * is shown or hidden.
+ */
+ manualMode: {
+ type: Boolean,
+ value: false,
+ observer: '_manualModeChanged'
+ },
+
+ /**
+ * Positions the tooltip to the top, right, bottom, left of its content.
+ */
+ position: {
+ type: String,
+ value: 'bottom'
+ },
+
+ /**
+ * If true, no parts of the tooltip will ever be shown offscreen.
+ */
+ fitToVisibleBounds: {
+ type: Boolean,
+ value: false
+ },
+
+ /**
+ * The spacing between the top of the tooltip and the element it is
+ * anchored to.
+ */
+ offset: {
+ type: Number,
+ value: 14
+ },
+
+ /**
+ * This property is deprecated, but left over so that it doesn't
+ * break exiting code. Please use `offset` instead. If both `offset` and
+ * `marginTop` are provided, `marginTop` will be ignored.
+ * @deprecated since version 1.0.3
+ */
+ marginTop: {
+ type: Number,
+ value: 14
+ },
+
+ /**
+ * The delay that will be applied before the `entry` animation is
+ * played when showing the tooltip.
+ */
+ animationDelay: {
+ type: Number,
+ value: 500
+ },
+
+ /**
+ * The entry and exit animations that will be played when showing and
+ * hiding the tooltip. If you want to override this, you must ensure
+ * that your animationConfig has the exact format below.
+ */
+ animationConfig: {
+ type: Object,
+ value: function() {
+ return {
+ 'entry': [{
+ name: 'fade-in-animation',
+ node: this,
+ timing: {delay: 0}
+ }],
+ 'exit': [{
+ name: 'fade-out-animation',
+ node: this
+ }]
+ }
+ }
+ },
+
+ _showing: {
+ type: Boolean,
+ value: false
+ }
+ },
+
+ listeners: {
+ 'neon-animation-finish': '_onAnimationFinish',
+ },
+
+ /**
+ * Returns the target element that this tooltip is anchored to. It is
+ * either the element given by the `for` attribute, or the immediate parent
+ * of the tooltip.
+ */
+ get target () {
+ var parentNode = Polymer.dom(this).parentNode;
+ // If the parentNode is a document fragment, then we need to use the host.
+ var ownerRoot = Polymer.dom(this).getOwnerRoot();
+
+ var target;
+ if (this.for) {
+ target = Polymer.dom(ownerRoot).querySelector('#' + this.for);
+ } else {
+ target = parentNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE ?
+ ownerRoot.host : parentNode;
+ }
+
+ return target;
+ },
+
+ attached: function() {
+ this._findTarget();
+ },
+
+ detached: function() {
+ if (!this.manualMode)
+ this._removeListeners();
+ },
+
+ show: function() {
+ // If the tooltip is already showing, there's nothing to do.
+ if (this._showing)
+ return;
+
+ if (Polymer.dom(this).textContent.trim() === ''){
+ // Check if effective children are also empty
+ var allChildrenEmpty = true;
+ var effectiveChildren = Polymer.dom(this).getEffectiveChildNodes();
+ for (var i = 0; i < effectiveChildren.length; i++) {
+ if (effectiveChildren[i].textContent.trim() !== '') {
+ allChildrenEmpty = false;
+ break;
+ }
+ }
+ if (allChildrenEmpty) {
+ return;
+ }
+ }
+
+
+ this.cancelAnimation();
+ this._showing = true;
+ this.toggleClass('hidden', false, this.$.tooltip);
+ this.updatePosition();
+
+ this.animationConfig.entry[0].timing = this.animationConfig.entry[0].timing || {};
+ this.animationConfig.entry[0].timing.delay = this.animationDelay;
+ this._animationPlaying = true;
+ this.playAnimation('entry');
+ },
+
+ hide: function() {
+ // If the tooltip is already hidden, there's nothing to do.
+ if (!this._showing) {
+ return;
+ }
+
+ // If the entry animation is still playing, don't try to play the exit
+ // animation since this will reset the opacity to 1. Just end the animation.
+ if (this._animationPlaying) {
+ this.cancelAnimation();
+ this._showing = false;
+ this._onAnimationFinish();
+ return;
+ }
+
+ this._showing = false;
+ this._animationPlaying = true;
+ this.playAnimation('exit');
+ },
+
+ updatePosition: function() {
+ if (!this._target || !this.offsetParent)
+ return;
+
+ var offset = this.offset;
+ // If a marginTop has been provided by the user (pre 1.0.3), use it.
+ if (this.marginTop != 14 && this.offset == 14)
+ offset = this.marginTop;
+
+ var parentRect = this.offsetParent.getBoundingClientRect();
+ var targetRect = this._target.getBoundingClientRect();
+ var thisRect = this.getBoundingClientRect();
+
+ var horizontalCenterOffset = (targetRect.width - thisRect.width) / 2;
+ var verticalCenterOffset = (targetRect.height - thisRect.height) / 2;
+
+ var targetLeft = targetRect.left - parentRect.left;
+ var targetTop = targetRect.top - parentRect.top;
+
+ var tooltipLeft, tooltipTop;
+
+ switch (this.position) {
+ case 'top':
+ tooltipLeft = targetLeft + horizontalCenterOffset;
+ tooltipTop = targetTop - thisRect.height - offset;
+ break;
+ case 'bottom':
+ tooltipLeft = targetLeft + horizontalCenterOffset;
+ tooltipTop = targetTop + targetRect.height + offset;
+ break;
+ case 'left':
+ tooltipLeft = targetLeft - thisRect.width - offset;
+ tooltipTop = targetTop + verticalCenterOffset;
+ break;
+ case 'right':
+ tooltipLeft = targetLeft + targetRect.width + offset;
+ tooltipTop = targetTop + verticalCenterOffset;
+ break;
+ }
+
+ // TODO(noms): This should use IronFitBehavior if possible.
+ if (this.fitToVisibleBounds) {
+ // Clip the left/right side
+ if (parentRect.left + tooltipLeft + thisRect.width > window.innerWidth) {
+ this.style.right = '0px';
+ this.style.left = 'auto';
+ } else {
+ this.style.left = Math.max(0, tooltipLeft) + 'px';
+ this.style.right = 'auto';
+ }
+
+ // Clip the top/bottom side.
+ if (parentRect.top + tooltipTop + thisRect.height > window.innerHeight) {
+ this.style.bottom = parentRect.height + 'px';
+ this.style.top = 'auto';
+ } else {
+ this.style.top = Math.max(-parentRect.top, tooltipTop) + 'px';
+ this.style.bottom = 'auto';
+ }
+ } else {
+ this.style.left = tooltipLeft + 'px';
+ this.style.top = tooltipTop + 'px';
+ }
+
+ },
+
+ _addListeners: function() {
+ if (this._target) {
+ this.listen(this._target, 'mouseenter', 'show');
+ this.listen(this._target, 'focus', 'show');
+ this.listen(this._target, 'mouseleave', 'hide');
+ this.listen(this._target, 'blur', 'hide');
+ this.listen(this._target, 'tap', 'hide');
+ }
+ this.listen(this, 'mouseenter', 'hide');
+ },
+
+ _findTarget: function() {
+ if (!this.manualMode)
+ this._removeListeners();
+
+ this._target = this.target;
+
+ if (!this.manualMode)
+ this._addListeners();
+ },
+
+ _manualModeChanged: function() {
+ if (this.manualMode)
+ this._removeListeners();
+ else
+ this._addListeners();
+ },
+
+ _onAnimationFinish: function() {
+ this._animationPlaying = false;
+ if (!this._showing) {
+ this.toggleClass('hidden', true, this.$.tooltip);
+ }
+ },
+
+ _removeListeners: function() {
+ if (this._target) {
+ this.unlisten(this._target, 'mouseenter', 'show');
+ this.unlisten(this._target, 'focus', 'show');
+ this.unlisten(this._target, 'mouseleave', 'hide');
+ this.unlisten(this._target, 'blur', 'hide');
+ this.unlisten(this._target, 'tap', 'hide');
+ }
+ this.unlisten(this, 'mouseenter', 'hide');
+ }
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/test/basic.html b/catapult/third_party/polymer/components/paper-tooltip/test/basic.html
new file mode 100644
index 00000000..45e386b0
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/test/basic.html
@@ -0,0 +1,552 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2015 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
+-->
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>paper-tooltip tests</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+ <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
+ <script src="../../web-component-tester/browser.js"></script>
+ <script src="../../test-fixture/test-fixture-mocha.js"></script>
+ <script src="../../iron-test-helpers/mock-interactions.js"></script>
+
+ <link rel="import" href="../../test-fixture/test-fixture.html">
+ <link rel="import" href="../paper-tooltip.html">
+ <link rel="import" href="test-button.html">
+ <link rel="import" href="test-icon.html">
+
+</head>
+<style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ #target {
+ width: 100px;
+ height: 20px;
+ background-color: red;
+ }
+ paper-tooltip {
+ width: 70px;
+ height: 30px;
+ }
+
+ .wide {
+ width: 200px;
+ }
+
+ [hidden] {
+ display: none;
+ }
+</style>
+
+<body>
+
+ <test-fixture id="basic">
+ <template>
+ <div>
+ <div id="target"></div>
+ <paper-tooltip for="target" animation-delay="0">Tooltip text</paper-tooltip>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="fitted">
+ <template>
+ <div>
+ <div id="target" style="position:absolute"></div>
+ <paper-tooltip for="target" class="wide" fit-to-visible-bounds>Tooltip text</paper-tooltip>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="no-text">
+ <template>
+ <div>
+ <div id="target"></div>
+ <paper-tooltip for="target"></paper-tooltip>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="dynamic">
+ <template>
+ <div>
+ <div id="target"></div>
+ <paper-tooltip>Tooltip text</paper-tooltip>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="custom">
+ <template>
+ <test-button></test-button>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="custom-with-content">
+ <template>
+ <test-icon>Tooltip text</test-icon>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="no-offset-parent">
+ <template>
+ <div>
+ <div id="target"></div>
+ <paper-tooltip for="target" animation-delay="0" hidden></paper-tooltip>
+ </div>
+ </template>
+ </test-fixture>
+
+ <test-fixture id="manual-mode">
+ <template>
+ <div>
+ <div id="target"></div>
+ <paper-tooltip for="target" manual-mode>Text</paper-tooltip>
+ </div>
+ </template>
+ </test-fixture>
+
+ <script>
+ function isHidden(element) {
+ var rect = element.getBoundingClientRect();
+ return (rect.width == 0 && rect.height == 0);
+ }
+
+ suite('basic', function() {
+ test('tooltip is shown when target is focused', function() {
+ var f = fixture('no-text');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isTrue(isHidden(actualTooltip));
+ });
+
+ test('tooltip is not shown if empty', function() {
+ var f = fixture('basic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+ });
+
+ test('tooltip doesn\'t throw an exception if it has no offsetParent', function() {
+ var f = fixture('no-offset-parent');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+ tooltip.updatePosition();
+ tooltip.show();
+
+ // Doesn't get shown since there's no position computed.
+ assert.isTrue(isHidden(actualTooltip));
+ });
+
+ test('tooltip is positioned correctly (bottom)', function() {
+ var f = fixture('basic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var divRect = target.getBoundingClientRect();
+ expect(divRect.width).to.be.equal(100);
+ expect(divRect.height).to.be.equal(20);
+
+ var contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.width).to.be.equal(70);
+ expect(contentRect.height).to.be.equal(30);
+
+ // The target div width is 100, and the tooltip width is 70, and
+ // it's centered. The height of the target div is 20, and the
+ // tooltip is 14px below.
+ expect(contentRect.left).to.be.equal((100 - 70)/2);
+ expect(contentRect.top).to.be.equal(20 + 14);
+
+ // Also check the math, just in case.
+ expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
+ expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
+ });
+
+ test('tooltip is positioned correctly (top)', function() {
+ var f = fixture('basic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+ tooltip.position = 'top';
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var divRect = target.getBoundingClientRect();
+ expect(divRect.width).to.be.equal(100);
+ expect(divRect.height).to.be.equal(20);
+
+ var contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.width).to.be.equal(70);
+ expect(contentRect.height).to.be.equal(30);
+
+ // The target div width is 100, and the tooltip width is 70, and
+ // it's centered. The height of the tooltip is 30, and the
+ // tooltip is 14px above the target.
+ expect(contentRect.left).to.be.equal((100 - 70)/2);
+ expect(contentRect.top).to.be.equal(0 - 30 - 14);
+
+ // Also check the math, just in case.
+ expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
+ expect(contentRect.top).to.be.equal(0 - contentRect.height - tooltip.offset);
+ });
+
+ test('tooltip is positioned correctly (right)', function() {
+ var f = fixture('basic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+ tooltip.position = 'right';
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var divRect = target.getBoundingClientRect();
+ expect(divRect.width).to.be.equal(100);
+ expect(divRect.height).to.be.equal(20);
+
+ var contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.width).to.be.equal(70);
+ expect(contentRect.height).to.be.equal(30);
+
+ // The target div width is 100, and the tooltip is 14px to the right.
+ // The target div height is 20, the height of the tooltip is 20px, and
+ // the tooltip is centered.
+ expect(contentRect.left).to.be.equal(100 + 14);
+ expect(contentRect.top).to.be.equal((20 - 30)/2);
+
+ // Also check the math, just in case.
+ expect(contentRect.left).to.be.equal(divRect.width + tooltip.offset);
+ expect(contentRect.top).to.be.equal((divRect.height - contentRect.height)/2);
+ });
+
+ test('tooltip is positioned correctly (left)', function() {
+ var f = fixture('basic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+ tooltip.position = 'left';
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var divRect = target.getBoundingClientRect();
+ expect(divRect.width).to.be.equal(100);
+ expect(divRect.height).to.be.equal(20);
+
+ var contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.width).to.be.equal(70);
+ expect(contentRect.height).to.be.equal(30);
+
+ // The tooltip width is 70px, and the tooltip is 14px to the left of the target.
+ // The target div height is 20, the height of the tooltip is 20px, and
+ // the tooltip is centered.
+ expect(contentRect.left).to.be.equal(0 - 70 - 14);
+ expect(contentRect.top).to.be.equal((20 - 30)/2);
+
+ // Also check the math, just in case.
+ expect(contentRect.left).to.be.equal(0 - contentRect.width - tooltip.offset);
+ expect(contentRect.top).to.be.equal((divRect.height - contentRect.height)/2);
+ });
+
+ test('tooltip is fitted correctly if out of bounds', function() {
+ var f = fixture('fitted');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+ target.style.top = 0;
+ target.style.left = 0;
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var contentRect = tooltip.getBoundingClientRect();
+ var divRect = target.getBoundingClientRect();
+
+ // Should be fitted on the left side.
+ expect(contentRect.left).to.be.equal(0);
+ expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
+ });
+
+ test('tooltip is positioned correctly after being dynamically set', function() {
+ var f = fixture('dynamic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ // Skip animations in this test, which means we'll show and hide
+ // the tooltip manually, instead of calling focus and blur.
+
+ // The tooltip is shown because it's a sibling of the target,
+ // but it's positioned incorrectly
+ tooltip.toggleClass('hidden', false, actualTooltip);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.left).to.not.be.equal((100 - 70)/2);
+
+ tooltip.for = 'target';
+
+ // The tooltip needs to hide before it gets repositioned.
+ tooltip.toggleClass('hidden', true, actualTooltip);
+ tooltip.updatePosition();
+ tooltip.toggleClass('hidden', false, actualTooltip);
+ assert.isFalse(isHidden(actualTooltip));
+
+ // The target div width is 100, and the tooltip width is 70, and
+ // it's centered. The height of the target div is 20, and the
+ // tooltip is 14px below.
+ contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.left).to.be.equal((100 - 70)/2);
+ expect(contentRect.top).to.be.equal(20 + 14);
+ });
+
+ test('tooltip is hidden after target is blurred', function(done) {
+ var f = fixture('basic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+ // Simulate but don't actually run the entry animation.
+ tooltip.toggleClass('hidden', false, actualTooltip);
+ tooltip._showing = true;
+ assert.isFalse(isHidden(actualTooltip));
+
+ tooltip.addEventListener('neon-animation-finish', function() {
+ assert.isTrue(isHidden(actualTooltip));
+ done();
+ });
+ MockInteractions.blur(target);
+ });
+
+ test('tooltip unlistens to target on detach', function(done) {
+ var f = fixture('basic');
+ var target = f.querySelector('#target');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ sinon.spy(tooltip, 'show');
+
+ MockInteractions.focus(target);
+ expect(tooltip.show.callCount).to.be.equal(1);
+
+ MockInteractions.focus(target);
+ expect(tooltip.show.callCount).to.be.equal(2);
+
+ f.removeChild(tooltip);
+
+ setTimeout(function() {
+ // No more listener means no more calling show.
+ MockInteractions.focus(target);
+ expect(tooltip.show.callCount).to.be.equal(2);
+ done();
+ }, 200);
+ });
+
+ test('tooltip ignores events in manual-mode', function() {
+ var f = fixture('manual-mode');
+
+ var tooltip = f.querySelector('paper-tooltip');
+ assert.isTrue(tooltip.manualMode);
+
+ tooltip.show();
+ assert.isTrue(tooltip._showing);
+
+ sinon.spy(tooltip, 'hide');
+
+ tooltip.fire('mouseenter');
+
+ var target = f.querySelector('#target');
+ target.dispatchEvent(new CustomEvent('mouseenter'));
+ target.dispatchEvent(new CustomEvent('focus'));
+ target.dispatchEvent(new CustomEvent('mouseleave'));
+ target.dispatchEvent(new CustomEvent('blur'));
+ target.dispatchEvent(new CustomEvent('tap'));
+
+ expect(tooltip.hide.callCount).to.be.equal(0);
+ });
+
+ test('changing manual-mode toggles event listeners', function() {
+ var f = fixture('manual-mode');
+
+ var tooltip = f.querySelector('paper-tooltip');
+ assert.isTrue(tooltip.manualMode);
+
+ sinon.spy(tooltip, '_addListeners');
+ sinon.spy(tooltip, '_removeListeners');
+ expect(tooltip._addListeners.callCount).to.be.equal(0);
+ expect(tooltip._removeListeners.callCount).to.be.equal(0);
+
+ tooltip.manualMode = false;
+ expect(tooltip._addListeners.callCount).to.be.equal(1);
+ expect(tooltip._removeListeners.callCount).to.be.equal(0);
+
+ tooltip.manualMode = true;
+ expect(tooltip._addListeners.callCount).to.be.equal(1);
+ expect(tooltip._removeListeners.callCount).to.be.equal(1);
+ });
+
+ test('changing for= re-targets event listeners', function() {
+ var f = fixture('dynamic');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ sinon.spy(tooltip, '_addListeners');
+ sinon.spy(tooltip, '_removeListeners');
+
+ expect(tooltip._removeListeners.callCount).to.be.equal(0);
+ expect(tooltip._addListeners.callCount).to.be.equal(0);
+
+ tooltip.for = 'target';
+
+ expect(tooltip._removeListeners.callCount).to.be.equal(1);
+ expect(tooltip._addListeners.callCount).to.be.equal(1);
+ });
+ });
+
+ suite('tooltip is inside a custom element', function() {
+ var f, tooltip, target;
+
+ setup(function() {
+ f = fixture('custom');
+ target = f.$.button;
+ tooltip = f.$.buttonTooltip;
+ });
+
+ test('tooltip is shown when target is focused', function() {
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+ });
+
+ test('tooltip is positioned correctly', function() {
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var divRect = target.getBoundingClientRect();
+ expect(divRect.width).to.be.equal(100);
+ expect(divRect.height).to.be.equal(20);
+
+ var contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.width).to.be.equal(70);
+ expect(contentRect.height).to.be.equal(30);
+
+ // The target div width is 100, and the tooltip width is 70, and
+ // it's centered. The height of the target div is 20, and the
+ // tooltip is 14px below.
+ expect(contentRect.left).to.be.equal((100 - 70)/2);
+ expect(contentRect.top).to.be.equal(20 + 14);
+
+ // Also check the math, just in case.
+ expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
+ expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
+ });
+ });
+
+ suite('tooltip is inside a custom element with content', function() {
+ var f, tooltip, target;
+
+ setup(function() {
+ f = fixture('custom-with-content');
+ target = f.$.icon;
+ tooltip = f.$.iconTooltip;
+ });
+
+ test('tooltip is shown when target is focused', function() {
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+ });
+
+ test('tooltip is positioned correctly', function() {
+ var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
+ assert.isTrue(isHidden(actualTooltip));
+
+ MockInteractions.focus(target);
+ assert.isFalse(isHidden(actualTooltip));
+
+ var divRect = target.getBoundingClientRect();
+ expect(divRect.width).to.be.equal(100);
+ expect(divRect.height).to.be.equal(20);
+
+ var contentRect = tooltip.getBoundingClientRect();
+ expect(contentRect.width).to.be.equal(70);
+ expect(contentRect.height).to.be.equal(30);
+
+ // The target div width is 100, and the tooltip width is 70, and
+ // it's centered. The height of the target div is 20, and the
+ // tooltip is 14px below.
+ expect(contentRect.left).to.be.equal((100 - 70)/2);
+ expect(contentRect.top).to.be.equal(20 + 14);
+
+ // Also check the math, just in case.
+ expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
+ expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
+ });
+ });
+
+ suite('a11y', function() {
+ test('has aria role "tooltip"', function() {
+ var f = fixture('basic');
+ var tooltip = f.querySelector('paper-tooltip');
+
+ assert.isTrue(tooltip.getAttribute('role') == 'tooltip');
+ });
+
+ var ignoredRules = ['roleTooltipRequiresDescribedby'];
+
+ a11ySuite('basic', ignoredRules);
+ a11ySuite('fitted', ignoredRules);
+ a11ySuite('no-text', ignoredRules);
+ a11ySuite('dynamic', ignoredRules);
+ a11ySuite('custom', ignoredRules);
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/test/index.html b/catapult/third_party/polymer/components/paper-tooltip/test/index.html
new file mode 100644
index 00000000..fbdc662d
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/test/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!--
+@license
+Copyright (c) 2015 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
+--><html><head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+ <title>paper-tooltip tests</title>
+ <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+ <script>
+ WCT.loadSuites([
+ 'basic.html',
+ 'basic.html?dom=shadow'
+ ]);
+ </script>
+
+
+</body></html>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/test/test-button.html b/catapult/third_party/polymer/components/paper-tooltip/test/test-button.html
new file mode 100644
index 00000000..fa871050
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/test/test-button.html
@@ -0,0 +1,43 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../paper-tooltip.html">
+
+<dom-module id="test-button">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ }
+
+ #button {
+ width: 100px;
+ height: 20px;
+ background-color: red;
+ }
+
+ paper-tooltip {
+ width: 70px;
+ height: 30px;
+ }
+
+ </style>
+
+ <div id="button"></div>
+ <paper-tooltip id="buttonTooltip" for="button">Tooltip text</paper-tooltip>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'test-button'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/paper-tooltip/test/test-icon.html b/catapult/third_party/polymer/components/paper-tooltip/test/test-icon.html
new file mode 100644
index 00000000..13454165
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-tooltip/test/test-icon.html
@@ -0,0 +1,43 @@
+<!--
+@license
+Copyright (c) 2015 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
+-->
+
+<link rel="import" href="../../polymer/polymer.html">
+<link rel="import" href="../paper-tooltip.html">
+
+<dom-module id="test-icon">
+ <template>
+ <style>
+ :host {
+ display: inline-block;
+ }
+
+ #icon {
+ width: 100px;
+ height: 20px;
+ background-color: red;
+ }
+
+ paper-tooltip {
+ width: 70px;
+ height: 30px;
+ }
+
+ </style>
+
+ <div id="icon"></div>
+ <paper-tooltip id="iconTooltip" for="icon"><content></content></paper-tooltip>
+ </template>
+
+ <script>
+ Polymer({
+ is: 'test-icon'
+ });
+ </script>
+</dom-module>
diff --git a/catapult/third_party/polymer/components/polymer/.bower.json b/catapult/third_party/polymer/components/polymer/.bower.json
new file mode 100644
index 00000000..df435e6a
--- /dev/null
+++ b/catapult/third_party/polymer/components/polymer/.bower.json
@@ -0,0 +1,45 @@
+{
+ "name": "polymer",
+ "version": "1.11.3",
+ "main": [
+ "polymer.html",
+ "polymer-mini.html",
+ "polymer-micro.html"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "ignore": [
+ "/.*",
+ "/test/",
+ "/util/",
+ "/explainer/",
+ "gulpfile.js",
+ "PRIMER.md",
+ "CONTRIBUTING.md",
+ "CHANGELOG.md"
+ ],
+ "authors": [
+ "The Polymer Authors (http://polymer.github.io/AUTHORS.txt)"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/Polymer/polymer.git"
+ },
+ "dependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.24"
+ },
+ "devDependencies": {
+ "web-component-tester": "*",
+ "iron-component-page": "polymerElements/iron-component-page#^1.1.6"
+ },
+ "private": true,
+ "homepage": "https://github.com/polymer/polymer",
+ "_release": "1.11.3",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.11.3",
+ "commit": "937021964c80c55ed3a2218d830b2520f8195d43"
+ },
+ "_source": "https://github.com/polymer/polymer.git",
+ "_target": "^1.0.0",
+ "_originalSource": "polymer/polymer"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/polymer/LICENSE.txt b/catapult/third_party/polymer/components/polymer/LICENSE.txt
new file mode 100644
index 00000000..95987bac
--- /dev/null
+++ b/catapult/third_party/polymer/components/polymer/LICENSE.txt
@@ -0,0 +1,27 @@
+// Copyright (c) 2014 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/catapult/third_party/polymer/components/polymer/bower.json b/catapult/third_party/polymer/components/polymer/bower.json
new file mode 100644
index 00000000..a2788405
--- /dev/null
+++ b/catapult/third_party/polymer/components/polymer/bower.json
@@ -0,0 +1,35 @@
+{
+ "name": "polymer",
+ "version": "1.11.3",
+ "main": [
+ "polymer.html",
+ "polymer-mini.html",
+ "polymer-micro.html"
+ ],
+ "license": "http://polymer.github.io/LICENSE.txt",
+ "ignore": [
+ "/.*",
+ "/test/",
+ "/util/",
+ "/explainer/",
+ "gulpfile.js",
+ "PRIMER.md",
+ "CONTRIBUTING.md",
+ "CHANGELOG.md"
+ ],
+ "authors": [
+ "The Polymer Authors (http://polymer.github.io/AUTHORS.txt)"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/Polymer/polymer.git"
+ },
+ "dependencies": {
+ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.24"
+ },
+ "devDependencies": {
+ "web-component-tester": "*",
+ "iron-component-page": "polymerElements/iron-component-page#^1.1.6"
+ },
+ "private": true
+}
diff --git a/catapult/third_party/polymer/components/polymer/build.log b/catapult/third_party/polymer/components/polymer/build.log
new file mode 100644
index 00000000..eef1ebad
--- /dev/null
+++ b/catapult/third_party/polymer/components/polymer/build.log
@@ -0,0 +1,576 @@
+BUILD LOG
+---------
+Build Time: 2018-02-12T15:48:46-0800
+
+NODEJS INFORMATION
+==================
+nodejs: v8.9.4
+dom-serializer: 0.1.0
+estraverse: 4.2.0
+expand-tilde: 1.2.2
+is-windows: 0.2.0
+jade: 0.26.3
+mocha: 2.5.3
+path-is-inside: 1.0.2
+@types/clone: 0.1.30
+@types/chalk: 0.4.31
+@types/express: 4.0.37
+@types/express-serve-static-core: 4.0.53
+@types/freeport: 1.0.21
+@types/mime: 2.0.0
+@types/launchpad: 0.6.0
+@types/parse5: 0.0.31
+@types/which: 1.0.28
+@types/node: 4.2.20
+@types/serve-static: 1.7.32
+accessibility-developer-tools: 2.12.0
+accepts: 1.3.4
+abbrev: 1.1.1
+after: 0.8.2
+acorn-jsx: 3.0.1
+ansi-red: 0.1.1
+ansi-cyan: 0.1.1
+ansi-escapes: 1.4.0
+ajv-keywords: 1.5.1
+adm-zip: 0.4.7
+ajv: 4.11.8
+agent-base: 2.1.1
+ansi-regex: 2.1.1
+ansi-wrap: 0.1.0
+ansi-styles: 2.2.1
+align-text: 0.1.4
+append-field: 0.1.0
+archy: 1.0.0
+arr-diff: 2.0.0
+archiver: 0.14.4
+arr-flatten: 1.1.0
+arr-union: 2.1.0
+argparse: 1.0.9
+array-differ: 1.0.0
+array-slice: 1.0.0
+array-flatten: 1.1.1
+array-each: 1.0.1
+array-uniq: 1.0.3
+array-union: 1.0.2
+arrify: 1.0.1
+asap: 2.0.6
+array-unique: 0.2.1
+assert-plus: 0.2.0
+arraybuffer.slice: 0.0.6
+asn1: 0.2.3
+assertion-error: 1.0.2
+async: 0.9.2
+asynckit: 0.4.0
+aws-sign2: 0.6.0
+babel-polyfill: 6.26.0
+balanced-match: 1.0.0
+babel-runtime: 6.26.0
+babel-code-frame: 6.26.0
+aws4: 1.6.0
+backo2: 1.0.2
+beeper: 1.1.1
+base64-arraybuffer: 0.1.5
+base64-js: 1.2.0
+base64id: 1.0.0
+binaryextensions: 1.0.1
+bcrypt-pbkdf: 1.0.1
+better-assert: 1.0.2
+bl: 1.2.1
+blob: 0.0.4
+brace-expansion: 1.1.8
+boxen: 0.3.1
+bluebird: 2.11.0
+boom: 2.10.1
+body-parser: 1.18.2
+bufferstreams: 1.1.1
+braces: 1.8.5
+caller-path: 0.1.0
+browserstack: 1.5.0
+callsites: 0.2.0
+buffer-crc32: 0.2.13
+callsite: 1.0.0
+capture-stack-trace: 1.0.0
+bytes: 3.0.0
+camelcase: 1.2.1
+busboy: 0.2.14
+chalk: 1.1.3
+center-align: 0.1.3
+chai: 3.5.0
+caseless: 0.11.0
+circular-json: 0.3.3
+cli-width: 2.2.0
+clone: 1.0.2
+clone-stats: 0.0.1
+cleankill: 1.0.3
+co: 4.6.0
+cli-cursor: 1.0.2
+cliui: 2.1.0
+commander: 2.3.0
+code-point-at: 1.1.0
+component-bind: 1.0.0
+combined-stream: 1.0.5
+component-emitter: 1.1.2
+concat-map: 0.0.1
+component-inherit: 0.0.3
+compress-commons: 0.2.9
+concat-stream: 1.6.0
+configstore: 2.1.0
+content-disposition: 0.5.2
+cookie: 0.3.1
+content-type: 1.0.4
+cookie-signature: 1.0.6
+core-util-is: 1.0.2
+ctype: 0.5.3
+crc32-stream: 0.3.4
+cryptiles: 2.0.5
+create-error-class: 3.0.2
+crc: 3.2.1
+dateformat: 2.2.0
+debuglog: 1.0.1
+core-js: 2.5.1
+debug: 2.6.9
+dashdash: 1.14.1
+d: 1.0.0
+decamelize: 1.2.0
+defaults: 1.0.3
+del: 2.2.2
+deep-eql: 0.1.3
+delayed-stream: 1.0.0
+deep-extend: 0.4.2
+deep-is: 0.1.3
+dezalgo: 1.0.3
+deprecated: 0.0.1
+depd: 1.1.1
+dicer: 0.2.5
+destroy: 1.0.4
+dom5: 1.3.6
+diff: 1.4.0
+detect-file: 0.1.0
+domelementtype: 1.3.0
+doctrine: 2.0.0
+domhandler: 2.4.1
+domutils: 1.6.2
+duplexer: 0.1.1
+dot-prop: 3.0.0
+duplexer2: 0.0.2
+ee-first: 1.1.1
+end-of-stream: 0.1.5
+ecc-jsbn: 0.1.1
+encodeurl: 1.0.1
+engine.io: 1.8.4
+engine.io-client: 1.8.4
+es6-map: 0.1.5
+entities: 1.1.1
+es6-iterator: 2.0.1
+engine.io-parser: 1.3.2
+error-ex: 1.3.1
+es6-weak-map: 2.0.2
+escape-string-regexp: 1.0.5
+escape-html: 1.0.3
+es6-promise: 2.3.0
+es5-ext: 0.10.35
+es6-symbol: 3.1.1
+escope: 3.6.0
+espree: 3.5.1
+esrecurse: 4.2.0
+esquery: 1.0.0
+es6-set: 0.1.5
+esutils: 2.0.2
+event-emitter: 0.3.5
+etag: 1.8.1
+eslint-plugin-html: 1.7.0
+exit-hook: 1.1.1
+expand-range: 1.8.2
+extend-shallow: 1.1.4
+expand-brackets: 0.1.5
+express: 4.16.2
+extsprintf: 1.3.0
+extend: 3.0.1
+extglob: 0.3.2
+fast-levenshtein: 2.0.6
+file-entry-cache: 2.0.0
+fd-slicer: 1.0.1
+figures: 1.7.0
+fancy-log: 1.3.0
+filename-regex: 2.0.1
+filled-array: 1.1.0
+finalhandler: 1.1.0
+find-index: 0.1.1
+fill-range: 2.2.3
+flat-cache: 1.3.0
+fined: 1.1.0
+findup-sync: 0.4.3
+first-chunk-stream: 1.0.0
+flagged-respawn: 0.3.2
+for-in: 1.0.2
+for-own: 0.1.5
+forwarded: 0.1.2
+form-data: 2.1.4
+freeport: 1.0.5
+forever-agent: 0.6.1
+fs.realpath: 1.0.0
+generate-object-property: 1.2.0
+gaze: 0.5.2
+generate-function: 2.0.0
+formatio: 1.1.1
+github-url-from-username-repo: 1.0.2
+github-url-from-git: 1.5.0
+glob: 7.1.2
+fresh: 0.5.2
+glob-stream: 3.1.18
+glob-watcher: 0.0.6
+glob2base: 0.0.12
+fs-exists-sync: 0.1.0
+getpass: 0.1.7
+globals: 9.18.0
+global-modules: 0.2.3
+globby: 5.0.0
+glob-base: 0.3.0
+glob-parent: 2.0.0
+globule: 0.1.0
+graceful-fs: 3.0.11
+global-prefix: 0.1.5
+gulp-audit: 1.0.0
+got: 5.7.1
+gulp-eslint: 3.0.1
+glogg: 1.0.0
+gulp-rename: 1.2.2
+gulp-replace: 0.5.4
+gulp-vulcanize: 6.1.0
+growl: 1.9.2
+gulp-util: 3.0.8
+gulplog: 1.0.0
+has-ansi: 2.0.0
+has-gulplog: 0.1.0
+has-cors: 1.1.0
+has-binary: 0.1.7
+hoek: 2.16.3
+has-color: 0.1.7
+htmlparser2: 3.9.2
+homedir-polyfill: 1.0.1
+hydrolysis: 1.25.0
+ignore: 3.3.5
+hawk: 3.1.3
+https-proxy-agent: 1.0.0
+http-signature: 1.1.1
+iconv-lite: 0.4.19
+inflight: 1.0.6
+imurmurhash: 0.1.4
+http-errors: 1.6.2
+inherits: 2.0.3
+ini: 1.3.4
+indexof: 0.0.1
+ipaddr.js: 1.5.2
+is-arrayish: 0.2.1
+inquirer: 0.12.0
+interpret: 1.0.4
+is-dotfile: 1.0.3
+is-buffer: 1.1.5
+is-extendable: 0.1.1
+is-equal-shallow: 0.1.3
+is-absolute: 0.2.6
+is-extglob: 1.0.0
+is-my-json-valid: 2.16.1
+is-npm: 1.0.0
+is-fullwidth-code-point: 1.0.0
+is-number: 2.1.0
+is-finite: 1.0.2
+is-obj: 1.0.1
+is-path-cwd: 1.0.0
+is-path-in-cwd: 1.0.0
+is-path-inside: 1.0.0
+is-glob: 2.0.1
+is-plain-object: 2.0.4
+is-property: 1.0.2
+is-redirect: 1.0.0
+is-resolvable: 1.0.0
+is-posix-bracket: 0.1.1
+is-retry-allowed: 1.1.0
+is-stream: 1.1.0
+is-relative: 0.2.1
+is-primitive: 2.0.0
+is-typedarray: 1.0.0
+is-utf8: 0.2.1
+isarray: 1.0.0
+isexe: 2.0.0
+jju: 1.3.0
+is-unc-path: 0.1.2
+isobject: 2.1.0
+istextorbinary: 1.0.2
+jsbn: 0.1.1
+json-parse-helpfulerror: 1.0.3
+json-schema: 0.2.3
+json-stable-stringify: 1.0.1
+isstream: 0.1.2
+js-tokens: 3.0.2
+jsonpointer: 4.0.1
+json-stringify-safe: 5.0.1
+json3: 3.3.2
+jsonify: 0.0.0
+kind-of: 3.2.2
+lazy-cache: 1.0.4
+latest-version: 2.0.0
+lazypipe: 1.0.1
+jsprim: 1.4.1
+lodash: 1.0.2
+lodash._basecopy: 3.0.1
+levn: 0.3.0
+lazystream: 0.1.0
+launchpad: 0.6.0
+lodash._basetostring: 3.0.1
+liftoff: 2.3.0
+lodash._basevalues: 3.0.0
+lodash._getnative: 3.9.1
+lodash._reescape: 3.0.0
+lodash._isiterateecall: 3.0.9
+lodash._reevaluate: 3.0.0
+lodash._reinterpolate: 3.0.0
+lodash.isarguments: 3.1.0
+lodash.escape: 3.2.0
+lodash.isarray: 3.0.4
+lodash.keys: 3.1.2
+lodash._root: 3.0.1
+lodash.restparam: 3.6.1
+lodash.template: 3.6.2
+lodash.templatesettings: 3.1.1
+lodash.isstring: 4.0.1
+lodash.mapvalues: 4.6.0
+lodash.isplainobject: 4.0.6
+lru-cache: 2.7.3
+longest: 1.0.1
+lolex: 1.3.2
+map-cache: 0.2.2
+lowercase-keys: 1.0.0
+media-typer: 0.3.0
+methods: 1.1.2
+mime-db: 1.30.0
+micromatch: 2.3.11
+minimist: 1.2.0
+minimatch: 3.0.4
+multipipe: 0.1.2
+mime-types: 2.1.17
+merge-descriptors: 1.0.1
+ms: 2.0.0
+natives: 1.1.0
+mute-stream: 0.0.5
+natural-compare: 1.4.0
+multer: 1.3.0
+node-int64: 0.3.3
+node-status-codes: 1.0.0
+negotiator: 0.6.1
+normalize-package-data: 1.0.3
+nomnom: 1.8.1
+nodegit-promise: 4.0.0
+oauth-sign: 0.8.2
+number-is-nan: 1.0.1
+object-assign: 4.1.1
+object-component: 0.0.3
+once: 1.4.0
+object.defaults: 1.1.0
+normalize-path: 2.1.1
+onetime: 1.1.0
+orchestrator: 0.3.8
+on-finished: 2.3.0
+os-homedir: 1.0.2
+object.pick: 1.3.0
+ordered-read-streams: 0.1.0
+options: 0.0.6
+package-json: 2.4.0
+object.omit: 2.0.1
+os-tmpdir: 1.0.2
+osenv: 0.1.4
+optionator: 0.8.2
+parse5: 1.5.1
+parse-filepath: 1.0.1
+parse-glob: 3.0.4
+parseqs: 0.0.5
+path-is-absolute: 1.0.1
+parse-json: 2.2.0
+parse-passwd: 1.0.0
+parseurl: 1.3.2
+parseuri: 0.0.5
+parsejson: 0.0.3
+path-root-regex: 0.1.2
+path-posix: 1.0.0
+pify: 2.3.0
+path-parse: 1.0.5
+pinkie-promise: 2.0.1
+path-to-regexp: 0.1.7
+plugin-error: 0.1.2
+path-root: 0.1.1
+polyclean: 1.3.1
+pinkie: 2.0.4
+prelude-ls: 1.1.2
+pluralize: 1.2.1
+plist: 2.1.0
+process-nextick-args: 1.0.7
+pend: 1.2.0
+prepend-http: 1.0.4
+pretty-hrtime: 1.0.3
+promisify-node: 0.4.0
+preserve: 0.2.0
+proxy-addr: 2.0.2
+punycode: 1.4.1
+progress: 1.1.8
+randomatic: 1.1.7
+q: 1.5.0
+qs: 6.5.1
+range-parser: 1.2.0
+readable-stream: 2.3.3
+read-package-json: 1.3.3
+read-installed: 3.1.5
+read-all-stream: 3.1.0
+readdir-scoped-modules: 1.0.2
+readline2: 1.0.1
+raw-body: 2.3.2
+rechoir: 0.6.2
+regenerator-runtime: 0.10.5
+registry-auth-token: 3.3.1
+regex-cache: 0.4.4
+remove-trailing-separator: 1.1.0
+registry-url: 3.1.0
+repeat-element: 1.1.2
+replacestream: 4.0.3
+replace-ext: 0.0.1
+repeat-string: 1.6.1
+resolve-from: 1.0.1
+require-uncached: 1.0.3
+resolve-dir: 0.1.1
+repeating: 2.0.1
+run-sequence: 1.2.2
+request: 2.79.0
+restore-cursor: 1.0.1
+run-async: 0.1.0
+safe-buffer: 5.1.1
+right-align: 0.1.3
+samsam: 1.1.2
+rx-lite: 3.1.2
+sauce-connect-launcher: 1.2.2
+sequencify: 0.0.7
+serve-static: 1.13.1
+send: 0.11.1
+resolve: 1.4.0
+semver-diff: 2.1.0
+sigmund: 1.0.1
+setprototypeof: 1.0.3
+slide: 1.1.6
+server-destroy: 1.0.1
+sinon-chai: 2.14.0
+serve-waterfall: 1.1.1
+sinon: 1.17.7
+sparkles: 1.0.0
+sntp: 1.0.9
+socket.io-client: 1.7.4
+source-map: 0.5.7
+socket.io-adapter: 0.5.0
+slice-ansi: 0.0.4
+sprintf-js: 1.0.3
+socket.io: 1.7.4
+stream-combiner: 0.2.2
+statuses: 1.3.1
+stream-consume: 0.1.0
+strip-ansi: 3.0.1
+stacky: 1.3.1
+socket.io-parser: 2.3.1
+streamsearch: 0.1.2
+string-width: 1.0.2
+strip-json-comments: 2.0.1
+stringstream: 0.0.5
+string_decoder: 1.0.3
+supports-color: 2.0.0
+through: 2.3.8
+table: 3.8.3
+textextensions: 1.0.2
+test-fixture: 3.0.0-rc.1
+temp: 0.8.3
+through2: 2.0.3
+text-table: 0.2.0
+time-stamp: 1.1.0
+tildify: 1.2.0
+to-array: 0.1.4
+timed-out: 3.1.3
+tar-stream: 1.5.2
+to-iso-string: 0.0.2
+tunnel-agent: 0.4.3
+tough-cookie: 2.3.3
+tryit: 1.0.3
+type-check: 0.3.2
+type-detect: 1.0.0
+tweetnacl: 0.14.5
+type-is: 1.6.15
+typedarray: 0.0.6
+unique-stream: 1.0.0
+uglify-to-browserify: 1.0.2
+unc-path-regex: 0.1.2
+ultron: 1.0.2
+unpipe: 1.0.0
+underscore: 1.6.0
+update-notifier: 0.6.3
+underscore.string: 3.0.3
+unzip-response: 1.0.2
+util-deprecate: 1.0.2
+urijs: 1.16.1
+url-parse-lax: 1.0.0
+util: 0.10.3
+util-extend: 1.0.3
+uuid: 2.0.3
+utils-merge: 1.0.1
+v8flags: 2.1.1
+vinyl: 0.5.3
+vinyl-fs: 0.3.14
+vary: 1.1.2
+vargs: 0.1.0
+wct-local: 2.0.15
+wct-sauce: 1.8.6
+verror: 1.10.0
+wrappy: 1.0.2
+wordwrap: 1.0.0
+widest-line: 1.0.0
+write: 0.2.1
+window-size: 0.1.0
+ws: 1.1.4
+write-file-atomic: 1.3.4
+xdg-basedir: 2.0.0
+wtf-8: 1.0.0
+xtend: 4.0.1
+xmlbuilder: 8.2.2
+xmlhttprequest-ssl: 1.5.3
+xmldom: 0.1.27
+yeast: 0.1.2
+acorn: 5.1.2
+zip-stream: 0.5.2
+yargs: 3.10.0
+yauzl: 2.8.0
+escodegen: 1.9.0
+eslint: 3.19.0
+esprima: 4.0.0
+gulp: 3.9.1
+har-validator: 2.0.6
+js-yaml: 3.10.0
+mime: 1.4.1
+mkdirp: 0.5.1
+nopt: 3.0.6
+node-uuid: 1.4.8
+rc: 1.2.2
+rimraf: 2.6.2
+semver: 4.3.6
+selenium-standalone: 5.11.2
+shelljs: 0.7.8
+sshpk: 1.13.1
+strip-bom: 1.0.0
+uglify-js: 2.8.29
+user-home: 1.1.1
+vulcanize: 1.16.0
+wd: 0.3.12
+which: 1.3.0
+web-component-tester: 4.3.6
+
+REPO REVISIONS
+==============
+polymer-1.x: 62bd3503279b8e954467dc3cb4e739330b9dda10
+
+BUILD HASHES
+============
+polymer-mini.html: 41538c3e880a3db75fc80c61a8b9e5a3e831de7d
+polymer-micro.html: e64e86493573e21587fbed115ddc7187b778718e
+polymer.html: 34bc3eda3e3a732823d953a3f8eccc0910e5de61 \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/polymer/polymer-micro.html b/catapult/third_party/polymer/components/polymer/polymer-micro.html
new file mode 100644
index 00000000..0944f7f6
--- /dev/null
+++ b/catapult/third_party/polymer/components/polymer/polymer-micro.html
@@ -0,0 +1,821 @@
+<!--
+@license
+Copyright (c) 2014 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
+--><script>(function () {
+function resolve() {
+document.body.removeAttribute('unresolved');
+}
+if (window.WebComponents) {
+addEventListener('WebComponentsReady', resolve);
+} else {
+if (document.readyState === 'interactive' || document.readyState === 'complete') {
+resolve();
+} else {
+addEventListener('DOMContentLoaded', resolve);
+}
+}
+}());window.Polymer = {
+Settings: function () {
+var settings = window.Polymer || {};
+if (!settings.noUrlSettings) {
+var parts = location.search.slice(1).split('&');
+for (var i = 0, o; i < parts.length && (o = parts[i]); i++) {
+o = o.split('=');
+o[0] && (settings[o[0]] = o[1] || true);
+}
+}
+settings.wantShadow = settings.dom === 'shadow';
+settings.hasShadow = Boolean(Element.prototype.createShadowRoot);
+settings.nativeShadow = settings.hasShadow && !window.ShadowDOMPolyfill;
+settings.useShadow = settings.wantShadow && settings.hasShadow;
+settings.hasNativeImports = Boolean('import' in document.createElement('link'));
+settings.useNativeImports = settings.hasNativeImports;
+settings.useNativeCustomElements = !window.CustomElements || window.CustomElements.useNative;
+settings.useNativeShadow = settings.useShadow && settings.nativeShadow;
+settings.usePolyfillProto = !settings.useNativeCustomElements && !Object.__proto__;
+settings.hasNativeCSSProperties = !navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/) && window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)');
+settings.useNativeCSSProperties = settings.hasNativeCSSProperties && settings.lazyRegister && settings.useNativeCSSProperties;
+settings.isIE = navigator.userAgent.match('Trident');
+settings.passiveTouchGestures = settings.passiveTouchGestures || false;
+return settings;
+}()
+};(function () {
+var userPolymer = window.Polymer;
+window.Polymer = function (prototype) {
+if (typeof prototype === 'function') {
+prototype = prototype.prototype;
+}
+if (!prototype) {
+prototype = {};
+}
+prototype = desugar(prototype);
+var customCtor = prototype === prototype.constructor.prototype ? prototype.constructor : null;
+var options = { prototype: prototype };
+if (prototype.extends) {
+options.extends = prototype.extends;
+}
+Polymer.telemetry._registrate(prototype);
+var ctor = document.registerElement(prototype.is, options);
+return customCtor || ctor;
+};
+var desugar = function (prototype) {
+var base = Polymer.Base;
+if (prototype.extends) {
+base = Polymer.Base._getExtendedPrototype(prototype.extends);
+}
+prototype = Polymer.Base.chainObject(prototype, base);
+prototype.registerCallback();
+return prototype;
+};
+if (userPolymer) {
+for (var i in userPolymer) {
+Polymer[i] = userPolymer[i];
+}
+}
+Polymer.Class = function (prototype) {
+if (!prototype.factoryImpl) {
+prototype.factoryImpl = function () {
+};
+}
+return desugar(prototype).constructor;
+};
+}());
+Polymer.telemetry = {
+registrations: [],
+_regLog: function (prototype) {
+console.log('[' + prototype.is + ']: registered');
+},
+_registrate: function (prototype) {
+this.registrations.push(prototype);
+Polymer.log && this._regLog(prototype);
+},
+dumpRegistrations: function () {
+this.registrations.forEach(this._regLog);
+}
+};Object.defineProperty(window, 'currentImport', {
+enumerable: true,
+configurable: true,
+get: function () {
+return (document._currentScript || document.currentScript || {}).ownerDocument;
+}
+});Polymer.RenderStatus = {
+_ready: false,
+_callbacks: [],
+whenReady: function (cb) {
+if (this._ready) {
+cb();
+} else {
+this._callbacks.push(cb);
+}
+},
+_makeReady: function () {
+this._ready = true;
+for (var i = 0; i < this._callbacks.length; i++) {
+this._callbacks[i]();
+}
+this._callbacks = [];
+},
+_catchFirstRender: function () {
+requestAnimationFrame(function () {
+Polymer.RenderStatus._makeReady();
+});
+},
+_afterNextRenderQueue: [],
+_waitingNextRender: false,
+afterNextRender: function (element, fn, args) {
+this._watchNextRender();
+this._afterNextRenderQueue.push([
+element,
+fn,
+args
+]);
+},
+hasRendered: function () {
+return this._ready;
+},
+_watchNextRender: function () {
+if (!this._waitingNextRender) {
+this._waitingNextRender = true;
+var fn = function () {
+Polymer.RenderStatus._flushNextRender();
+};
+if (!this._ready) {
+this.whenReady(fn);
+} else {
+requestAnimationFrame(fn);
+}
+}
+},
+_flushNextRender: function () {
+var self = this;
+setTimeout(function () {
+self._flushRenderCallbacks(self._afterNextRenderQueue);
+self._afterNextRenderQueue = [];
+self._waitingNextRender = false;
+});
+},
+_flushRenderCallbacks: function (callbacks) {
+for (var i = 0, h; i < callbacks.length; i++) {
+h = callbacks[i];
+h[1].apply(h[0], h[2] || Polymer.nar);
+}
+}
+};
+if (window.HTMLImports) {
+HTMLImports.whenReady(function () {
+Polymer.RenderStatus._catchFirstRender();
+});
+} else {
+Polymer.RenderStatus._catchFirstRender();
+}
+Polymer.ImportStatus = Polymer.RenderStatus;
+Polymer.ImportStatus.whenLoaded = Polymer.ImportStatus.whenReady;(function () {
+'use strict';
+var settings = Polymer.Settings;
+Polymer.Base = {
+__isPolymerInstance__: true,
+_addFeature: function (feature) {
+this.mixin(this, feature);
+},
+registerCallback: function () {
+if (settings.lazyRegister === 'max') {
+if (this.beforeRegister) {
+this.beforeRegister();
+}
+} else {
+this._desugarBehaviors();
+for (var i = 0, b; i < this.behaviors.length; i++) {
+b = this.behaviors[i];
+if (b.beforeRegister) {
+b.beforeRegister.call(this);
+}
+}
+if (this.beforeRegister) {
+this.beforeRegister();
+}
+}
+this._registerFeatures();
+if (!settings.lazyRegister) {
+this.ensureRegisterFinished();
+}
+},
+createdCallback: function () {
+if (settings.disableUpgradeEnabled) {
+if (this.hasAttribute('disable-upgrade')) {
+this._propertySetter = disableUpgradePropertySetter;
+this._configValue = null;
+this.__data__ = {};
+return;
+} else {
+this.__hasInitialized = true;
+}
+}
+this.__initialize();
+},
+__initialize: function () {
+if (!this.__hasRegisterFinished) {
+this._ensureRegisterFinished(this.__proto__);
+}
+Polymer.telemetry.instanceCount++;
+this.root = this;
+for (var i = 0, b; i < this.behaviors.length; i++) {
+b = this.behaviors[i];
+if (b.created) {
+b.created.call(this);
+}
+}
+if (this.created) {
+this.created();
+}
+this._initFeatures();
+},
+ensureRegisterFinished: function () {
+this._ensureRegisterFinished(this);
+},
+_ensureRegisterFinished: function (proto) {
+if (proto.__hasRegisterFinished !== proto.is || !proto.is) {
+if (settings.lazyRegister === 'max') {
+proto._desugarBehaviors();
+for (var i = 0, b; i < proto.behaviors.length; i++) {
+b = proto.behaviors[i];
+if (b.beforeRegister) {
+b.beforeRegister.call(proto);
+}
+}
+}
+proto.__hasRegisterFinished = proto.is;
+if (proto._finishRegisterFeatures) {
+proto._finishRegisterFeatures();
+}
+for (var j = 0, pb; j < proto.behaviors.length; j++) {
+pb = proto.behaviors[j];
+if (pb.registered) {
+pb.registered.call(proto);
+}
+}
+if (proto.registered) {
+proto.registered();
+}
+if (settings.usePolyfillProto && proto !== this) {
+proto.extend(this, proto);
+}
+}
+},
+attachedCallback: function () {
+var self = this;
+Polymer.RenderStatus.whenReady(function () {
+self.isAttached = true;
+for (var i = 0, b; i < self.behaviors.length; i++) {
+b = self.behaviors[i];
+if (b.attached) {
+b.attached.call(self);
+}
+}
+if (self.attached) {
+self.attached();
+}
+});
+},
+detachedCallback: function () {
+var self = this;
+Polymer.RenderStatus.whenReady(function () {
+self.isAttached = false;
+for (var i = 0, b; i < self.behaviors.length; i++) {
+b = self.behaviors[i];
+if (b.detached) {
+b.detached.call(self);
+}
+}
+if (self.detached) {
+self.detached();
+}
+});
+},
+attributeChangedCallback: function (name, oldValue, newValue) {
+this._attributeChangedImpl(name);
+for (var i = 0, b; i < this.behaviors.length; i++) {
+b = this.behaviors[i];
+if (b.attributeChanged) {
+b.attributeChanged.call(this, name, oldValue, newValue);
+}
+}
+if (this.attributeChanged) {
+this.attributeChanged(name, oldValue, newValue);
+}
+},
+_attributeChangedImpl: function (name) {
+this._setAttributeToProperty(this, name);
+},
+extend: function (target, source) {
+if (target && source) {
+var n$ = Object.getOwnPropertyNames(source);
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) {
+this.copyOwnProperty(n, source, target);
+}
+}
+return target || source;
+},
+mixin: function (target, source) {
+for (var i in source) {
+target[i] = source[i];
+}
+return target;
+},
+copyOwnProperty: function (name, source, target) {
+var pd = Object.getOwnPropertyDescriptor(source, name);
+if (pd) {
+Object.defineProperty(target, name, pd);
+}
+},
+_logger: function (level, args) {
+if (args.length === 1 && Array.isArray(args[0])) {
+args = args[0];
+}
+switch (level) {
+case 'log':
+case 'warn':
+case 'error':
+console[level].apply(console, args);
+break;
+}
+},
+_log: function () {
+var args = Array.prototype.slice.call(arguments, 0);
+this._logger('log', args);
+},
+_warn: function () {
+var args = Array.prototype.slice.call(arguments, 0);
+this._logger('warn', args);
+},
+_error: function () {
+var args = Array.prototype.slice.call(arguments, 0);
+this._logger('error', args);
+},
+_logf: function () {
+return this._logPrefix.concat(this.is).concat(Array.prototype.slice.call(arguments, 0));
+}
+};
+Polymer.Base._logPrefix = function () {
+var color = window.chrome && !/edge/i.test(navigator.userAgent) || /firefox/i.test(navigator.userAgent);
+return color ? [
+'%c[%s::%s]:',
+'font-weight: bold; background-color:#EEEE00;'
+] : ['[%s::%s]:'];
+}();
+Polymer.Base.chainObject = function (object, inherited) {
+if (object && inherited && object !== inherited) {
+if (!Object.__proto__) {
+object = Polymer.Base.extend(Object.create(inherited), object);
+}
+object.__proto__ = inherited;
+}
+return object;
+};
+Polymer.Base = Polymer.Base.chainObject(Polymer.Base, HTMLElement.prototype);
+Polymer.BaseDescriptors = {};
+var disableUpgradePropertySetter;
+if (settings.disableUpgradeEnabled) {
+disableUpgradePropertySetter = function (property, value) {
+this.__data__[property] = value;
+};
+var origAttributeChangedCallback = Polymer.Base.attributeChangedCallback;
+Polymer.Base.attributeChangedCallback = function (name, oldValue, newValue) {
+if (!this.__hasInitialized && name === 'disable-upgrade') {
+this.__hasInitialized = true;
+this._propertySetter = Polymer.Bind._modelApi._propertySetter;
+this._configValue = Polymer.Base._configValue;
+this.__initialize();
+}
+origAttributeChangedCallback.call(this, name, oldValue, newValue);
+};
+}
+if (window.CustomElements) {
+Polymer.instanceof = CustomElements.instanceof;
+} else {
+Polymer.instanceof = function (obj, ctor) {
+return obj instanceof ctor;
+};
+}
+Polymer.isInstance = function (obj) {
+return Boolean(obj && obj.__isPolymerInstance__);
+};
+Polymer.telemetry.instanceCount = 0;
+}());(function () {
+var modules = {};
+var lcModules = {};
+var findModule = function (id) {
+return modules[id] || lcModules[id.toLowerCase()];
+};
+var DomModule = function () {
+return document.createElement('dom-module');
+};
+DomModule.prototype = Object.create(HTMLElement.prototype);
+Polymer.Base.mixin(DomModule.prototype, {
+createdCallback: function () {
+this.register();
+},
+register: function (id) {
+id = id || this.id || this.getAttribute('name') || this.getAttribute('is');
+if (id) {
+this.id = id;
+modules[id] = this;
+lcModules[id.toLowerCase()] = this;
+}
+},
+import: function (id, selector) {
+if (id) {
+var m = findModule(id);
+if (!m) {
+forceDomModulesUpgrade();
+m = findModule(id);
+}
+if (m && selector) {
+m = m.querySelector(selector);
+}
+return m;
+}
+}
+});
+Object.defineProperty(DomModule.prototype, 'constructor', {
+value: DomModule,
+configurable: true,
+writable: true
+});
+var cePolyfill = window.CustomElements && !CustomElements.useNative;
+document.registerElement('dom-module', DomModule);
+function forceDomModulesUpgrade() {
+if (cePolyfill) {
+var script = document._currentScript || document.currentScript;
+var doc = script && script.ownerDocument || document;
+var modules = doc.querySelectorAll('dom-module');
+for (var i = modules.length - 1, m; i >= 0 && (m = modules[i]); i--) {
+if (m.__upgraded__) {
+return;
+} else {
+CustomElements.upgrade(m);
+}
+}
+}
+}
+}());Polymer.Base._addFeature({
+_prepIs: function () {
+if (!this.is) {
+var module = (document._currentScript || document.currentScript).parentNode;
+if (module.localName === 'dom-module') {
+var id = module.id || module.getAttribute('name') || module.getAttribute('is');
+this.is = id;
+}
+}
+if (this.is) {
+this.is = this.is.toLowerCase();
+}
+}
+});Polymer.Base._addFeature({
+behaviors: [],
+_desugarBehaviors: function () {
+if (this.behaviors.length) {
+this.behaviors = this._desugarSomeBehaviors(this.behaviors);
+}
+},
+_desugarSomeBehaviors: function (behaviors) {
+var behaviorSet = [];
+behaviors = this._flattenBehaviorsList(behaviors);
+for (var i = behaviors.length - 1; i >= 0; i--) {
+var b = behaviors[i];
+if (behaviorSet.indexOf(b) === -1) {
+this._mixinBehavior(b);
+behaviorSet.unshift(b);
+}
+}
+return behaviorSet;
+},
+_flattenBehaviorsList: function (behaviors) {
+var flat = [];
+for (var i = 0; i < behaviors.length; i++) {
+var b = behaviors[i];
+if (b instanceof Array) {
+flat = flat.concat(this._flattenBehaviorsList(b));
+} else if (b) {
+flat.push(b);
+} else {
+this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for missing or 404 import'));
+}
+}
+return flat;
+},
+_mixinBehavior: function (b) {
+var n$ = Object.getOwnPropertyNames(b);
+var useAssignment = b._noAccessors;
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) {
+if (!Polymer.Base._behaviorProperties[n] && !this.hasOwnProperty(n)) {
+if (useAssignment) {
+this[n] = b[n];
+} else {
+this.copyOwnProperty(n, b, this);
+}
+}
+}
+},
+_prepBehaviors: function () {
+this._prepFlattenedBehaviors(this.behaviors);
+},
+_prepFlattenedBehaviors: function (behaviors) {
+for (var i = 0, l = behaviors.length; i < l; i++) {
+this._prepBehavior(behaviors[i]);
+}
+this._prepBehavior(this);
+},
+_marshalBehaviors: function () {
+for (var i = 0; i < this.behaviors.length; i++) {
+this._marshalBehavior(this.behaviors[i]);
+}
+this._marshalBehavior(this);
+}
+});
+Polymer.Base._behaviorProperties = {
+hostAttributes: true,
+beforeRegister: true,
+registered: true,
+properties: true,
+observers: true,
+listeners: true,
+created: true,
+attached: true,
+detached: true,
+attributeChanged: true,
+ready: true,
+_noAccessors: true
+};Polymer.Base._addFeature({
+_getExtendedPrototype: function (tag) {
+return this._getExtendedNativePrototype(tag);
+},
+_nativePrototypes: {},
+_getExtendedNativePrototype: function (tag) {
+var p = this._nativePrototypes[tag];
+if (!p) {
+p = Object.create(this.getNativePrototype(tag));
+var p$ = Object.getOwnPropertyNames(Polymer.Base);
+for (var i = 0, n; i < p$.length && (n = p$[i]); i++) {
+if (!Polymer.BaseDescriptors[n]) {
+p[n] = Polymer.Base[n];
+}
+}
+Object.defineProperties(p, Polymer.BaseDescriptors);
+this._nativePrototypes[tag] = p;
+}
+return p;
+},
+getNativePrototype: function (tag) {
+return Object.getPrototypeOf(document.createElement(tag));
+}
+});Polymer.Base._addFeature({
+_prepConstructor: function () {
+this._factoryArgs = this.extends ? [
+this.extends,
+this.is
+] : [this.is];
+var ctor = function () {
+return this._factory(arguments);
+};
+if (this.hasOwnProperty('extends')) {
+ctor.extends = this.extends;
+}
+Object.defineProperty(this, 'constructor', {
+value: ctor,
+writable: true,
+configurable: true
+});
+ctor.prototype = this;
+},
+_factory: function (args) {
+var elt = document.createElement.apply(document, this._factoryArgs);
+if (this.factoryImpl) {
+this.factoryImpl.apply(elt, args);
+}
+return elt;
+}
+});Polymer.nob = Object.create(null);
+Polymer.Base._addFeature({
+getPropertyInfo: function (property) {
+var info = this._getPropertyInfo(property, this.properties);
+if (!info) {
+for (var i = 0; i < this.behaviors.length; i++) {
+info = this._getPropertyInfo(property, this.behaviors[i].properties);
+if (info) {
+return info;
+}
+}
+}
+return info || Polymer.nob;
+},
+_getPropertyInfo: function (property, properties) {
+var p = properties && properties[property];
+if (typeof p === 'function') {
+p = properties[property] = { type: p };
+}
+if (p) {
+p.defined = true;
+}
+return p;
+},
+_prepPropertyInfo: function () {
+this._propertyInfo = {};
+for (var i = 0; i < this.behaviors.length; i++) {
+this._addPropertyInfo(this._propertyInfo, this.behaviors[i].properties);
+}
+this._addPropertyInfo(this._propertyInfo, this.properties);
+this._addPropertyInfo(this._propertyInfo, this._propertyEffects);
+},
+_addPropertyInfo: function (target, source) {
+if (source) {
+var t, s;
+for (var i in source) {
+t = target[i];
+s = source[i];
+if (i[0] === '_' && !s.readOnly) {
+continue;
+}
+if (!target[i]) {
+target[i] = {
+type: typeof s === 'function' ? s : s.type,
+readOnly: s.readOnly,
+attribute: Polymer.CaseMap.camelToDashCase(i)
+};
+} else {
+if (!t.type) {
+t.type = s.type;
+}
+if (!t.readOnly) {
+t.readOnly = s.readOnly;
+}
+}
+}
+}
+}
+});
+(function () {
+var propertiesDesc = {
+configurable: true,
+writable: true,
+enumerable: true,
+value: {}
+};
+Polymer.BaseDescriptors.properties = propertiesDesc;
+Object.defineProperty(Polymer.Base, 'properties', propertiesDesc);
+}());Polymer.CaseMap = {
+_caseMap: {},
+_rx: {
+dashToCamel: /-[a-z]/g,
+camelToDash: /([A-Z])/g
+},
+dashToCamelCase: function (dash) {
+return this._caseMap[dash] || (this._caseMap[dash] = dash.indexOf('-') < 0 ? dash : dash.replace(this._rx.dashToCamel, function (m) {
+return m[1].toUpperCase();
+}));
+},
+camelToDashCase: function (camel) {
+return this._caseMap[camel] || (this._caseMap[camel] = camel.replace(this._rx.camelToDash, '-$1').toLowerCase());
+}
+};Polymer.Base._addFeature({
+_addHostAttributes: function (attributes) {
+if (!this._aggregatedAttributes) {
+this._aggregatedAttributes = {};
+}
+if (attributes) {
+this.mixin(this._aggregatedAttributes, attributes);
+}
+},
+_marshalHostAttributes: function () {
+if (this._aggregatedAttributes) {
+this._applyAttributes(this, this._aggregatedAttributes);
+}
+},
+_applyAttributes: function (node, attr$) {
+for (var n in attr$) {
+if (!this.hasAttribute(n) && n !== 'class') {
+var v = attr$[n];
+this.serializeValueToAttribute(v, n, this);
+}
+}
+},
+_marshalAttributes: function () {
+this._takeAttributesToModel(this);
+},
+_takeAttributesToModel: function (model) {
+if (this.hasAttributes()) {
+for (var i in this._propertyInfo) {
+var info = this._propertyInfo[i];
+if (this.hasAttribute(info.attribute)) {
+this._setAttributeToProperty(model, info.attribute, i, info);
+}
+}
+}
+},
+_setAttributeToProperty: function (model, attribute, property, info) {
+if (!this._serializing) {
+property = property || Polymer.CaseMap.dashToCamelCase(attribute);
+info = info || this._propertyInfo && this._propertyInfo[property];
+if (info && !info.readOnly) {
+var v = this.getAttribute(attribute);
+model[property] = this.deserialize(v, info.type);
+}
+}
+},
+_serializing: false,
+reflectPropertyToAttribute: function (property, attribute, value) {
+this._serializing = true;
+value = value === undefined ? this[property] : value;
+this.serializeValueToAttribute(value, attribute || Polymer.CaseMap.camelToDashCase(property));
+this._serializing = false;
+},
+serializeValueToAttribute: function (value, attribute, node) {
+var str = this.serialize(value);
+node = node || this;
+if (str === undefined) {
+node.removeAttribute(attribute);
+} else {
+node.setAttribute(attribute, str);
+}
+},
+deserialize: function (value, type) {
+switch (type) {
+case Number:
+value = Number(value);
+break;
+case Boolean:
+value = value != null;
+break;
+case Object:
+try {
+value = JSON.parse(value);
+} catch (x) {
+}
+break;
+case Array:
+try {
+value = JSON.parse(value);
+} catch (x) {
+value = null;
+console.warn('Polymer::Attributes: couldn`t decode Array as JSON');
+}
+break;
+case Date:
+value = new Date(value);
+break;
+case String:
+default:
+break;
+}
+return value;
+},
+serialize: function (value) {
+switch (typeof value) {
+case 'boolean':
+return value ? '' : undefined;
+case 'object':
+if (value instanceof Date) {
+return value.toString();
+} else if (value) {
+try {
+return JSON.stringify(value);
+} catch (x) {
+return '';
+}
+}
+default:
+return value != null ? value : undefined;
+}
+}
+});Polymer.version = "1.11.3";Polymer.Base._addFeature({
+_registerFeatures: function () {
+this._prepIs();
+this._prepBehaviors();
+this._prepConstructor();
+this._prepPropertyInfo();
+},
+_prepBehavior: function (b) {
+this._addHostAttributes(b.hostAttributes);
+},
+_marshalBehavior: function (b) {
+},
+_initFeatures: function () {
+this._marshalHostAttributes();
+this._marshalBehaviors();
+}
+});</script>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catapult/third_party/polymer/components/polymer/polymer-mini.html b/catapult/third_party/polymer/components/polymer/polymer-mini.html
new file mode 100644
index 00000000..ac250d72
--- /dev/null
+++ b/catapult/third_party/polymer/components/polymer/polymer-mini.html
@@ -0,0 +1,2246 @@
+<!--
+@license
+Copyright (c) 2014 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
+--><link rel="import" href="polymer-micro.html"><script>(function () {
+function resolveCss(cssText, ownerDocument) {
+return cssText.replace(CSS_URL_RX, function (m, pre, url, post) {
+return pre + '\'' + resolve(url.replace(/["']/g, ''), ownerDocument) + '\'' + post;
+});
+}
+function resolveAttrs(element, ownerDocument) {
+for (var name in URL_ATTRS) {
+var a$ = URL_ATTRS[name];
+for (var i = 0, l = a$.length, a, at, v; i < l && (a = a$[i]); i++) {
+if (name === '*' || element.localName === name) {
+at = element.attributes[a];
+v = at && at.value;
+if (v && v.search(BINDING_RX) < 0) {
+at.value = a === 'style' ? resolveCss(v, ownerDocument) : resolve(v, ownerDocument);
+}
+}
+}
+}
+}
+function resolve(url, ownerDocument) {
+if (url && ABS_URL.test(url)) {
+return url;
+}
+var resolver = getUrlResolver(ownerDocument);
+resolver.href = url;
+return resolver.href || url;
+}
+var tempDoc;
+var tempDocBase;
+function resolveUrl(url, baseUri) {
+if (!tempDoc) {
+tempDoc = document.implementation.createHTMLDocument('temp');
+tempDocBase = tempDoc.createElement('base');
+tempDoc.head.appendChild(tempDocBase);
+}
+tempDocBase.href = baseUri;
+return resolve(url, tempDoc);
+}
+function getUrlResolver(ownerDocument) {
+return ownerDocument.body.__urlResolver || (ownerDocument.body.__urlResolver = ownerDocument.createElement('a'));
+}
+function pathFromUrl(url) {
+return url.substring(0, url.lastIndexOf('/') + 1);
+}
+var CSS_URL_RX = /(url\()([^)]*)(\))/g;
+var URL_ATTRS = {
+'*': [
+'href',
+'src',
+'style',
+'url'
+],
+form: ['action']
+};
+var ABS_URL = /(^\/)|(^#)|(^[\w-\d]*:)/;
+var BINDING_RX = /\{\{|\[\[/;
+Polymer.ResolveUrl = {
+resolveCss: resolveCss,
+resolveAttrs: resolveAttrs,
+resolveUrl: resolveUrl,
+pathFromUrl: pathFromUrl
+};
+Polymer.rootPath = Polymer.Settings.rootPath || pathFromUrl(document.baseURI || window.location.href);
+}());Polymer.Base._addFeature({
+_prepTemplate: function () {
+var module;
+if (this._template === undefined) {
+module = Polymer.DomModule.import(this.is);
+this._template = module && module.querySelector('template');
+}
+if (module) {
+var assetPath = module.getAttribute('assetpath') || '';
+var importURL = Polymer.ResolveUrl.resolveUrl(assetPath, module.ownerDocument.baseURI);
+this._importPath = Polymer.ResolveUrl.pathFromUrl(importURL);
+} else {
+this._importPath = '';
+}
+if (this._template && this._template.hasAttribute('is')) {
+this._warn(this._logf('_prepTemplate', 'top-level Polymer template ' + 'must not be a type-extension, found', this._template, 'Move inside simple <template>.'));
+}
+if (this._template && !this._template.content && window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
+HTMLTemplateElement.decorate(this._template);
+}
+},
+_stampTemplate: function () {
+if (this._template) {
+this.root = this.instanceTemplate(this._template);
+}
+},
+instanceTemplate: function (template) {
+var dom = document.importNode(template._content || template.content, true);
+return dom;
+}
+});(function () {
+var baseAttachedCallback = Polymer.Base.attachedCallback;
+var baseDetachedCallback = Polymer.Base.detachedCallback;
+Polymer.Base._addFeature({
+_hostStack: [],
+ready: function () {
+},
+_registerHost: function (host) {
+this.dataHost = host = host || Polymer.Base._hostStack[Polymer.Base._hostStack.length - 1];
+if (host && host._clients) {
+host._clients.push(this);
+}
+this._clients = null;
+this._clientsReadied = false;
+},
+_beginHosting: function () {
+Polymer.Base._hostStack.push(this);
+if (!this._clients) {
+this._clients = [];
+}
+},
+_endHosting: function () {
+Polymer.Base._hostStack.pop();
+},
+_tryReady: function () {
+this._readied = false;
+if (this._canReady()) {
+this._ready();
+}
+},
+_canReady: function () {
+return !this.dataHost || this.dataHost._clientsReadied;
+},
+_ready: function () {
+this._beforeClientsReady();
+if (this._template) {
+this._setupRoot();
+this._readyClients();
+}
+this._clientsReadied = true;
+this._clients = null;
+this._afterClientsReady();
+this._readySelf();
+},
+_readyClients: function () {
+this._beginDistribute();
+var c$ = this._clients;
+if (c$) {
+for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
+c._ready();
+}
+}
+this._finishDistribute();
+},
+_readySelf: function () {
+for (var i = 0, b; i < this.behaviors.length; i++) {
+b = this.behaviors[i];
+if (b.ready) {
+b.ready.call(this);
+}
+}
+if (this.ready) {
+this.ready();
+}
+this._readied = true;
+if (this._attachedPending) {
+this._attachedPending = false;
+this.attachedCallback();
+}
+},
+_beforeClientsReady: function () {
+},
+_afterClientsReady: function () {
+},
+_beforeAttached: function () {
+},
+attachedCallback: function () {
+if (this._readied) {
+this._beforeAttached();
+baseAttachedCallback.call(this);
+} else {
+this._attachedPending = true;
+}
+},
+detachedCallback: function () {
+if (this._readied) {
+baseDetachedCallback.call(this);
+} else {
+this._attachedPending = false;
+}
+}
+});
+}());Polymer.ArraySplice = function () {
+function newSplice(index, removed, addedCount) {
+return {
+index: index,
+removed: removed,
+addedCount: addedCount
+};
+}
+var EDIT_LEAVE = 0;
+var EDIT_UPDATE = 1;
+var EDIT_ADD = 2;
+var EDIT_DELETE = 3;
+function ArraySplice() {
+}
+ArraySplice.prototype = {
+calcEditDistances: function (current, currentStart, currentEnd, old, oldStart, oldEnd) {
+var rowCount = oldEnd - oldStart + 1;
+var columnCount = currentEnd - currentStart + 1;
+var distances = new Array(rowCount);
+for (var i = 0; i < rowCount; i++) {
+distances[i] = new Array(columnCount);
+distances[i][0] = i;
+}
+for (var j = 0; j < columnCount; j++)
+distances[0][j] = j;
+for (i = 1; i < rowCount; i++) {
+for (j = 1; j < columnCount; j++) {
+if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
+distances[i][j] = distances[i - 1][j - 1];
+else {
+var north = distances[i - 1][j] + 1;
+var west = distances[i][j - 1] + 1;
+distances[i][j] = north < west ? north : west;
+}
+}
+}
+return distances;
+},
+spliceOperationsFromEditDistances: function (distances) {
+var i = distances.length - 1;
+var j = distances[0].length - 1;
+var current = distances[i][j];
+var edits = [];
+while (i > 0 || j > 0) {
+if (i == 0) {
+edits.push(EDIT_ADD);
+j--;
+continue;
+}
+if (j == 0) {
+edits.push(EDIT_DELETE);
+i--;
+continue;
+}
+var northWest = distances[i - 1][j - 1];
+var west = distances[i - 1][j];
+var north = distances[i][j - 1];
+var min;
+if (west < north)
+min = west < northWest ? west : northWest;
+else
+min = north < northWest ? north : northWest;
+if (min == northWest) {
+if (northWest == current) {
+edits.push(EDIT_LEAVE);
+} else {
+edits.push(EDIT_UPDATE);
+current = northWest;
+}
+i--;
+j--;
+} else if (min == west) {
+edits.push(EDIT_DELETE);
+i--;
+current = west;
+} else {
+edits.push(EDIT_ADD);
+j--;
+current = north;
+}
+}
+edits.reverse();
+return edits;
+},
+calcSplices: function (current, currentStart, currentEnd, old, oldStart, oldEnd) {
+var prefixCount = 0;
+var suffixCount = 0;
+var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+if (currentStart == 0 && oldStart == 0)
+prefixCount = this.sharedPrefix(current, old, minLength);
+if (currentEnd == current.length && oldEnd == old.length)
+suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+currentStart += prefixCount;
+oldStart += prefixCount;
+currentEnd -= suffixCount;
+oldEnd -= suffixCount;
+if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
+return [];
+if (currentStart == currentEnd) {
+var splice = newSplice(currentStart, [], 0);
+while (oldStart < oldEnd)
+splice.removed.push(old[oldStart++]);
+return [splice];
+} else if (oldStart == oldEnd)
+return [newSplice(currentStart, [], currentEnd - currentStart)];
+var ops = this.spliceOperationsFromEditDistances(this.calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
+splice = undefined;
+var splices = [];
+var index = currentStart;
+var oldIndex = oldStart;
+for (var i = 0; i < ops.length; i++) {
+switch (ops[i]) {
+case EDIT_LEAVE:
+if (splice) {
+splices.push(splice);
+splice = undefined;
+}
+index++;
+oldIndex++;
+break;
+case EDIT_UPDATE:
+if (!splice)
+splice = newSplice(index, [], 0);
+splice.addedCount++;
+index++;
+splice.removed.push(old[oldIndex]);
+oldIndex++;
+break;
+case EDIT_ADD:
+if (!splice)
+splice = newSplice(index, [], 0);
+splice.addedCount++;
+index++;
+break;
+case EDIT_DELETE:
+if (!splice)
+splice = newSplice(index, [], 0);
+splice.removed.push(old[oldIndex]);
+oldIndex++;
+break;
+}
+}
+if (splice) {
+splices.push(splice);
+}
+return splices;
+},
+sharedPrefix: function (current, old, searchLength) {
+for (var i = 0; i < searchLength; i++)
+if (!this.equals(current[i], old[i]))
+return i;
+return searchLength;
+},
+sharedSuffix: function (current, old, searchLength) {
+var index1 = current.length;
+var index2 = old.length;
+var count = 0;
+while (count < searchLength && this.equals(current[--index1], old[--index2]))
+count++;
+return count;
+},
+calculateSplices: function (current, previous) {
+return this.calcSplices(current, 0, current.length, previous, 0, previous.length);
+},
+equals: function (currentValue, previousValue) {
+return currentValue === previousValue;
+}
+};
+return new ArraySplice();
+}();Polymer.domInnerHTML = function () {
+var escapeAttrRegExp = /[&\u00A0"]/g;
+var escapeDataRegExp = /[&\u00A0<>]/g;
+function escapeReplace(c) {
+switch (c) {
+case '&':
+return '&amp;';
+case '<':
+return '&lt;';
+case '>':
+return '&gt;';
+case '"':
+return '&quot;';
+case '\xA0':
+return '&nbsp;';
+}
+}
+function escapeAttr(s) {
+return s.replace(escapeAttrRegExp, escapeReplace);
+}
+function escapeData(s) {
+return s.replace(escapeDataRegExp, escapeReplace);
+}
+function makeSet(arr) {
+var set = {};
+for (var i = 0; i < arr.length; i++) {
+set[arr[i]] = true;
+}
+return set;
+}
+var voidElements = makeSet([
+'area',
+'base',
+'br',
+'col',
+'command',
+'embed',
+'hr',
+'img',
+'input',
+'keygen',
+'link',
+'meta',
+'param',
+'source',
+'track',
+'wbr'
+]);
+var plaintextParents = makeSet([
+'style',
+'script',
+'xmp',
+'iframe',
+'noembed',
+'noframes',
+'plaintext',
+'noscript'
+]);
+function getOuterHTML(node, parentNode, composed) {
+switch (node.nodeType) {
+case Node.ELEMENT_NODE:
+var tagName = node.localName;
+var s = '<' + tagName;
+var attrs = node.attributes;
+for (var i = 0, attr; attr = attrs[i]; i++) {
+s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
+}
+s += '>';
+if (voidElements[tagName]) {
+return s;
+}
+return s + getInnerHTML(node, composed) + '</' + tagName + '>';
+case Node.TEXT_NODE:
+var data = node.data;
+if (parentNode && plaintextParents[parentNode.localName]) {
+return data;
+}
+return escapeData(data);
+case Node.COMMENT_NODE:
+return '<!--' + node.data + '-->';
+default:
+console.error(node);
+throw new Error('not implemented');
+}
+}
+function getInnerHTML(node, composed) {
+if (node instanceof HTMLTemplateElement)
+node = node.content;
+var s = '';
+var c$ = Polymer.dom(node).childNodes;
+for (var i = 0, l = c$.length, child; i < l && (child = c$[i]); i++) {
+s += getOuterHTML(child, node, composed);
+}
+return s;
+}
+return { getInnerHTML: getInnerHTML };
+}();(function () {
+'use strict';
+var nativeInsertBefore = Element.prototype.insertBefore;
+var nativeAppendChild = Element.prototype.appendChild;
+var nativeRemoveChild = Element.prototype.removeChild;
+Polymer.TreeApi = {
+arrayCopyChildNodes: function (parent) {
+var copy = [], i = 0;
+for (var n = parent.firstChild; n; n = n.nextSibling) {
+copy[i++] = n;
+}
+return copy;
+},
+arrayCopyChildren: function (parent) {
+var copy = [], i = 0;
+for (var n = parent.firstElementChild; n; n = n.nextElementSibling) {
+copy[i++] = n;
+}
+return copy;
+},
+arrayCopy: function (a$) {
+var l = a$.length;
+var copy = new Array(l);
+for (var i = 0; i < l; i++) {
+copy[i] = a$[i];
+}
+return copy;
+}
+};
+Polymer.TreeApi.Logical = {
+hasParentNode: function (node) {
+return Boolean(node.__dom && node.__dom.parentNode);
+},
+hasChildNodes: function (node) {
+return Boolean(node.__dom && node.__dom.childNodes !== undefined);
+},
+getChildNodes: function (node) {
+return this.hasChildNodes(node) ? this._getChildNodes(node) : node.childNodes;
+},
+_getChildNodes: function (node) {
+if (!node.__dom.childNodes) {
+node.__dom.childNodes = [];
+for (var n = node.__dom.firstChild; n; n = n.__dom.nextSibling) {
+node.__dom.childNodes.push(n);
+}
+}
+return node.__dom.childNodes;
+},
+getParentNode: function (node) {
+return node.__dom && node.__dom.parentNode !== undefined ? node.__dom.parentNode : node.parentNode;
+},
+getFirstChild: function (node) {
+return node.__dom && node.__dom.firstChild !== undefined ? node.__dom.firstChild : node.firstChild;
+},
+getLastChild: function (node) {
+return node.__dom && node.__dom.lastChild !== undefined ? node.__dom.lastChild : node.lastChild;
+},
+getNextSibling: function (node) {
+return node.__dom && node.__dom.nextSibling !== undefined ? node.__dom.nextSibling : node.nextSibling;
+},
+getPreviousSibling: function (node) {
+return node.__dom && node.__dom.previousSibling !== undefined ? node.__dom.previousSibling : node.previousSibling;
+},
+getFirstElementChild: function (node) {
+return node.__dom && node.__dom.firstChild !== undefined ? this._getFirstElementChild(node) : node.firstElementChild;
+},
+_getFirstElementChild: function (node) {
+var n = node.__dom.firstChild;
+while (n && n.nodeType !== Node.ELEMENT_NODE) {
+n = n.__dom.nextSibling;
+}
+return n;
+},
+getLastElementChild: function (node) {
+return node.__dom && node.__dom.lastChild !== undefined ? this._getLastElementChild(node) : node.lastElementChild;
+},
+_getLastElementChild: function (node) {
+var n = node.__dom.lastChild;
+while (n && n.nodeType !== Node.ELEMENT_NODE) {
+n = n.__dom.previousSibling;
+}
+return n;
+},
+getNextElementSibling: function (node) {
+return node.__dom && node.__dom.nextSibling !== undefined ? this._getNextElementSibling(node) : node.nextElementSibling;
+},
+_getNextElementSibling: function (node) {
+var n = node.__dom.nextSibling;
+while (n && n.nodeType !== Node.ELEMENT_NODE) {
+n = n.__dom.nextSibling;
+}
+return n;
+},
+getPreviousElementSibling: function (node) {
+return node.__dom && node.__dom.previousSibling !== undefined ? this._getPreviousElementSibling(node) : node.previousElementSibling;
+},
+_getPreviousElementSibling: function (node) {
+var n = node.__dom.previousSibling;
+while (n && n.nodeType !== Node.ELEMENT_NODE) {
+n = n.__dom.previousSibling;
+}
+return n;
+},
+saveChildNodes: function (node) {
+if (!this.hasChildNodes(node)) {
+node.__dom = node.__dom || {};
+node.__dom.firstChild = node.firstChild;
+node.__dom.lastChild = node.lastChild;
+node.__dom.childNodes = [];
+for (var n = node.firstChild; n; n = n.nextSibling) {
+n.__dom = n.__dom || {};
+n.__dom.parentNode = node;
+node.__dom.childNodes.push(n);
+n.__dom.nextSibling = n.nextSibling;
+n.__dom.previousSibling = n.previousSibling;
+}
+}
+},
+recordInsertBefore: function (node, container, ref_node) {
+container.__dom.childNodes = null;
+if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+for (var n = node.firstChild; n; n = n.nextSibling) {
+this._linkNode(n, container, ref_node);
+}
+} else {
+this._linkNode(node, container, ref_node);
+}
+},
+_linkNode: function (node, container, ref_node) {
+node.__dom = node.__dom || {};
+container.__dom = container.__dom || {};
+if (ref_node) {
+ref_node.__dom = ref_node.__dom || {};
+}
+node.__dom.previousSibling = ref_node ? ref_node.__dom.previousSibling : container.__dom.lastChild;
+if (node.__dom.previousSibling) {
+node.__dom.previousSibling.__dom.nextSibling = node;
+}
+node.__dom.nextSibling = ref_node || null;
+if (node.__dom.nextSibling) {
+node.__dom.nextSibling.__dom.previousSibling = node;
+}
+node.__dom.parentNode = container;
+if (ref_node) {
+if (ref_node === container.__dom.firstChild) {
+container.__dom.firstChild = node;
+}
+} else {
+container.__dom.lastChild = node;
+if (!container.__dom.firstChild) {
+container.__dom.firstChild = node;
+}
+}
+container.__dom.childNodes = null;
+},
+recordRemoveChild: function (node, container) {
+node.__dom = node.__dom || {};
+container.__dom = container.__dom || {};
+if (node === container.__dom.firstChild) {
+container.__dom.firstChild = node.__dom.nextSibling;
+}
+if (node === container.__dom.lastChild) {
+container.__dom.lastChild = node.__dom.previousSibling;
+}
+var p = node.__dom.previousSibling;
+var n = node.__dom.nextSibling;
+if (p) {
+p.__dom.nextSibling = n;
+}
+if (n) {
+n.__dom.previousSibling = p;
+}
+node.__dom.parentNode = node.__dom.previousSibling = node.__dom.nextSibling = undefined;
+container.__dom.childNodes = null;
+}
+};
+Polymer.TreeApi.Composed = {
+getChildNodes: function (node) {
+return Polymer.TreeApi.arrayCopyChildNodes(node);
+},
+getParentNode: function (node) {
+return node.parentNode;
+},
+clearChildNodes: function (node) {
+node.textContent = '';
+},
+insertBefore: function (parentNode, newChild, refChild) {
+return nativeInsertBefore.call(parentNode, newChild, refChild || null);
+},
+appendChild: function (parentNode, newChild) {
+return nativeAppendChild.call(parentNode, newChild);
+},
+removeChild: function (parentNode, node) {
+return nativeRemoveChild.call(parentNode, node);
+}
+};
+}());Polymer.DomApi = function () {
+'use strict';
+var Settings = Polymer.Settings;
+var TreeApi = Polymer.TreeApi;
+var DomApi = function (node) {
+this.node = needsToWrap ? DomApi.wrap(node) : node;
+};
+var needsToWrap = Settings.hasShadow && !Settings.nativeShadow;
+DomApi.wrap = window.wrap ? window.wrap : function (node) {
+return node;
+};
+DomApi.prototype = {
+flush: function () {
+Polymer.dom.flush();
+},
+deepContains: function (node) {
+if (this.node.contains(node)) {
+return true;
+}
+var n = node;
+var doc = node.ownerDocument;
+while (n && n !== doc && n !== this.node) {
+n = Polymer.dom(n).parentNode || n.host;
+}
+return n === this.node;
+},
+queryDistributedElements: function (selector) {
+var c$ = this.getEffectiveChildNodes();
+var list = [];
+for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
+if (c.nodeType === Node.ELEMENT_NODE && DomApi.matchesSelector.call(c, selector)) {
+list.push(c);
+}
+}
+return list;
+},
+getEffectiveChildNodes: function () {
+var list = [];
+var c$ = this.childNodes;
+for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
+if (c.localName === CONTENT) {
+var d$ = dom(c).getDistributedNodes();
+for (var j = 0; j < d$.length; j++) {
+list.push(d$[j]);
+}
+} else {
+list.push(c);
+}
+}
+return list;
+},
+observeNodes: function (callback) {
+if (callback) {
+if (!this.observer) {
+this.observer = this.node.localName === CONTENT ? new DomApi.DistributedNodesObserver(this) : new DomApi.EffectiveNodesObserver(this);
+}
+return this.observer.addListener(callback);
+}
+},
+unobserveNodes: function (handle) {
+if (this.observer) {
+this.observer.removeListener(handle);
+}
+},
+notifyObserver: function () {
+if (this.observer) {
+this.observer.notify();
+}
+},
+_query: function (matcher, node, halter) {
+node = node || this.node;
+var list = [];
+this._queryElements(TreeApi.Logical.getChildNodes(node), matcher, halter, list);
+return list;
+},
+_queryElements: function (elements, matcher, halter, list) {
+for (var i = 0, l = elements.length, c; i < l && (c = elements[i]); i++) {
+if (c.nodeType === Node.ELEMENT_NODE) {
+if (this._queryElement(c, matcher, halter, list)) {
+return true;
+}
+}
+}
+},
+_queryElement: function (node, matcher, halter, list) {
+var result = matcher(node);
+if (result) {
+list.push(node);
+}
+if (halter && halter(result)) {
+return result;
+}
+this._queryElements(TreeApi.Logical.getChildNodes(node), matcher, halter, list);
+}
+};
+var CONTENT = DomApi.CONTENT = 'content';
+var dom = DomApi.factory = function (node) {
+node = node || document;
+if (!node.__domApi) {
+node.__domApi = new DomApi.ctor(node);
+}
+return node.__domApi;
+};
+DomApi.hasApi = function (node) {
+return Boolean(node.__domApi);
+};
+DomApi.ctor = DomApi;
+Polymer.dom = function (obj, patch) {
+if (obj instanceof Event) {
+return Polymer.EventApi.factory(obj);
+} else {
+return DomApi.factory(obj, patch);
+}
+};
+var p = Element.prototype;
+DomApi.matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
+return DomApi;
+}();(function () {
+'use strict';
+var Settings = Polymer.Settings;
+var DomApi = Polymer.DomApi;
+var dom = DomApi.factory;
+var TreeApi = Polymer.TreeApi;
+var getInnerHTML = Polymer.domInnerHTML.getInnerHTML;
+var CONTENT = DomApi.CONTENT;
+if (Settings.useShadow) {
+return;
+}
+var nativeCloneNode = Element.prototype.cloneNode;
+var nativeImportNode = Document.prototype.importNode;
+Polymer.Base.mixin(DomApi.prototype, {
+_lazyDistribute: function (host) {
+if (host.shadyRoot && host.shadyRoot._distributionClean) {
+host.shadyRoot._distributionClean = false;
+Polymer.dom.addDebouncer(host.debounce('_distribute', host._distributeContent));
+}
+},
+appendChild: function (node) {
+return this.insertBefore(node);
+},
+insertBefore: function (node, ref_node) {
+if (ref_node && TreeApi.Logical.getParentNode(ref_node) !== this.node) {
+throw Error('The ref_node to be inserted before is not a child ' + 'of this node');
+}
+if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
+var parent = TreeApi.Logical.getParentNode(node);
+if (parent) {
+if (DomApi.hasApi(parent)) {
+dom(parent).notifyObserver();
+}
+this._removeNode(node);
+} else {
+this._removeOwnerShadyRoot(node);
+}
+}
+if (!this._addNode(node, ref_node)) {
+if (ref_node) {
+ref_node = ref_node.localName === CONTENT ? this._firstComposedNode(ref_node) : ref_node;
+}
+var container = this.node._isShadyRoot ? this.node.host : this.node;
+if (ref_node) {
+TreeApi.Composed.insertBefore(container, node, ref_node);
+} else {
+TreeApi.Composed.appendChild(container, node);
+}
+}
+this.notifyObserver();
+return node;
+},
+_addNode: function (node, ref_node) {
+var root = this.getOwnerRoot();
+if (root) {
+var ipAdded = this._maybeAddInsertionPoint(node, this.node);
+if (!root._invalidInsertionPoints) {
+root._invalidInsertionPoints = ipAdded;
+}
+this._addNodeToHost(root.host, node);
+}
+if (TreeApi.Logical.hasChildNodes(this.node)) {
+TreeApi.Logical.recordInsertBefore(node, this.node, ref_node);
+}
+var handled = this._maybeDistribute(node) || this.node.shadyRoot;
+if (handled) {
+if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+while (node.firstChild) {
+TreeApi.Composed.removeChild(node, node.firstChild);
+}
+} else {
+var parent = TreeApi.Composed.getParentNode(node);
+if (parent) {
+TreeApi.Composed.removeChild(parent, node);
+}
+}
+}
+return handled;
+},
+removeChild: function (node) {
+if (TreeApi.Logical.getParentNode(node) !== this.node) {
+throw Error('The node to be removed is not a child of this node: ' + node);
+}
+if (!this._removeNode(node)) {
+var container = this.node._isShadyRoot ? this.node.host : this.node;
+var parent = TreeApi.Composed.getParentNode(node);
+if (container === parent) {
+TreeApi.Composed.removeChild(container, node);
+}
+}
+this.notifyObserver();
+return node;
+},
+_removeNode: function (node) {
+var logicalParent = TreeApi.Logical.hasParentNode(node) && TreeApi.Logical.getParentNode(node);
+var distributed;
+var root = this._ownerShadyRootForNode(node);
+if (logicalParent) {
+distributed = dom(node)._maybeDistributeParent();
+TreeApi.Logical.recordRemoveChild(node, logicalParent);
+if (root && this._removeDistributedChildren(root, node)) {
+root._invalidInsertionPoints = true;
+this._lazyDistribute(root.host);
+}
+}
+this._removeOwnerShadyRoot(node);
+if (root) {
+this._removeNodeFromHost(root.host, node);
+}
+return distributed;
+},
+replaceChild: function (node, ref_node) {
+this.insertBefore(node, ref_node);
+this.removeChild(ref_node);
+return node;
+},
+_hasCachedOwnerRoot: function (node) {
+return Boolean(node._ownerShadyRoot !== undefined);
+},
+getOwnerRoot: function () {
+return this._ownerShadyRootForNode(this.node);
+},
+_ownerShadyRootForNode: function (node) {
+if (!node) {
+return;
+}
+var root = node._ownerShadyRoot;
+if (root === undefined) {
+if (node._isShadyRoot) {
+root = node;
+} else {
+var parent = TreeApi.Logical.getParentNode(node);
+if (parent) {
+root = parent._isShadyRoot ? parent : this._ownerShadyRootForNode(parent);
+} else {
+root = null;
+}
+}
+if (root || document.documentElement.contains(node)) {
+node._ownerShadyRoot = root;
+}
+}
+return root;
+},
+_maybeDistribute: function (node) {
+var fragContent = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent && dom(node).querySelector(CONTENT);
+var wrappedContent = fragContent && TreeApi.Logical.getParentNode(fragContent).nodeType !== Node.DOCUMENT_FRAGMENT_NODE;
+var hasContent = fragContent || node.localName === CONTENT;
+if (hasContent) {
+var root = this.getOwnerRoot();
+if (root) {
+this._lazyDistribute(root.host);
+}
+}
+var needsDist = this._nodeNeedsDistribution(this.node);
+if (needsDist) {
+this._lazyDistribute(this.node);
+}
+return needsDist || hasContent && !wrappedContent;
+},
+_maybeAddInsertionPoint: function (node, parent) {
+var added;
+if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent) {
+var c$ = dom(node).querySelectorAll(CONTENT);
+for (var i = 0, n, np, na; i < c$.length && (n = c$[i]); i++) {
+np = TreeApi.Logical.getParentNode(n);
+if (np === node) {
+np = parent;
+}
+na = this._maybeAddInsertionPoint(n, np);
+added = added || na;
+}
+} else if (node.localName === CONTENT) {
+TreeApi.Logical.saveChildNodes(parent);
+TreeApi.Logical.saveChildNodes(node);
+added = true;
+}
+return added;
+},
+_updateInsertionPoints: function (host) {
+var i$ = host.shadyRoot._insertionPoints = dom(host.shadyRoot).querySelectorAll(CONTENT);
+for (var i = 0, c; i < i$.length; i++) {
+c = i$[i];
+TreeApi.Logical.saveChildNodes(c);
+TreeApi.Logical.saveChildNodes(TreeApi.Logical.getParentNode(c));
+}
+},
+_nodeNeedsDistribution: function (node) {
+return node && node.shadyRoot && DomApi.hasInsertionPoint(node.shadyRoot);
+},
+_addNodeToHost: function (host, node) {
+if (host._elementAdd) {
+host._elementAdd(node);
+}
+},
+_removeNodeFromHost: function (host, node) {
+if (host._elementRemove) {
+host._elementRemove(node);
+}
+},
+_removeDistributedChildren: function (root, container) {
+var hostNeedsDist;
+var ip$ = root._insertionPoints;
+for (var i = 0; i < ip$.length; i++) {
+var content = ip$[i];
+if (this._contains(container, content)) {
+var dc$ = dom(content).getDistributedNodes();
+for (var j = 0; j < dc$.length; j++) {
+hostNeedsDist = true;
+var node = dc$[j];
+var parent = TreeApi.Composed.getParentNode(node);
+if (parent) {
+TreeApi.Composed.removeChild(parent, node);
+}
+}
+}
+}
+return hostNeedsDist;
+},
+_contains: function (container, node) {
+while (node) {
+if (node == container) {
+return true;
+}
+node = TreeApi.Logical.getParentNode(node);
+}
+},
+_removeOwnerShadyRoot: function (node) {
+if (this._hasCachedOwnerRoot(node)) {
+var c$ = TreeApi.Logical.getChildNodes(node);
+for (var i = 0, l = c$.length, n; i < l && (n = c$[i]); i++) {
+this._removeOwnerShadyRoot(n);
+}
+}
+node._ownerShadyRoot = undefined;
+},
+_firstComposedNode: function (content) {
+var n$ = dom(content).getDistributedNodes();
+for (var i = 0, l = n$.length, n, p$; i < l && (n = n$[i]); i++) {
+p$ = dom(n).getDestinationInsertionPoints();
+if (p$[p$.length - 1] === content) {
+return n;
+}
+}
+},
+querySelector: function (selector) {
+var result = this._query(function (n) {
+return DomApi.matchesSelector.call(n, selector);
+}, this.node, function (n) {
+return Boolean(n);
+})[0];
+return result || null;
+},
+querySelectorAll: function (selector) {
+return this._query(function (n) {
+return DomApi.matchesSelector.call(n, selector);
+}, this.node);
+},
+getDestinationInsertionPoints: function () {
+return this.node._destinationInsertionPoints || [];
+},
+getDistributedNodes: function () {
+return this.node._distributedNodes || [];
+},
+_clear: function () {
+while (this.childNodes.length) {
+this.removeChild(this.childNodes[0]);
+}
+},
+setAttribute: function (name, value) {
+this.node.setAttribute(name, value);
+this._maybeDistributeParent();
+},
+removeAttribute: function (name) {
+this.node.removeAttribute(name);
+this._maybeDistributeParent();
+},
+_maybeDistributeParent: function () {
+if (this._nodeNeedsDistribution(this.parentNode)) {
+this._lazyDistribute(this.parentNode);
+return true;
+}
+},
+cloneNode: function (deep) {
+var n = nativeCloneNode.call(this.node, false);
+if (deep) {
+var c$ = this.childNodes;
+var d = dom(n);
+for (var i = 0, nc; i < c$.length; i++) {
+nc = dom(c$[i]).cloneNode(true);
+d.appendChild(nc);
+}
+}
+return n;
+},
+importNode: function (externalNode, deep) {
+var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
+var n = nativeImportNode.call(doc, externalNode, false);
+if (deep) {
+var c$ = TreeApi.Logical.getChildNodes(externalNode);
+var d = dom(n);
+for (var i = 0, nc; i < c$.length; i++) {
+nc = dom(doc).importNode(c$[i], true);
+d.appendChild(nc);
+}
+}
+return n;
+},
+_getComposedInnerHTML: function () {
+return getInnerHTML(this.node, true);
+}
+});
+Object.defineProperties(DomApi.prototype, {
+activeElement: {
+get: function () {
+var active = document.activeElement;
+if (!active) {
+return null;
+}
+var isShadyRoot = !!this.node._isShadyRoot;
+if (this.node !== document) {
+if (!isShadyRoot) {
+return null;
+}
+if (this.node.host === active || !this.node.host.contains(active)) {
+return null;
+}
+}
+var activeRoot = dom(active).getOwnerRoot();
+while (activeRoot && activeRoot !== this.node) {
+active = activeRoot.host;
+activeRoot = dom(active).getOwnerRoot();
+}
+if (this.node === document) {
+return activeRoot ? null : active;
+} else {
+return activeRoot === this.node ? active : null;
+}
+},
+configurable: true
+},
+childNodes: {
+get: function () {
+var c$ = TreeApi.Logical.getChildNodes(this.node);
+return Array.isArray(c$) ? c$ : TreeApi.arrayCopyChildNodes(this.node);
+},
+configurable: true
+},
+children: {
+get: function () {
+if (TreeApi.Logical.hasChildNodes(this.node)) {
+return Array.prototype.filter.call(this.childNodes, function (n) {
+return n.nodeType === Node.ELEMENT_NODE;
+});
+} else {
+return TreeApi.arrayCopyChildren(this.node);
+}
+},
+configurable: true
+},
+parentNode: {
+get: function () {
+return TreeApi.Logical.getParentNode(this.node);
+},
+configurable: true
+},
+firstChild: {
+get: function () {
+return TreeApi.Logical.getFirstChild(this.node);
+},
+configurable: true
+},
+lastChild: {
+get: function () {
+return TreeApi.Logical.getLastChild(this.node);
+},
+configurable: true
+},
+nextSibling: {
+get: function () {
+return TreeApi.Logical.getNextSibling(this.node);
+},
+configurable: true
+},
+previousSibling: {
+get: function () {
+return TreeApi.Logical.getPreviousSibling(this.node);
+},
+configurable: true
+},
+firstElementChild: {
+get: function () {
+return TreeApi.Logical.getFirstElementChild(this.node);
+},
+configurable: true
+},
+lastElementChild: {
+get: function () {
+return TreeApi.Logical.getLastElementChild(this.node);
+},
+configurable: true
+},
+nextElementSibling: {
+get: function () {
+return TreeApi.Logical.getNextElementSibling(this.node);
+},
+configurable: true
+},
+previousElementSibling: {
+get: function () {
+return TreeApi.Logical.getPreviousElementSibling(this.node);
+},
+configurable: true
+},
+textContent: {
+get: function () {
+var nt = this.node.nodeType;
+if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
+return this.node.textContent;
+} else {
+var tc = [];
+for (var i = 0, cn = this.childNodes, c; c = cn[i]; i++) {
+if (c.nodeType !== Node.COMMENT_NODE) {
+tc.push(c.textContent);
+}
+}
+return tc.join('');
+}
+},
+set: function (text) {
+var nt = this.node.nodeType;
+if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
+this.node.textContent = text;
+} else {
+this._clear();
+if (text) {
+this.appendChild(document.createTextNode(text));
+}
+}
+},
+configurable: true
+},
+innerHTML: {
+get: function () {
+var nt = this.node.nodeType;
+if (nt === Node.TEXT_NODE || nt === Node.COMMENT_NODE) {
+return null;
+} else {
+return getInnerHTML(this.node);
+}
+},
+set: function (text) {
+var nt = this.node.nodeType;
+if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) {
+this._clear();
+var d = document.createElement('div');
+d.innerHTML = text;
+var c$ = TreeApi.arrayCopyChildNodes(d);
+for (var i = 0; i < c$.length; i++) {
+this.appendChild(c$[i]);
+}
+}
+},
+configurable: true
+}
+});
+DomApi.hasInsertionPoint = function (root) {
+return Boolean(root && root._insertionPoints.length);
+};
+}());(function () {
+'use strict';
+var Settings = Polymer.Settings;
+var TreeApi = Polymer.TreeApi;
+var DomApi = Polymer.DomApi;
+if (!Settings.useShadow) {
+return;
+}
+Polymer.Base.mixin(DomApi.prototype, {
+querySelectorAll: function (selector) {
+return TreeApi.arrayCopy(this.node.querySelectorAll(selector));
+},
+getOwnerRoot: function () {
+var n = this.node;
+while (n) {
+if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
+return n;
+}
+n = n.parentNode;
+}
+},
+importNode: function (externalNode, deep) {
+var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
+return doc.importNode(externalNode, deep);
+},
+getDestinationInsertionPoints: function () {
+var n$ = this.node.getDestinationInsertionPoints && this.node.getDestinationInsertionPoints();
+return n$ ? TreeApi.arrayCopy(n$) : [];
+},
+getDistributedNodes: function () {
+var n$ = this.node.getDistributedNodes && this.node.getDistributedNodes();
+return n$ ? TreeApi.arrayCopy(n$) : [];
+}
+});
+Object.defineProperties(DomApi.prototype, {
+activeElement: {
+get: function () {
+var node = DomApi.wrap(this.node);
+var activeElement = node.activeElement;
+return node.contains(activeElement) ? activeElement : null;
+},
+configurable: true
+},
+childNodes: {
+get: function () {
+return TreeApi.arrayCopyChildNodes(this.node);
+},
+configurable: true
+},
+children: {
+get: function () {
+return TreeApi.arrayCopyChildren(this.node);
+},
+configurable: true
+},
+textContent: {
+get: function () {
+return this.node.textContent;
+},
+set: function (value) {
+return this.node.textContent = value;
+},
+configurable: true
+},
+innerHTML: {
+get: function () {
+return this.node.innerHTML;
+},
+set: function (value) {
+return this.node.innerHTML = value;
+},
+configurable: true
+}
+});
+var forwardMethods = function (m$) {
+for (var i = 0; i < m$.length; i++) {
+forwardMethod(m$[i]);
+}
+};
+var forwardMethod = function (method) {
+DomApi.prototype[method] = function () {
+return this.node[method].apply(this.node, arguments);
+};
+};
+forwardMethods([
+'cloneNode',
+'appendChild',
+'insertBefore',
+'removeChild',
+'replaceChild',
+'setAttribute',
+'removeAttribute',
+'querySelector'
+]);
+var forwardProperties = function (f$) {
+for (var i = 0; i < f$.length; i++) {
+forwardProperty(f$[i]);
+}
+};
+var forwardProperty = function (name) {
+Object.defineProperty(DomApi.prototype, name, {
+get: function () {
+return this.node[name];
+},
+configurable: true
+});
+};
+forwardProperties([
+'parentNode',
+'firstChild',
+'lastChild',
+'nextSibling',
+'previousSibling',
+'firstElementChild',
+'lastElementChild',
+'nextElementSibling',
+'previousElementSibling'
+]);
+}());Polymer.Base.mixin(Polymer.dom, {
+_flushGuard: 0,
+_FLUSH_MAX: 100,
+_needsTakeRecords: !Polymer.Settings.useNativeCustomElements,
+_debouncers: [],
+_staticFlushList: [],
+_finishDebouncer: null,
+flush: function () {
+this._flushGuard = 0;
+this._prepareFlush();
+while (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) {
+while (this._debouncers.length) {
+this._debouncers.shift().complete();
+}
+if (this._finishDebouncer) {
+this._finishDebouncer.complete();
+}
+this._prepareFlush();
+this._flushGuard++;
+}
+if (this._flushGuard >= this._FLUSH_MAX) {
+console.warn('Polymer.dom.flush aborted. Flush may not be complete.');
+}
+},
+_prepareFlush: function () {
+if (this._needsTakeRecords) {
+CustomElements.takeRecords();
+}
+for (var i = 0; i < this._staticFlushList.length; i++) {
+this._staticFlushList[i]();
+}
+},
+addStaticFlush: function (fn) {
+this._staticFlushList.push(fn);
+},
+removeStaticFlush: function (fn) {
+var i = this._staticFlushList.indexOf(fn);
+if (i >= 0) {
+this._staticFlushList.splice(i, 1);
+}
+},
+addDebouncer: function (debouncer) {
+this._debouncers.push(debouncer);
+this._finishDebouncer = Polymer.Debounce(this._finishDebouncer, this._finishFlush);
+},
+_finishFlush: function () {
+Polymer.dom._debouncers = [];
+}
+});Polymer.EventApi = function () {
+'use strict';
+var DomApi = Polymer.DomApi.ctor;
+var Settings = Polymer.Settings;
+DomApi.Event = function (event) {
+this.event = event;
+};
+if (Settings.useShadow) {
+DomApi.Event.prototype = {
+get rootTarget() {
+return this.event.path[0];
+},
+get localTarget() {
+return this.event.target;
+},
+get path() {
+var path = this.event.path;
+if (!Array.isArray(path)) {
+path = Array.prototype.slice.call(path);
+}
+return path;
+}
+};
+} else {
+DomApi.Event.prototype = {
+get rootTarget() {
+return this.event.target;
+},
+get localTarget() {
+var current = this.event.currentTarget;
+var currentRoot = current && Polymer.dom(current).getOwnerRoot();
+var p$ = this.path;
+for (var i = 0; i < p$.length; i++) {
+if (Polymer.dom(p$[i]).getOwnerRoot() === currentRoot) {
+return p$[i];
+}
+}
+},
+get path() {
+if (!this.event._path) {
+var path = [];
+var current = this.rootTarget;
+while (current) {
+path.push(current);
+var insertionPoints = Polymer.dom(current).getDestinationInsertionPoints();
+if (insertionPoints.length) {
+for (var i = 0; i < insertionPoints.length - 1; i++) {
+path.push(insertionPoints[i]);
+}
+current = insertionPoints[insertionPoints.length - 1];
+} else {
+current = Polymer.dom(current).parentNode || current.host;
+}
+}
+path.push(window);
+this.event._path = path;
+}
+return this.event._path;
+}
+};
+}
+var factory = function (event) {
+if (!event.__eventApi) {
+event.__eventApi = new DomApi.Event(event);
+}
+return event.__eventApi;
+};
+return { factory: factory };
+}();(function () {
+'use strict';
+var DomApi = Polymer.DomApi.ctor;
+var useShadow = Polymer.Settings.useShadow;
+Object.defineProperty(DomApi.prototype, 'classList', {
+get: function () {
+if (!this._classList) {
+this._classList = new DomApi.ClassList(this);
+}
+return this._classList;
+},
+configurable: true
+});
+DomApi.ClassList = function (host) {
+this.domApi = host;
+this.node = host.node;
+};
+DomApi.ClassList.prototype = {
+add: function () {
+this.node.classList.add.apply(this.node.classList, arguments);
+this._distributeParent();
+},
+remove: function () {
+this.node.classList.remove.apply(this.node.classList, arguments);
+this._distributeParent();
+},
+toggle: function () {
+this.node.classList.toggle.apply(this.node.classList, arguments);
+this._distributeParent();
+},
+_distributeParent: function () {
+if (!useShadow) {
+this.domApi._maybeDistributeParent();
+}
+},
+contains: function () {
+return this.node.classList.contains.apply(this.node.classList, arguments);
+}
+};
+}());(function () {
+'use strict';
+var DomApi = Polymer.DomApi.ctor;
+var Settings = Polymer.Settings;
+DomApi.EffectiveNodesObserver = function (domApi) {
+this.domApi = domApi;
+this.node = this.domApi.node;
+this._listeners = [];
+};
+DomApi.EffectiveNodesObserver.prototype = {
+addListener: function (callback) {
+if (!this._isSetup) {
+this._setup();
+this._isSetup = true;
+}
+var listener = {
+fn: callback,
+_nodes: []
+};
+this._listeners.push(listener);
+this._scheduleNotify();
+return listener;
+},
+removeListener: function (handle) {
+var i = this._listeners.indexOf(handle);
+if (i >= 0) {
+this._listeners.splice(i, 1);
+handle._nodes = [];
+}
+if (!this._hasListeners()) {
+this._cleanup();
+this._isSetup = false;
+}
+},
+_setup: function () {
+this._observeContentElements(this.domApi.childNodes);
+},
+_cleanup: function () {
+this._unobserveContentElements(this.domApi.childNodes);
+},
+_hasListeners: function () {
+return Boolean(this._listeners.length);
+},
+_scheduleNotify: function () {
+if (this._debouncer) {
+this._debouncer.stop();
+}
+this._debouncer = Polymer.Debounce(this._debouncer, this._notify);
+this._debouncer.context = this;
+Polymer.dom.addDebouncer(this._debouncer);
+},
+notify: function () {
+if (this._hasListeners()) {
+this._scheduleNotify();
+}
+},
+_notify: function () {
+this._beforeCallListeners();
+this._callListeners();
+},
+_beforeCallListeners: function () {
+this._updateContentElements();
+},
+_updateContentElements: function () {
+this._observeContentElements(this.domApi.childNodes);
+},
+_observeContentElements: function (elements) {
+for (var i = 0, n; i < elements.length && (n = elements[i]); i++) {
+if (this._isContent(n)) {
+n.__observeNodesMap = n.__observeNodesMap || new WeakMap();
+if (!n.__observeNodesMap.has(this)) {
+n.__observeNodesMap.set(this, this._observeContent(n));
+}
+}
+}
+},
+_observeContent: function (content) {
+var self = this;
+var h = Polymer.dom(content).observeNodes(function () {
+self._scheduleNotify();
+});
+h._avoidChangeCalculation = true;
+return h;
+},
+_unobserveContentElements: function (elements) {
+for (var i = 0, n, h; i < elements.length && (n = elements[i]); i++) {
+if (this._isContent(n)) {
+h = n.__observeNodesMap.get(this);
+if (h) {
+Polymer.dom(n).unobserveNodes(h);
+n.__observeNodesMap.delete(this);
+}
+}
+}
+},
+_isContent: function (node) {
+return node.localName === 'content';
+},
+_callListeners: function () {
+var o$ = this._listeners;
+var nodes = this._getEffectiveNodes();
+for (var i = 0, o; i < o$.length && (o = o$[i]); i++) {
+var info = this._generateListenerInfo(o, nodes);
+if (info || o._alwaysNotify) {
+this._callListener(o, info);
+}
+}
+},
+_getEffectiveNodes: function () {
+return this.domApi.getEffectiveChildNodes();
+},
+_generateListenerInfo: function (listener, newNodes) {
+if (listener._avoidChangeCalculation) {
+return true;
+}
+var oldNodes = listener._nodes;
+var info = {
+target: this.node,
+addedNodes: [],
+removedNodes: []
+};
+var splices = Polymer.ArraySplice.calculateSplices(newNodes, oldNodes);
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) {
+for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) {
+info.removedNodes.push(n);
+}
+}
+for (i = 0, s; i < splices.length && (s = splices[i]); i++) {
+for (j = s.index; j < s.index + s.addedCount; j++) {
+info.addedNodes.push(newNodes[j]);
+}
+}
+listener._nodes = newNodes;
+if (info.addedNodes.length || info.removedNodes.length) {
+return info;
+}
+},
+_callListener: function (listener, info) {
+return listener.fn.call(this.node, info);
+},
+enableShadowAttributeTracking: function () {
+}
+};
+if (Settings.useShadow) {
+var baseSetup = DomApi.EffectiveNodesObserver.prototype._setup;
+var baseCleanup = DomApi.EffectiveNodesObserver.prototype._cleanup;
+Polymer.Base.mixin(DomApi.EffectiveNodesObserver.prototype, {
+_setup: function () {
+if (!this._observer) {
+var self = this;
+this._mutationHandler = function (mxns) {
+if (mxns && mxns.length) {
+self._scheduleNotify();
+}
+};
+this._observer = new MutationObserver(this._mutationHandler);
+this._boundFlush = function () {
+self._flush();
+};
+Polymer.dom.addStaticFlush(this._boundFlush);
+this._observer.observe(this.node, { childList: true });
+}
+baseSetup.call(this);
+},
+_cleanup: function () {
+this._observer.disconnect();
+this._observer = null;
+this._mutationHandler = null;
+Polymer.dom.removeStaticFlush(this._boundFlush);
+baseCleanup.call(this);
+},
+_flush: function () {
+if (this._observer) {
+this._mutationHandler(this._observer.takeRecords());
+}
+},
+enableShadowAttributeTracking: function () {
+if (this._observer) {
+this._makeContentListenersAlwaysNotify();
+this._observer.disconnect();
+this._observer.observe(this.node, {
+childList: true,
+attributes: true,
+subtree: true
+});
+var root = this.domApi.getOwnerRoot();
+var host = root && root.host;
+if (host && Polymer.dom(host).observer) {
+Polymer.dom(host).observer.enableShadowAttributeTracking();
+}
+}
+},
+_makeContentListenersAlwaysNotify: function () {
+for (var i = 0, h; i < this._listeners.length; i++) {
+h = this._listeners[i];
+h._alwaysNotify = h._isContentListener;
+}
+}
+});
+}
+}());(function () {
+'use strict';
+var DomApi = Polymer.DomApi.ctor;
+var Settings = Polymer.Settings;
+DomApi.DistributedNodesObserver = function (domApi) {
+DomApi.EffectiveNodesObserver.call(this, domApi);
+};
+DomApi.DistributedNodesObserver.prototype = Object.create(DomApi.EffectiveNodesObserver.prototype);
+Polymer.Base.mixin(DomApi.DistributedNodesObserver.prototype, {
+_setup: function () {
+},
+_cleanup: function () {
+},
+_beforeCallListeners: function () {
+},
+_getEffectiveNodes: function () {
+return this.domApi.getDistributedNodes();
+}
+});
+if (Settings.useShadow) {
+Polymer.Base.mixin(DomApi.DistributedNodesObserver.prototype, {
+_setup: function () {
+if (!this._observer) {
+var root = this.domApi.getOwnerRoot();
+var host = root && root.host;
+if (host) {
+var self = this;
+this._observer = Polymer.dom(host).observeNodes(function () {
+self._scheduleNotify();
+});
+this._observer._isContentListener = true;
+if (this._hasAttrSelect()) {
+Polymer.dom(host).observer.enableShadowAttributeTracking();
+}
+}
+}
+},
+_hasAttrSelect: function () {
+var select = this.node.getAttribute('select');
+return select && select.match(/[[.]+/);
+},
+_cleanup: function () {
+var root = this.domApi.getOwnerRoot();
+var host = root && root.host;
+if (host) {
+Polymer.dom(host).unobserveNodes(this._observer);
+}
+this._observer = null;
+}
+});
+}
+}());(function () {
+var DomApi = Polymer.DomApi;
+var TreeApi = Polymer.TreeApi;
+Polymer.Base._addFeature({
+_prepShady: function () {
+this._useContent = this._useContent || Boolean(this._template);
+},
+_setupShady: function () {
+this.shadyRoot = null;
+if (!this.__domApi) {
+this.__domApi = null;
+}
+if (!this.__dom) {
+this.__dom = null;
+}
+if (!this._ownerShadyRoot) {
+this._ownerShadyRoot = undefined;
+}
+},
+_poolContent: function () {
+if (this._useContent) {
+TreeApi.Logical.saveChildNodes(this);
+}
+},
+_setupRoot: function () {
+if (this._useContent) {
+this._createLocalRoot();
+if (!this.dataHost) {
+upgradeLogicalChildren(TreeApi.Logical.getChildNodes(this));
+}
+}
+},
+_createLocalRoot: function () {
+this.shadyRoot = this.root;
+this.shadyRoot._distributionClean = false;
+this.shadyRoot._hasDistributed = false;
+this.shadyRoot._isShadyRoot = true;
+this.shadyRoot._dirtyRoots = [];
+var i$ = this.shadyRoot._insertionPoints = !this._notes || this._notes._hasContent ? this.shadyRoot.querySelectorAll('content') : [];
+TreeApi.Logical.saveChildNodes(this.shadyRoot);
+for (var i = 0, c; i < i$.length; i++) {
+c = i$[i];
+TreeApi.Logical.saveChildNodes(c);
+TreeApi.Logical.saveChildNodes(c.parentNode);
+}
+this.shadyRoot.host = this;
+},
+distributeContent: function (updateInsertionPoints) {
+if (this.shadyRoot) {
+this.shadyRoot._invalidInsertionPoints = this.shadyRoot._invalidInsertionPoints || updateInsertionPoints;
+var host = getTopDistributingHost(this);
+Polymer.dom(this)._lazyDistribute(host);
+}
+},
+_distributeContent: function () {
+if (this._useContent && !this.shadyRoot._distributionClean) {
+if (this.shadyRoot._invalidInsertionPoints) {
+Polymer.dom(this)._updateInsertionPoints(this);
+this.shadyRoot._invalidInsertionPoints = false;
+}
+this._beginDistribute();
+this._distributeDirtyRoots();
+this._finishDistribute();
+}
+},
+_beginDistribute: function () {
+if (this._useContent && DomApi.hasInsertionPoint(this.shadyRoot)) {
+this._resetDistribution();
+this._distributePool(this.shadyRoot, this._collectPool());
+}
+},
+_distributeDirtyRoots: function () {
+var c$ = this.shadyRoot._dirtyRoots;
+for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
+c._distributeContent();
+}
+this.shadyRoot._dirtyRoots = [];
+},
+_finishDistribute: function () {
+if (this._useContent) {
+this.shadyRoot._distributionClean = true;
+if (DomApi.hasInsertionPoint(this.shadyRoot)) {
+this._composeTree();
+notifyContentObservers(this.shadyRoot);
+} else {
+if (!this.shadyRoot._hasDistributed) {
+TreeApi.Composed.clearChildNodes(this);
+this.appendChild(this.shadyRoot);
+} else {
+var children = this._composeNode(this);
+this._updateChildNodes(this, children);
+}
+}
+if (!this.shadyRoot._hasDistributed) {
+notifyInitialDistribution(this);
+}
+this.shadyRoot._hasDistributed = true;
+}
+},
+elementMatches: function (selector, node) {
+node = node || this;
+return DomApi.matchesSelector.call(node, selector);
+},
+_resetDistribution: function () {
+var children = TreeApi.Logical.getChildNodes(this);
+for (var i = 0; i < children.length; i++) {
+var child = children[i];
+if (child._destinationInsertionPoints) {
+child._destinationInsertionPoints = undefined;
+}
+if (isInsertionPoint(child)) {
+clearDistributedDestinationInsertionPoints(child);
+}
+}
+var root = this.shadyRoot;
+var p$ = root._insertionPoints;
+for (var j = 0; j < p$.length; j++) {
+p$[j]._distributedNodes = [];
+}
+},
+_collectPool: function () {
+var pool = [];
+var children = TreeApi.Logical.getChildNodes(this);
+for (var i = 0; i < children.length; i++) {
+var child = children[i];
+if (isInsertionPoint(child)) {
+pool.push.apply(pool, child._distributedNodes);
+} else {
+pool.push(child);
+}
+}
+return pool;
+},
+_distributePool: function (node, pool) {
+var p$ = node._insertionPoints;
+for (var i = 0, l = p$.length, p; i < l && (p = p$[i]); i++) {
+this._distributeInsertionPoint(p, pool);
+maybeRedistributeParent(p, this);
+}
+},
+_distributeInsertionPoint: function (content, pool) {
+var anyDistributed = false;
+for (var i = 0, l = pool.length, node; i < l; i++) {
+node = pool[i];
+if (!node) {
+continue;
+}
+if (this._matchesContentSelect(node, content)) {
+distributeNodeInto(node, content);
+pool[i] = undefined;
+anyDistributed = true;
+}
+}
+if (!anyDistributed) {
+var children = TreeApi.Logical.getChildNodes(content);
+for (var j = 0; j < children.length; j++) {
+distributeNodeInto(children[j], content);
+}
+}
+},
+_composeTree: function () {
+this._updateChildNodes(this, this._composeNode(this));
+var p$ = this.shadyRoot._insertionPoints;
+for (var i = 0, l = p$.length, p, parent; i < l && (p = p$[i]); i++) {
+parent = TreeApi.Logical.getParentNode(p);
+if (!parent._useContent && parent !== this && parent !== this.shadyRoot) {
+this._updateChildNodes(parent, this._composeNode(parent));
+}
+}
+},
+_composeNode: function (node) {
+var children = [];
+var c$ = TreeApi.Logical.getChildNodes(node.shadyRoot || node);
+for (var i = 0; i < c$.length; i++) {
+var child = c$[i];
+if (isInsertionPoint(child)) {
+var distributedNodes = child._distributedNodes;
+for (var j = 0; j < distributedNodes.length; j++) {
+var distributedNode = distributedNodes[j];
+if (isFinalDestination(child, distributedNode)) {
+children.push(distributedNode);
+}
+}
+} else {
+children.push(child);
+}
+}
+return children;
+},
+_updateChildNodes: function (container, children) {
+var composed = TreeApi.Composed.getChildNodes(container);
+var splices = Polymer.ArraySplice.calculateSplices(children, composed);
+for (var i = 0, d = 0, s; i < splices.length && (s = splices[i]); i++) {
+for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) {
+if (TreeApi.Composed.getParentNode(n) === container) {
+TreeApi.Composed.removeChild(container, n);
+}
+composed.splice(s.index + d, 1);
+}
+d -= s.addedCount;
+}
+for (var i = 0, s, next; i < splices.length && (s = splices[i]); i++) {
+next = composed[s.index];
+for (j = s.index, n; j < s.index + s.addedCount; j++) {
+n = children[j];
+TreeApi.Composed.insertBefore(container, n, next);
+composed.splice(j, 0, n);
+}
+}
+},
+_matchesContentSelect: function (node, contentElement) {
+var select = contentElement.getAttribute('select');
+if (!select) {
+return true;
+}
+select = select.trim();
+if (!select) {
+return true;
+}
+if (!(node instanceof Element)) {
+return false;
+}
+var validSelectors = /^(:not\()?[*.#[a-zA-Z_|]/;
+if (!validSelectors.test(select)) {
+return false;
+}
+return this.elementMatches(select, node);
+},
+_elementAdd: function () {
+},
+_elementRemove: function () {
+}
+});
+var domHostDesc = {
+get: function () {
+var root = Polymer.dom(this).getOwnerRoot();
+return root && root.host;
+},
+configurable: true
+};
+Object.defineProperty(Polymer.Base, 'domHost', domHostDesc);
+Polymer.BaseDescriptors.domHost = domHostDesc;
+function distributeNodeInto(child, insertionPoint) {
+insertionPoint._distributedNodes.push(child);
+var points = child._destinationInsertionPoints;
+if (!points) {
+child._destinationInsertionPoints = [insertionPoint];
+} else {
+points.push(insertionPoint);
+}
+}
+function clearDistributedDestinationInsertionPoints(content) {
+var e$ = content._distributedNodes;
+if (e$) {
+for (var i = 0; i < e$.length; i++) {
+var d = e$[i]._destinationInsertionPoints;
+if (d) {
+d.splice(d.indexOf(content) + 1, d.length);
+}
+}
+}
+}
+function maybeRedistributeParent(content, host) {
+var parent = TreeApi.Logical.getParentNode(content);
+if (parent && parent.shadyRoot && DomApi.hasInsertionPoint(parent.shadyRoot) && parent.shadyRoot._distributionClean) {
+parent.shadyRoot._distributionClean = false;
+host.shadyRoot._dirtyRoots.push(parent);
+}
+}
+function isFinalDestination(insertionPoint, node) {
+var points = node._destinationInsertionPoints;
+return points && points[points.length - 1] === insertionPoint;
+}
+function isInsertionPoint(node) {
+return node.localName == 'content';
+}
+function getTopDistributingHost(host) {
+while (host && hostNeedsRedistribution(host)) {
+host = host.domHost;
+}
+return host;
+}
+function hostNeedsRedistribution(host) {
+var c$ = TreeApi.Logical.getChildNodes(host);
+for (var i = 0, c; i < c$.length; i++) {
+c = c$[i];
+if (c.localName && c.localName === 'content') {
+return host.domHost;
+}
+}
+}
+function notifyContentObservers(root) {
+for (var i = 0, c; i < root._insertionPoints.length; i++) {
+c = root._insertionPoints[i];
+if (DomApi.hasApi(c)) {
+Polymer.dom(c).notifyObserver();
+}
+}
+}
+function notifyInitialDistribution(host) {
+if (DomApi.hasApi(host)) {
+Polymer.dom(host).notifyObserver();
+}
+}
+var needsUpgrade = window.CustomElements && !CustomElements.useNative;
+function upgradeLogicalChildren(children) {
+if (needsUpgrade && children) {
+for (var i = 0; i < children.length; i++) {
+CustomElements.upgrade(children[i]);
+}
+}
+}
+}());if (Polymer.Settings.useShadow) {
+Polymer.Base._addFeature({
+_poolContent: function () {
+},
+_beginDistribute: function () {
+},
+distributeContent: function () {
+},
+_distributeContent: function () {
+},
+_finishDistribute: function () {
+},
+_createLocalRoot: function () {
+this.createShadowRoot();
+this.shadowRoot.appendChild(this.root);
+this.root = this.shadowRoot;
+}
+});
+}Polymer.Async = {
+_currVal: 0,
+_lastVal: 0,
+_callbacks: [],
+_twiddleContent: 0,
+_twiddle: document.createTextNode(''),
+run: function (callback, waitTime) {
+if (waitTime > 0) {
+return ~setTimeout(callback, waitTime);
+} else {
+this._twiddle.textContent = this._twiddleContent++;
+this._callbacks.push(callback);
+return this._currVal++;
+}
+},
+cancel: function (handle) {
+if (handle < 0) {
+clearTimeout(~handle);
+} else {
+var idx = handle - this._lastVal;
+if (idx >= 0) {
+if (!this._callbacks[idx]) {
+throw 'invalid async handle: ' + handle;
+}
+this._callbacks[idx] = null;
+}
+}
+},
+_atEndOfMicrotask: function () {
+var len = this._callbacks.length;
+for (var i = 0; i < len; i++) {
+var cb = this._callbacks[i];
+if (cb) {
+try {
+cb();
+} catch (e) {
+i++;
+this._callbacks.splice(0, i);
+this._lastVal += i;
+this._twiddle.textContent = this._twiddleContent++;
+throw e;
+}
+}
+}
+this._callbacks.splice(0, len);
+this._lastVal += len;
+}
+};
+new window.MutationObserver(function () {
+Polymer.Async._atEndOfMicrotask();
+}).observe(Polymer.Async._twiddle, { characterData: true });Polymer.Debounce = function () {
+var Async = Polymer.Async;
+var Debouncer = function (context) {
+this.context = context;
+var self = this;
+this.boundComplete = function () {
+self.complete();
+};
+};
+Debouncer.prototype = {
+go: function (callback, wait) {
+var h;
+this.finish = function () {
+Async.cancel(h);
+};
+h = Async.run(this.boundComplete, wait);
+this.callback = callback;
+},
+stop: function () {
+if (this.finish) {
+this.finish();
+this.finish = null;
+this.callback = null;
+}
+},
+complete: function () {
+if (this.finish) {
+var callback = this.callback;
+this.stop();
+callback.call(this.context);
+}
+}
+};
+function debounce(debouncer, callback, wait) {
+if (debouncer) {
+debouncer.stop();
+} else {
+debouncer = new Debouncer(this);
+}
+debouncer.go(callback, wait);
+return debouncer;
+}
+return debounce;
+}();Polymer.Base._addFeature({
+_setupDebouncers: function () {
+this._debouncers = {};
+},
+debounce: function (jobName, callback, wait) {
+return this._debouncers[jobName] = Polymer.Debounce.call(this, this._debouncers[jobName], callback, wait);
+},
+isDebouncerActive: function (jobName) {
+var debouncer = this._debouncers[jobName];
+return !!(debouncer && debouncer.finish);
+},
+flushDebouncer: function (jobName) {
+var debouncer = this._debouncers[jobName];
+if (debouncer) {
+debouncer.complete();
+}
+},
+cancelDebouncer: function (jobName) {
+var debouncer = this._debouncers[jobName];
+if (debouncer) {
+debouncer.stop();
+}
+}
+});Polymer.DomModule = document.createElement('dom-module');
+Polymer.Base._addFeature({
+_registerFeatures: function () {
+this._prepIs();
+this._prepBehaviors();
+this._prepConstructor();
+this._prepTemplate();
+this._prepShady();
+this._prepPropertyInfo();
+},
+_prepBehavior: function (b) {
+this._addHostAttributes(b.hostAttributes);
+},
+_initFeatures: function () {
+this._registerHost();
+if (this._template) {
+this._poolContent();
+this._beginHosting();
+this._stampTemplate();
+this._endHosting();
+}
+this._marshalHostAttributes();
+this._setupDebouncers();
+this._marshalBehaviors();
+this._tryReady();
+},
+_marshalBehavior: function (b) {
+}
+});</script>
+
+
+
+
+
+
diff --git a/catapult/third_party/polymer/components/polymer/polymer.html b/catapult/third_party/polymer/components/polymer/polymer.html
new file mode 100644
index 00000000..c7c8170a
--- /dev/null
+++ b/catapult/third_party/polymer/components/polymer/polymer.html
@@ -0,0 +1,5555 @@
+<!--
+@license
+Copyright (c) 2015 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
+--><!--
+@license
+Copyright (c) 2014 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
+--><link rel="import" href="polymer-mini.html"><script>(function () {
+Polymer.nar = [];
+var disableUpgradeEnabled = Polymer.Settings.disableUpgradeEnabled;
+Polymer.Annotations = {
+parseAnnotations: function (template, stripWhiteSpace) {
+var list = [];
+var content = template._content || template.content;
+this._parseNodeAnnotations(content, list, stripWhiteSpace || template.hasAttribute('strip-whitespace'));
+return list;
+},
+_parseNodeAnnotations: function (node, list, stripWhiteSpace) {
+return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list, stripWhiteSpace);
+},
+_bindingRegex: function () {
+var IDENT = '(?:' + '[a-zA-Z_$][\\w.:$\\-*]*' + ')';
+var NUMBER = '(?:' + '[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?' + ')';
+var SQUOTE_STRING = '(?:' + '\'(?:[^\'\\\\]|\\\\.)*\'' + ')';
+var DQUOTE_STRING = '(?:' + '"(?:[^"\\\\]|\\\\.)*"' + ')';
+var STRING = '(?:' + SQUOTE_STRING + '|' + DQUOTE_STRING + ')';
+var ARGUMENT = '(?:' + IDENT + '|' + NUMBER + '|' + STRING + '\\s*' + ')';
+var ARGUMENTS = '(?:' + ARGUMENT + '(?:,\\s*' + ARGUMENT + ')*' + ')';
+var ARGUMENT_LIST = '(?:' + '\\(\\s*' + '(?:' + ARGUMENTS + '?' + ')' + '\\)\\s*' + ')';
+var BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')';
+var OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*';
+var CLOSE_BRACKET = '(?:]]|}})';
+var NEGATE = '(?:(!)\\s*)?';
+var EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET;
+return new RegExp(EXPRESSION, 'g');
+}(),
+_parseBindings: function (text) {
+var re = this._bindingRegex;
+var parts = [];
+var lastIndex = 0;
+var m;
+while ((m = re.exec(text)) !== null) {
+if (m.index > lastIndex) {
+parts.push({ literal: text.slice(lastIndex, m.index) });
+}
+var mode = m[1][0];
+var negate = Boolean(m[2]);
+var value = m[3].trim();
+var customEvent, notifyEvent, colon;
+if (mode == '{' && (colon = value.indexOf('::')) > 0) {
+notifyEvent = value.substring(colon + 2);
+value = value.substring(0, colon);
+customEvent = true;
+}
+parts.push({
+compoundIndex: parts.length,
+value: value,
+mode: mode,
+negate: negate,
+event: notifyEvent,
+customEvent: customEvent
+});
+lastIndex = re.lastIndex;
+}
+if (lastIndex && lastIndex < text.length) {
+var literal = text.substring(lastIndex);
+if (literal) {
+parts.push({ literal: literal });
+}
+}
+if (parts.length) {
+return parts;
+}
+},
+_literalFromParts: function (parts) {
+var s = '';
+for (var i = 0; i < parts.length; i++) {
+var literal = parts[i].literal;
+s += literal || '';
+}
+return s;
+},
+_parseTextNodeAnnotation: function (node, list) {
+var parts = this._parseBindings(node.textContent);
+if (parts) {
+node.textContent = this._literalFromParts(parts) || ' ';
+var annote = {
+bindings: [{
+kind: 'text',
+name: 'textContent',
+parts: parts,
+isCompound: parts.length !== 1
+}]
+};
+list.push(annote);
+return annote;
+}
+},
+_parseElementAnnotations: function (element, list, stripWhiteSpace) {
+var annote = {
+bindings: [],
+events: []
+};
+if (element.localName === 'content') {
+list._hasContent = true;
+}
+this._parseChildNodesAnnotations(element, annote, list, stripWhiteSpace);
+if (element.attributes) {
+this._parseNodeAttributeAnnotations(element, annote, list);
+if (this.prepElement) {
+this.prepElement(element);
+}
+}
+if (annote.bindings.length || annote.events.length || annote.id) {
+list.push(annote);
+}
+return annote;
+},
+_parseChildNodesAnnotations: function (root, annote, list, stripWhiteSpace) {
+if (root.firstChild) {
+var node = root.firstChild;
+var i = 0;
+while (node) {
+var next = node.nextSibling;
+if (node.localName === 'template' && !node.hasAttribute('preserve-content')) {
+this._parseTemplate(node, i, list, annote, stripWhiteSpace);
+}
+if (node.localName == 'slot') {
+node = this._replaceSlotWithContent(node);
+}
+if (node.nodeType === Node.TEXT_NODE) {
+var n = next;
+while (n && n.nodeType === Node.TEXT_NODE) {
+node.textContent += n.textContent;
+next = n.nextSibling;
+root.removeChild(n);
+n = next;
+}
+if (stripWhiteSpace && !node.textContent.trim()) {
+root.removeChild(node);
+i--;
+}
+}
+if (node.parentNode) {
+var childAnnotation = this._parseNodeAnnotations(node, list, stripWhiteSpace);
+if (childAnnotation) {
+childAnnotation.parent = annote;
+childAnnotation.index = i;
+}
+}
+node = next;
+i++;
+}
+}
+},
+_replaceSlotWithContent: function (slot) {
+var content = slot.ownerDocument.createElement('content');
+while (slot.firstChild) {
+content.appendChild(slot.firstChild);
+}
+var attrs = slot.attributes;
+for (var i = 0; i < attrs.length; i++) {
+var attr = attrs[i];
+content.setAttribute(attr.name, attr.value);
+}
+var name = slot.getAttribute('name');
+if (name) {
+content.setAttribute('select', '[slot=\'' + name + '\']');
+}
+slot.parentNode.replaceChild(content, slot);
+return content;
+},
+_parseTemplate: function (node, index, list, parent, stripWhiteSpace) {
+var content = document.createDocumentFragment();
+content._notes = this.parseAnnotations(node, stripWhiteSpace);
+content.appendChild(node.content);
+list.push({
+bindings: Polymer.nar,
+events: Polymer.nar,
+templateContent: content,
+parent: parent,
+index: index
+});
+},
+_parseNodeAttributeAnnotations: function (node, annotation) {
+var attrs = Array.prototype.slice.call(node.attributes);
+for (var i = attrs.length - 1, a; a = attrs[i]; i--) {
+var n = a.name;
+var v = a.value;
+var b;
+if (n.slice(0, 3) === 'on-') {
+node.removeAttribute(n);
+annotation.events.push({
+name: n.slice(3),
+value: v
+});
+} else if (b = this._parseNodeAttributeAnnotation(node, n, v)) {
+annotation.bindings.push(b);
+} else if (n === 'id') {
+annotation.id = v;
+}
+}
+},
+_parseNodeAttributeAnnotation: function (node, name, value) {
+var parts = this._parseBindings(value);
+if (parts) {
+var origName = name;
+var kind = 'property';
+if (name[name.length - 1] == '$') {
+name = name.slice(0, -1);
+kind = 'attribute';
+}
+var literal = this._literalFromParts(parts);
+if (literal && kind == 'attribute') {
+node.setAttribute(name, literal);
+}
+if (node.localName === 'input' && origName === 'value') {
+node.setAttribute(origName, '');
+}
+if (disableUpgradeEnabled && origName === 'disable-upgrade$') {
+node.setAttribute(name, '');
+}
+node.removeAttribute(origName);
+var propertyName = Polymer.CaseMap.dashToCamelCase(name);
+if (kind === 'property') {
+name = propertyName;
+}
+return {
+kind: kind,
+name: name,
+propertyName: propertyName,
+parts: parts,
+literal: literal,
+isCompound: parts.length !== 1
+};
+}
+},
+findAnnotatedNode: function (root, annote) {
+var parent = annote.parent && Polymer.Annotations.findAnnotatedNode(root, annote.parent);
+if (parent) {
+for (var n = parent.firstChild, i = 0; n; n = n.nextSibling) {
+if (annote.index === i++) {
+return n;
+}
+}
+} else {
+return root;
+}
+}
+};
+}());Polymer.Path = {
+root: function (path) {
+var dotIndex = path.indexOf('.');
+if (dotIndex === -1) {
+return path;
+}
+return path.slice(0, dotIndex);
+},
+isDeep: function (path) {
+return path.indexOf('.') !== -1;
+},
+isAncestor: function (base, path) {
+return base.indexOf(path + '.') === 0;
+},
+isDescendant: function (base, path) {
+return path.indexOf(base + '.') === 0;
+},
+translate: function (base, newBase, path) {
+return newBase + path.slice(base.length);
+},
+matches: function (base, wildcard, path) {
+return base === path || this.isAncestor(base, path) || Boolean(wildcard) && this.isDescendant(base, path);
+}
+};Polymer.Base._addFeature({
+_prepAnnotations: function () {
+if (!this._template) {
+this._notes = [];
+} else {
+var self = this;
+Polymer.Annotations.prepElement = function (element) {
+self._prepElement(element);
+};
+if (this._template._content && this._template._content._notes) {
+this._notes = this._template._content._notes;
+} else {
+this._notes = Polymer.Annotations.parseAnnotations(this._template);
+this._processAnnotations(this._notes);
+}
+Polymer.Annotations.prepElement = null;
+}
+},
+_processAnnotations: function (notes) {
+for (var i = 0; i < notes.length; i++) {
+var note = notes[i];
+for (var j = 0; j < note.bindings.length; j++) {
+var b = note.bindings[j];
+for (var k = 0; k < b.parts.length; k++) {
+var p = b.parts[k];
+if (!p.literal) {
+var signature = this._parseMethod(p.value);
+if (signature) {
+p.signature = signature;
+} else {
+p.model = Polymer.Path.root(p.value);
+}
+}
+}
+}
+if (note.templateContent) {
+this._processAnnotations(note.templateContent._notes);
+var pp = note.templateContent._parentProps = this._discoverTemplateParentProps(note.templateContent._notes);
+var bindings = [];
+for (var prop in pp) {
+var name = '_parent_' + prop;
+bindings.push({
+index: note.index,
+kind: 'property',
+name: name,
+propertyName: name,
+parts: [{
+mode: '{',
+model: prop,
+value: prop
+}]
+});
+}
+note.bindings = note.bindings.concat(bindings);
+}
+}
+},
+_discoverTemplateParentProps: function (notes) {
+var pp = {};
+for (var i = 0, n; i < notes.length && (n = notes[i]); i++) {
+for (var j = 0, b$ = n.bindings, b; j < b$.length && (b = b$[j]); j++) {
+for (var k = 0, p$ = b.parts, p; k < p$.length && (p = p$[k]); k++) {
+if (p.signature) {
+var args = p.signature.args;
+for (var kk = 0; kk < args.length; kk++) {
+var model = args[kk].model;
+if (model) {
+pp[model] = true;
+}
+}
+if (p.signature.dynamicFn) {
+pp[p.signature.method] = true;
+}
+} else {
+if (p.model) {
+pp[p.model] = true;
+}
+}
+}
+}
+if (n.templateContent) {
+var tpp = n.templateContent._parentProps;
+Polymer.Base.mixin(pp, tpp);
+}
+}
+return pp;
+},
+_prepElement: function (element) {
+Polymer.ResolveUrl.resolveAttrs(element, this._template.ownerDocument);
+},
+_findAnnotatedNode: Polymer.Annotations.findAnnotatedNode,
+_marshalAnnotationReferences: function () {
+if (this._template) {
+this._marshalIdNodes();
+this._marshalAnnotatedNodes();
+this._marshalAnnotatedListeners();
+}
+},
+_configureAnnotationReferences: function () {
+var notes = this._notes;
+var nodes = this._nodes;
+for (var i = 0; i < notes.length; i++) {
+var note = notes[i];
+var node = nodes[i];
+this._configureTemplateContent(note, node);
+this._configureCompoundBindings(note, node);
+}
+},
+_configureTemplateContent: function (note, node) {
+if (note.templateContent) {
+node._content = note.templateContent;
+}
+},
+_configureCompoundBindings: function (note, node) {
+var bindings = note.bindings;
+for (var i = 0; i < bindings.length; i++) {
+var binding = bindings[i];
+if (binding.isCompound) {
+var storage = node.__compoundStorage__ || (node.__compoundStorage__ = {});
+var parts = binding.parts;
+var literals = new Array(parts.length);
+for (var j = 0; j < parts.length; j++) {
+literals[j] = parts[j].literal;
+}
+var name = binding.name;
+storage[name] = literals;
+if (binding.literal && binding.kind == 'property') {
+if (node._configValue) {
+node._configValue(name, binding.literal);
+} else {
+node[name] = binding.literal;
+}
+}
+}
+}
+},
+_marshalIdNodes: function () {
+this.$ = {};
+for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) {
+if (a.id) {
+this.$[a.id] = this._findAnnotatedNode(this.root, a);
+}
+}
+},
+_marshalAnnotatedNodes: function () {
+if (this._notes && this._notes.length) {
+var r = new Array(this._notes.length);
+for (var i = 0; i < this._notes.length; i++) {
+r[i] = this._findAnnotatedNode(this.root, this._notes[i]);
+}
+this._nodes = r;
+}
+},
+_marshalAnnotatedListeners: function () {
+for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) {
+if (a.events && a.events.length) {
+var node = this._findAnnotatedNode(this.root, a);
+for (var j = 0, e$ = a.events, e; j < e$.length && (e = e$[j]); j++) {
+this.listen(node, e.name, e.value);
+}
+}
+}
+}
+});Polymer.Base._addFeature({
+listeners: {},
+_listenListeners: function (listeners) {
+var node, name, eventName;
+for (eventName in listeners) {
+if (eventName.indexOf('.') < 0) {
+node = this;
+name = eventName;
+} else {
+name = eventName.split('.');
+node = this.$[name[0]];
+name = name[1];
+}
+this.listen(node, name, listeners[eventName]);
+}
+},
+listen: function (node, eventName, methodName) {
+var handler = this._recallEventHandler(this, eventName, node, methodName);
+if (!handler) {
+handler = this._createEventHandler(node, eventName, methodName);
+}
+if (handler._listening) {
+return;
+}
+this._listen(node, eventName, handler);
+handler._listening = true;
+},
+_boundListenerKey: function (eventName, methodName) {
+return eventName + ':' + methodName;
+},
+_recordEventHandler: function (host, eventName, target, methodName, handler) {
+var hbl = host.__boundListeners;
+if (!hbl) {
+hbl = host.__boundListeners = new WeakMap();
+}
+var bl = hbl.get(target);
+if (!bl) {
+bl = {};
+if (!Polymer.Settings.isIE || target != window) {
+hbl.set(target, bl);
+}
+}
+var key = this._boundListenerKey(eventName, methodName);
+bl[key] = handler;
+},
+_recallEventHandler: function (host, eventName, target, methodName) {
+var hbl = host.__boundListeners;
+if (!hbl) {
+return;
+}
+var bl = hbl.get(target);
+if (!bl) {
+return;
+}
+var key = this._boundListenerKey(eventName, methodName);
+return bl[key];
+},
+_createEventHandler: function (node, eventName, methodName) {
+var host = this;
+var handler = function (e) {
+if (host[methodName]) {
+host[methodName](e, e.detail);
+} else {
+host._warn(host._logf('_createEventHandler', 'listener method `' + methodName + '` not defined'));
+}
+};
+handler._listening = false;
+this._recordEventHandler(host, eventName, node, methodName, handler);
+return handler;
+},
+unlisten: function (node, eventName, methodName) {
+var handler = this._recallEventHandler(this, eventName, node, methodName);
+if (handler) {
+this._unlisten(node, eventName, handler);
+handler._listening = false;
+}
+},
+_listen: function (node, eventName, handler) {
+node.addEventListener(eventName, handler);
+},
+_unlisten: function (node, eventName, handler) {
+node.removeEventListener(eventName, handler);
+}
+});(function () {
+'use strict';
+var wrap = Polymer.DomApi.wrap;
+var HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string';
+var GESTURE_KEY = '__polymerGestures';
+var HANDLED_OBJ = '__polymerGesturesHandled';
+var TOUCH_ACTION = '__polymerGesturesTouchAction';
+var TAP_DISTANCE = 25;
+var TRACK_DISTANCE = 5;
+var TRACK_LENGTH = 2;
+var MOUSE_TIMEOUT = 2500;
+var MOUSE_EVENTS = [
+'mousedown',
+'mousemove',
+'mouseup',
+'click'
+];
+var MOUSE_WHICH_TO_BUTTONS = [
+0,
+1,
+4,
+2
+];
+var MOUSE_HAS_BUTTONS = function () {
+try {
+return new MouseEvent('test', { buttons: 1 }).buttons === 1;
+} catch (e) {
+return false;
+}
+}();
+function isMouseEvent(name) {
+return MOUSE_EVENTS.indexOf(name) > -1;
+}
+var SUPPORTS_PASSIVE = false;
+(function () {
+try {
+var opts = Object.defineProperty({}, 'passive', {
+get: function () {
+SUPPORTS_PASSIVE = true;
+}
+});
+window.addEventListener('test', null, opts);
+window.removeEventListener('test', null, opts);
+} catch (e) {
+}
+}());
+function PASSIVE_TOUCH(eventName) {
+if (isMouseEvent(eventName) || eventName === 'touchend') {
+return;
+}
+if (HAS_NATIVE_TA && SUPPORTS_PASSIVE && Polymer.Settings.passiveTouchGestures) {
+return { passive: true };
+}
+}
+var IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
+var mouseCanceller = function (mouseEvent) {
+var sc = mouseEvent.sourceCapabilities;
+if (sc && !sc.firesTouchEvents) {
+return;
+}
+mouseEvent[HANDLED_OBJ] = { skip: true };
+if (mouseEvent.type === 'click') {
+var path = Polymer.dom(mouseEvent).path;
+if (path) {
+for (var i = 0; i < path.length; i++) {
+if (path[i] === POINTERSTATE.mouse.target) {
+return;
+}
+}
+}
+mouseEvent.preventDefault();
+mouseEvent.stopPropagation();
+}
+};
+function setupTeardownMouseCanceller(setup) {
+var events = IS_TOUCH_ONLY ? ['click'] : MOUSE_EVENTS;
+for (var i = 0, en; i < events.length; i++) {
+en = events[i];
+if (setup) {
+document.addEventListener(en, mouseCanceller, true);
+} else {
+document.removeEventListener(en, mouseCanceller, true);
+}
+}
+}
+function ignoreMouse(ev) {
+if (!POINTERSTATE.mouse.mouseIgnoreJob) {
+setupTeardownMouseCanceller(true);
+}
+var unset = function () {
+setupTeardownMouseCanceller();
+POINTERSTATE.mouse.target = null;
+POINTERSTATE.mouse.mouseIgnoreJob = null;
+};
+POINTERSTATE.mouse.target = Polymer.dom(ev).rootTarget;
+POINTERSTATE.mouse.mouseIgnoreJob = Polymer.Debounce(POINTERSTATE.mouse.mouseIgnoreJob, unset, MOUSE_TIMEOUT);
+}
+function hasLeftMouseButton(ev) {
+var type = ev.type;
+if (!isMouseEvent(type)) {
+return false;
+}
+if (type === 'mousemove') {
+var buttons = ev.buttons === undefined ? 1 : ev.buttons;
+if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) {
+buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
+}
+return Boolean(buttons & 1);
+} else {
+var button = ev.button === undefined ? 0 : ev.button;
+return button === 0;
+}
+}
+function isSyntheticClick(ev) {
+if (ev.type === 'click') {
+if (ev.detail === 0) {
+return true;
+}
+var t = Gestures.findOriginalTarget(ev);
+var bcr = t.getBoundingClientRect();
+var x = ev.pageX, y = ev.pageY;
+return !(x >= bcr.left && x <= bcr.right && (y >= bcr.top && y <= bcr.bottom));
+}
+return false;
+}
+var POINTERSTATE = {
+mouse: {
+target: null,
+mouseIgnoreJob: null
+},
+touch: {
+x: 0,
+y: 0,
+id: -1,
+scrollDecided: false
+}
+};
+function firstTouchAction(ev) {
+var path = Polymer.dom(ev).path;
+var ta = 'auto';
+for (var i = 0, n; i < path.length; i++) {
+n = path[i];
+if (n[TOUCH_ACTION]) {
+ta = n[TOUCH_ACTION];
+break;
+}
+}
+return ta;
+}
+function trackDocument(stateObj, movefn, upfn) {
+stateObj.movefn = movefn;
+stateObj.upfn = upfn;
+document.addEventListener('mousemove', movefn);
+document.addEventListener('mouseup', upfn);
+}
+function untrackDocument(stateObj) {
+document.removeEventListener('mousemove', stateObj.movefn);
+document.removeEventListener('mouseup', stateObj.upfn);
+stateObj.movefn = null;
+stateObj.upfn = null;
+}
+document.addEventListener('touchend', ignoreMouse, SUPPORTS_PASSIVE ? { passive: true } : false);
+var Gestures = {
+gestures: {},
+recognizers: [],
+deepTargetFind: function (x, y) {
+var node = document.elementFromPoint(x, y);
+var next = node;
+while (next && next.shadowRoot) {
+next = next.shadowRoot.elementFromPoint(x, y);
+if (next) {
+node = next;
+}
+}
+return node;
+},
+findOriginalTarget: function (ev) {
+if (ev.path) {
+return ev.path[0];
+}
+return ev.target;
+},
+handleNative: function (ev) {
+var handled;
+var type = ev.type;
+var node = wrap(ev.currentTarget);
+var gobj = node[GESTURE_KEY];
+if (!gobj) {
+return;
+}
+var gs = gobj[type];
+if (!gs) {
+return;
+}
+if (!ev[HANDLED_OBJ]) {
+ev[HANDLED_OBJ] = {};
+if (type.slice(0, 5) === 'touch') {
+var t = ev.changedTouches[0];
+if (type === 'touchstart') {
+if (ev.touches.length === 1) {
+POINTERSTATE.touch.id = t.identifier;
+}
+}
+if (POINTERSTATE.touch.id !== t.identifier) {
+return;
+}
+if (!HAS_NATIVE_TA) {
+if (type === 'touchstart' || type === 'touchmove') {
+Gestures.handleTouchAction(ev);
+}
+}
+}
+}
+handled = ev[HANDLED_OBJ];
+if (handled.skip) {
+return;
+}
+var recognizers = Gestures.recognizers;
+for (var i = 0, r; i < recognizers.length; i++) {
+r = recognizers[i];
+if (gs[r.name] && !handled[r.name]) {
+if (r.flow && r.flow.start.indexOf(ev.type) > -1 && r.reset) {
+r.reset();
+}
+}
+}
+for (i = 0, r; i < recognizers.length; i++) {
+r = recognizers[i];
+if (gs[r.name] && !handled[r.name]) {
+handled[r.name] = true;
+r[type](ev);
+}
+}
+},
+handleTouchAction: function (ev) {
+var t = ev.changedTouches[0];
+var type = ev.type;
+if (type === 'touchstart') {
+POINTERSTATE.touch.x = t.clientX;
+POINTERSTATE.touch.y = t.clientY;
+POINTERSTATE.touch.scrollDecided = false;
+} else if (type === 'touchmove') {
+if (POINTERSTATE.touch.scrollDecided) {
+return;
+}
+POINTERSTATE.touch.scrollDecided = true;
+var ta = firstTouchAction(ev);
+var prevent = false;
+var dx = Math.abs(POINTERSTATE.touch.x - t.clientX);
+var dy = Math.abs(POINTERSTATE.touch.y - t.clientY);
+if (!ev.cancelable) {
+} else if (ta === 'none') {
+prevent = true;
+} else if (ta === 'pan-x') {
+prevent = dy > dx;
+} else if (ta === 'pan-y') {
+prevent = dx > dy;
+}
+if (prevent) {
+ev.preventDefault();
+} else {
+Gestures.prevent('track');
+}
+}
+},
+add: function (node, evType, handler) {
+node = wrap(node);
+var recognizer = this.gestures[evType];
+var deps = recognizer.deps;
+var name = recognizer.name;
+var gobj = node[GESTURE_KEY];
+if (!gobj) {
+node[GESTURE_KEY] = gobj = {};
+}
+for (var i = 0, dep, gd; i < deps.length; i++) {
+dep = deps[i];
+if (IS_TOUCH_ONLY && isMouseEvent(dep) && dep !== 'click') {
+continue;
+}
+gd = gobj[dep];
+if (!gd) {
+gobj[dep] = gd = { _count: 0 };
+}
+if (gd._count === 0) {
+node.addEventListener(dep, this.handleNative, PASSIVE_TOUCH(dep));
+}
+gd[name] = (gd[name] || 0) + 1;
+gd._count = (gd._count || 0) + 1;
+}
+node.addEventListener(evType, handler);
+if (recognizer.touchAction) {
+this.setTouchAction(node, recognizer.touchAction);
+}
+},
+remove: function (node, evType, handler) {
+node = wrap(node);
+var recognizer = this.gestures[evType];
+var deps = recognizer.deps;
+var name = recognizer.name;
+var gobj = node[GESTURE_KEY];
+if (gobj) {
+for (var i = 0, dep, gd; i < deps.length; i++) {
+dep = deps[i];
+gd = gobj[dep];
+if (gd && gd[name]) {
+gd[name] = (gd[name] || 1) - 1;
+gd._count = (gd._count || 1) - 1;
+if (gd._count === 0) {
+node.removeEventListener(dep, this.handleNative, PASSIVE_TOUCH(dep));
+}
+}
+}
+}
+node.removeEventListener(evType, handler);
+},
+register: function (recog) {
+this.recognizers.push(recog);
+for (var i = 0; i < recog.emits.length; i++) {
+this.gestures[recog.emits[i]] = recog;
+}
+},
+findRecognizerByEvent: function (evName) {
+for (var i = 0, r; i < this.recognizers.length; i++) {
+r = this.recognizers[i];
+for (var j = 0, n; j < r.emits.length; j++) {
+n = r.emits[j];
+if (n === evName) {
+return r;
+}
+}
+}
+return null;
+},
+setTouchAction: function (node, value) {
+if (HAS_NATIVE_TA) {
+node.style.touchAction = value;
+}
+node[TOUCH_ACTION] = value;
+},
+fire: function (target, type, detail) {
+var ev = Polymer.Base.fire(type, detail, {
+node: target,
+bubbles: true,
+cancelable: true
+});
+if (ev.defaultPrevented) {
+var preventer = detail.preventer || detail.sourceEvent;
+if (preventer && preventer.preventDefault) {
+preventer.preventDefault();
+}
+}
+},
+prevent: function (evName) {
+var recognizer = this.findRecognizerByEvent(evName);
+if (recognizer.info) {
+recognizer.info.prevent = true;
+}
+},
+resetMouseCanceller: function () {
+if (POINTERSTATE.mouse.mouseIgnoreJob) {
+POINTERSTATE.mouse.mouseIgnoreJob.complete();
+}
+}
+};
+Gestures.register({
+name: 'downup',
+deps: [
+'mousedown',
+'touchstart',
+'touchend'
+],
+flow: {
+start: [
+'mousedown',
+'touchstart'
+],
+end: [
+'mouseup',
+'touchend'
+]
+},
+emits: [
+'down',
+'up'
+],
+info: {
+movefn: null,
+upfn: null
+},
+reset: function () {
+untrackDocument(this.info);
+},
+mousedown: function (e) {
+if (!hasLeftMouseButton(e)) {
+return;
+}
+var t = Gestures.findOriginalTarget(e);
+var self = this;
+var movefn = function movefn(e) {
+if (!hasLeftMouseButton(e)) {
+self.fire('up', t, e);
+untrackDocument(self.info);
+}
+};
+var upfn = function upfn(e) {
+if (hasLeftMouseButton(e)) {
+self.fire('up', t, e);
+}
+untrackDocument(self.info);
+};
+trackDocument(this.info, movefn, upfn);
+this.fire('down', t, e);
+},
+touchstart: function (e) {
+this.fire('down', Gestures.findOriginalTarget(e), e.changedTouches[0], e);
+},
+touchend: function (e) {
+this.fire('up', Gestures.findOriginalTarget(e), e.changedTouches[0], e);
+},
+fire: function (type, target, event, preventer) {
+Gestures.fire(target, type, {
+x: event.clientX,
+y: event.clientY,
+sourceEvent: event,
+preventer: preventer,
+prevent: function (e) {
+return Gestures.prevent(e);
+}
+});
+}
+});
+Gestures.register({
+name: 'track',
+touchAction: 'none',
+deps: [
+'mousedown',
+'touchstart',
+'touchmove',
+'touchend'
+],
+flow: {
+start: [
+'mousedown',
+'touchstart'
+],
+end: [
+'mouseup',
+'touchend'
+]
+},
+emits: ['track'],
+info: {
+x: 0,
+y: 0,
+state: 'start',
+started: false,
+moves: [],
+addMove: function (move) {
+if (this.moves.length > TRACK_LENGTH) {
+this.moves.shift();
+}
+this.moves.push(move);
+},
+movefn: null,
+upfn: null,
+prevent: false
+},
+reset: function () {
+this.info.state = 'start';
+this.info.started = false;
+this.info.moves = [];
+this.info.x = 0;
+this.info.y = 0;
+this.info.prevent = false;
+untrackDocument(this.info);
+},
+hasMovedEnough: function (x, y) {
+if (this.info.prevent) {
+return false;
+}
+if (this.info.started) {
+return true;
+}
+var dx = Math.abs(this.info.x - x);
+var dy = Math.abs(this.info.y - y);
+return dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE;
+},
+mousedown: function (e) {
+if (!hasLeftMouseButton(e)) {
+return;
+}
+var t = Gestures.findOriginalTarget(e);
+var self = this;
+var movefn = function movefn(e) {
+var x = e.clientX, y = e.clientY;
+if (self.hasMovedEnough(x, y)) {
+self.info.state = self.info.started ? e.type === 'mouseup' ? 'end' : 'track' : 'start';
+if (self.info.state === 'start') {
+Gestures.prevent('tap');
+}
+self.info.addMove({
+x: x,
+y: y
+});
+if (!hasLeftMouseButton(e)) {
+self.info.state = 'end';
+untrackDocument(self.info);
+}
+self.fire(t, e);
+self.info.started = true;
+}
+};
+var upfn = function upfn(e) {
+if (self.info.started) {
+movefn(e);
+}
+untrackDocument(self.info);
+};
+trackDocument(this.info, movefn, upfn);
+this.info.x = e.clientX;
+this.info.y = e.clientY;
+},
+touchstart: function (e) {
+var ct = e.changedTouches[0];
+this.info.x = ct.clientX;
+this.info.y = ct.clientY;
+},
+touchmove: function (e) {
+var t = Gestures.findOriginalTarget(e);
+var ct = e.changedTouches[0];
+var x = ct.clientX, y = ct.clientY;
+if (this.hasMovedEnough(x, y)) {
+if (this.info.state === 'start') {
+Gestures.prevent('tap');
+}
+this.info.addMove({
+x: x,
+y: y
+});
+this.fire(t, ct);
+this.info.state = 'track';
+this.info.started = true;
+}
+},
+touchend: function (e) {
+var t = Gestures.findOriginalTarget(e);
+var ct = e.changedTouches[0];
+if (this.info.started) {
+this.info.state = 'end';
+this.info.addMove({
+x: ct.clientX,
+y: ct.clientY
+});
+this.fire(t, ct, e);
+}
+},
+fire: function (target, touch, preventer) {
+var secondlast = this.info.moves[this.info.moves.length - 2];
+var lastmove = this.info.moves[this.info.moves.length - 1];
+var dx = lastmove.x - this.info.x;
+var dy = lastmove.y - this.info.y;
+var ddx, ddy = 0;
+if (secondlast) {
+ddx = lastmove.x - secondlast.x;
+ddy = lastmove.y - secondlast.y;
+}
+return Gestures.fire(target, 'track', {
+state: this.info.state,
+x: touch.clientX,
+y: touch.clientY,
+dx: dx,
+dy: dy,
+ddx: ddx,
+ddy: ddy,
+sourceEvent: touch,
+preventer: preventer,
+hover: function () {
+return Gestures.deepTargetFind(touch.clientX, touch.clientY);
+}
+});
+}
+});
+Gestures.register({
+name: 'tap',
+deps: [
+'mousedown',
+'click',
+'touchstart',
+'touchend'
+],
+flow: {
+start: [
+'mousedown',
+'touchstart'
+],
+end: [
+'click',
+'touchend'
+]
+},
+emits: ['tap'],
+info: {
+x: NaN,
+y: NaN,
+prevent: false
+},
+reset: function () {
+this.info.x = NaN;
+this.info.y = NaN;
+this.info.prevent = false;
+},
+save: function (e) {
+this.info.x = e.clientX;
+this.info.y = e.clientY;
+},
+mousedown: function (e) {
+if (hasLeftMouseButton(e)) {
+this.save(e);
+}
+},
+click: function (e) {
+if (hasLeftMouseButton(e)) {
+this.forward(e);
+}
+},
+touchstart: function (e) {
+this.save(e.changedTouches[0], e);
+},
+touchend: function (e) {
+this.forward(e.changedTouches[0], e);
+},
+forward: function (e, preventer) {
+var dx = Math.abs(e.clientX - this.info.x);
+var dy = Math.abs(e.clientY - this.info.y);
+var t = Gestures.findOriginalTarget(e);
+if (isNaN(dx) || isNaN(dy) || dx <= TAP_DISTANCE && dy <= TAP_DISTANCE || isSyntheticClick(e)) {
+if (!this.info.prevent) {
+Gestures.fire(t, 'tap', {
+x: e.clientX,
+y: e.clientY,
+sourceEvent: e,
+preventer: preventer
+});
+}
+}
+}
+});
+var DIRECTION_MAP = {
+x: 'pan-x',
+y: 'pan-y',
+none: 'none',
+all: 'auto'
+};
+Polymer.Base._addFeature({
+_setupGestures: function () {
+this.__polymerGestures = null;
+},
+_listen: function (node, eventName, handler) {
+if (Gestures.gestures[eventName]) {
+Gestures.add(node, eventName, handler);
+} else {
+node.addEventListener(eventName, handler);
+}
+},
+_unlisten: function (node, eventName, handler) {
+if (Gestures.gestures[eventName]) {
+Gestures.remove(node, eventName, handler);
+} else {
+node.removeEventListener(eventName, handler);
+}
+},
+setScrollDirection: function (direction, node) {
+node = node || this;
+Gestures.setTouchAction(node, DIRECTION_MAP[direction] || 'auto');
+}
+});
+Polymer.Gestures = Gestures;
+}());(function () {
+'use strict';
+Polymer.Base._addFeature({
+$$: function (slctr) {
+return Polymer.dom(this.root).querySelector(slctr);
+},
+toggleClass: function (name, bool, node) {
+node = node || this;
+if (arguments.length == 1) {
+bool = !node.classList.contains(name);
+}
+if (bool) {
+Polymer.dom(node).classList.add(name);
+} else {
+Polymer.dom(node).classList.remove(name);
+}
+},
+toggleAttribute: function (name, bool, node) {
+node = node || this;
+if (arguments.length == 1) {
+bool = !node.hasAttribute(name);
+}
+if (bool) {
+Polymer.dom(node).setAttribute(name, '');
+} else {
+Polymer.dom(node).removeAttribute(name);
+}
+},
+classFollows: function (name, toElement, fromElement) {
+if (fromElement) {
+Polymer.dom(fromElement).classList.remove(name);
+}
+if (toElement) {
+Polymer.dom(toElement).classList.add(name);
+}
+},
+attributeFollows: function (name, toElement, fromElement) {
+if (fromElement) {
+Polymer.dom(fromElement).removeAttribute(name);
+}
+if (toElement) {
+Polymer.dom(toElement).setAttribute(name, '');
+}
+},
+getEffectiveChildNodes: function () {
+return Polymer.dom(this).getEffectiveChildNodes();
+},
+getEffectiveChildren: function () {
+var list = Polymer.dom(this).getEffectiveChildNodes();
+return list.filter(function (n) {
+return n.nodeType === Node.ELEMENT_NODE;
+});
+},
+getEffectiveTextContent: function () {
+var cn = this.getEffectiveChildNodes();
+var tc = [];
+for (var i = 0, c; c = cn[i]; i++) {
+if (c.nodeType !== Node.COMMENT_NODE) {
+tc.push(Polymer.dom(c).textContent);
+}
+}
+return tc.join('');
+},
+queryEffectiveChildren: function (slctr) {
+var e$ = Polymer.dom(this).queryDistributedElements(slctr);
+return e$ && e$[0];
+},
+queryAllEffectiveChildren: function (slctr) {
+return Polymer.dom(this).queryDistributedElements(slctr);
+},
+getContentChildNodes: function (slctr) {
+var content = Polymer.dom(this.root).querySelector(slctr || 'content');
+return content ? Polymer.dom(content).getDistributedNodes() : [];
+},
+getContentChildren: function (slctr) {
+return this.getContentChildNodes(slctr).filter(function (n) {
+return n.nodeType === Node.ELEMENT_NODE;
+});
+},
+fire: function (type, detail, options) {
+options = options || Polymer.nob;
+var node = options.node || this;
+detail = detail === null || detail === undefined ? {} : detail;
+var bubbles = options.bubbles === undefined ? true : options.bubbles;
+var cancelable = Boolean(options.cancelable);
+var useCache = options._useCache;
+var event = this._getEvent(type, bubbles, cancelable, useCache);
+event.detail = detail;
+if (useCache) {
+this.__eventCache[type] = null;
+}
+node.dispatchEvent(event);
+if (useCache) {
+this.__eventCache[type] = event;
+}
+return event;
+},
+__eventCache: {},
+_getEvent: function (type, bubbles, cancelable, useCache) {
+var event = useCache && this.__eventCache[type];
+if (!event || (event.bubbles != bubbles || event.cancelable != cancelable)) {
+event = new Event(type, {
+bubbles: Boolean(bubbles),
+cancelable: cancelable
+});
+}
+return event;
+},
+async: function (callback, waitTime) {
+var self = this;
+return Polymer.Async.run(function () {
+callback.call(self);
+}, waitTime);
+},
+cancelAsync: function (handle) {
+Polymer.Async.cancel(handle);
+},
+arrayDelete: function (path, item) {
+var index;
+if (Array.isArray(path)) {
+index = path.indexOf(item);
+if (index >= 0) {
+return path.splice(index, 1);
+}
+} else {
+var arr = this._get(path);
+index = arr.indexOf(item);
+if (index >= 0) {
+return this.splice(path, index, 1);
+}
+}
+},
+transform: function (transform, node) {
+node = node || this;
+node.style.webkitTransform = transform;
+node.style.transform = transform;
+},
+translate3d: function (x, y, z, node) {
+node = node || this;
+this.transform('translate3d(' + x + ',' + y + ',' + z + ')', node);
+},
+importHref: function (href, onload, onerror, optAsync) {
+var link = document.createElement('link');
+link.rel = 'import';
+link.href = href;
+var list = Polymer.Base.importHref.imported = Polymer.Base.importHref.imported || {};
+var cached = list[link.href];
+var imprt = cached || link;
+var self = this;
+var loadListener = function (e) {
+e.target.__firedLoad = true;
+e.target.removeEventListener('load', loadListener);
+e.target.removeEventListener('error', errorListener);
+return onload.call(self, e);
+};
+var errorListener = function (e) {
+e.target.__firedError = true;
+e.target.removeEventListener('load', loadListener);
+e.target.removeEventListener('error', errorListener);
+return onerror.call(self, e);
+};
+if (onload) {
+imprt.addEventListener('load', loadListener);
+}
+if (onerror) {
+imprt.addEventListener('error', errorListener);
+}
+if (cached) {
+if (cached.__firedLoad) {
+cached.dispatchEvent(new Event('load'));
+}
+if (cached.__firedError) {
+cached.dispatchEvent(new Event('error'));
+}
+} else {
+list[link.href] = link;
+optAsync = Boolean(optAsync);
+if (optAsync) {
+link.setAttribute('async', '');
+}
+document.head.appendChild(link);
+}
+return imprt;
+},
+create: function (tag, props) {
+var elt = document.createElement(tag);
+if (props) {
+for (var n in props) {
+elt[n] = props[n];
+}
+}
+return elt;
+},
+isLightDescendant: function (node) {
+return this !== node && this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot();
+},
+isLocalDescendant: function (node) {
+return this.root === Polymer.dom(node).getOwnerRoot();
+}
+});
+if (!Polymer.Settings.useNativeCustomElements) {
+var importHref = Polymer.Base.importHref;
+Polymer.Base.importHref = function (href, onload, onerror, optAsync) {
+CustomElements.ready = false;
+var loadFn = function (e) {
+CustomElements.upgradeDocumentTree(document);
+CustomElements.ready = true;
+if (onload) {
+return onload.call(this, e);
+}
+};
+return importHref.call(this, href, loadFn, onerror, optAsync);
+};
+}
+}());Polymer.Bind = {
+prepareModel: function (model) {
+Polymer.Base.mixin(model, this._modelApi);
+},
+_modelApi: {
+_notifyChange: function (source, event, value) {
+value = value === undefined ? this[source] : value;
+event = event || Polymer.CaseMap.camelToDashCase(source) + '-changed';
+this.fire(event, { value: value }, {
+bubbles: false,
+cancelable: false,
+_useCache: Polymer.Settings.eventDataCache || !Polymer.Settings.isIE
+});
+},
+_propertySetter: function (property, value, effects, fromAbove) {
+var old = this.__data__[property];
+if (old !== value && (old === old || value === value)) {
+this.__data__[property] = value;
+if (typeof value == 'object') {
+this._clearPath(property);
+}
+if (this._propertyChanged) {
+this._propertyChanged(property, value, old);
+}
+if (effects) {
+this._effectEffects(property, value, effects, old, fromAbove);
+}
+}
+return old;
+},
+__setProperty: function (property, value, quiet, node) {
+node = node || this;
+var effects = node._propertyEffects && node._propertyEffects[property];
+if (effects) {
+node._propertySetter(property, value, effects, quiet);
+} else if (node[property] !== value) {
+node[property] = value;
+}
+},
+_effectEffects: function (property, value, effects, old, fromAbove) {
+for (var i = 0, l = effects.length, fx; i < l && (fx = effects[i]); i++) {
+fx.fn.call(this, property, this[property], fx.effect, old, fromAbove);
+}
+},
+_clearPath: function (path) {
+for (var prop in this.__data__) {
+if (Polymer.Path.isDescendant(path, prop)) {
+this.__data__[prop] = undefined;
+}
+}
+}
+},
+ensurePropertyEffects: function (model, property) {
+if (!model._propertyEffects) {
+model._propertyEffects = {};
+}
+var fx = model._propertyEffects[property];
+if (!fx) {
+fx = model._propertyEffects[property] = [];
+}
+return fx;
+},
+addPropertyEffect: function (model, property, kind, effect) {
+var fx = this.ensurePropertyEffects(model, property);
+var propEffect = {
+kind: kind,
+effect: effect,
+fn: Polymer.Bind['_' + kind + 'Effect']
+};
+fx.push(propEffect);
+return propEffect;
+},
+createBindings: function (model) {
+var fx$ = model._propertyEffects;
+if (fx$) {
+for (var n in fx$) {
+var fx = fx$[n];
+fx.sort(this._sortPropertyEffects);
+this._createAccessors(model, n, fx);
+}
+}
+},
+_sortPropertyEffects: function () {
+var EFFECT_ORDER = {
+'compute': 0,
+'annotation': 1,
+'annotatedComputation': 2,
+'reflect': 3,
+'notify': 4,
+'observer': 5,
+'complexObserver': 6,
+'function': 7
+};
+return function (a, b) {
+return EFFECT_ORDER[a.kind] - EFFECT_ORDER[b.kind];
+};
+}(),
+_createAccessors: function (model, property, effects) {
+var defun = {
+get: function () {
+return this.__data__[property];
+}
+};
+var setter = function (value) {
+this._propertySetter(property, value, effects);
+};
+var info = model.getPropertyInfo && model.getPropertyInfo(property);
+if (info && info.readOnly) {
+if (!info.computed) {
+model['_set' + this.upper(property)] = setter;
+}
+} else {
+defun.set = setter;
+}
+Object.defineProperty(model, property, defun);
+},
+upper: function (name) {
+return name[0].toUpperCase() + name.substring(1);
+},
+_addAnnotatedListener: function (model, index, property, path, event, negated) {
+if (!model._bindListeners) {
+model._bindListeners = [];
+}
+var fn = this._notedListenerFactory(property, path, Polymer.Path.isDeep(path), negated);
+var eventName = event || Polymer.CaseMap.camelToDashCase(property) + '-changed';
+model._bindListeners.push({
+index: index,
+property: property,
+path: path,
+changedFn: fn,
+event: eventName
+});
+},
+_isEventBogus: function (e, target) {
+return e.path && e.path[0] !== target;
+},
+_notedListenerFactory: function (property, path, isStructured, negated) {
+return function (target, value, targetPath) {
+if (targetPath) {
+var newPath = Polymer.Path.translate(property, path, targetPath);
+this._notifyPath(newPath, value);
+} else {
+value = target[property];
+if (negated) {
+value = !value;
+}
+if (!isStructured) {
+this[path] = value;
+} else {
+if (this.__data__[path] != value) {
+this.set(path, value);
+}
+}
+}
+};
+},
+prepareInstance: function (inst) {
+inst.__data__ = Object.create(null);
+},
+setupBindListeners: function (inst) {
+var b$ = inst._bindListeners;
+for (var i = 0, l = b$.length, info; i < l && (info = b$[i]); i++) {
+var node = inst._nodes[info.index];
+this._addNotifyListener(node, inst, info.event, info.changedFn);
+}
+},
+_addNotifyListener: function (element, context, event, changedFn) {
+element.addEventListener(event, function (e) {
+return context._notifyListener(changedFn, e);
+});
+}
+};Polymer.Base.mixin(Polymer.Bind, {
+_shouldAddListener: function (effect) {
+return effect.name && effect.kind != 'attribute' && effect.kind != 'text' && !effect.isCompound && effect.parts[0].mode === '{';
+},
+_annotationEffect: function (source, value, effect) {
+if (source != effect.value) {
+value = this._get(effect.value);
+this.__data__[effect.value] = value;
+}
+this._applyEffectValue(effect, value);
+},
+_reflectEffect: function (source, value, effect) {
+this.reflectPropertyToAttribute(source, effect.attribute, value);
+},
+_notifyEffect: function (source, value, effect, old, fromAbove) {
+if (!fromAbove) {
+this._notifyChange(source, effect.event, value);
+}
+},
+_functionEffect: function (source, value, fn, old, fromAbove) {
+fn.call(this, source, value, old, fromAbove);
+},
+_observerEffect: function (source, value, effect, old) {
+var fn = this[effect.method];
+if (fn) {
+fn.call(this, value, old);
+} else {
+this._warn(this._logf('_observerEffect', 'observer method `' + effect.method + '` not defined'));
+}
+},
+_complexObserverEffect: function (source, value, effect) {
+var fn = this[effect.method];
+if (fn) {
+var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
+if (args) {
+fn.apply(this, args);
+}
+} else if (effect.dynamicFn) {
+} else {
+this._warn(this._logf('_complexObserverEffect', 'observer method `' + effect.method + '` not defined'));
+}
+},
+_computeEffect: function (source, value, effect) {
+var fn = this[effect.method];
+if (fn) {
+var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
+if (args) {
+var computedvalue = fn.apply(this, args);
+this.__setProperty(effect.name, computedvalue);
+}
+} else if (effect.dynamicFn) {
+} else {
+this._warn(this._logf('_computeEffect', 'compute method `' + effect.method + '` not defined'));
+}
+},
+_annotatedComputationEffect: function (source, value, effect) {
+var computedHost = this._rootDataHost || this;
+var fn = computedHost[effect.method];
+if (fn) {
+var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value);
+if (args) {
+var computedvalue = fn.apply(computedHost, args);
+this._applyEffectValue(effect, computedvalue);
+}
+} else if (effect.dynamicFn) {
+} else {
+computedHost._warn(computedHost._logf('_annotatedComputationEffect', 'compute method `' + effect.method + '` not defined'));
+}
+},
+_marshalArgs: function (model, effect, path, value) {
+var values = [];
+var args = effect.args;
+var bailoutEarly = args.length > 1 || effect.dynamicFn;
+for (var i = 0, l = args.length; i < l; i++) {
+var arg = args[i];
+var name = arg.name;
+var v;
+if (arg.literal) {
+v = arg.value;
+} else if (path === name) {
+v = value;
+} else {
+v = model[name];
+if (v === undefined && arg.structured) {
+v = Polymer.Base._get(name, model);
+}
+}
+if (bailoutEarly && v === undefined) {
+return;
+}
+if (arg.wildcard) {
+var matches = Polymer.Path.isAncestor(path, name);
+values[i] = {
+path: matches ? path : name,
+value: matches ? value : v,
+base: v
+};
+} else {
+values[i] = v;
+}
+}
+return values;
+}
+});Polymer.Base._addFeature({
+_addPropertyEffect: function (property, kind, effect) {
+var prop = Polymer.Bind.addPropertyEffect(this, property, kind, effect);
+prop.pathFn = this['_' + prop.kind + 'PathEffect'];
+},
+_prepEffects: function () {
+Polymer.Bind.prepareModel(this);
+this._addAnnotationEffects(this._notes);
+},
+_prepBindings: function () {
+Polymer.Bind.createBindings(this);
+},
+_addPropertyEffects: function (properties) {
+if (properties) {
+for (var p in properties) {
+var prop = properties[p];
+if (prop.observer) {
+this._addObserverEffect(p, prop.observer);
+}
+if (prop.computed) {
+prop.readOnly = true;
+this._addComputedEffect(p, prop.computed);
+}
+if (prop.notify) {
+this._addPropertyEffect(p, 'notify', { event: Polymer.CaseMap.camelToDashCase(p) + '-changed' });
+}
+if (prop.reflectToAttribute) {
+var attr = Polymer.CaseMap.camelToDashCase(p);
+if (attr[0] === '-') {
+this._warn(this._logf('_addPropertyEffects', 'Property ' + p + ' cannot be reflected to attribute ' + attr + ' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.'));
+} else {
+this._addPropertyEffect(p, 'reflect', { attribute: attr });
+}
+}
+if (prop.readOnly) {
+Polymer.Bind.ensurePropertyEffects(this, p);
+}
+}
+}
+},
+_addComputedEffect: function (name, expression) {
+var sig = this._parseMethod(expression);
+var dynamicFn = sig.dynamicFn;
+for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) {
+this._addPropertyEffect(arg.model, 'compute', {
+method: sig.method,
+args: sig.args,
+trigger: arg,
+name: name,
+dynamicFn: dynamicFn
+});
+}
+if (dynamicFn) {
+this._addPropertyEffect(sig.method, 'compute', {
+method: sig.method,
+args: sig.args,
+trigger: null,
+name: name,
+dynamicFn: dynamicFn
+});
+}
+},
+_addObserverEffect: function (property, observer) {
+this._addPropertyEffect(property, 'observer', {
+method: observer,
+property: property
+});
+},
+_addComplexObserverEffects: function (observers) {
+if (observers) {
+for (var i = 0, o; i < observers.length && (o = observers[i]); i++) {
+this._addComplexObserverEffect(o);
+}
+}
+},
+_addComplexObserverEffect: function (observer) {
+var sig = this._parseMethod(observer);
+if (!sig) {
+throw new Error('Malformed observer expression \'' + observer + '\'');
+}
+var dynamicFn = sig.dynamicFn;
+for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) {
+this._addPropertyEffect(arg.model, 'complexObserver', {
+method: sig.method,
+args: sig.args,
+trigger: arg,
+dynamicFn: dynamicFn
+});
+}
+if (dynamicFn) {
+this._addPropertyEffect(sig.method, 'complexObserver', {
+method: sig.method,
+args: sig.args,
+trigger: null,
+dynamicFn: dynamicFn
+});
+}
+},
+_addAnnotationEffects: function (notes) {
+for (var i = 0, note; i < notes.length && (note = notes[i]); i++) {
+var b$ = note.bindings;
+for (var j = 0, binding; j < b$.length && (binding = b$[j]); j++) {
+this._addAnnotationEffect(binding, i);
+}
+}
+},
+_addAnnotationEffect: function (note, index) {
+if (Polymer.Bind._shouldAddListener(note)) {
+Polymer.Bind._addAnnotatedListener(this, index, note.name, note.parts[0].value, note.parts[0].event, note.parts[0].negate);
+}
+for (var i = 0; i < note.parts.length; i++) {
+var part = note.parts[i];
+if (part.signature) {
+this._addAnnotatedComputationEffect(note, part, index);
+} else if (!part.literal) {
+if (note.kind === 'attribute' && note.name[0] === '-') {
+this._warn(this._logf('_addAnnotationEffect', 'Cannot set attribute ' + note.name + ' because "-" is not a valid attribute starting character'));
+} else {
+this._addPropertyEffect(part.model, 'annotation', {
+kind: note.kind,
+index: index,
+name: note.name,
+propertyName: note.propertyName,
+value: part.value,
+isCompound: note.isCompound,
+compoundIndex: part.compoundIndex,
+event: part.event,
+customEvent: part.customEvent,
+negate: part.negate
+});
+}
+}
+}
+},
+_addAnnotatedComputationEffect: function (note, part, index) {
+var sig = part.signature;
+if (sig.static) {
+this.__addAnnotatedComputationEffect('__static__', index, note, part, null);
+} else {
+for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) {
+if (!arg.literal) {
+this.__addAnnotatedComputationEffect(arg.model, index, note, part, arg);
+}
+}
+if (sig.dynamicFn) {
+this.__addAnnotatedComputationEffect(sig.method, index, note, part, null);
+}
+}
+},
+__addAnnotatedComputationEffect: function (property, index, note, part, trigger) {
+this._addPropertyEffect(property, 'annotatedComputation', {
+index: index,
+isCompound: note.isCompound,
+compoundIndex: part.compoundIndex,
+kind: note.kind,
+name: note.name,
+negate: part.negate,
+method: part.signature.method,
+args: part.signature.args,
+trigger: trigger,
+dynamicFn: part.signature.dynamicFn
+});
+},
+_parseMethod: function (expression) {
+var m = expression.match(/([^\s]+?)\(([\s\S]*)\)/);
+if (m) {
+var sig = {
+method: m[1],
+static: true
+};
+if (this.getPropertyInfo(sig.method) !== Polymer.nob) {
+sig.static = false;
+sig.dynamicFn = true;
+}
+if (m[2].trim()) {
+var args = m[2].replace(/\\,/g, '&comma;').split(',');
+return this._parseArgs(args, sig);
+} else {
+sig.args = Polymer.nar;
+return sig;
+}
+}
+},
+_parseArgs: function (argList, sig) {
+sig.args = argList.map(function (rawArg) {
+var arg = this._parseArg(rawArg);
+if (!arg.literal) {
+sig.static = false;
+}
+return arg;
+}, this);
+return sig;
+},
+_parseArg: function (rawArg) {
+var arg = rawArg.trim().replace(/&comma;/g, ',').replace(/\\(.)/g, '$1');
+var a = { name: arg };
+var fc = arg[0];
+if (fc === '-') {
+fc = arg[1];
+}
+if (fc >= '0' && fc <= '9') {
+fc = '#';
+}
+switch (fc) {
+case '\'':
+case '"':
+a.value = arg.slice(1, -1);
+a.literal = true;
+break;
+case '#':
+a.value = Number(arg);
+a.literal = true;
+break;
+}
+if (!a.literal) {
+a.model = Polymer.Path.root(arg);
+a.structured = Polymer.Path.isDeep(arg);
+if (a.structured) {
+a.wildcard = arg.slice(-2) == '.*';
+if (a.wildcard) {
+a.name = arg.slice(0, -2);
+}
+}
+}
+return a;
+},
+_marshalInstanceEffects: function () {
+Polymer.Bind.prepareInstance(this);
+if (this._bindListeners) {
+Polymer.Bind.setupBindListeners(this);
+}
+},
+_applyEffectValue: function (info, value) {
+var node = this._nodes[info.index];
+var property = info.name;
+value = this._computeFinalAnnotationValue(node, property, value, info);
+if (info.kind == 'attribute') {
+this.serializeValueToAttribute(value, property, node);
+} else {
+var pinfo = node._propertyInfo && node._propertyInfo[property];
+if (pinfo && pinfo.readOnly) {
+return;
+}
+this.__setProperty(property, value, Polymer.Settings.suppressBindingNotifications, node);
+}
+},
+_computeFinalAnnotationValue: function (node, property, value, info) {
+if (info.negate) {
+value = !value;
+}
+if (info.isCompound) {
+var storage = node.__compoundStorage__[property];
+storage[info.compoundIndex] = value;
+value = storage.join('');
+}
+if (info.kind !== 'attribute') {
+if (property === 'className') {
+value = this._scopeElementClass(node, value);
+}
+if (property === 'textContent' || node.localName == 'input' && property == 'value') {
+value = value == undefined ? '' : value;
+}
+}
+return value;
+},
+_executeStaticEffects: function () {
+if (this._propertyEffects && this._propertyEffects.__static__) {
+this._effectEffects('__static__', null, this._propertyEffects.__static__);
+}
+}
+});(function () {
+var usePolyfillProto = Polymer.Settings.usePolyfillProto;
+var avoidInstanceProperties = Boolean(Object.getOwnPropertyDescriptor(document.documentElement, 'properties'));
+Polymer.Base._addFeature({
+_setupConfigure: function (initialConfig) {
+this._config = {};
+this._handlers = [];
+this._aboveConfig = null;
+if (initialConfig) {
+for (var i in initialConfig) {
+if (initialConfig[i] !== undefined) {
+this._config[i] = initialConfig[i];
+}
+}
+}
+},
+_marshalAttributes: function () {
+this._takeAttributesToModel(this._config);
+},
+_attributeChangedImpl: function (name) {
+var model = this._clientsReadied ? this : this._config;
+this._setAttributeToProperty(model, name);
+},
+_configValue: function (name, value) {
+var info = this._propertyInfo[name];
+if (!info || !info.readOnly) {
+this._config[name] = value;
+}
+},
+_beforeClientsReady: function () {
+this._configure();
+},
+_configure: function () {
+this._configureAnnotationReferences();
+this._configureInstanceProperties();
+this._aboveConfig = this.mixin({}, this._config);
+var config = {};
+for (var i = 0; i < this.behaviors.length; i++) {
+this._configureProperties(this.behaviors[i].properties, config);
+}
+this._configureProperties(avoidInstanceProperties ? this.__proto__.properties : this.properties, config);
+this.mixin(config, this._aboveConfig);
+this._config = config;
+if (this._clients && this._clients.length) {
+this._distributeConfig(this._config);
+}
+},
+_configureInstanceProperties: function () {
+for (var i in this._propertyEffects) {
+if (!usePolyfillProto && this.hasOwnProperty(i)) {
+this._configValue(i, this[i]);
+delete this[i];
+}
+}
+},
+_configureProperties: function (properties, config) {
+for (var i in properties) {
+var c = properties[i];
+if (c.value !== undefined) {
+var value = c.value;
+if (typeof value == 'function') {
+value = value.call(this, this._config);
+}
+config[i] = value;
+}
+}
+},
+_distributeConfig: function (config) {
+var fx$ = this._propertyEffects;
+if (fx$) {
+for (var p in config) {
+var fx = fx$[p];
+if (fx) {
+for (var i = 0, l = fx.length, x; i < l && (x = fx[i]); i++) {
+if (x.kind === 'annotation') {
+var node = this._nodes[x.effect.index];
+var name = x.effect.propertyName;
+var isAttr = x.effect.kind == 'attribute';
+var hasEffect = node._propertyEffects && node._propertyEffects[name];
+if (node._configValue && (hasEffect || !isAttr)) {
+var value = p === x.effect.value ? config[p] : this._get(x.effect.value, config);
+value = this._computeFinalAnnotationValue(node, name, value, x.effect);
+if (isAttr) {
+value = node.deserialize(this.serialize(value), node._propertyInfo[name].type);
+}
+node._configValue(name, value);
+}
+}
+}
+}
+}
+}
+},
+_afterClientsReady: function () {
+this.importPath = this._importPath;
+this.rootPath = Polymer.rootPath;
+this._executeStaticEffects();
+this._applyConfig(this._config, this._aboveConfig);
+this._flushHandlers();
+},
+_applyConfig: function (config, aboveConfig) {
+for (var n in config) {
+if (this[n] === undefined) {
+this.__setProperty(n, config[n], n in aboveConfig);
+}
+}
+},
+_notifyListener: function (fn, e) {
+if (!Polymer.Bind._isEventBogus(e, e.target)) {
+var value, path;
+if (e.detail) {
+value = e.detail.value;
+path = e.detail.path;
+}
+if (!this._clientsReadied) {
+this._queueHandler([
+fn,
+e.target,
+value,
+path
+]);
+} else {
+return fn.call(this, e.target, value, path);
+}
+}
+},
+_queueHandler: function (args) {
+this._handlers.push(args);
+},
+_flushHandlers: function () {
+var h$ = this._handlers;
+for (var i = 0, l = h$.length, h; i < l && (h = h$[i]); i++) {
+h[0].call(this, h[1], h[2], h[3]);
+}
+this._handlers = [];
+}
+});
+}());(function () {
+'use strict';
+var Path = Polymer.Path;
+Polymer.Base._addFeature({
+notifyPath: function (path, value, fromAbove) {
+var info = {};
+var v = this._get(path, this, info);
+if (arguments.length === 1) {
+value = v;
+}
+if (info.path) {
+this._notifyPath(info.path, value, fromAbove);
+}
+},
+_notifyPath: function (path, value, fromAbove) {
+var old = this._propertySetter(path, value);
+if (old !== value && (old === old || value === value)) {
+this._pathEffector(path, value);
+if (!fromAbove) {
+this._notifyPathUp(path, value);
+}
+return true;
+}
+},
+_getPathParts: function (path) {
+if (Array.isArray(path)) {
+var parts = [];
+for (var i = 0; i < path.length; i++) {
+var args = path[i].toString().split('.');
+for (var j = 0; j < args.length; j++) {
+parts.push(args[j]);
+}
+}
+return parts;
+} else {
+return path.toString().split('.');
+}
+},
+set: function (path, value, root) {
+var prop = root || this;
+var parts = this._getPathParts(path);
+var array;
+var last = parts[parts.length - 1];
+if (parts.length > 1) {
+for (var i = 0; i < parts.length - 1; i++) {
+var part = parts[i];
+if (array && part[0] == '#') {
+prop = Polymer.Collection.get(array).getItem(part);
+} else {
+prop = prop[part];
+if (array && parseInt(part, 10) == part) {
+parts[i] = Polymer.Collection.get(array).getKey(prop);
+}
+}
+if (!prop) {
+return;
+}
+array = Array.isArray(prop) ? prop : null;
+}
+if (array) {
+var coll = Polymer.Collection.get(array);
+var old, key;
+if (last[0] == '#') {
+key = last;
+old = coll.getItem(key);
+last = array.indexOf(old);
+coll.setItem(key, value);
+} else if (parseInt(last, 10) == last) {
+old = prop[last];
+key = coll.getKey(old);
+parts[i] = key;
+coll.setItem(key, value);
+}
+}
+prop[last] = value;
+if (!root) {
+this._notifyPath(parts.join('.'), value);
+}
+} else {
+prop[path] = value;
+}
+},
+get: function (path, root) {
+return this._get(path, root);
+},
+_get: function (path, root, info) {
+var prop = root || this;
+var parts = this._getPathParts(path);
+var array;
+for (var i = 0; i < parts.length; i++) {
+if (!prop) {
+return;
+}
+var part = parts[i];
+if (array && part[0] == '#') {
+prop = Polymer.Collection.get(array).getItem(part);
+} else {
+prop = prop[part];
+if (info && array && parseInt(part, 10) == part) {
+parts[i] = Polymer.Collection.get(array).getKey(prop);
+}
+}
+array = Array.isArray(prop) ? prop : null;
+}
+if (info) {
+info.path = parts.join('.');
+}
+return prop;
+},
+_pathEffector: function (path, value) {
+var model = Path.root(path);
+var fx$ = this._propertyEffects && this._propertyEffects[model];
+if (fx$) {
+for (var i = 0, fx; i < fx$.length && (fx = fx$[i]); i++) {
+var fxFn = fx.pathFn;
+if (fxFn) {
+fxFn.call(this, path, value, fx.effect);
+}
+}
+}
+if (this._boundPaths) {
+this._notifyBoundPaths(path, value);
+}
+},
+_annotationPathEffect: function (path, value, effect) {
+if (Path.matches(effect.value, false, path)) {
+Polymer.Bind._annotationEffect.call(this, path, value, effect);
+} else if (!effect.negate && Path.isDescendant(effect.value, path)) {
+var node = this._nodes[effect.index];
+if (node && node._notifyPath) {
+var newPath = Path.translate(effect.value, effect.name, path);
+node._notifyPath(newPath, value, true);
+}
+}
+},
+_complexObserverPathEffect: function (path, value, effect) {
+if (Path.matches(effect.trigger.name, effect.trigger.wildcard, path)) {
+Polymer.Bind._complexObserverEffect.call(this, path, value, effect);
+}
+},
+_computePathEffect: function (path, value, effect) {
+if (Path.matches(effect.trigger.name, effect.trigger.wildcard, path)) {
+Polymer.Bind._computeEffect.call(this, path, value, effect);
+}
+},
+_annotatedComputationPathEffect: function (path, value, effect) {
+if (Path.matches(effect.trigger.name, effect.trigger.wildcard, path)) {
+Polymer.Bind._annotatedComputationEffect.call(this, path, value, effect);
+}
+},
+linkPaths: function (to, from) {
+this._boundPaths = this._boundPaths || {};
+if (from) {
+this._boundPaths[to] = from;
+} else {
+this.unlinkPaths(to);
+}
+},
+unlinkPaths: function (path) {
+if (this._boundPaths) {
+delete this._boundPaths[path];
+}
+},
+_notifyBoundPaths: function (path, value) {
+for (var a in this._boundPaths) {
+var b = this._boundPaths[a];
+if (Path.isDescendant(a, path)) {
+this._notifyPath(Path.translate(a, b, path), value);
+} else if (Path.isDescendant(b, path)) {
+this._notifyPath(Path.translate(b, a, path), value);
+}
+}
+},
+_notifyPathUp: function (path, value) {
+var rootName = Path.root(path);
+var dashCaseName = Polymer.CaseMap.camelToDashCase(rootName);
+var eventName = dashCaseName + this._EVENT_CHANGED;
+this.fire(eventName, {
+path: path,
+value: value
+}, {
+bubbles: false,
+_useCache: Polymer.Settings.eventDataCache || !Polymer.Settings.isIE
+});
+},
+_EVENT_CHANGED: '-changed',
+notifySplices: function (path, splices) {
+var info = {};
+var array = this._get(path, this, info);
+this._notifySplices(array, info.path, splices);
+},
+_notifySplices: function (array, path, splices) {
+var change = {
+keySplices: Polymer.Collection.applySplices(array, splices),
+indexSplices: splices
+};
+var splicesPath = path + '.splices';
+this._notifyPath(splicesPath, change);
+this._notifyPath(path + '.length', array.length);
+this.__data__[splicesPath] = {
+keySplices: null,
+indexSplices: null
+};
+},
+_notifySplice: function (array, path, index, added, removed) {
+this._notifySplices(array, path, [{
+index: index,
+addedCount: added,
+removed: removed,
+object: array,
+type: 'splice'
+}]);
+},
+push: function (path) {
+var info = {};
+var array = this._get(path, this, info);
+var args = Array.prototype.slice.call(arguments, 1);
+var len = array.length;
+var ret = array.push.apply(array, args);
+if (args.length) {
+this._notifySplice(array, info.path, len, args.length, []);
+}
+return ret;
+},
+pop: function (path) {
+var info = {};
+var array = this._get(path, this, info);
+var hadLength = Boolean(array.length);
+var args = Array.prototype.slice.call(arguments, 1);
+var ret = array.pop.apply(array, args);
+if (hadLength) {
+this._notifySplice(array, info.path, array.length, 0, [ret]);
+}
+return ret;
+},
+splice: function (path, start) {
+var info = {};
+var array = this._get(path, this, info);
+if (start < 0) {
+start = array.length - Math.floor(-start);
+} else {
+start = Math.floor(start);
+}
+if (!start) {
+start = 0;
+}
+var args = Array.prototype.slice.call(arguments, 1);
+var ret = array.splice.apply(array, args);
+var addedCount = Math.max(args.length - 2, 0);
+if (addedCount || ret.length) {
+this._notifySplice(array, info.path, start, addedCount, ret);
+}
+return ret;
+},
+shift: function (path) {
+var info = {};
+var array = this._get(path, this, info);
+var hadLength = Boolean(array.length);
+var args = Array.prototype.slice.call(arguments, 1);
+var ret = array.shift.apply(array, args);
+if (hadLength) {
+this._notifySplice(array, info.path, 0, 0, [ret]);
+}
+return ret;
+},
+unshift: function (path) {
+var info = {};
+var array = this._get(path, this, info);
+var args = Array.prototype.slice.call(arguments, 1);
+var ret = array.unshift.apply(array, args);
+if (args.length) {
+this._notifySplice(array, info.path, 0, args.length, []);
+}
+return ret;
+},
+prepareModelNotifyPath: function (model) {
+this.mixin(model, {
+fire: Polymer.Base.fire,
+_getEvent: Polymer.Base._getEvent,
+__eventCache: Polymer.Base.__eventCache,
+notifyPath: Polymer.Base.notifyPath,
+_get: Polymer.Base._get,
+_EVENT_CHANGED: Polymer.Base._EVENT_CHANGED,
+_notifyPath: Polymer.Base._notifyPath,
+_notifyPathUp: Polymer.Base._notifyPathUp,
+_pathEffector: Polymer.Base._pathEffector,
+_annotationPathEffect: Polymer.Base._annotationPathEffect,
+_complexObserverPathEffect: Polymer.Base._complexObserverPathEffect,
+_annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect,
+_computePathEffect: Polymer.Base._computePathEffect,
+_notifyBoundPaths: Polymer.Base._notifyBoundPaths,
+_getPathParts: Polymer.Base._getPathParts
+});
+}
+});
+}());Polymer.Base._addFeature({
+resolveUrl: function (url) {
+return Polymer.ResolveUrl.resolveUrl(url, this._importPath);
+}
+});Polymer.CssParse = function () {
+return {
+parse: function (text) {
+text = this._clean(text);
+return this._parseCss(this._lex(text), text);
+},
+_clean: function (cssText) {
+return cssText.replace(this._rx.comments, '').replace(this._rx.port, '');
+},
+_lex: function (text) {
+var root = {
+start: 0,
+end: text.length
+};
+var n = root;
+for (var i = 0, l = text.length; i < l; i++) {
+switch (text[i]) {
+case this.OPEN_BRACE:
+if (!n.rules) {
+n.rules = [];
+}
+var p = n;
+var previous = p.rules[p.rules.length - 1];
+n = {
+start: i + 1,
+parent: p,
+previous: previous
+};
+p.rules.push(n);
+break;
+case this.CLOSE_BRACE:
+n.end = i + 1;
+n = n.parent || root;
+break;
+}
+}
+return root;
+},
+_parseCss: function (node, text) {
+var t = text.substring(node.start, node.end - 1);
+node.parsedCssText = node.cssText = t.trim();
+if (node.parent) {
+var ss = node.previous ? node.previous.end : node.parent.start;
+t = text.substring(ss, node.start - 1);
+t = this._expandUnicodeEscapes(t);
+t = t.replace(this._rx.multipleSpaces, ' ');
+t = t.substring(t.lastIndexOf(';') + 1);
+var s = node.parsedSelector = node.selector = t.trim();
+node.atRule = s.indexOf(this.AT_START) === 0;
+if (node.atRule) {
+if (s.indexOf(this.MEDIA_START) === 0) {
+node.type = this.types.MEDIA_RULE;
+} else if (s.match(this._rx.keyframesRule)) {
+node.type = this.types.KEYFRAMES_RULE;
+node.keyframesName = node.selector.split(this._rx.multipleSpaces).pop();
+}
+} else {
+if (s.indexOf(this.VAR_START) === 0) {
+node.type = this.types.MIXIN_RULE;
+} else {
+node.type = this.types.STYLE_RULE;
+}
+}
+}
+var r$ = node.rules;
+if (r$) {
+for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
+this._parseCss(r, text);
+}
+}
+return node;
+},
+_expandUnicodeEscapes: function (s) {
+return s.replace(/\\([0-9a-f]{1,6})\s/gi, function () {
+var code = arguments[1], repeat = 6 - code.length;
+while (repeat--) {
+code = '0' + code;
+}
+return '\\' + code;
+});
+},
+stringify: function (node, preserveProperties, text) {
+text = text || '';
+var cssText = '';
+if (node.cssText || node.rules) {
+var r$ = node.rules;
+if (r$ && !this._hasMixinRules(r$)) {
+for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
+cssText = this.stringify(r, preserveProperties, cssText);
+}
+} else {
+cssText = preserveProperties ? node.cssText : this.removeCustomProps(node.cssText);
+cssText = cssText.trim();
+if (cssText) {
+cssText = ' ' + cssText + '\n';
+}
+}
+}
+if (cssText) {
+if (node.selector) {
+text += node.selector + ' ' + this.OPEN_BRACE + '\n';
+}
+text += cssText;
+if (node.selector) {
+text += this.CLOSE_BRACE + '\n\n';
+}
+}
+return text;
+},
+_hasMixinRules: function (rules) {
+return rules[0].selector.indexOf(this.VAR_START) === 0;
+},
+removeCustomProps: function (cssText) {
+cssText = this.removeCustomPropAssignment(cssText);
+return this.removeCustomPropApply(cssText);
+},
+removeCustomPropAssignment: function (cssText) {
+return cssText.replace(this._rx.customProp, '').replace(this._rx.mixinProp, '');
+},
+removeCustomPropApply: function (cssText) {
+return cssText.replace(this._rx.mixinApply, '').replace(this._rx.varApply, '');
+},
+types: {
+STYLE_RULE: 1,
+KEYFRAMES_RULE: 7,
+MEDIA_RULE: 4,
+MIXIN_RULE: 1000
+},
+OPEN_BRACE: '{',
+CLOSE_BRACE: '}',
+_rx: {
+comments: /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,
+port: /@import[^;]*;/gim,
+customProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,
+mixinProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,
+mixinApply: /@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,
+varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,
+keyframesRule: /^@[^\s]*keyframes/,
+multipleSpaces: /\s+/g
+},
+VAR_START: '--',
+MEDIA_START: '@media',
+AT_START: '@'
+};
+}();Polymer.StyleUtil = function () {
+var settings = Polymer.Settings;
+return {
+unscopedStyleImports: new WeakMap(),
+SHADY_UNSCOPED_ATTR: 'shady-unscoped',
+NATIVE_VARIABLES: Polymer.Settings.useNativeCSSProperties,
+MODULE_STYLES_SELECTOR: 'style, link[rel=import][type~=css], template',
+INCLUDE_ATTR: 'include',
+toCssText: function (rules, callback) {
+if (typeof rules === 'string') {
+rules = this.parser.parse(rules);
+}
+if (callback) {
+this.forEachRule(rules, callback);
+}
+return this.parser.stringify(rules, this.NATIVE_VARIABLES);
+},
+forRulesInStyles: function (styles, styleRuleCallback, keyframesRuleCallback) {
+if (styles) {
+for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
+this.forEachRuleInStyle(s, styleRuleCallback, keyframesRuleCallback);
+}
+}
+},
+forActiveRulesInStyles: function (styles, styleRuleCallback, keyframesRuleCallback) {
+if (styles) {
+for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
+this.forEachRuleInStyle(s, styleRuleCallback, keyframesRuleCallback, true);
+}
+}
+},
+rulesForStyle: function (style) {
+if (!style.__cssRules && style.textContent) {
+style.__cssRules = this.parser.parse(style.textContent);
+}
+return style.__cssRules;
+},
+isKeyframesSelector: function (rule) {
+return rule.parent && rule.parent.type === this.ruleTypes.KEYFRAMES_RULE;
+},
+forEachRuleInStyle: function (style, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) {
+var rules = this.rulesForStyle(style);
+var styleCallback, keyframeCallback;
+if (styleRuleCallback) {
+styleCallback = function (rule) {
+styleRuleCallback(rule, style);
+};
+}
+if (keyframesRuleCallback) {
+keyframeCallback = function (rule) {
+keyframesRuleCallback(rule, style);
+};
+}
+this.forEachRule(rules, styleCallback, keyframeCallback, onlyActiveRules);
+},
+forEachRule: function (node, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) {
+if (!node) {
+return;
+}
+var skipRules = false;
+if (onlyActiveRules) {
+if (node.type === this.ruleTypes.MEDIA_RULE) {
+var matchMedia = node.selector.match(this.rx.MEDIA_MATCH);
+if (matchMedia) {
+if (!window.matchMedia(matchMedia[1]).matches) {
+skipRules = true;
+}
+}
+}
+}
+if (node.type === this.ruleTypes.STYLE_RULE) {
+styleRuleCallback(node);
+} else if (keyframesRuleCallback && node.type === this.ruleTypes.KEYFRAMES_RULE) {
+keyframesRuleCallback(node);
+} else if (node.type === this.ruleTypes.MIXIN_RULE) {
+skipRules = true;
+}
+var r$ = node.rules;
+if (r$ && !skipRules) {
+for (var i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
+this.forEachRule(r, styleRuleCallback, keyframesRuleCallback, onlyActiveRules);
+}
+}
+},
+applyCss: function (cssText, moniker, target, contextNode) {
+var style = this.createScopeStyle(cssText, moniker);
+return this.applyStyle(style, target, contextNode);
+},
+applyStyle: function (style, target, contextNode) {
+target = target || document.head;
+var after = contextNode && contextNode.nextSibling || target.firstChild;
+this.__lastHeadApplyNode = style;
+return target.insertBefore(style, after);
+},
+createScopeStyle: function (cssText, moniker) {
+var style = document.createElement('style');
+if (moniker) {
+style.setAttribute('scope', moniker);
+}
+style.textContent = cssText;
+return style;
+},
+__lastHeadApplyNode: null,
+applyStylePlaceHolder: function (moniker) {
+var placeHolder = document.createComment(' Shady DOM styles for ' + moniker + ' ');
+var after = this.__lastHeadApplyNode ? this.__lastHeadApplyNode.nextSibling : null;
+var scope = document.head;
+scope.insertBefore(placeHolder, after || scope.firstChild);
+this.__lastHeadApplyNode = placeHolder;
+return placeHolder;
+},
+cssFromModules: function (moduleIds, warnIfNotFound) {
+var modules = moduleIds.trim().split(/\s+/);
+var cssText = '';
+for (var i = 0; i < modules.length; i++) {
+cssText += this.cssFromModule(modules[i], warnIfNotFound);
+}
+return cssText;
+},
+cssFromModule: function (moduleId, warnIfNotFound) {
+var m = Polymer.DomModule.import(moduleId);
+if (m && !m._cssText) {
+m._cssText = this.cssFromElement(m);
+}
+if (!m && warnIfNotFound) {
+console.warn('Could not find style data in module named', moduleId);
+}
+return m && m._cssText || '';
+},
+cssFromElement: function (element) {
+var cssText = '';
+var content = element.content || element;
+var e$ = Polymer.TreeApi.arrayCopy(content.querySelectorAll(this.MODULE_STYLES_SELECTOR));
+for (var i = 0, e; i < e$.length; i++) {
+e = e$[i];
+if (e.localName === 'template') {
+if (!e.hasAttribute('preserve-content')) {
+cssText += this.cssFromElement(e);
+}
+} else {
+if (e.localName === 'style') {
+var include = e.getAttribute(this.INCLUDE_ATTR);
+if (include) {
+cssText += this.cssFromModules(include, true);
+}
+e = e.__appliedElement || e;
+e.parentNode.removeChild(e);
+var css = this.resolveCss(e.textContent, element.ownerDocument);
+if (!settings.useNativeShadow && e.hasAttribute(this.SHADY_UNSCOPED_ATTR)) {
+e.textContent = css;
+document.head.insertBefore(e, document.head.firstChild);
+} else {
+cssText += css;
+}
+} else if (e.import && e.import.body) {
+var importCss = this.resolveCss(e.import.body.textContent, e.import);
+if (!settings.useNativeShadow && e.hasAttribute(this.SHADY_UNSCOPED_ATTR)) {
+if (!this.unscopedStyleImports.has(e.import)) {
+this.unscopedStyleImports.set(e.import, true);
+var importStyle = document.createElement('style');
+importStyle.setAttribute(this.SHADY_UNSCOPED_ATTR, '');
+importStyle.textContent = importCss;
+document.head.insertBefore(importStyle, document.head.firstChild);
+}
+} else {
+cssText += importCss;
+}
+}
+}
+}
+return cssText;
+},
+styleIncludesToTemplate: function (targetTemplate) {
+var styles = targetTemplate.content.querySelectorAll('style[include]');
+for (var i = 0, s; i < styles.length; i++) {
+s = styles[i];
+s.parentNode.insertBefore(this._includesToFragment(s.getAttribute('include')), s);
+}
+},
+_includesToFragment: function (styleIncludes) {
+var includeArray = styleIncludes.trim().split(' ');
+var frag = document.createDocumentFragment();
+for (var i = 0; i < includeArray.length; i++) {
+var t = Polymer.DomModule.import(includeArray[i], 'template');
+if (t) {
+this._addStylesToFragment(frag, t.content);
+}
+}
+return frag;
+},
+_addStylesToFragment: function (frag, source) {
+var s$ = source.querySelectorAll('style');
+for (var i = 0, s; i < s$.length; i++) {
+s = s$[i];
+var include = s.getAttribute('include');
+if (include) {
+frag.appendChild(this._includesToFragment(include));
+}
+if (s.textContent) {
+frag.appendChild(s.cloneNode(true));
+}
+}
+},
+isTargetedBuild: function (buildType) {
+return settings.useNativeShadow ? buildType === 'shadow' : buildType === 'shady';
+},
+cssBuildTypeForModule: function (module) {
+var dm = Polymer.DomModule.import(module);
+if (dm) {
+return this.getCssBuildType(dm);
+}
+},
+getCssBuildType: function (element) {
+return element.getAttribute('css-build');
+},
+_findMatchingParen: function (text, start) {
+var level = 0;
+for (var i = start, l = text.length; i < l; i++) {
+switch (text[i]) {
+case '(':
+level++;
+break;
+case ')':
+if (--level === 0) {
+return i;
+}
+break;
+}
+}
+return -1;
+},
+processVariableAndFallback: function (str, callback) {
+var start = str.indexOf('var(');
+if (start === -1) {
+return callback(str, '', '', '');
+}
+var end = this._findMatchingParen(str, start + 3);
+var inner = str.substring(start + 4, end);
+var prefix = str.substring(0, start);
+var suffix = this.processVariableAndFallback(str.substring(end + 1), callback);
+var comma = inner.indexOf(',');
+if (comma === -1) {
+return callback(prefix, inner.trim(), '', suffix);
+}
+var value = inner.substring(0, comma).trim();
+var fallback = inner.substring(comma + 1).trim();
+return callback(prefix, value, fallback, suffix);
+},
+rx: {
+VAR_ASSIGN: /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\s}])|$)/gi,
+MIXIN_MATCH: /(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,
+VAR_CONSUMED: /(--[\w-]+)\s*([:,;)]|$)/gi,
+ANIMATION_MATCH: /(animation\s*:)|(animation-name\s*:)/,
+MEDIA_MATCH: /@media[^(]*(\([^)]*\))/,
+IS_VAR: /^--/,
+BRACKETED: /\{[^}]*\}/g,
+HOST_PREFIX: '(?:^|[^.#[:])',
+HOST_SUFFIX: '($|[.:[\\s>+~])'
+},
+resolveCss: Polymer.ResolveUrl.resolveCss,
+parser: Polymer.CssParse,
+ruleTypes: Polymer.CssParse.types
+};
+}();Polymer.StyleTransformer = function () {
+var styleUtil = Polymer.StyleUtil;
+var settings = Polymer.Settings;
+var api = {
+dom: function (node, scope, useAttr, shouldRemoveScope) {
+this._transformDom(node, scope || '', useAttr, shouldRemoveScope);
+},
+_transformDom: function (node, selector, useAttr, shouldRemoveScope) {
+if (node.setAttribute) {
+this.element(node, selector, useAttr, shouldRemoveScope);
+}
+var c$ = Polymer.dom(node).childNodes;
+for (var i = 0; i < c$.length; i++) {
+this._transformDom(c$[i], selector, useAttr, shouldRemoveScope);
+}
+},
+element: function (element, scope, useAttr, shouldRemoveScope) {
+if (useAttr) {
+if (shouldRemoveScope) {
+element.removeAttribute(SCOPE_NAME);
+} else {
+element.setAttribute(SCOPE_NAME, scope);
+}
+} else {
+if (scope) {
+if (element.classList) {
+if (shouldRemoveScope) {
+element.classList.remove(SCOPE_NAME);
+element.classList.remove(scope);
+} else {
+element.classList.add(SCOPE_NAME);
+element.classList.add(scope);
+}
+} else if (element.getAttribute) {
+var c = element.getAttribute(CLASS);
+if (shouldRemoveScope) {
+if (c) {
+element.setAttribute(CLASS, c.replace(SCOPE_NAME, '').replace(scope, ''));
+}
+} else {
+element.setAttribute(CLASS, (c ? c + ' ' : '') + SCOPE_NAME + ' ' + scope);
+}
+}
+}
+}
+},
+elementStyles: function (element, callback) {
+var styles = element._styles;
+var cssText = '';
+var cssBuildType = element.__cssBuild;
+var passthrough = settings.useNativeShadow || cssBuildType === 'shady';
+var cb;
+if (passthrough) {
+var self = this;
+cb = function (rule) {
+rule.selector = self._slottedToContent(rule.selector);
+rule.selector = rule.selector.replace(ROOT, ':host > *');
+rule.selector = self._dirShadowTransform(rule.selector);
+if (callback) {
+callback(rule);
+}
+};
+}
+for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
+var rules = styleUtil.rulesForStyle(s);
+cssText += passthrough ? styleUtil.toCssText(rules, cb) : this.css(rules, element.is, element.extends, callback, element._scopeCssViaAttr) + '\n\n';
+}
+return cssText.trim();
+},
+css: function (rules, scope, ext, callback, useAttr) {
+var hostScope = this._calcHostScope(scope, ext);
+scope = this._calcElementScope(scope, useAttr);
+var self = this;
+return styleUtil.toCssText(rules, function (rule) {
+if (!rule.isScoped) {
+self.rule(rule, scope, hostScope);
+rule.isScoped = true;
+}
+if (callback) {
+callback(rule, scope, hostScope);
+}
+});
+},
+_calcElementScope: function (scope, useAttr) {
+if (scope) {
+return useAttr ? CSS_ATTR_PREFIX + scope + CSS_ATTR_SUFFIX : CSS_CLASS_PREFIX + scope;
+} else {
+return '';
+}
+},
+_calcHostScope: function (scope, ext) {
+return ext ? '[is=' + scope + ']' : scope;
+},
+rule: function (rule, scope, hostScope) {
+this._transformRule(rule, this._transformComplexSelector, scope, hostScope);
+},
+_transformRule: function (rule, transformer, scope, hostScope) {
+rule.selector = rule.transformedSelector = this._transformRuleCss(rule, transformer, scope, hostScope);
+},
+_splitSelectorList: function (selector) {
+var parts = [];
+var part = '';
+for (var i = 0; i >= 0 && i < selector.length; i++) {
+if (selector[i] === '(') {
+var end = styleUtil._findMatchingParen(selector, i);
+part += selector.slice(i, end + 1);
+i = end;
+} else if (selector[i] === COMPLEX_SELECTOR_SEP) {
+parts.push(part);
+part = '';
+} else {
+part += selector[i];
+}
+}
+if (part) {
+parts.push(part);
+}
+if (parts.length === 0) {
+parts.push(selector);
+}
+return parts;
+},
+_transformRuleCss: function (rule, transformer, scope, hostScope) {
+var p$ = this._splitSelectorList(rule.selector);
+if (!styleUtil.isKeyframesSelector(rule)) {
+for (var i = 0, l = p$.length, p; i < l && (p = p$[i]); i++) {
+p$[i] = transformer.call(this, p, scope, hostScope);
+}
+}
+return p$.join(COMPLEX_SELECTOR_SEP);
+},
+_ensureScopedDir: function (s) {
+var m = s.match(DIR_PAREN);
+if (m && m[1] === '' && m[0].length === s.length) {
+s = '*' + s;
+}
+return s;
+},
+_additionalDirSelectors: function (dir, after, prefix) {
+if (!dir || !after) {
+return '';
+}
+prefix = prefix || '';
+return COMPLEX_SELECTOR_SEP + prefix + ' ' + dir + ' ' + after;
+},
+_transformComplexSelector: function (selector, scope, hostScope) {
+var stop = false;
+var hostContext = false;
+var dir = false;
+var self = this;
+selector = selector.trim();
+selector = this._slottedToContent(selector);
+selector = selector.replace(ROOT, ':host > *');
+selector = selector.replace(CONTENT_START, HOST + ' $1');
+selector = this._ensureScopedDir(selector);
+selector = selector.replace(SIMPLE_SELECTOR_SEP, function (m, c, s) {
+if (!stop) {
+var info = self._transformCompoundSelector(s, c, scope, hostScope);
+stop = stop || info.stop;
+hostContext = hostContext || info.hostContext;
+dir = dir || info.dir;
+c = info.combinator;
+s = info.value;
+} else {
+s = s.replace(SCOPE_JUMP, ' ');
+}
+return c + s;
+});
+if (hostContext) {
+selector = selector.replace(HOST_CONTEXT_PAREN, function (m, pre, paren, post) {
+var replacement = pre + paren + ' ' + hostScope + post + COMPLEX_SELECTOR_SEP + ' ' + pre + hostScope + paren + post;
+if (dir) {
+replacement += self._additionalDirSelectors(paren, post, hostScope);
+}
+return replacement;
+});
+}
+return selector;
+},
+_transformDir: function (s) {
+s = s.replace(HOST_DIR, HOST_DIR_REPLACE);
+s = s.replace(DIR_PAREN, DIR_REPLACE);
+return s;
+},
+_transformCompoundSelector: function (selector, combinator, scope, hostScope) {
+var jumpIndex = selector.search(SCOPE_JUMP);
+var hostContext = false;
+var dir = false;
+if (selector.match(DIR_PAREN)) {
+selector = this._transformDir(selector);
+dir = true;
+}
+if (selector.indexOf(HOST_CONTEXT) >= 0) {
+hostContext = true;
+} else if (selector.indexOf(HOST) >= 0) {
+selector = this._transformHostSelector(selector, hostScope);
+} else if (jumpIndex !== 0) {
+selector = scope ? this._transformSimpleSelector(selector, scope) : selector;
+}
+if (selector.indexOf(CONTENT) >= 0) {
+combinator = '';
+}
+var stop;
+if (jumpIndex >= 0) {
+selector = selector.replace(SCOPE_JUMP, ' ');
+stop = true;
+}
+return {
+value: selector,
+combinator: combinator,
+stop: stop,
+hostContext: hostContext,
+dir: dir
+};
+},
+_transformSimpleSelector: function (selector, scope) {
+var p$ = selector.split(PSEUDO_PREFIX);
+p$[0] += scope;
+return p$.join(PSEUDO_PREFIX);
+},
+_transformHostSelector: function (selector, hostScope) {
+var m = selector.match(HOST_PAREN);
+var paren = m && m[2].trim() || '';
+if (paren) {
+if (!paren[0].match(SIMPLE_SELECTOR_PREFIX)) {
+var typeSelector = paren.split(SIMPLE_SELECTOR_PREFIX)[0];
+if (typeSelector === hostScope) {
+return paren;
+} else {
+return SELECTOR_NO_MATCH;
+}
+} else {
+return selector.replace(HOST_PAREN, function (m, host, paren) {
+return hostScope + paren;
+});
+}
+} else {
+return selector.replace(HOST, hostScope);
+}
+},
+documentRule: function (rule) {
+rule.selector = rule.parsedSelector;
+this.normalizeRootSelector(rule);
+if (!settings.useNativeShadow) {
+this._transformRule(rule, this._transformDocumentSelector);
+}
+},
+normalizeRootSelector: function (rule) {
+rule.selector = rule.selector.replace(ROOT, 'html');
+var parts = this._splitSelectorList(rule.selector);
+parts = parts.filter(function (part) {
+return !part.match(HOST_OR_HOST_GT_STAR);
+});
+rule.selector = parts.join(COMPLEX_SELECTOR_SEP);
+},
+_transformDocumentSelector: function (selector) {
+return this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR);
+},
+_slottedToContent: function (cssText) {
+return cssText.replace(SLOTTED_PAREN, CONTENT + '> $1');
+},
+_dirShadowTransform: function (selector) {
+if (!selector.match(/:dir\(/)) {
+return selector;
+}
+return this._splitSelectorList(selector).map(function (s) {
+s = this._ensureScopedDir(s);
+s = this._transformDir(s);
+var m = HOST_CONTEXT_PAREN.exec(s);
+if (m) {
+s += this._additionalDirSelectors(m[2], m[3], '');
+}
+return s;
+}, this).join(COMPLEX_SELECTOR_SEP);
+},
+SCOPE_NAME: 'style-scope'
+};
+var SCOPE_NAME = api.SCOPE_NAME;
+var SCOPE_DOC_SELECTOR = ':not([' + SCOPE_NAME + '])' + ':not(.' + SCOPE_NAME + ')';
+var COMPLEX_SELECTOR_SEP = ',';
+var SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=\[])+)/g;
+var SIMPLE_SELECTOR_PREFIX = /[[.:#*]/;
+var HOST = ':host';
+var ROOT = ':root';
+var HOST_PAREN = /(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/;
+var HOST_CONTEXT = ':host-context';
+var HOST_CONTEXT_PAREN = /(.*)(?::host-context)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))(.*)/;
+var CONTENT = '::content';
+var SCOPE_JUMP = /::content|::shadow|\/deep\//;
+var CSS_CLASS_PREFIX = '.';
+var CSS_ATTR_PREFIX = '[' + SCOPE_NAME + '~=';
+var CSS_ATTR_SUFFIX = ']';
+var PSEUDO_PREFIX = ':';
+var CLASS = 'class';
+var CONTENT_START = new RegExp('^(' + CONTENT + ')');
+var SELECTOR_NO_MATCH = 'should_not_match';
+var SLOTTED_PAREN = /(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g;
+var HOST_OR_HOST_GT_STAR = /:host(?:\s*>\s*\*)?/;
+var DIR_PAREN = /(.*):dir\((ltr|rtl)\)/;
+var DIR_REPLACE = ':host-context([dir="$2"]) $1';
+var HOST_DIR = /:host\(:dir\((rtl|ltr)\)\)/g;
+var HOST_DIR_REPLACE = ':host-context([dir="$1"])';
+return api;
+}();Polymer.StyleExtends = function () {
+var styleUtil = Polymer.StyleUtil;
+return {
+hasExtends: function (cssText) {
+return Boolean(cssText.match(this.rx.EXTEND));
+},
+transform: function (style) {
+var rules = styleUtil.rulesForStyle(style);
+var self = this;
+styleUtil.forEachRule(rules, function (rule) {
+self._mapRuleOntoParent(rule);
+if (rule.parent) {
+var m;
+while (m = self.rx.EXTEND.exec(rule.cssText)) {
+var extend = m[1];
+var extendor = self._findExtendor(extend, rule);
+if (extendor) {
+self._extendRule(rule, extendor);
+}
+}
+}
+rule.cssText = rule.cssText.replace(self.rx.EXTEND, '');
+});
+return styleUtil.toCssText(rules, function (rule) {
+if (rule.selector.match(self.rx.STRIP)) {
+rule.cssText = '';
+}
+}, true);
+},
+_mapRuleOntoParent: function (rule) {
+if (rule.parent) {
+var map = rule.parent.map || (rule.parent.map = {});
+var parts = rule.selector.split(',');
+for (var i = 0, p; i < parts.length; i++) {
+p = parts[i];
+map[p.trim()] = rule;
+}
+return map;
+}
+},
+_findExtendor: function (extend, rule) {
+return rule.parent && rule.parent.map && rule.parent.map[extend] || this._findExtendor(extend, rule.parent);
+},
+_extendRule: function (target, source) {
+if (target.parent !== source.parent) {
+this._cloneAndAddRuleToParent(source, target.parent);
+}
+target.extends = target.extends || [];
+target.extends.push(source);
+source.selector = source.selector.replace(this.rx.STRIP, '');
+source.selector = (source.selector && source.selector + ',\n') + target.selector;
+if (source.extends) {
+source.extends.forEach(function (e) {
+this._extendRule(target, e);
+}, this);
+}
+},
+_cloneAndAddRuleToParent: function (rule, parent) {
+rule = Object.create(rule);
+rule.parent = parent;
+if (rule.extends) {
+rule.extends = rule.extends.slice();
+}
+parent.rules.push(rule);
+},
+rx: {
+EXTEND: /@extends\(([^)]*)\)\s*?;/gim,
+STRIP: /%[^,]*$/
+}
+};
+}();Polymer.ApplyShim = function () {
+'use strict';
+var styleUtil = Polymer.StyleUtil;
+var MIXIN_MATCH = styleUtil.rx.MIXIN_MATCH;
+var VAR_ASSIGN = styleUtil.rx.VAR_ASSIGN;
+var BAD_VAR = /var\(\s*(--[^,]*),\s*(--[^)]*)\)/g;
+var APPLY_NAME_CLEAN = /;\s*/m;
+var INITIAL_INHERIT = /^\s*(initial)|(inherit)\s*$/;
+var MIXIN_VAR_SEP = '_-_';
+var mixinMap = {};
+function mapSet(name, props) {
+name = name.trim();
+mixinMap[name] = {
+properties: props,
+dependants: {}
+};
+}
+function mapGet(name) {
+name = name.trim();
+return mixinMap[name];
+}
+function replaceInitialOrInherit(property, value) {
+var match = INITIAL_INHERIT.exec(value);
+if (match) {
+if (match[1]) {
+value = ApplyShim._getInitialValueForProperty(property);
+} else {
+value = 'apply-shim-inherit';
+}
+}
+return value;
+}
+function cssTextToMap(text) {
+var props = text.split(';');
+var property, value;
+var out = {};
+for (var i = 0, p, sp; i < props.length; i++) {
+p = props[i];
+if (p) {
+sp = p.split(':');
+if (sp.length > 1) {
+property = sp[0].trim();
+value = replaceInitialOrInherit(property, sp.slice(1).join(':'));
+out[property] = value;
+}
+}
+}
+return out;
+}
+function invalidateMixinEntry(mixinEntry) {
+var currentProto = ApplyShim.__currentElementProto;
+var currentElementName = currentProto && currentProto.is;
+for (var elementName in mixinEntry.dependants) {
+if (elementName !== currentElementName) {
+mixinEntry.dependants[elementName].__applyShimInvalid = true;
+}
+}
+}
+function produceCssProperties(matchText, propertyName, valueProperty, valueMixin) {
+if (valueProperty) {
+styleUtil.processVariableAndFallback(valueProperty, function (prefix, value) {
+if (value && mapGet(value)) {
+valueMixin = '@apply ' + value + ';';
+}
+});
+}
+if (!valueMixin) {
+return matchText;
+}
+var mixinAsProperties = consumeCssProperties(valueMixin);
+var prefix = matchText.slice(0, matchText.indexOf('--'));
+var mixinValues = cssTextToMap(mixinAsProperties);
+var combinedProps = mixinValues;
+var mixinEntry = mapGet(propertyName);
+var oldProps = mixinEntry && mixinEntry.properties;
+if (oldProps) {
+combinedProps = Object.create(oldProps);
+combinedProps = Polymer.Base.mixin(combinedProps, mixinValues);
+} else {
+mapSet(propertyName, combinedProps);
+}
+var out = [];
+var p, v;
+var needToInvalidate = false;
+for (p in combinedProps) {
+v = mixinValues[p];
+if (v === undefined) {
+v = 'initial';
+}
+if (oldProps && !(p in oldProps)) {
+needToInvalidate = true;
+}
+out.push(propertyName + MIXIN_VAR_SEP + p + ': ' + v);
+}
+if (needToInvalidate) {
+invalidateMixinEntry(mixinEntry);
+}
+if (mixinEntry) {
+mixinEntry.properties = combinedProps;
+}
+if (valueProperty) {
+prefix = matchText + ';' + prefix;
+}
+return prefix + out.join('; ') + ';';
+}
+function fixVars(matchText, varA, varB) {
+return 'var(' + varA + ',' + 'var(' + varB + '))';
+}
+function atApplyToCssProperties(mixinName, fallbacks) {
+mixinName = mixinName.replace(APPLY_NAME_CLEAN, '');
+var vars = [];
+var mixinEntry = mapGet(mixinName);
+if (!mixinEntry) {
+mapSet(mixinName, {});
+mixinEntry = mapGet(mixinName);
+}
+if (mixinEntry) {
+var currentProto = ApplyShim.__currentElementProto;
+if (currentProto) {
+mixinEntry.dependants[currentProto.is] = currentProto;
+}
+var p, parts, f;
+for (p in mixinEntry.properties) {
+f = fallbacks && fallbacks[p];
+parts = [
+p,
+': var(',
+mixinName,
+MIXIN_VAR_SEP,
+p
+];
+if (f) {
+parts.push(',', f);
+}
+parts.push(')');
+vars.push(parts.join(''));
+}
+}
+return vars.join('; ');
+}
+function consumeCssProperties(text) {
+var m;
+while (m = MIXIN_MATCH.exec(text)) {
+var matchText = m[0];
+var mixinName = m[1];
+var idx = m.index;
+var applyPos = idx + matchText.indexOf('@apply');
+var afterApplyPos = idx + matchText.length;
+var textBeforeApply = text.slice(0, applyPos);
+var textAfterApply = text.slice(afterApplyPos);
+var defaults = cssTextToMap(textBeforeApply);
+var replacement = atApplyToCssProperties(mixinName, defaults);
+text = [
+textBeforeApply,
+replacement,
+textAfterApply
+].join('');
+MIXIN_MATCH.lastIndex = idx + replacement.length;
+}
+return text;
+}
+var ApplyShim = {
+_measureElement: null,
+_map: mixinMap,
+_separator: MIXIN_VAR_SEP,
+transform: function (styles, elementProto) {
+this.__currentElementProto = elementProto;
+styleUtil.forRulesInStyles(styles, this._boundFindDefinitions);
+styleUtil.forRulesInStyles(styles, this._boundFindApplications);
+if (elementProto) {
+elementProto.__applyShimInvalid = false;
+}
+this.__currentElementProto = null;
+},
+_findDefinitions: function (rule) {
+var cssText = rule.parsedCssText;
+cssText = cssText.replace(BAD_VAR, fixVars);
+cssText = cssText.replace(VAR_ASSIGN, produceCssProperties);
+rule.cssText = cssText;
+if (rule.selector === ':root') {
+rule.selector = ':host > *';
+}
+},
+_findApplications: function (rule) {
+rule.cssText = consumeCssProperties(rule.cssText);
+},
+transformRule: function (rule) {
+this._findDefinitions(rule);
+this._findApplications(rule);
+},
+_getInitialValueForProperty: function (property) {
+if (!this._measureElement) {
+this._measureElement = document.createElement('meta');
+this._measureElement.style.all = 'initial';
+document.head.appendChild(this._measureElement);
+}
+return window.getComputedStyle(this._measureElement).getPropertyValue(property);
+}
+};
+ApplyShim._boundTransformRule = ApplyShim.transformRule.bind(ApplyShim);
+ApplyShim._boundFindDefinitions = ApplyShim._findDefinitions.bind(ApplyShim);
+ApplyShim._boundFindApplications = ApplyShim._findApplications.bind(ApplyShim);
+return ApplyShim;
+}();(function () {
+var prepElement = Polymer.Base._prepElement;
+var nativeShadow = Polymer.Settings.useNativeShadow;
+var styleUtil = Polymer.StyleUtil;
+var styleTransformer = Polymer.StyleTransformer;
+var styleExtends = Polymer.StyleExtends;
+var applyShim = Polymer.ApplyShim;
+var settings = Polymer.Settings;
+Polymer.Base._addFeature({
+_prepElement: function (element) {
+if (this._encapsulateStyle && this.__cssBuild !== 'shady') {
+styleTransformer.element(element, this.is, this._scopeCssViaAttr);
+}
+prepElement.call(this, element);
+},
+_prepStyles: function () {
+if (this._encapsulateStyle === undefined) {
+this._encapsulateStyle = !nativeShadow;
+}
+if (!nativeShadow) {
+this._scopeStyle = styleUtil.applyStylePlaceHolder(this.is);
+}
+this.__cssBuild = styleUtil.cssBuildTypeForModule(this.is);
+},
+_prepShimStyles: function () {
+if (this._template) {
+var hasTargetedCssBuild = styleUtil.isTargetedBuild(this.__cssBuild);
+if (settings.useNativeCSSProperties && this.__cssBuild === 'shadow' && hasTargetedCssBuild) {
+if (settings.preserveStyleIncludes) {
+styleUtil.styleIncludesToTemplate(this._template);
+}
+return;
+}
+this._styles = this._styles || this._collectStyles();
+if (settings.useNativeCSSProperties && !this.__cssBuild) {
+applyShim.transform(this._styles, this);
+}
+var cssText = settings.useNativeCSSProperties && hasTargetedCssBuild ? this._styles.length && this._styles[0].textContent.trim() : styleTransformer.elementStyles(this);
+this._prepStyleProperties();
+if (!this._needsStyleProperties() && cssText) {
+styleUtil.applyCss(cssText, this.is, nativeShadow ? this._template.content : null, this._scopeStyle);
+}
+} else {
+this._styles = [];
+}
+},
+_collectStyles: function () {
+var styles = [];
+var cssText = '', m$ = this.styleModules;
+if (m$) {
+for (var i = 0, l = m$.length, m; i < l && (m = m$[i]); i++) {
+cssText += styleUtil.cssFromModule(m);
+}
+}
+cssText += styleUtil.cssFromModule(this.is);
+var p = this._template && this._template.parentNode;
+if (this._template && (!p || p.id.toLowerCase() !== this.is)) {
+cssText += styleUtil.cssFromElement(this._template);
+}
+if (cssText) {
+var style = document.createElement('style');
+style.textContent = cssText;
+if (styleExtends.hasExtends(style.textContent)) {
+cssText = styleExtends.transform(style);
+}
+styles.push(style);
+}
+return styles;
+},
+_elementAdd: function (node) {
+if (this._encapsulateStyle) {
+if (node.__styleScoped) {
+node.__styleScoped = false;
+} else {
+styleTransformer.dom(node, this.is, this._scopeCssViaAttr);
+}
+}
+},
+_elementRemove: function (node) {
+if (this._encapsulateStyle) {
+styleTransformer.dom(node, this.is, this._scopeCssViaAttr, true);
+}
+},
+scopeSubtree: function (container, shouldObserve) {
+if (nativeShadow) {
+return;
+}
+var self = this;
+var scopify = function (node) {
+if (node.nodeType === Node.ELEMENT_NODE) {
+var className = node.getAttribute('class');
+node.setAttribute('class', self._scopeElementClass(node, className));
+var n$ = node.querySelectorAll('*');
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) {
+className = n.getAttribute('class');
+n.setAttribute('class', self._scopeElementClass(n, className));
+}
+}
+};
+scopify(container);
+if (shouldObserve) {
+var mo = new MutationObserver(function (mxns) {
+for (var i = 0, m; i < mxns.length && (m = mxns[i]); i++) {
+if (m.addedNodes) {
+for (var j = 0; j < m.addedNodes.length; j++) {
+scopify(m.addedNodes[j]);
+}
+}
+}
+});
+mo.observe(container, {
+childList: true,
+subtree: true
+});
+return mo;
+}
+}
+});
+}());Polymer.StyleProperties = function () {
+'use strict';
+var matchesSelector = Polymer.DomApi.matchesSelector;
+var styleUtil = Polymer.StyleUtil;
+var styleTransformer = Polymer.StyleTransformer;
+var IS_IE = navigator.userAgent.match('Trident');
+var settings = Polymer.Settings;
+return {
+decorateStyles: function (styles, scope) {
+var self = this, props = {}, keyframes = [], ruleIndex = 0;
+var scopeSelector = styleTransformer._calcHostScope(scope.is, scope.extends);
+styleUtil.forRulesInStyles(styles, function (rule, style) {
+self.decorateRule(rule);
+rule.index = ruleIndex++;
+self.whenHostOrRootRule(scope, rule, style, function (info) {
+if (rule.parent.type === styleUtil.ruleTypes.MEDIA_RULE) {
+scope.__notStyleScopeCacheable = true;
+}
+if (info.isHost) {
+var hostContextOrFunction = info.selector.split(' ').some(function (s) {
+return s.indexOf(scopeSelector) === 0 && s.length !== scopeSelector.length;
+});
+scope.__notStyleScopeCacheable = scope.__notStyleScopeCacheable || hostContextOrFunction;
+}
+});
+self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
+}, function onKeyframesRule(rule) {
+keyframes.push(rule);
+});
+styles._keyframes = keyframes;
+var names = [];
+for (var i in props) {
+names.push(i);
+}
+return names;
+},
+decorateRule: function (rule) {
+if (rule.propertyInfo) {
+return rule.propertyInfo;
+}
+var info = {}, properties = {};
+var hasProperties = this.collectProperties(rule, properties);
+if (hasProperties) {
+info.properties = properties;
+rule.rules = null;
+}
+info.cssText = this.collectCssText(rule);
+rule.propertyInfo = info;
+return info;
+},
+collectProperties: function (rule, properties) {
+var info = rule.propertyInfo;
+if (info) {
+if (info.properties) {
+Polymer.Base.mixin(properties, info.properties);
+return true;
+}
+} else {
+var m, rx = this.rx.VAR_ASSIGN;
+var cssText = rule.parsedCssText;
+var value;
+var any;
+while (m = rx.exec(cssText)) {
+value = (m[2] || m[3]).trim();
+if (value !== 'inherit') {
+properties[m[1].trim()] = value;
+}
+any = true;
+}
+return any;
+}
+},
+collectCssText: function (rule) {
+return this.collectConsumingCssText(rule.parsedCssText);
+},
+collectConsumingCssText: function (cssText) {
+return cssText.replace(this.rx.BRACKETED, '').replace(this.rx.VAR_ASSIGN, '');
+},
+collectPropertiesInCssText: function (cssText, props) {
+var m;
+while (m = this.rx.VAR_CONSUMED.exec(cssText)) {
+var name = m[1];
+if (m[2] !== ':') {
+props[name] = true;
+}
+}
+},
+reify: function (props) {
+var names = Object.getOwnPropertyNames(props);
+for (var i = 0, n; i < names.length; i++) {
+n = names[i];
+props[n] = this.valueForProperty(props[n], props);
+}
+},
+valueForProperty: function (property, props) {
+if (property) {
+if (property.indexOf(';') >= 0) {
+property = this.valueForProperties(property, props);
+} else {
+var self = this;
+var fn = function (prefix, value, fallback, suffix) {
+var propertyValue = self.valueForProperty(props[value], props);
+if (!propertyValue || propertyValue === 'initial') {
+propertyValue = self.valueForProperty(props[fallback] || fallback, props) || fallback;
+} else if (propertyValue === 'apply-shim-inherit') {
+propertyValue = 'inherit';
+}
+return prefix + (propertyValue || '') + suffix;
+};
+property = styleUtil.processVariableAndFallback(property, fn);
+}
+}
+return property && property.trim() || '';
+},
+valueForProperties: function (property, props) {
+var parts = property.split(';');
+for (var i = 0, p, m; i < parts.length; i++) {
+if (p = parts[i]) {
+this.rx.MIXIN_MATCH.lastIndex = 0;
+m = this.rx.MIXIN_MATCH.exec(p);
+if (m) {
+p = this.valueForProperty(props[m[1]], props);
+} else {
+var colon = p.indexOf(':');
+if (colon !== -1) {
+var pp = p.substring(colon);
+pp = pp.trim();
+pp = this.valueForProperty(pp, props) || pp;
+p = p.substring(0, colon) + pp;
+}
+}
+parts[i] = p && p.lastIndexOf(';') === p.length - 1 ? p.slice(0, -1) : p || '';
+}
+}
+return parts.join(';');
+},
+applyProperties: function (rule, props) {
+var output = '';
+if (!rule.propertyInfo) {
+this.decorateRule(rule);
+}
+if (rule.propertyInfo.cssText) {
+output = this.valueForProperties(rule.propertyInfo.cssText, props);
+}
+rule.cssText = output;
+},
+applyKeyframeTransforms: function (rule, keyframeTransforms) {
+var input = rule.cssText;
+var output = rule.cssText;
+if (rule.hasAnimations == null) {
+rule.hasAnimations = this.rx.ANIMATION_MATCH.test(input);
+}
+if (rule.hasAnimations) {
+var transform;
+if (rule.keyframeNamesToTransform == null) {
+rule.keyframeNamesToTransform = [];
+for (var keyframe in keyframeTransforms) {
+transform = keyframeTransforms[keyframe];
+output = transform(input);
+if (input !== output) {
+input = output;
+rule.keyframeNamesToTransform.push(keyframe);
+}
+}
+} else {
+for (var i = 0; i < rule.keyframeNamesToTransform.length; ++i) {
+transform = keyframeTransforms[rule.keyframeNamesToTransform[i]];
+input = transform(input);
+}
+output = input;
+}
+}
+rule.cssText = output;
+},
+propertyDataFromStyles: function (styles, element) {
+var props = {}, self = this;
+var o = [];
+styleUtil.forActiveRulesInStyles(styles, function (rule) {
+if (!rule.propertyInfo) {
+self.decorateRule(rule);
+}
+var selectorToMatch = rule.transformedSelector || rule.parsedSelector;
+if (element && rule.propertyInfo.properties && selectorToMatch) {
+if (matchesSelector.call(element, selectorToMatch)) {
+self.collectProperties(rule, props);
+addToBitMask(rule.index, o);
+}
+}
+});
+return {
+properties: props,
+key: o
+};
+},
+_rootSelector: /:root|:host\s*>\s*\*/,
+_checkRoot: function (hostScope, selector) {
+return Boolean(selector.match(this._rootSelector)) || hostScope === 'html' && selector.indexOf('html') > -1;
+},
+whenHostOrRootRule: function (scope, rule, style, callback) {
+if (!rule.propertyInfo) {
+self.decorateRule(rule);
+}
+if (!rule.propertyInfo.properties) {
+return;
+}
+var hostScope = scope.is ? styleTransformer._calcHostScope(scope.is, scope.extends) : 'html';
+var parsedSelector = rule.parsedSelector;
+var isRoot = this._checkRoot(hostScope, parsedSelector);
+var isHost = !isRoot && parsedSelector.indexOf(':host') === 0;
+var cssBuild = scope.__cssBuild || style.__cssBuild;
+if (cssBuild === 'shady') {
+isRoot = parsedSelector === hostScope + ' > *.' + hostScope || parsedSelector.indexOf('html') > -1;
+isHost = !isRoot && parsedSelector.indexOf(hostScope) === 0;
+}
+if (!isRoot && !isHost) {
+return;
+}
+var selectorToMatch = hostScope;
+if (isHost) {
+if (settings.useNativeShadow && !rule.transformedSelector) {
+rule.transformedSelector = styleTransformer._transformRuleCss(rule, styleTransformer._transformComplexSelector, scope.is, hostScope);
+}
+selectorToMatch = rule.transformedSelector || rule.parsedSelector;
+}
+if (isRoot && hostScope === 'html') {
+selectorToMatch = rule.transformedSelector || rule.parsedSelector;
+}
+callback({
+selector: selectorToMatch,
+isHost: isHost,
+isRoot: isRoot
+});
+},
+hostAndRootPropertiesForScope: function (scope) {
+var hostProps = {}, rootProps = {}, self = this;
+styleUtil.forActiveRulesInStyles(scope._styles, function (rule, style) {
+self.whenHostOrRootRule(scope, rule, style, function (info) {
+var element = scope._element || scope;
+if (matchesSelector.call(element, info.selector)) {
+if (info.isHost) {
+self.collectProperties(rule, hostProps);
+} else {
+self.collectProperties(rule, rootProps);
+}
+}
+});
+});
+return {
+rootProps: rootProps,
+hostProps: hostProps
+};
+},
+transformStyles: function (element, properties, scopeSelector) {
+var self = this;
+var hostSelector = styleTransformer._calcHostScope(element.is, element.extends);
+var rxHostSelector = element.extends ? '\\' + hostSelector.slice(0, -1) + '\\]' : hostSelector;
+var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector + this.rx.HOST_SUFFIX);
+var keyframeTransforms = this._elementKeyframeTransforms(element, scopeSelector);
+return styleTransformer.elementStyles(element, function (rule) {
+self.applyProperties(rule, properties);
+if (!settings.useNativeShadow && !Polymer.StyleUtil.isKeyframesSelector(rule) && rule.cssText) {
+self.applyKeyframeTransforms(rule, keyframeTransforms);
+self._scopeSelector(rule, hostRx, hostSelector, element._scopeCssViaAttr, scopeSelector);
+}
+});
+},
+_elementKeyframeTransforms: function (element, scopeSelector) {
+var keyframesRules = element._styles._keyframes;
+var keyframeTransforms = {};
+if (!settings.useNativeShadow && keyframesRules) {
+for (var i = 0, keyframesRule = keyframesRules[i]; i < keyframesRules.length; keyframesRule = keyframesRules[++i]) {
+this._scopeKeyframes(keyframesRule, scopeSelector);
+keyframeTransforms[keyframesRule.keyframesName] = this._keyframesRuleTransformer(keyframesRule);
+}
+}
+return keyframeTransforms;
+},
+_keyframesRuleTransformer: function (keyframesRule) {
+return function (cssText) {
+return cssText.replace(keyframesRule.keyframesNameRx, keyframesRule.transformedKeyframesName);
+};
+},
+_scopeKeyframes: function (rule, scopeId) {
+rule.keyframesNameRx = new RegExp('\\b' + rule.keyframesName + '(?!\\B|-)', 'g');
+rule.transformedKeyframesName = rule.keyframesName + '-' + scopeId;
+rule.transformedSelector = rule.transformedSelector || rule.selector;
+rule.selector = rule.transformedSelector.replace(rule.keyframesName, rule.transformedKeyframesName);
+},
+_hasDirOrHostContext: function (parsedSelector) {
+return /:host-context|:dir/.test(parsedSelector);
+},
+_scopeSelector: function (rule, hostRx, hostSelector, viaAttr, scopeId) {
+rule.transformedSelector = rule.transformedSelector || rule.selector;
+var selector = rule.transformedSelector;
+var scope = styleTransformer._calcElementScope(scopeId, viaAttr);
+var hostScope = styleTransformer._calcElementScope(hostSelector, viaAttr);
+var parts = selector.split(',');
+var isDirOrHostContextSelector = this._hasDirOrHostContext(rule.parsedSelector);
+for (var i = 0, l = parts.length, p; i < l && (p = parts[i]); i++) {
+parts[i] = p.match(hostRx) ? p.replace(hostSelector, scope) : isDirOrHostContextSelector ? p.replace(hostScope, scope + ' ' + hostScope) : scope + ' ' + p;
+}
+rule.selector = parts.join(',');
+},
+applyElementScopeSelector: function (element, selector, old, viaAttr) {
+var c = viaAttr ? element.getAttribute(styleTransformer.SCOPE_NAME) : element.getAttribute('class') || '';
+var v = old ? c.replace(old, selector) : (c ? c + ' ' : '') + this.XSCOPE_NAME + ' ' + selector;
+if (c !== v) {
+if (viaAttr) {
+element.setAttribute(styleTransformer.SCOPE_NAME, v);
+} else {
+element.setAttribute('class', v);
+}
+}
+},
+applyElementStyle: function (element, properties, selector, style) {
+var cssText = style ? style.textContent || '' : this.transformStyles(element, properties, selector);
+var s = element._customStyle;
+if (s && !settings.useNativeShadow && s !== style) {
+s._useCount--;
+if (s._useCount <= 0 && s.parentNode) {
+s.parentNode.removeChild(s);
+}
+}
+if (settings.useNativeShadow) {
+if (element._customStyle) {
+element._customStyle.textContent = cssText;
+style = element._customStyle;
+} else if (cssText) {
+style = styleUtil.applyCss(cssText, selector, element.root, element._scopeStyle);
+}
+} else {
+if (!style) {
+if (cssText) {
+style = styleUtil.applyCss(cssText, selector, null, element._scopeStyle);
+}
+} else if (!style.parentNode) {
+if (IS_IE && cssText.indexOf('@media') > -1) {
+style.textContent = cssText;
+}
+styleUtil.applyStyle(style, null, element._scopeStyle);
+}
+}
+if (style) {
+style._useCount = style._useCount || 0;
+if (element._customStyle != style) {
+style._useCount++;
+}
+element._customStyle = style;
+}
+return style;
+},
+mixinCustomStyle: function (props, customStyle) {
+var v;
+for (var i in customStyle) {
+v = customStyle[i];
+if (v || v === 0) {
+props[i] = v;
+}
+}
+},
+updateNativeStyleProperties: function (element, properties) {
+var oldPropertyNames = element.__customStyleProperties;
+if (oldPropertyNames) {
+for (var i = 0; i < oldPropertyNames.length; i++) {
+element.style.removeProperty(oldPropertyNames[i]);
+}
+}
+var propertyNames = [];
+for (var p in properties) {
+if (properties[p] !== null) {
+element.style.setProperty(p, properties[p]);
+propertyNames.push(p);
+}
+}
+element.__customStyleProperties = propertyNames;
+},
+rx: styleUtil.rx,
+XSCOPE_NAME: 'x-scope'
+};
+function addToBitMask(n, bits) {
+var o = parseInt(n / 32);
+var v = 1 << n % 32;
+bits[o] = (bits[o] || 0) | v;
+}
+}();(function () {
+Polymer.StyleCache = function () {
+this.cache = {};
+};
+Polymer.StyleCache.prototype = {
+MAX: 100,
+store: function (is, data, keyValues, keyStyles) {
+data.keyValues = keyValues;
+data.styles = keyStyles;
+var s$ = this.cache[is] = this.cache[is] || [];
+s$.push(data);
+if (s$.length > this.MAX) {
+s$.shift();
+}
+},
+retrieve: function (is, keyValues, keyStyles) {
+var cache = this.cache[is];
+if (cache) {
+for (var i = cache.length - 1, data; i >= 0; i--) {
+data = cache[i];
+if (keyStyles === data.styles && this._objectsEqual(keyValues, data.keyValues)) {
+return data;
+}
+}
+}
+},
+clear: function () {
+this.cache = {};
+},
+_objectsEqual: function (target, source) {
+var t, s;
+for (var i in target) {
+t = target[i], s = source[i];
+if (!(typeof t === 'object' && t ? this._objectsStrictlyEqual(t, s) : t === s)) {
+return false;
+}
+}
+if (Array.isArray(target)) {
+return target.length === source.length;
+}
+return true;
+},
+_objectsStrictlyEqual: function (target, source) {
+return this._objectsEqual(target, source) && this._objectsEqual(source, target);
+}
+};
+}());Polymer.StyleDefaults = function () {
+var styleProperties = Polymer.StyleProperties;
+var StyleCache = Polymer.StyleCache;
+var nativeVariables = Polymer.Settings.useNativeCSSProperties;
+var api = {
+_styles: [],
+_properties: null,
+customStyle: {},
+_styleCache: new StyleCache(),
+_element: Polymer.DomApi.wrap(document.documentElement),
+addStyle: function (style) {
+this._styles.push(style);
+this._properties = null;
+},
+get _styleProperties() {
+if (!this._properties) {
+styleProperties.decorateStyles(this._styles, this);
+this._styles._scopeStyleProperties = null;
+this._properties = styleProperties.hostAndRootPropertiesForScope(this).rootProps;
+styleProperties.mixinCustomStyle(this._properties, this.customStyle);
+styleProperties.reify(this._properties);
+}
+return this._properties;
+},
+hasStyleProperties: function () {
+return Boolean(this._properties);
+},
+_needsStyleProperties: function () {
+},
+_computeStyleProperties: function () {
+return this._styleProperties;
+},
+updateStyles: function (properties) {
+this._properties = null;
+if (properties) {
+Polymer.Base.mixin(this.customStyle, properties);
+}
+this._styleCache.clear();
+for (var i = 0, s; i < this._styles.length; i++) {
+s = this._styles[i];
+s = s.__importElement || s;
+s._apply();
+}
+if (nativeVariables) {
+styleProperties.updateNativeStyleProperties(document.documentElement, this.customStyle);
+}
+}
+};
+return api;
+}();(function () {
+'use strict';
+var serializeValueToAttribute = Polymer.Base.serializeValueToAttribute;
+var propertyUtils = Polymer.StyleProperties;
+var styleTransformer = Polymer.StyleTransformer;
+var styleDefaults = Polymer.StyleDefaults;
+var nativeShadow = Polymer.Settings.useNativeShadow;
+var nativeVariables = Polymer.Settings.useNativeCSSProperties;
+Polymer.Base._addFeature({
+_prepStyleProperties: function () {
+if (!nativeVariables) {
+this._ownStylePropertyNames = this._styles && this._styles.length ? propertyUtils.decorateStyles(this._styles, this) : null;
+}
+},
+customStyle: null,
+getComputedStyleValue: function (property) {
+if (!nativeVariables && !this._styleProperties) {
+this._computeStyleProperties();
+}
+return !nativeVariables && this._styleProperties && this._styleProperties[property] || getComputedStyle(this).getPropertyValue(property);
+},
+_setupStyleProperties: function () {
+this.customStyle = {};
+this._styleCache = null;
+this._styleProperties = null;
+this._scopeSelector = null;
+this._ownStyleProperties = null;
+this._customStyle = null;
+},
+_needsStyleProperties: function () {
+return Boolean(!nativeVariables && this._ownStylePropertyNames && this._ownStylePropertyNames.length);
+},
+_validateApplyShim: function () {
+if (this.__applyShimInvalid) {
+Polymer.ApplyShim.transform(this._styles, this.__proto__);
+var cssText = styleTransformer.elementStyles(this);
+if (nativeShadow) {
+var templateStyle = this._template.content.querySelector('style');
+if (templateStyle) {
+templateStyle.textContent = cssText;
+}
+} else {
+var shadyStyle = this._scopeStyle && this._scopeStyle.nextSibling;
+if (shadyStyle) {
+shadyStyle.textContent = cssText;
+}
+}
+}
+},
+_beforeAttached: function () {
+if ((!this._scopeSelector || this.__stylePropertiesInvalid) && this._needsStyleProperties()) {
+this.__stylePropertiesInvalid = false;
+this._updateStyleProperties();
+}
+},
+_findStyleHost: function () {
+var e = this, root;
+while (root = Polymer.dom(e).getOwnerRoot()) {
+if (Polymer.isInstance(root.host)) {
+return root.host;
+}
+e = root.host;
+}
+return styleDefaults;
+},
+_updateStyleProperties: function () {
+var info, scope = this._findStyleHost();
+if (!scope._styleProperties) {
+scope._computeStyleProperties();
+}
+if (!scope._styleCache) {
+scope._styleCache = new Polymer.StyleCache();
+}
+var scopeData = propertyUtils.propertyDataFromStyles(scope._styles, this);
+var scopeCacheable = !this.__notStyleScopeCacheable;
+if (scopeCacheable) {
+scopeData.key.customStyle = this.customStyle;
+info = scope._styleCache.retrieve(this.is, scopeData.key, this._styles);
+}
+var scopeCached = Boolean(info);
+if (scopeCached) {
+this._styleProperties = info._styleProperties;
+} else {
+this._computeStyleProperties(scopeData.properties);
+}
+this._computeOwnStyleProperties();
+if (!scopeCached) {
+info = styleCache.retrieve(this.is, this._ownStyleProperties, this._styles);
+}
+var globalCached = Boolean(info) && !scopeCached;
+var style = this._applyStyleProperties(info);
+if (!scopeCached) {
+style = style && nativeShadow ? style.cloneNode(true) : style;
+info = {
+style: style,
+_scopeSelector: this._scopeSelector,
+_styleProperties: this._styleProperties
+};
+if (scopeCacheable) {
+scopeData.key.customStyle = {};
+this.mixin(scopeData.key.customStyle, this.customStyle);
+scope._styleCache.store(this.is, info, scopeData.key, this._styles);
+}
+if (!globalCached) {
+styleCache.store(this.is, Object.create(info), this._ownStyleProperties, this._styles);
+}
+}
+},
+_computeStyleProperties: function (scopeProps) {
+var scope = this._findStyleHost();
+if (!scope._styleProperties) {
+scope._computeStyleProperties();
+}
+var props = Object.create(scope._styleProperties);
+var hostAndRootProps = propertyUtils.hostAndRootPropertiesForScope(this);
+this.mixin(props, hostAndRootProps.hostProps);
+scopeProps = scopeProps || propertyUtils.propertyDataFromStyles(scope._styles, this).properties;
+this.mixin(props, scopeProps);
+this.mixin(props, hostAndRootProps.rootProps);
+propertyUtils.mixinCustomStyle(props, this.customStyle);
+propertyUtils.reify(props);
+this._styleProperties = props;
+},
+_computeOwnStyleProperties: function () {
+var props = {};
+for (var i = 0, n; i < this._ownStylePropertyNames.length; i++) {
+n = this._ownStylePropertyNames[i];
+props[n] = this._styleProperties[n];
+}
+this._ownStyleProperties = props;
+},
+_scopeCount: 0,
+_applyStyleProperties: function (info) {
+var oldScopeSelector = this._scopeSelector;
+this._scopeSelector = info ? info._scopeSelector : this.is + '-' + this.__proto__._scopeCount++;
+var style = propertyUtils.applyElementStyle(this, this._styleProperties, this._scopeSelector, info && info.style);
+if (!nativeShadow) {
+propertyUtils.applyElementScopeSelector(this, this._scopeSelector, oldScopeSelector, this._scopeCssViaAttr);
+}
+return style;
+},
+serializeValueToAttribute: function (value, attribute, node) {
+node = node || this;
+if (attribute === 'class' && !nativeShadow) {
+var host = node === this ? this.domHost || this.dataHost : this;
+if (host) {
+value = host._scopeElementClass(node, value);
+}
+}
+node = this.shadyRoot && this.shadyRoot._hasDistributed ? Polymer.dom(node) : node;
+serializeValueToAttribute.call(this, value, attribute, node);
+},
+_scopeElementClass: function (element, selector) {
+if (!nativeShadow && !this._scopeCssViaAttr) {
+selector = (selector ? selector + ' ' : '') + SCOPE_NAME + ' ' + this.is + (element._scopeSelector ? ' ' + XSCOPE_NAME + ' ' + element._scopeSelector : '');
+}
+return selector;
+},
+updateStyles: function (properties) {
+if (properties) {
+this.mixin(this.customStyle, properties);
+}
+if (nativeVariables) {
+propertyUtils.updateNativeStyleProperties(this, this.customStyle);
+} else {
+if (this.isAttached) {
+if (this._needsStyleProperties()) {
+this._updateStyleProperties();
+} else {
+this._styleProperties = null;
+}
+} else {
+this.__stylePropertiesInvalid = true;
+}
+if (this._styleCache) {
+this._styleCache.clear();
+}
+this._updateRootStyles();
+}
+},
+_updateRootStyles: function (root) {
+root = root || this.root;
+var c$ = Polymer.dom(root)._query(function (e) {
+return e.shadyRoot || e.shadowRoot;
+});
+for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
+if (c.updateStyles) {
+c.updateStyles();
+}
+}
+}
+});
+Polymer.updateStyles = function (properties) {
+styleDefaults.updateStyles(properties);
+Polymer.Base._updateRootStyles(document);
+};
+var styleCache = new Polymer.StyleCache();
+Polymer.customStyleCache = styleCache;
+var SCOPE_NAME = styleTransformer.SCOPE_NAME;
+var XSCOPE_NAME = propertyUtils.XSCOPE_NAME;
+}());Polymer.Base._addFeature({
+_registerFeatures: function () {
+this._prepIs();
+if (this.factoryImpl) {
+this._prepConstructor();
+}
+this._prepStyles();
+},
+_finishRegisterFeatures: function () {
+this._prepTemplate();
+this._prepShimStyles();
+this._prepAnnotations();
+this._prepEffects();
+this._prepBehaviors();
+this._prepPropertyInfo();
+this._prepBindings();
+this._prepShady();
+},
+_prepBehavior: function (b) {
+this._addPropertyEffects(b.properties);
+this._addComplexObserverEffects(b.observers);
+this._addHostAttributes(b.hostAttributes);
+},
+_initFeatures: function () {
+this._setupGestures();
+this._setupConfigure(this.__data__);
+this._setupStyleProperties();
+this._setupDebouncers();
+this._setupShady();
+this._registerHost();
+if (this._template) {
+this._validateApplyShim();
+this._poolContent();
+this._beginHosting();
+this._stampTemplate();
+this._endHosting();
+this._marshalAnnotationReferences();
+}
+this._marshalInstanceEffects();
+this._marshalBehaviors();
+this._marshalHostAttributes();
+this._marshalAttributes();
+this._tryReady();
+},
+_marshalBehavior: function (b) {
+if (b.listeners) {
+this._listenListeners(b.listeners);
+}
+}
+});(function () {
+var propertyUtils = Polymer.StyleProperties;
+var styleUtil = Polymer.StyleUtil;
+var cssParse = Polymer.CssParse;
+var styleDefaults = Polymer.StyleDefaults;
+var styleTransformer = Polymer.StyleTransformer;
+var applyShim = Polymer.ApplyShim;
+var debounce = Polymer.Debounce;
+var settings = Polymer.Settings;
+var updateDebouncer;
+Polymer({
+is: 'custom-style',
+extends: 'style',
+_template: null,
+properties: { include: String },
+ready: function () {
+this.__appliedElement = this.__appliedElement || this;
+this.__cssBuild = styleUtil.getCssBuildType(this);
+if (this.__appliedElement !== this) {
+this.__appliedElement.__cssBuild = this.__cssBuild;
+}
+if (this.ownerDocument !== window.document && this.__appliedElement === this) {
+document.head.appendChild(this);
+}
+this._tryApply();
+},
+attached: function () {
+this._tryApply();
+},
+_tryApply: function () {
+if (!this._appliesToDocument) {
+if (this.parentNode && this.parentNode.localName !== 'dom-module') {
+this._appliesToDocument = true;
+var e = this.__appliedElement;
+if (!settings.useNativeCSSProperties) {
+this.__needsUpdateStyles = styleDefaults.hasStyleProperties();
+styleDefaults.addStyle(e);
+}
+if (e.textContent || this.include) {
+this._apply(true);
+} else {
+var self = this;
+var observer = new MutationObserver(function () {
+observer.disconnect();
+self._apply(true);
+});
+observer.observe(e, { childList: true });
+}
+}
+}
+},
+_updateStyles: function () {
+Polymer.updateStyles();
+},
+_apply: function (initialApply) {
+var e = this.__appliedElement;
+if (this.include) {
+e.textContent = styleUtil.cssFromModules(this.include, true) + e.textContent;
+}
+if (!e.textContent) {
+return;
+}
+var buildType = this.__cssBuild;
+var targetedBuild = styleUtil.isTargetedBuild(buildType);
+if (settings.useNativeCSSProperties && targetedBuild) {
+return;
+}
+var styleRules = styleUtil.rulesForStyle(e);
+if (!targetedBuild) {
+styleUtil.forEachRule(styleRules, function (rule) {
+styleTransformer.documentRule(rule);
+});
+if (settings.useNativeCSSProperties && !buildType) {
+applyShim.transform([e]);
+}
+}
+if (settings.useNativeCSSProperties) {
+e.textContent = styleUtil.toCssText(styleRules);
+} else {
+var self = this;
+var fn = function fn() {
+self._flushCustomProperties();
+};
+if (initialApply) {
+Polymer.RenderStatus.whenReady(fn);
+} else {
+fn();
+}
+}
+},
+_flushCustomProperties: function () {
+if (this.__needsUpdateStyles) {
+this.__needsUpdateStyles = false;
+updateDebouncer = debounce(updateDebouncer, this._updateStyles);
+} else {
+this._applyCustomProperties();
+}
+},
+_applyCustomProperties: function () {
+var element = this.__appliedElement;
+this._computeStyleProperties();
+var props = this._styleProperties;
+var rules = styleUtil.rulesForStyle(element);
+if (!rules) {
+return;
+}
+element.textContent = styleUtil.toCssText(rules, function (rule) {
+var css = rule.cssText = rule.parsedCssText;
+if (rule.propertyInfo && rule.propertyInfo.cssText) {
+css = cssParse.removeCustomPropAssignment(css);
+rule.cssText = propertyUtils.valueForProperties(css, props);
+}
+});
+}
+});
+}());Polymer.Templatizer = {
+properties: { __hideTemplateChildren__: { observer: '_showHideChildren' } },
+_instanceProps: Polymer.nob,
+_parentPropPrefix: '_parent_',
+templatize: function (template) {
+this._templatized = template;
+if (!template._content) {
+template._content = template.content;
+}
+if (template._content._ctor) {
+this.ctor = template._content._ctor;
+this._prepParentProperties(this.ctor.prototype, template);
+return;
+}
+var archetype = Object.create(Polymer.Base);
+this._customPrepAnnotations(archetype, template);
+this._prepParentProperties(archetype, template);
+archetype._prepEffects();
+this._customPrepEffects(archetype);
+archetype._prepBehaviors();
+archetype._prepPropertyInfo();
+archetype._prepBindings();
+archetype._notifyPathUp = this._notifyPathUpImpl;
+archetype._scopeElementClass = this._scopeElementClassImpl;
+archetype.listen = this._listenImpl;
+archetype._showHideChildren = this._showHideChildrenImpl;
+archetype.__setPropertyOrig = this.__setProperty;
+archetype.__setProperty = this.__setPropertyImpl;
+var _constructor = this._constructorImpl;
+var ctor = function TemplateInstance(model, host) {
+_constructor.call(this, model, host);
+};
+ctor.prototype = archetype;
+archetype.constructor = ctor;
+template._content._ctor = ctor;
+this.ctor = ctor;
+},
+_getRootDataHost: function () {
+return this.dataHost && this.dataHost._rootDataHost || this.dataHost;
+},
+_showHideChildrenImpl: function (hide) {
+var c = this._children;
+for (var i = 0; i < c.length; i++) {
+var n = c[i];
+if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
+if (n.nodeType === Node.TEXT_NODE) {
+if (hide) {
+n.__polymerTextContent__ = n.textContent;
+n.textContent = '';
+} else {
+n.textContent = n.__polymerTextContent__;
+}
+} else if (n.style) {
+if (hide) {
+n.__polymerDisplay__ = n.style.display;
+n.style.display = 'none';
+} else {
+n.style.display = n.__polymerDisplay__;
+}
+}
+}
+n.__hideTemplateChildren__ = hide;
+}
+},
+__setPropertyImpl: function (property, value, fromAbove, node) {
+if (node && node.__hideTemplateChildren__ && property == 'textContent') {
+property = '__polymerTextContent__';
+}
+this.__setPropertyOrig(property, value, fromAbove, node);
+},
+_debounceTemplate: function (fn) {
+Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', fn));
+},
+_flushTemplates: function () {
+Polymer.dom.flush();
+},
+_customPrepEffects: function (archetype) {
+var parentProps = archetype._parentProps;
+for (var prop in parentProps) {
+archetype._addPropertyEffect(prop, 'function', this._createHostPropEffector(prop));
+}
+for (prop in this._instanceProps) {
+archetype._addPropertyEffect(prop, 'function', this._createInstancePropEffector(prop));
+}
+},
+_customPrepAnnotations: function (archetype, template) {
+var t = archetype._template = document.createElement('template');
+var c = t._content = template._content;
+if (!c._notes) {
+var rootDataHost = archetype._rootDataHost;
+if (rootDataHost) {
+Polymer.Annotations.prepElement = function () {
+rootDataHost._prepElement();
+};
+}
+c._notes = Polymer.Annotations.parseAnnotations(template);
+Polymer.Annotations.prepElement = null;
+this._processAnnotations(c._notes);
+}
+archetype._notes = c._notes;
+archetype._parentProps = c._parentProps;
+},
+_prepParentProperties: function (archetype, template) {
+var parentProps = this._parentProps = archetype._parentProps;
+if (this._forwardParentProp && parentProps) {
+var proto = archetype._parentPropProto;
+var prop;
+if (!proto) {
+for (prop in this._instanceProps) {
+delete parentProps[prop];
+}
+proto = archetype._parentPropProto = Object.create(null);
+if (template != this) {
+Polymer.Bind.prepareModel(proto);
+Polymer.Base.prepareModelNotifyPath(proto);
+}
+for (prop in parentProps) {
+var parentProp = this._parentPropPrefix + prop;
+var effects = [
+{
+kind: 'function',
+effect: this._createForwardPropEffector(prop),
+fn: Polymer.Bind._functionEffect
+},
+{
+kind: 'notify',
+fn: Polymer.Bind._notifyEffect,
+effect: { event: Polymer.CaseMap.camelToDashCase(parentProp) + '-changed' }
+}
+];
+proto._propertyEffects = proto._propertyEffects || {};
+proto._propertyEffects[parentProp] = effects;
+Polymer.Bind._createAccessors(proto, parentProp, effects);
+}
+}
+var self = this;
+if (template != this) {
+Polymer.Bind.prepareInstance(template);
+template._forwardParentProp = function (source, value) {
+self._forwardParentProp(source, value);
+};
+}
+this._extendTemplate(template, proto);
+template._pathEffector = function (path, value, fromAbove) {
+return self._pathEffectorImpl(path, value, fromAbove);
+};
+}
+},
+_createForwardPropEffector: function (prop) {
+return function (source, value) {
+this._forwardParentProp(prop, value);
+};
+},
+_createHostPropEffector: function (prop) {
+var prefix = this._parentPropPrefix;
+return function (source, value) {
+this.dataHost._templatized[prefix + prop] = value;
+};
+},
+_createInstancePropEffector: function (prop) {
+return function (source, value, old, fromAbove) {
+if (!fromAbove) {
+this.dataHost._forwardInstanceProp(this, prop, value);
+}
+};
+},
+_extendTemplate: function (template, proto) {
+var n$ = Object.getOwnPropertyNames(proto);
+if (proto._propertySetter) {
+template._propertySetter = proto._propertySetter;
+}
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) {
+var val = template[n];
+if (val && n == '_propertyEffects') {
+var pe = Polymer.Base.mixin({}, val);
+template._propertyEffects = Polymer.Base.mixin(pe, proto._propertyEffects);
+} else {
+var pd = Object.getOwnPropertyDescriptor(proto, n);
+Object.defineProperty(template, n, pd);
+if (val !== undefined) {
+template._propertySetter(n, val);
+}
+}
+}
+},
+_showHideChildren: function (hidden) {
+},
+_forwardInstancePath: function (inst, path, value) {
+},
+_forwardInstanceProp: function (inst, prop, value) {
+},
+_notifyPathUpImpl: function (path, value) {
+var dataHost = this.dataHost;
+var root = Polymer.Path.root(path);
+dataHost._forwardInstancePath.call(dataHost, this, path, value);
+if (root in dataHost._parentProps) {
+dataHost._templatized._notifyPath(dataHost._parentPropPrefix + path, value);
+}
+},
+_pathEffectorImpl: function (path, value, fromAbove) {
+if (this._forwardParentPath) {
+if (path.indexOf(this._parentPropPrefix) === 0) {
+var subPath = path.substring(this._parentPropPrefix.length);
+var model = Polymer.Path.root(subPath);
+if (model in this._parentProps) {
+this._forwardParentPath(subPath, value);
+}
+}
+}
+Polymer.Base._pathEffector.call(this._templatized, path, value, fromAbove);
+},
+_constructorImpl: function (model, host) {
+this._rootDataHost = host._getRootDataHost();
+this._setupConfigure(model);
+this._registerHost(host);
+this._beginHosting();
+this.root = this.instanceTemplate(this._template);
+this.root.__noContent = !this._notes._hasContent;
+this.root.__styleScoped = true;
+this._endHosting();
+this._marshalAnnotatedNodes();
+this._marshalInstanceEffects();
+this._marshalAnnotatedListeners();
+var children = [];
+for (var n = this.root.firstChild; n; n = n.nextSibling) {
+children.push(n);
+n._templateInstance = this;
+}
+this._children = children;
+if (host.__hideTemplateChildren__) {
+this._showHideChildren(true);
+}
+this._tryReady();
+},
+_listenImpl: function (node, eventName, methodName) {
+var model = this;
+var host = this._rootDataHost;
+var handler = host._createEventHandler(node, eventName, methodName);
+var decorated = function (e) {
+e.model = model;
+handler(e);
+};
+host._listen(node, eventName, decorated);
+},
+_scopeElementClassImpl: function (node, value) {
+var host = this._rootDataHost;
+if (host) {
+return host._scopeElementClass(node, value);
+}
+return value;
+},
+stamp: function (model) {
+model = model || {};
+if (this._parentProps) {
+var templatized = this._templatized;
+for (var prop in this._parentProps) {
+if (model[prop] === undefined) {
+model[prop] = templatized[this._parentPropPrefix + prop];
+}
+}
+}
+return new this.ctor(model, this);
+},
+modelForElement: function (el) {
+var model;
+while (el) {
+if (model = el._templateInstance) {
+if (model.dataHost != this) {
+el = model.dataHost;
+} else {
+return model;
+}
+} else {
+el = el.parentNode;
+}
+}
+}
+};Polymer({
+is: 'dom-template',
+extends: 'template',
+_template: null,
+behaviors: [Polymer.Templatizer],
+ready: function () {
+this.templatize(this);
+}
+});Polymer._collections = new WeakMap();
+Polymer.Collection = function (userArray) {
+Polymer._collections.set(userArray, this);
+this.userArray = userArray;
+this.store = userArray.slice();
+this.initMap();
+};
+Polymer.Collection.prototype = {
+constructor: Polymer.Collection,
+initMap: function () {
+var omap = this.omap = new WeakMap();
+var pmap = this.pmap = {};
+var s = this.store;
+for (var i = 0; i < s.length; i++) {
+var item = s[i];
+if (item && typeof item == 'object') {
+omap.set(item, i);
+} else {
+pmap[item] = i;
+}
+}
+},
+add: function (item) {
+var key = this.store.push(item) - 1;
+if (item && typeof item == 'object') {
+this.omap.set(item, key);
+} else {
+this.pmap[item] = key;
+}
+return '#' + key;
+},
+removeKey: function (key) {
+if (key = this._parseKey(key)) {
+this._removeFromMap(this.store[key]);
+delete this.store[key];
+}
+},
+_removeFromMap: function (item) {
+if (item && typeof item == 'object') {
+this.omap.delete(item);
+} else {
+delete this.pmap[item];
+}
+},
+remove: function (item) {
+var key = this.getKey(item);
+this.removeKey(key);
+return key;
+},
+getKey: function (item) {
+var key;
+if (item && typeof item == 'object') {
+key = this.omap.get(item);
+} else {
+key = this.pmap[item];
+}
+if (key != undefined) {
+return '#' + key;
+}
+},
+getKeys: function () {
+return Object.keys(this.store).map(function (key) {
+return '#' + key;
+});
+},
+_parseKey: function (key) {
+if (key && key[0] == '#') {
+return key.slice(1);
+}
+},
+setItem: function (key, item) {
+if (key = this._parseKey(key)) {
+var old = this.store[key];
+if (old) {
+this._removeFromMap(old);
+}
+if (item && typeof item == 'object') {
+this.omap.set(item, key);
+} else {
+this.pmap[item] = key;
+}
+this.store[key] = item;
+}
+},
+getItem: function (key) {
+if (key = this._parseKey(key)) {
+return this.store[key];
+}
+},
+getItems: function () {
+var items = [], store = this.store;
+for (var key in store) {
+items.push(store[key]);
+}
+return items;
+},
+_applySplices: function (splices) {
+var keyMap = {}, key;
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) {
+s.addedKeys = [];
+for (var j = 0; j < s.removed.length; j++) {
+key = this.getKey(s.removed[j]);
+keyMap[key] = keyMap[key] ? null : -1;
+}
+for (j = 0; j < s.addedCount; j++) {
+var item = this.userArray[s.index + j];
+key = this.getKey(item);
+key = key === undefined ? this.add(item) : key;
+keyMap[key] = keyMap[key] ? null : 1;
+s.addedKeys.push(key);
+}
+}
+var removed = [];
+var added = [];
+for (key in keyMap) {
+if (keyMap[key] < 0) {
+this.removeKey(key);
+removed.push(key);
+}
+if (keyMap[key] > 0) {
+added.push(key);
+}
+}
+return [{
+removed: removed,
+added: added
+}];
+}
+};
+Polymer.Collection.get = function (userArray) {
+return Polymer._collections.get(userArray) || new Polymer.Collection(userArray);
+};
+Polymer.Collection.applySplices = function (userArray, splices) {
+var coll = Polymer._collections.get(userArray);
+return coll ? coll._applySplices(splices) : null;
+};Polymer({
+is: 'dom-repeat',
+extends: 'template',
+_template: null,
+properties: {
+items: { type: Array },
+as: {
+type: String,
+value: 'item'
+},
+indexAs: {
+type: String,
+value: 'index'
+},
+sort: {
+type: Function,
+observer: '_sortChanged'
+},
+filter: {
+type: Function,
+observer: '_filterChanged'
+},
+observe: {
+type: String,
+observer: '_observeChanged'
+},
+delay: Number,
+renderedItemCount: {
+type: Number,
+notify: !Polymer.Settings.suppressTemplateNotifications,
+readOnly: true
+},
+initialCount: {
+type: Number,
+observer: '_initializeChunking'
+},
+targetFramerate: {
+type: Number,
+value: 20
+},
+notifyDomChange: { type: Boolean },
+_targetFrameTime: {
+type: Number,
+computed: '_computeFrameTime(targetFramerate)'
+}
+},
+behaviors: [Polymer.Templatizer],
+observers: ['_itemsChanged(items.*)'],
+created: function () {
+this._instances = [];
+this._pool = [];
+this._limit = Infinity;
+var self = this;
+this._boundRenderChunk = function () {
+self._renderChunk();
+};
+},
+detached: function () {
+this.__isDetached = true;
+for (var i = 0; i < this._instances.length; i++) {
+this._detachInstance(i);
+}
+},
+attached: function () {
+if (this.__isDetached) {
+this.__isDetached = false;
+var refNode;
+var parentNode = Polymer.dom(this).parentNode;
+if (parentNode.localName == this.is) {
+refNode = parentNode;
+parentNode = Polymer.dom(parentNode).parentNode;
+} else {
+refNode = this;
+}
+var parent = Polymer.dom(parentNode);
+for (var i = 0; i < this._instances.length; i++) {
+this._attachInstance(i, parent, refNode);
+}
+}
+},
+ready: function () {
+this._instanceProps = { __key__: true };
+this._instanceProps[this.as] = true;
+this._instanceProps[this.indexAs] = true;
+if (!this.ctor) {
+this.templatize(this);
+}
+},
+_sortChanged: function (sort) {
+var dataHost = this._getRootDataHost();
+this._sortFn = sort && (typeof sort == 'function' ? sort : function () {
+return dataHost[sort].apply(dataHost, arguments);
+});
+this._needFullRefresh = true;
+if (this.items) {
+this._debounceTemplate(this._render);
+}
+},
+_filterChanged: function (filter) {
+var dataHost = this._getRootDataHost();
+this._filterFn = filter && (typeof filter == 'function' ? filter : function () {
+return dataHost[filter].apply(dataHost, arguments);
+});
+this._needFullRefresh = true;
+if (this.items) {
+this._debounceTemplate(this._render);
+}
+},
+_computeFrameTime: function (rate) {
+return Math.ceil(1000 / rate);
+},
+_initializeChunking: function () {
+if (this.initialCount) {
+this._limit = this.initialCount;
+this._chunkCount = this.initialCount;
+this._lastChunkTime = performance.now();
+}
+},
+_tryRenderChunk: function () {
+if (this.items && this._limit < this.items.length) {
+this.debounce('renderChunk', this._requestRenderChunk);
+}
+},
+_requestRenderChunk: function () {
+requestAnimationFrame(this._boundRenderChunk);
+},
+_renderChunk: function () {
+var currChunkTime = performance.now();
+var ratio = this._targetFrameTime / (currChunkTime - this._lastChunkTime);
+this._chunkCount = Math.round(this._chunkCount * ratio) || 1;
+this._limit += this._chunkCount;
+this._lastChunkTime = currChunkTime;
+this._debounceTemplate(this._render);
+},
+_observeChanged: function () {
+this._observePaths = this.observe && this.observe.replace('.*', '.').split(' ');
+},
+_itemsChanged: function (change) {
+if (change.path == 'items') {
+if (Array.isArray(this.items)) {
+this.collection = Polymer.Collection.get(this.items);
+} else if (!this.items) {
+this.collection = null;
+} else {
+this._error(this._logf('dom-repeat', 'expected array for `items`,' + ' found', this.items));
+}
+this._keySplices = [];
+this._indexSplices = [];
+this._needFullRefresh = true;
+this._initializeChunking();
+this._debounceTemplate(this._render);
+} else if (change.path == 'items.splices') {
+this._keySplices = this._keySplices.concat(change.value.keySplices);
+this._indexSplices = this._indexSplices.concat(change.value.indexSplices);
+this._debounceTemplate(this._render);
+} else {
+var subpath = change.path.slice(6);
+this._forwardItemPath(subpath, change.value);
+this._checkObservedPaths(subpath);
+}
+},
+_checkObservedPaths: function (path) {
+if (this._observePaths) {
+path = path.substring(path.indexOf('.') + 1);
+var paths = this._observePaths;
+for (var i = 0; i < paths.length; i++) {
+if (path.indexOf(paths[i]) === 0) {
+this._needFullRefresh = true;
+if (this.delay) {
+this.debounce('render', this._render, this.delay);
+} else {
+this._debounceTemplate(this._render);
+}
+return;
+}
+}
+}
+},
+render: function () {
+this._needFullRefresh = true;
+this._debounceTemplate(this._render);
+this._flushTemplates();
+},
+_render: function () {
+if (this._needFullRefresh) {
+this._applyFullRefresh();
+this._needFullRefresh = false;
+} else if (this._keySplices.length) {
+if (this._sortFn) {
+this._applySplicesUserSort(this._keySplices);
+} else {
+if (this._filterFn) {
+this._applyFullRefresh();
+} else {
+this._applySplicesArrayOrder(this._indexSplices);
+}
+}
+} else {
+}
+this._keySplices = [];
+this._indexSplices = [];
+var keyToIdx = this._keyToInstIdx = {};
+for (var i = this._instances.length - 1; i >= 0; i--) {
+var inst = this._instances[i];
+if (inst.isPlaceholder && i < this._limit) {
+inst = this._insertInstance(i, inst.__key__);
+} else if (!inst.isPlaceholder && i >= this._limit) {
+inst = this._downgradeInstance(i, inst.__key__);
+}
+keyToIdx[inst.__key__] = i;
+if (!inst.isPlaceholder) {
+inst.__setProperty(this.indexAs, i, true);
+}
+}
+this._pool.length = 0;
+this._setRenderedItemCount(this._instances.length);
+if (!Polymer.Settings.suppressTemplateNotifications || this.notifyDomChange) {
+this.fire('dom-change');
+}
+this._tryRenderChunk();
+},
+_applyFullRefresh: function () {
+var c = this.collection;
+var keys;
+if (this._sortFn) {
+keys = c ? c.getKeys() : [];
+} else {
+keys = [];
+var items = this.items;
+if (items) {
+for (var i = 0; i < items.length; i++) {
+keys.push(c.getKey(items[i]));
+}
+}
+}
+var self = this;
+if (this._filterFn) {
+keys = keys.filter(function (a) {
+return self._filterFn(c.getItem(a));
+});
+}
+if (this._sortFn) {
+keys.sort(function (a, b) {
+return self._sortFn(c.getItem(a), c.getItem(b));
+});
+}
+for (i = 0; i < keys.length; i++) {
+var key = keys[i];
+var inst = this._instances[i];
+if (inst) {
+inst.__key__ = key;
+if (!inst.isPlaceholder && i < this._limit) {
+inst.__setProperty(this.as, c.getItem(key), true);
+}
+} else if (i < this._limit) {
+this._insertInstance(i, key);
+} else {
+this._insertPlaceholder(i, key);
+}
+}
+for (var j = this._instances.length - 1; j >= i; j--) {
+this._detachAndRemoveInstance(j);
+}
+},
+_numericSort: function (a, b) {
+return a - b;
+},
+_applySplicesUserSort: function (splices) {
+var c = this.collection;
+var keyMap = {};
+var key;
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) {
+for (var j = 0; j < s.removed.length; j++) {
+key = s.removed[j];
+keyMap[key] = keyMap[key] ? null : -1;
+}
+for (j = 0; j < s.added.length; j++) {
+key = s.added[j];
+keyMap[key] = keyMap[key] ? null : 1;
+}
+}
+var removedIdxs = [];
+var addedKeys = [];
+for (key in keyMap) {
+if (keyMap[key] === -1) {
+removedIdxs.push(this._keyToInstIdx[key]);
+}
+if (keyMap[key] === 1) {
+addedKeys.push(key);
+}
+}
+if (removedIdxs.length) {
+removedIdxs.sort(this._numericSort);
+for (i = removedIdxs.length - 1; i >= 0; i--) {
+var idx = removedIdxs[i];
+if (idx !== undefined) {
+this._detachAndRemoveInstance(idx);
+}
+}
+}
+var self = this;
+if (addedKeys.length) {
+if (this._filterFn) {
+addedKeys = addedKeys.filter(function (a) {
+return self._filterFn(c.getItem(a));
+});
+}
+addedKeys.sort(function (a, b) {
+return self._sortFn(c.getItem(a), c.getItem(b));
+});
+var start = 0;
+for (i = 0; i < addedKeys.length; i++) {
+start = this._insertRowUserSort(start, addedKeys[i]);
+}
+}
+},
+_insertRowUserSort: function (start, key) {
+var c = this.collection;
+var item = c.getItem(key);
+var end = this._instances.length - 1;
+var idx = -1;
+while (start <= end) {
+var mid = start + end >> 1;
+var midKey = this._instances[mid].__key__;
+var cmp = this._sortFn(c.getItem(midKey), item);
+if (cmp < 0) {
+start = mid + 1;
+} else if (cmp > 0) {
+end = mid - 1;
+} else {
+idx = mid;
+break;
+}
+}
+if (idx < 0) {
+idx = end + 1;
+}
+this._insertPlaceholder(idx, key);
+return idx;
+},
+_applySplicesArrayOrder: function (splices) {
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) {
+for (var j = 0; j < s.removed.length; j++) {
+this._detachAndRemoveInstance(s.index);
+}
+for (j = 0; j < s.addedKeys.length; j++) {
+this._insertPlaceholder(s.index + j, s.addedKeys[j]);
+}
+}
+},
+_detachInstance: function (idx) {
+var inst = this._instances[idx];
+if (!inst.isPlaceholder) {
+for (var i = 0; i < inst._children.length; i++) {
+var el = inst._children[i];
+Polymer.dom(inst.root).appendChild(el);
+}
+return inst;
+}
+},
+_attachInstance: function (idx, parent, refNode) {
+var inst = this._instances[idx];
+if (!inst.isPlaceholder) {
+parent.insertBefore(inst.root, refNode);
+}
+},
+_detachAndRemoveInstance: function (idx) {
+var inst = this._detachInstance(idx);
+if (inst) {
+this._pool.push(inst);
+}
+this._instances.splice(idx, 1);
+},
+_insertPlaceholder: function (idx, key) {
+this._instances.splice(idx, 0, {
+isPlaceholder: true,
+__key__: key
+});
+},
+_stampInstance: function (idx, key) {
+var model = { __key__: key };
+model[this.as] = this.collection.getItem(key);
+model[this.indexAs] = idx;
+return this.stamp(model);
+},
+_insertInstance: function (idx, key) {
+var inst = this._pool.pop();
+if (inst) {
+inst.__setProperty(this.as, this.collection.getItem(key), true);
+inst.__setProperty('__key__', key, true);
+} else {
+inst = this._stampInstance(idx, key);
+}
+var beforeRow = this._instances[idx + 1];
+var beforeNode = beforeRow && !beforeRow.isPlaceholder ? beforeRow._children[0] : this;
+var parentNode = Polymer.dom(this).parentNode;
+if (parentNode.localName == this.is) {
+if (beforeNode == this) {
+beforeNode = parentNode;
+}
+parentNode = Polymer.dom(parentNode).parentNode;
+}
+Polymer.dom(parentNode).insertBefore(inst.root, beforeNode);
+this._instances[idx] = inst;
+return inst;
+},
+_downgradeInstance: function (idx, key) {
+var inst = this._detachInstance(idx);
+if (inst) {
+this._pool.push(inst);
+}
+inst = {
+isPlaceholder: true,
+__key__: key
+};
+this._instances[idx] = inst;
+return inst;
+},
+_showHideChildren: function (hidden) {
+for (var i = 0; i < this._instances.length; i++) {
+if (!this._instances[i].isPlaceholder)
+this._instances[i]._showHideChildren(hidden);
+}
+},
+_forwardInstanceProp: function (inst, prop, value) {
+if (prop == this.as) {
+var idx;
+if (this._sortFn || this._filterFn) {
+idx = this.items.indexOf(this.collection.getItem(inst.__key__));
+} else {
+idx = inst[this.indexAs];
+}
+this.set('items.' + idx, value);
+}
+},
+_forwardInstancePath: function (inst, path, value) {
+if (path.indexOf(this.as + '.') === 0) {
+this._notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.length + 1), value);
+}
+},
+_forwardParentProp: function (prop, value) {
+var i$ = this._instances;
+for (var i = 0, inst; i < i$.length && (inst = i$[i]); i++) {
+if (!inst.isPlaceholder) {
+inst.__setProperty(prop, value, true);
+}
+}
+},
+_forwardParentPath: function (path, value) {
+var i$ = this._instances;
+for (var i = 0, inst; i < i$.length && (inst = i$[i]); i++) {
+if (!inst.isPlaceholder) {
+inst._notifyPath(path, value, true);
+}
+}
+},
+_forwardItemPath: function (path, value) {
+if (this._keyToInstIdx) {
+var dot = path.indexOf('.');
+var key = path.substring(0, dot < 0 ? path.length : dot);
+var idx = this._keyToInstIdx[key];
+var inst = this._instances[idx];
+if (inst && !inst.isPlaceholder) {
+if (dot >= 0) {
+path = this.as + '.' + path.substring(dot + 1);
+inst._notifyPath(path, value, true);
+} else {
+inst.__setProperty(this.as, value, true);
+}
+}
+}
+},
+itemForElement: function (el) {
+var instance = this.modelForElement(el);
+return instance && instance[this.as];
+},
+keyForElement: function (el) {
+var instance = this.modelForElement(el);
+return instance && instance.__key__;
+},
+indexForElement: function (el) {
+var instance = this.modelForElement(el);
+return instance && instance[this.indexAs];
+}
+});Polymer({
+is: 'array-selector',
+_template: null,
+properties: {
+items: {
+type: Array,
+observer: 'clearSelection'
+},
+multi: {
+type: Boolean,
+value: false,
+observer: 'clearSelection'
+},
+selected: {
+type: Object,
+notify: true
+},
+selectedItem: {
+type: Object,
+notify: true
+},
+toggle: {
+type: Boolean,
+value: false
+}
+},
+clearSelection: function () {
+if (Array.isArray(this.selected)) {
+for (var i = 0; i < this.selected.length; i++) {
+this.unlinkPaths('selected.' + i);
+}
+} else {
+this.unlinkPaths('selected');
+this.unlinkPaths('selectedItem');
+}
+if (this.multi) {
+if (!this.selected || this.selected.length) {
+this.selected = [];
+this._selectedColl = Polymer.Collection.get(this.selected);
+}
+} else {
+this.selected = null;
+this._selectedColl = null;
+}
+this.selectedItem = null;
+},
+isSelected: function (item) {
+if (this.multi) {
+return this._selectedColl.getKey(item) !== undefined;
+} else {
+return this.selected == item;
+}
+},
+deselect: function (item) {
+if (this.multi) {
+if (this.isSelected(item)) {
+var skey = this._selectedColl.getKey(item);
+this.arrayDelete('selected', item);
+this.unlinkPaths('selected.' + skey);
+}
+} else {
+this.selected = null;
+this.selectedItem = null;
+this.unlinkPaths('selected');
+this.unlinkPaths('selectedItem');
+}
+},
+select: function (item) {
+var icol = Polymer.Collection.get(this.items);
+var key = icol.getKey(item);
+if (this.multi) {
+if (this.isSelected(item)) {
+if (this.toggle) {
+this.deselect(item);
+}
+} else {
+this.push('selected', item);
+var skey = this._selectedColl.getKey(item);
+this.linkPaths('selected.' + skey, 'items.' + key);
+}
+} else {
+if (this.toggle && item == this.selected) {
+this.deselect();
+} else {
+this.selected = item;
+this.selectedItem = item;
+this.linkPaths('selected', 'items.' + key);
+this.linkPaths('selectedItem', 'items.' + key);
+}
+}
+}
+});Polymer({
+is: 'dom-if',
+extends: 'template',
+_template: null,
+properties: {
+'if': {
+type: Boolean,
+value: false,
+observer: '_queueRender'
+},
+restamp: {
+type: Boolean,
+value: false,
+observer: '_queueRender'
+},
+notifyDomChange: { type: Boolean }
+},
+behaviors: [Polymer.Templatizer],
+_queueRender: function () {
+this._debounceTemplate(this._render);
+},
+detached: function () {
+var parentNode = this.parentNode;
+if (parentNode && parentNode.localName == this.is) {
+parentNode = Polymer.dom(parentNode).parentNode;
+}
+if (!parentNode || parentNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE && (!Polymer.Settings.hasShadow || !(parentNode instanceof ShadowRoot))) {
+this._teardownInstance();
+}
+},
+attached: function () {
+if (this.if && this.ctor) {
+this.async(this._ensureInstance);
+}
+},
+render: function () {
+this._flushTemplates();
+},
+_render: function () {
+if (this.if) {
+if (!this.ctor) {
+this.templatize(this);
+}
+this._ensureInstance();
+this._showHideChildren();
+} else if (this.restamp) {
+this._teardownInstance();
+}
+if (!this.restamp && this._instance) {
+this._showHideChildren();
+}
+if (this.if != this._lastIf) {
+if (!Polymer.Settings.suppressTemplateNotifications || this.notifyDomChange) {
+this.fire('dom-change');
+}
+this._lastIf = this.if;
+}
+},
+_ensureInstance: function () {
+var refNode;
+var parentNode = Polymer.dom(this).parentNode;
+if (parentNode && parentNode.localName == this.is) {
+refNode = parentNode;
+parentNode = Polymer.dom(parentNode).parentNode;
+} else {
+refNode = this;
+}
+if (parentNode) {
+if (!this._instance) {
+this._instance = this.stamp();
+var root = this._instance.root;
+Polymer.dom(parentNode).insertBefore(root, refNode);
+} else {
+var c$ = this._instance._children;
+if (c$ && c$.length) {
+var lastChild = Polymer.dom(refNode).previousSibling;
+if (lastChild !== c$[c$.length - 1]) {
+for (var i = 0, n; i < c$.length && (n = c$[i]); i++) {
+Polymer.dom(parentNode).insertBefore(n, refNode);
+}
+}
+}
+}
+}
+},
+_teardownInstance: function () {
+if (this._instance) {
+var c$ = this._instance._children;
+if (c$ && c$.length) {
+var parent = Polymer.dom(Polymer.dom(c$[0]).parentNode);
+for (var i = 0, n; i < c$.length && (n = c$[i]); i++) {
+parent.removeChild(n);
+}
+}
+this._instance = null;
+}
+},
+_showHideChildren: function () {
+var hidden = this.__hideTemplateChildren__ || !this.if;
+if (this._instance) {
+this._instance._showHideChildren(hidden);
+}
+},
+_forwardParentProp: function (prop, value) {
+if (this._instance) {
+this._instance.__setProperty(prop, value, true);
+}
+},
+_forwardParentPath: function (path, value) {
+if (this._instance) {
+this._instance._notifyPath(path, value, true);
+}
+}
+});Polymer({
+is: 'dom-bind',
+properties: { notifyDomChange: { type: Boolean } },
+extends: 'template',
+_template: null,
+created: function () {
+var self = this;
+Polymer.RenderStatus.whenReady(function () {
+if (document.readyState == 'loading') {
+document.addEventListener('DOMContentLoaded', function () {
+self._markImportsReady();
+});
+} else {
+self._markImportsReady();
+}
+});
+},
+_ensureReady: function () {
+if (!this._readied) {
+this._readySelf();
+}
+},
+_markImportsReady: function () {
+this._importsReady = true;
+this._ensureReady();
+},
+_registerFeatures: function () {
+this._prepConstructor();
+},
+_insertChildren: function () {
+var refNode;
+var parentNode = Polymer.dom(this).parentNode;
+if (parentNode.localName == this.is) {
+refNode = parentNode;
+parentNode = Polymer.dom(parentNode).parentNode;
+} else {
+refNode = this;
+}
+Polymer.dom(parentNode).insertBefore(this.root, refNode);
+},
+_removeChildren: function () {
+if (this._children) {
+for (var i = 0; i < this._children.length; i++) {
+this.root.appendChild(this._children[i]);
+}
+}
+},
+_initFeatures: function () {
+},
+_scopeElementClass: function (element, selector) {
+if (this.dataHost) {
+return this.dataHost._scopeElementClass(element, selector);
+} else {
+return selector;
+}
+},
+_configureInstanceProperties: function () {
+},
+_prepConfigure: function () {
+var config = {};
+for (var prop in this._propertyEffects) {
+config[prop] = this[prop];
+}
+var setupConfigure = this._setupConfigure;
+this._setupConfigure = function () {
+setupConfigure.call(this, config);
+};
+},
+attached: function () {
+if (this._importsReady) {
+this.render();
+}
+},
+detached: function () {
+this._removeChildren();
+},
+render: function () {
+this._ensureReady();
+if (!this._children) {
+this._template = this;
+this._prepAnnotations();
+this._prepEffects();
+this._prepBehaviors();
+this._prepConfigure();
+this._prepBindings();
+this._prepPropertyInfo();
+Polymer.Base._initFeatures.call(this);
+this._children = Polymer.TreeApi.arrayCopyChildNodes(this.root);
+}
+this._insertChildren();
+if (!Polymer.Settings.suppressTemplateNotifications || this.notifyDomChange) {
+this.fire('dom-change');
+}
+}
+});</script>
+
+
+
+
+
+
+
+
+
diff --git a/catapult/third_party/polymer/components/promise-polyfill/.bower.json b/catapult/third_party/polymer/components/promise-polyfill/.bower.json
new file mode 100644
index 00000000..d6e8c0d1
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/.bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "promise-polyfill",
+ "version": "1.0.1",
+ "homepage": "https://github.com/taylorhakes/promise-polyfill",
+ "authors": [
+ "Taylor Hakes"
+ ],
+ "description": "Lightweight promise polyfill for the browser and node. A+ Compliant.",
+ "main": "Promise.js",
+ "moduleType": [
+ "globals",
+ "node"
+ ],
+ "keywords": [
+ "promise",
+ "es6",
+ "polyfill",
+ "html5"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "dependencies": {
+ "polymer": "polymer/polymer#^1.0.0"
+ },
+ "_release": "1.0.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.0.1",
+ "commit": "263b98bcc9b746a8852bf57ce1500c71222680b9"
+ },
+ "_source": "https://github.com/polymerlabs/promise-polyfill.git",
+ "_target": "^1.0.0",
+ "_originalSource": "polymerlabs/promise-polyfill"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/promise-polyfill/CONTRIBUTING.md b/catapult/third_party/polymer/components/promise-polyfill/CONTRIBUTING.md
new file mode 100644
index 00000000..093090d4
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/CONTRIBUTING.md
@@ -0,0 +1,77 @@
+<!--
+This file is autogenerated based on
+https://github.com/PolymerElements/ContributionGuide/blob/master/CONTRIBUTING.md
+
+If you edit that file, it will get updated everywhere else.
+If you edit this file, your changes will get overridden :)
+
+You can however override the jsbin link with one that's customized to this
+specific element:
+
+jsbin=https://jsbin.com/cagaye/edit?html,output
+-->
+
+# Polymer Elements
+## Guide for Contributors
+
+Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines:
+
+### Filing Issues
+
+**If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions:
+
+ 1. **Who will use the feature?** _“As someone filling out a form…”_
+ 2. **When will they use the feature?** _“When I enter an invalid value…”_
+ 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_
+
+**If you are filing an issue to report a bug**, please provide:
+
+ 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug:
+
+ ```markdown
+ The `paper-foo` element causes the page to turn pink when clicked.
+
+ ## Expected outcome
+
+ The page stays the same color.
+
+ ## Actual outcome
+
+ The page turns pink.
+
+ ## Steps to reproduce
+
+ 1. Put a `paper-foo` element in the page.
+ 2. Open the page in a web browser.
+ 3. Click the `paper-foo` element.
+ ```
+
+ 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output).
+
+ 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers.
+
+### Submitting Pull Requests
+
+**Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request.
+
+When submitting pull requests, please provide:
+
+ 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax:
+
+ ```markdown
+ (For a single issue)
+ Fixes #20
+
+ (For multiple issues)
+ Fixes #32, fixes #40
+ ```
+
+ 2. **A succinct description of the design** used to fix any related issues. For example:
+
+ ```markdown
+ This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked.
+ ```
+
+ 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered.
+
+If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that!
diff --git a/catapult/third_party/polymer/components/promise-polyfill/Gruntfile.js b/catapult/third_party/polymer/components/promise-polyfill/Gruntfile.js
new file mode 100644
index 00000000..74f4fe54
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/Gruntfile.js
@@ -0,0 +1,49 @@
+/**
+@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
+*/
+module.exports = function(grunt) {
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+
+ uglify: {
+ options: {
+ banner: '/*! <%= pkg.name %> <%= pkg.version %> */\n'
+ },
+ dist: {
+ files: {
+ 'Promise.min.uglify.js': ['Promise.js']
+ }
+ }
+ },
+
+ closurecompiler: {
+ options: {
+ compilation_level: 'ADVANCED_OPTIMIZATIONS',
+ },
+ dist: {
+ files: {
+ 'Promise.min.js': ['Promise.js']
+ }
+ }
+ },
+
+ bytesize: {
+ dist: {
+ src: ['Promise*.js']
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-closurecompiler');
+ grunt.loadNpmTasks('grunt-bytesize');
+
+ grunt.registerTask('build', ['closurecompiler', 'bytesize']);
+};
diff --git a/catapult/third_party/polymer/components/promise-polyfill/LICENSE b/catapult/third_party/polymer/components/promise-polyfill/LICENSE
new file mode 100644
index 00000000..94b9dac3
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Taylor Hakes
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/promise-polyfill/Promise-Statics.js b/catapult/third_party/polymer/components/promise-polyfill/Promise-Statics.js
new file mode 100644
index 00000000..49eeb8b3
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/Promise-Statics.js
@@ -0,0 +1,49 @@
+/**
+@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
+*/
+Promise.all = Promise.all || function () {
+ var args = Array.prototype.slice.call(arguments.length === 1 && Array.isArray(arguments[0]) ? arguments[0] : arguments);
+
+ return new Promise(function (resolve, reject) {
+ if (args.length === 0) return resolve([]);
+ var remaining = args.length;
+ function res(i, val) {
+ try {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then;
+ if (typeof then === 'function') {
+ then.call(val, function (val) { res(i, val) }, reject);
+ return;
+ }
+ }
+ args[i] = val;
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ } catch (ex) {
+ reject(ex);
+ }
+ }
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i]);
+ }
+ });
+};
+
+Promise.race = Promise.race || function(values) {
+ // TODO(bradfordcsmith): To be consistent with the ECMAScript spec, this
+ // method should take any iterable, not just an array.
+ var forcedArray = /** @type {!Array<!Thenable>} */ (values);
+ return new Promise(function (resolve, reject) {
+ for(var i = 0, len = forcedArray.length; i < len; i++) {
+ forcedArray[i].then(resolve, reject);
+ }
+ });
+};
+
diff --git a/catapult/third_party/polymer/components/promise-polyfill/Promise.js b/catapult/third_party/polymer/components/promise-polyfill/Promise.js
new file mode 100644
index 00000000..b8388bf3
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/Promise.js
@@ -0,0 +1,137 @@
+/**
+@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
+*/
+function MakePromise (asap) {
+ function Promise(fn) {
+ if (typeof this !== 'object' || typeof fn !== 'function') throw new TypeError();
+ this._state = null;
+ this._value = null;
+ this._deferreds = []
+
+ doResolve(fn, resolve.bind(this), reject.bind(this));
+ }
+
+ function handle(deferred) {
+ var me = this;
+ if (this._state === null) {
+ this._deferreds.push(deferred);
+ return
+ }
+ asap(function() {
+ var cb = me._state ? deferred.onFulfilled : deferred.onRejected
+ if (typeof cb !== 'function') {
+ (me._state ? deferred.resolve : deferred.reject)(me._value);
+ return;
+ }
+ var ret;
+ try {
+ ret = cb(me._value);
+ }
+ catch (e) {
+ deferred.reject(e);
+ return;
+ }
+ deferred.resolve(ret);
+ })
+ }
+
+ function resolve(newValue) {
+ try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ if (newValue === this) throw new TypeError();
+ if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
+ var then = newValue.then;
+ if (typeof then === 'function') {
+ doResolve(then.bind(newValue), resolve.bind(this), reject.bind(this));
+ return;
+ }
+ }
+ this._state = true;
+ this._value = newValue;
+ finale.call(this);
+ } catch (e) { reject.call(this, e); }
+ }
+
+ function reject(newValue) {
+ this._state = false;
+ this._value = newValue;
+ finale.call(this);
+ }
+
+ function finale() {
+ for (var i = 0, len = this._deferreds.length; i < len; i++) {
+ handle.call(this, this._deferreds[i]);
+ }
+ this._deferreds = null;
+ }
+
+ /**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+ function doResolve(fn, onFulfilled, onRejected) {
+ var done = false;
+ try {
+ fn(function (value) {
+ if (done) return;
+ done = true;
+ onFulfilled(value);
+ }, function (reason) {
+ if (done) return;
+ done = true;
+ onRejected(reason);
+ })
+ } catch (ex) {
+ if (done) return;
+ done = true;
+ onRejected(ex);
+ }
+ }
+
+ Promise.prototype['catch'] = function (onRejected) {
+ return this.then(null, onRejected);
+ };
+
+ Promise.prototype.then = function(onFulfilled, onRejected) {
+ var me = this;
+ return new Promise(function(resolve, reject) {
+ handle.call(me, {
+ onFulfilled: onFulfilled,
+ onRejected: onRejected,
+ resolve: resolve,
+ reject: reject
+ });
+ })
+ };
+
+ Promise.resolve = function (value) {
+ if (value && typeof value === 'object' && value.constructor === Promise) {
+ return value;
+ }
+
+ return new Promise(function (resolve) {
+ resolve(value);
+ });
+ };
+
+ Promise.reject = function (value) {
+ return new Promise(function (resolve, reject) {
+ reject(value);
+ });
+ };
+
+
+ return Promise;
+}
+
+if (typeof module !== 'undefined') {
+ module.exports = MakePromise;
+}
+
diff --git a/catapult/third_party/polymer/components/promise-polyfill/Promise.min.js b/catapult/third_party/polymer/components/promise-polyfill/Promise.min.js
new file mode 100644
index 00000000..79610599
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/Promise.min.js
@@ -0,0 +1,12 @@
+/**
+@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
+*/
+function m(n){function b(a){if("object"!==typeof this||"function"!==typeof a)throw new TypeError;this.c=this.a=null;this.b=[];g(a,h.bind(this),d.bind(this))}function k(a){var c=this;null===this.a?this.b.push(a):n(function(){var f=c.a?a.d:a.e;if("function"!==typeof f)(c.a?a.resolve:a.reject)(c.c);else{var e;try{e=f(c.c)}catch(b){a.reject(b);return}a.resolve(e)}})}function h(a){try{if(a===this)throw new TypeError;if(a&&("object"===typeof a||"function"===typeof a)){var c=a.then;if("function"===typeof c){g(c.bind(a),
+h.bind(this),d.bind(this));return}}this.a=!0;this.c=a;l.call(this)}catch(b){d.call(this,b)}}function d(a){this.a=!1;this.c=a;l.call(this)}function l(){for(var a=0,c=this.b.length;a<c;a++)k.call(this,this.b[a]);this.b=null}function g(a,c,b){var e=!1;try{a(function(a){e||(e=!0,c(a))},function(a){e||(e=!0,b(a))})}catch(d){e||(e=!0,b(d))}}b.prototype["catch"]=function(a){return this.then(null,a)};b.prototype.then=function(a,c){var f=this;return new b(function(b,d){k.call(f,{d:a,e:c,resolve:b,reject:d})})};
+b.resolve=function(a){return a&&"object"===typeof a&&a.constructor===b?a:new b(function(b){b(a)})};b.reject=function(a){return new b(function(b,d){d(a)})};return b}"undefined"!==typeof module&&(module.f=m);
diff --git a/catapult/third_party/polymer/components/promise-polyfill/README.md b/catapult/third_party/polymer/components/promise-polyfill/README.md
new file mode 100644
index 00000000..4dc7fd59
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/README.md
@@ -0,0 +1,16 @@
+
+<!---
+
+This README is automatically generated from the comments in these files:
+
+
+Edit those files, and our readme bot will duplicate them over here!
+Edit this file, and the bot will squash your changes :)
+
+The bot does some handling of markdown. Please file a bug if it does the wrong
+thing! https://github.com/PolymerLabs/tedium/issues
+
+-->
+
+[![Build status](https://travis-ci.org/PolymerLabs/promise-polyfill.svg?branch=master)](https://travis-ci.org/PolymerLabs/promise-polyfill)
+
diff --git a/catapult/third_party/polymer/components/promise-polyfill/bower.json b/catapult/third_party/polymer/components/promise-polyfill/bower.json
new file mode 100644
index 00000000..59163de1
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "promise-polyfill",
+ "version": "1.0.1",
+ "homepage": "https://github.com/taylorhakes/promise-polyfill",
+ "authors": [
+ "Taylor Hakes"
+ ],
+ "description": "Lightweight promise polyfill for the browser and node. A+ Compliant.",
+ "main": "Promise.js",
+ "moduleType": [
+ "globals",
+ "node"
+ ],
+ "keywords": [
+ "promise",
+ "es6",
+ "polyfill",
+ "html5"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "dependencies": {
+ "polymer": "polymer/polymer#^1.0.0"
+ }
+}
diff --git a/catapult/third_party/polymer/components/promise-polyfill/package.json b/catapult/third_party/polymer/components/promise-polyfill/package.json
new file mode 100644
index 00000000..d6d16dce
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "promise-polyfill",
+ "version": "2.0.1",
+ "description": "Lightweight promise polyfill. A+ compliant",
+ "main": "Promise.js",
+ "scripts": {
+ "test": "./node_modules/.bin/promises-aplus-tests tests/adapter.js; ./node_modules/.bin/promises-es6-tests tests/adapter.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://taylorhakes@github.com/taylorhakes/promise-polyfill.git"
+ },
+ "author": "Taylor Hakes",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/taylorhakes/promise-polyfill/issues"
+ },
+ "homepage": "https://github.com/taylorhakes/promise-polyfill",
+ "devDependencies": {
+ "grunt": "^0.4.5",
+ "grunt-bytesize": "^0.1.1",
+ "grunt-closurecompiler": "^0.9.9",
+ "grunt-contrib-uglify": "^0.4.0",
+ "mocha": "^2.2.1",
+ "promises-aplus-tests": "*",
+ "promises-es6-tests": "^0.5.0"
+ },
+ "keywords": [
+ "promise",
+ "promise-polyfill",
+ "ES6",
+ "promises-aplus"
+ ],
+ "dependencies": {}
+}
diff --git a/catapult/third_party/polymer/components/promise-polyfill/promise-polyfill-lite.html b/catapult/third_party/polymer/components/promise-polyfill/promise-polyfill-lite.html
new file mode 100644
index 00000000..fe14c0bf
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/promise-polyfill-lite.html
@@ -0,0 +1,16 @@
+<!--
+@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
+-->
+<link rel="import" href="../polymer/polymer.html">
+<script src='./Promise.js'></script>
+<script>
+if (!window.Promise) {
+ window.Promise = MakePromise(Polymer.Base.async);
+}
+</script>
diff --git a/catapult/third_party/polymer/components/promise-polyfill/promise-polyfill.html b/catapult/third_party/polymer/components/promise-polyfill/promise-polyfill.html
new file mode 100644
index 00000000..7cd341f3
--- /dev/null
+++ b/catapult/third_party/polymer/components/promise-polyfill/promise-polyfill.html
@@ -0,0 +1,11 @@
+<!--
+@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
+-->
+<link rel="import" href="./promise-polyfill-lite.html">
+<script src='./Promise-Statics.js'></script>
diff --git a/catapult/third_party/polymer/components/shadycss/.bower.json b/catapult/third_party/polymer/components/shadycss/.bower.json
new file mode 100644
index 00000000..295c4e17
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/.bower.json
@@ -0,0 +1,14 @@
+{
+ "name": "shadycss",
+ "homepage": "https://github.com/webcomponents/shadycss",
+ "version": "1.7.1",
+ "_release": "1.7.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.7.1",
+ "commit": "c9fc89fee69293a24526a55b93c35a3ab7ea0254"
+ },
+ "_source": "https://github.com/webcomponents/shadycss.git",
+ "_target": "^v1.1.0",
+ "_originalSource": "webcomponents/shadycss"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/.eslintignore b/catapult/third_party/polymer/components/shadycss/.eslintignore
new file mode 100644
index 00000000..5dbb9320
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/.eslintignore
@@ -0,0 +1 @@
+tests/module/generated/*
diff --git a/catapult/third_party/polymer/components/shadycss/.eslintrc.json b/catapult/third_party/polymer/components/shadycss/.eslintrc.json
new file mode 100644
index 00000000..4496895c
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/.eslintrc.json
@@ -0,0 +1,14 @@
+{
+ "extends": "eslint:recommended",
+ "env": {
+ "browser": true,
+ "es6": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 6,
+ "sourceType": "module"
+ },
+ "plugins": [
+ "html"
+ ]
+}
diff --git a/catapult/third_party/polymer/components/shadycss/.gitattributes b/catapult/third_party/polymer/components/shadycss/.gitattributes
new file mode 100644
index 00000000..8c82fe25
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/.gitattributes
@@ -0,0 +1 @@
+*.min.* binary
diff --git a/catapult/third_party/polymer/components/shadycss/.github/CODEOWNERS b/catapult/third_party/polymer/components/shadycss/.github/CODEOWNERS
new file mode 100644
index 00000000..93bd76fe
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @azakus
diff --git a/catapult/third_party/polymer/components/shadycss/.gitignore b/catapult/third_party/polymer/components/shadycss/.gitignore
new file mode 100644
index 00000000..9e3f967c
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+tests/module/generated
diff --git a/catapult/third_party/polymer/components/shadycss/.travis.yml b/catapult/third_party/polymer/components/shadycss/.travis.yml
new file mode 100644
index 00000000..9aad7b34
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/.travis.yml
@@ -0,0 +1,17 @@
+language: node_js
+sudo: false
+dist: trusty
+node_js: '9'
+addons:
+ firefox: latest
+ chrome: stable
+before_script:
+- npm run lint
+- npm run build
+script:
+- xvfb-run wct
+- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'windows 10/microsoftedge@17' -s 'windows 10/microsoftedge@15' -s 'windows 8.1/internet explorer@11' -s 'macos 10.13/safari@12' -s 'macos 10.13/safari@11' -s 'macos 10.12/safari@10' -s 'os x 10.11/safari@9' -s 'Linux/chrome@41'; fi
+env:
+ global:
+ - secure: E6mivJ+7GgNYdqqfMTnlHjx+J+SlLLoDlQgiRxXR++Z4sHa0q2NPDM0zrt9XwiVFs42NemNo4WM4lOF5biuMnlEY+ftbuWN8osSHN9fFu2ZRJ02gILICMG5yoFY0z5u3LJt8TVjxk4CmUZBPpRlIqZho8e+xrGoPEYmf2xYT2CpDSoeJhva9DeBHf9IltHievP4Yl6PM+4oBXvhqnwt8AdoFbbvaaooWNATKaG/xHQaIJVxhM3Peb1T67DK2PoQmq2+EPlCELXGWYc2+SszUq3AfQaE3p3K8/5NJ+327IW6mUK9bZGeAMKKh1A8HaxKY2f4dLecFsYv2r/V6hCSi786CigN6C1/3x+N/X7sE+nB9ukLYxhG8tIWb35C612nDjxt+KlEK4wnyjb/6YMXcWmx3Qc8/4gEQUQzyzvVOFY/2eO780eN0OOdOqrTQUfs7hTNBjBZlkMm7bu1hdPNkMBKy7mejsipDkimFvg5HyNo5PDt2PuYPAajb+IdhU9PHXXlS3xae0spqqXLu+BF0r4sA3JwAbMq/8NGHhyS1MWYn4mtxRmooTTO+87PFpzTzccALUbtIH6GEKiVNl5yGevFwrxgNtsipNHxTVfXB2Ce40tqV9p/HPsLKk6ZYSsLjQ192jW1QGp3UdacuxopA0aCmRPwXO5UGJx9s8M+TWqM=
+ - secure: pp0nViYpl4BbxCpGtyvx5RBOZZF0ZRuwkfpwOyPQBNKzEieWlKv3KOXpIkqgligpaFB1MvpkLon1I8ab7o1J2cvW3/DBAfPRtNNL6UvHgrndusKbW52YLeIiQ+Q8GUw24GnRVWvPQnsndNZElThq/0252dtHqI7Rxyw5Atd4sB8YkeRibFvJqEWnzP7durGRX7qwij2d2zPAG+E2SskZkgs3lA84V4W0g8tJz51djxFDKWnIwwWqLKhR5AA54RhJFq6ZyonLkbzJTXn3Towo29dswtn7Fe+XKcDGoqsNlOb84zADbvNMA4eEExc0jSJa8PekkrxVbakhj4aG1Z/S4sE9rcjF/7OnHUyhn1n6hCcWjOA7mTJL5vgjVHM4z+iV/R6dGq73Zji3CMe/oWQKzk97zBF+IG5MrFAsXdgJUbvKEJmldI+HGnUjDc81rnLyDp/85ORy+YkM9UlDLKwMjlzrWqfuR2Zf8GuaoCWlQXkMCcjSbgVjTQovHsQDye+ZyTVD5QA0ELe0xDGC1QH+mUmgKmX29soSZIAM9Y4vk9oea7DoPMr1sPbfBzJ+AHibiKd6yrdd/IQfUnrTEVPo3VKIURdx2y7nPqBfEOo/Nsz3qiAzHPtegdWz1+kEXCTSqgB77+THZHoJG3fi3TvnWOelbO4wmvn3ovoYteKVLjM=
diff --git a/catapult/third_party/polymer/components/shadycss/LICENSE.md b/catapult/third_party/polymer/components/shadycss/LICENSE.md
new file mode 100644
index 00000000..bb3f4403
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/LICENSE.md
@@ -0,0 +1,19 @@
+# License
+
+Everything in this repo is BSD style license unless otherwise specified.
+
+Copyright (c) 2015 The Polymer Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+* Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/README.md b/catapult/third_party/polymer/components/shadycss/README.md
new file mode 100644
index 00000000..e26cc2b3
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/README.md
@@ -0,0 +1,428 @@
+# ShadyCSS
+
+ShadyCSS provides a library to simulate ShadowDOM style encapsulation (ScopingShim), a shim for the proposed CSS mixin `@apply` syntax (ApplyShim), and a library to integrate document-level stylesheets with both of the former libraries (CustomStyleInterface).
+
+## Requirements
+ShadyCSS requires support for the `<template>` element, ShadowDOM, MutationObserver, Promise, and Object.assign
+
+## Loading
+
+ShadyCSS can be used by loading the ScopingShim, ApplyShim, CustomStyleInterface, or any combination of those.
+
+The most-supported loading order is:
+1. ScopingShim
+1. ApplyShim
+1. CustomStyleInterface
+
+All libraries will expose an object on `window` named `ShadyCSS` with the following interface:
+
+```js
+ShadyCSS = {
+ prepareTemplate(templateElement, elementName, elementExtension){},
+ styleElement(element){},
+ styleSubtree(element, overrideProperties){},
+ styleDocument(overrideProperties){},
+ getComputedStyleValue(element, propertyName){
+ return // style value for property name on element
+ },
+ nativeCss: Boolean,
+ nativeShadow: Boolean
+}
+```
+
+## About ScopingShim
+
+ScopingShim provides simulated ShadyDOM style encapsulation, and a shim for CSS Custom Properties.
+
+ScopingShim works by rewriting style contents and transforming selectors to enforce scoping.
+Additionally, if CSS Custom Properties is not detected, ScopingShim will replace CSS Custom Property usage with realized values.
+
+### Example:
+Here's an example of a custom element when Scoping Shim is not needed.
+
+```html
+<my-element>
+ <!-- shadow-root -->
+ <style>
+ :host {
+ display: block;
+ }
+ #container slot::slotted(*) {
+ color: gray;
+ }
+ #foo {
+ color: black;
+ }
+ </style>
+ <div id="foo">Shadow</div>
+ <div id="container">
+ <slot>
+ <!-- span distributed here -->
+ </slot>
+ </div>
+ <!-- /shadow-root -->
+ <span>Light</span>
+</my-element>
+```
+
+becomes:
+
+```html
+<style scope="my-element">
+my-element {
+ display: block;
+}
+#container.my-element > * {
+ color: gray;
+}
+#foo.my-element {
+ color: black;
+}
+</style>
+<my-element>
+<div id="foo">Shadow</div>
+<div id="container">
+ <span>Light</span>
+</div>
+</my-element>
+```
+
+## About ApplyShim
+
+ApplyShim provides a shim for the `@apply` syntax proposed at https://tabatkins.github.io/specs/css-apply-rule/, which expands the definition CSS Custom Properties to include objects that can be applied as a block.
+
+This is done by transforming the block definition into a set of CSS Custom Properties, and replacing uses of `@apply` with consumption of those custom properties.
+
+### Status:
+
+The `@apply` proposal has been abandoned in favor of the ::part/::theme [Shadow Parts spec](https://tabatkins.github.io/specs/css-shadow-parts/). Therefore, the ApplyShim library is deprecated and provided only for backwards compatibility. Support going forward will be limited to critical bug fixes.
+
+### Known Issues:
+
+* Mixin properties cannot be modified at runtime.
+* Nested mixins are not supported.
+* Shorthand properties are not expanded and may conflict with more explicit properties. Whenever shorthand notations are used in conjunction with their expanded forms in `@apply`, depending in the order of usage of the mixins, properties can be overridden. This means that using both `background-color: green;` and `background: red;` in two separate CSS selectors
+ can result in `background-color: transparent` in the selector that `background: red;` is specified.
+
+ ```css
+ #nonexistent {
+ --my-mixin: {
+ background: red;
+ }
+ }
+ ```
+ with an element style definition of
+ ```css
+ :host {
+ display: block;
+ background-color: green;
+ @apply(--my-mixin);
+ }
+ ```
+ results in the background being `transparent`, as an empty `background` definition replaces
+ the `@apply` definition.
+
+ For this reason, we recommend avoiding shorthand properties.
+
+### Example:
+
+Here we define a block called `--mixin` at the document level, and apply that block to `my-element` somewhere in the page.
+
+```css
+html {
+ --mixin: {
+ border: 2px solid black;
+ background-color: green;
+ }
+}
+
+my-element {
+ border: 1px dotted orange;
+ @apply --mixin;
+}
+```
+
+becomes:
+
+```css
+html {
+ --mixin_-_border: 2px solid black;
+ --mixin_-_background-color: green;
+}
+
+my-element {
+ border: var(--mixin_-_border, 1px dotted orange);
+ background-color: var(--mixin_-_background-color);
+}
+```
+
+## About CustomStyleInterface
+
+CustomStyleInterface provides API to process `<style>` elements that are not inside of
+ShadowRoots, and simulate upper-boundary style scoping for ShadyDOM.
+
+To add document-level styles to ShadyCSS, one can call `CustomStyleInterface.addCustomStyle(styleElement)` or `CustomStyleInterface.addCustomStyle({getStyle: () => styleElement})`
+
+An example usage of the document-level styling api can be found in `examples/document-style-lib.js`, and another example that uses a custom element wrapper can be found in `examples/custom-style-element.js`
+
+### Example:
+
+```html
+<style class="document-style">
+html {
+ --content-color: brown;
+}
+</style>
+<my-element>This text will be brown!</my-element>
+<script>
+CustomStyleInterface.addCustomStyle(document.querySelector('style.document-style'));
+</script>
+```
+
+Another example with a wrapper `<custom-style>` element
+
+```html
+<custom-style>
+ <style>
+ html {
+ --content-color: brown;
+ }
+ </style>
+</custom-style>
+<script>
+class CustomStyle extends HTMLElement {
+ constructor() {
+ CustomStyleInterface.addCustomStyle(this);
+ }
+ getStyle() {
+ return this.querySelector('style');
+ }
+}
+</script>
+<my-element>This this text will be brown!</my-element>
+```
+
+Another example with a function that produces style elements
+
+```html
+<my-element>This this text will be brown!</my-element>
+<script>
+CustomStyleInterface.addCustomStyle({
+ getStyle() {
+ const s = document.createElement('style');
+ s.textContent = 'html{ --content-color: brown }';
+ return s;
+ }
+});
+</script>
+```
+
+## Usage
+
+To use ShadyCSS:
+
+1. First, call `ShadyCSS.prepareTemplate(template, name)` on a
+`<template>` element that will be imported into a `shadowRoot`.
+
+2. When the element instance is connected, call `ShadyCSS.styleElement(element)`
+
+3. Create and stamp the element's shadowRoot
+
+4. Whenever dynamic updates are required, call `ShadyCSS.styleSubtree(element)`.
+
+5. If a styling change is made that may affect the whole document, call
+`ShadyCSS.styleDocument()`.
+
+The following example uses ShadyCSS and ShadyDOM to define a custom element.
+
+```html
+<template id="myElementTemplate">
+ <style>
+ :host {
+ display: block;
+ padding: 8px;
+ }
+
+ #content {
+ background-color: var(--content-color);
+ }
+
+ .slot-container ::slotted(*) {
+ border: 1px solid steelblue;
+ margin: 4px;
+ }
+ </style>
+ <div id="content">Content</div>
+ <div class="slot-container">
+ <slot></slot>
+ </div>
+</template>
+<script>
+ // Use polyfill only in browsers that lack native Shadow DOM.
+ window.ShadyCSS && ShadyCSS.prepareTemplate(myElementTemplate, 'my-element');
+
+ class MyElement extends HTMLElement {
+ connectedCallback() {
+ window.ShadyCSS && ShadyCSS.styleElement(this);
+ if (!this.shadowRoot) {
+ this.attachShadow({mode: 'open'});
+ this.shadowRoot.appendChild(
+ document.importNode(myElementTemplate.content, true));
+ }
+ }
+ }
+
+ customElements.define('my-element', MyElement);
+</script>
+```
+
+## Type Extension elements
+
+ShadyCSS can also be used with type extension elements by supplying the base
+element name to `prepareTemplate` as a third argument.
+
+### Example
+
+```html
+<template id="myElementTemplate">
+ <style>
+ :host {
+ display: block;
+ padding: 8px;
+ }
+
+ #content {
+ background-color: var(--content-color);
+ }
+
+ .slot-container ::slotted(*) {
+ border: 1px solid steelblue;
+ margin: 4px;
+ }
+ </style>
+ <div id="content">Content</div>
+ <div class="slot-container">
+ <slot></slot>
+ </div>
+</template>
+<script>
+ window.ShadyCSS && ShadyCSS.prepareTemplate(myElementTemplate, 'my-element', 'div');
+
+ class MyElement extends HTMLDivElement {
+ connectedCallback() {
+ window.ShadyCSS && ShadyCSS.styleElement(this);
+ if (!this.shadowRoot) {
+ this.attachShadow({mode: 'open'});
+ this.shadowRoot.appendChild(
+ document.importNode(myElementTemplate.content, true));
+ }
+ }
+ }
+
+ customElements.define('my-element', MyElement, {extends: 'div'});
+</script>
+```
+
+## Imperative values for Custom properties
+
+To set the value of a CSS Custom Property imperatively, `ShadyCSS.styleSubtree`
+and `ShadyCSS.styleDocument` support an additional argument of an object mapping
+variable name to value.
+
+When using ApplyShim, defining new mixins or new values for current mixins imperatively is not
+supported.
+
+### Example
+```html
+<my-element id="a">Text</my-element>
+<my-element>Text</my-element>
+<script>
+let el = document.querySelector('my-element#a');
+// Set the color of all my-element instances to 'green'
+ShadyCSS.styleDocument({'--content-color' : 'green'});
+// Set the color my-element#a's text to 'red'
+ShadyCSS.styleSubtree(el, {'--content-color' : 'red'});
+</script>
+```
+
+## Limitations
+
+### Selector scoping
+
+To use the `::slotted` pseudo-element, you must select it as a descendant of some context element.
+```css
+/* Bad */
+::slotted() {}
+
+/* Good */
+.context ::slotted() {}
+```
+
+Since ShadyCSS removes all `<slot>` elements, you cannot select them directly or use any other selectors along with the `::slotted` pseudo-element selector.
+```html
+<!-- Bad -->
+<style>
+ .foo .bar::slotted(*) {}
+</style>
+<span class="foo">
+ <slot class="bar"></slot>
+</span>
+```
+
+```html
+<!-- Good -->
+<style>
+ .foo ::slotted(*) {}
+</style>
+<span class="foo">
+ <slot></slot>
+</span>
+```
+
+### Custom properties and `@apply`
+
+Dynamic changes are not automatically applied. If elements change such that they
+conditionally match selectors they did not previously, `ShadyCSS.styleDocument()`
+must be called.
+
+For a given element's shadowRoot, only 1 value is allowed per custom properties.
+Properties cannot change from parent to child as they can under native custom
+properties; they can only change when a shadowRoot boundary is crossed.
+
+To receive a custom property, an element must directly match a selector that
+defines the property in its host's stylesheet.
+
+### `<custom-style>` Flash of unstyled content
+
+If `ShadyCSS.applyStyle` is never called, `<custom-style>` elements will process
+after HTML Imports have loaded, after the document loads, or after the next paint.
+This means that there may be a flash of unstyled content on the first load.
+
+### Mixins do not cascade throught `<slot>`
+
+Crawling the DOM and updating styles is very expensive, and we found that trying to
+update mixins through `<slot>` insertion points to be too expensive to justify for both
+polyfilled CSS Mixins and polyfilled CSS Custom Properties.
+
+### External stylesheets not currently supported
+
+External stylesheets loaded via `<link rel="stylesheet">` within a shadow root or
+`@import` syntax inside a shadow root's stylesheet are not currently shimmed by
+the polyfill. This is mainly due to the fact that shimming them would require
+a fetch of the stylesheet text that is async cannot be easily coordinated with
+the upgrade timing of custom elements using them, making it impossible to avoid
+"flash of unstyled content" when running on polyfill.
+
+### Document level styling is not scoped by default
+
+ShadyCSS mimics the behavior of shadow dom, but it is not able to prevent document
+level styling to affect elements inside a shady dom. Global styles defined in
+`index.html` or any styles not processed by ShadyCSS will affect all elements on the page.
+
+To scope document level styling, the style must be wrapped in the `<custom-style>` element
+found in Polymer, or use the `CustomStyleInterface` library to modify document level styles.
+
+### Dynamically created styles are not supported
+
+ShadyCSS works by processing a template for a given custom element class. Only the style
+elements present in that template will be scoped for the custom element's ShadowRoot.
diff --git a/catapult/third_party/polymer/components/shadycss/apply-shim.html b/catapult/third_party/polymer/components/shadycss/apply-shim.html
new file mode 100644
index 00000000..d344ec37
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/apply-shim.html
@@ -0,0 +1,10 @@
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<script src="apply-shim.min.js"></script>
diff --git a/catapult/third_party/polymer/components/shadycss/apply-shim.min.js b/catapult/third_party/polymer/components/shadycss/apply-shim.min.js
new file mode 100644
index 00000000..273f2017
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/apply-shim.min.js
@@ -0,0 +1,32 @@
+(function(){/*
+
+Copyright (c) 2017 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
+*/
+'use strict';var l=!(window.ShadyDOM&&window.ShadyDOM.inUse),p;function r(a){p=a&&a.shimcssproperties?!1:l||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var t;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(t=window.ShadyCSS.cssBuild);window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?p=window.ShadyCSS.nativeCss:window.ShadyCSS?(r(window.ShadyCSS),window.ShadyCSS=void 0):r(window.WebComponents&&window.WebComponents.flags);
+var u=p,v=t;function w(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""}
+function x(a){a=a.replace(aa,"").replace(ba,"");var b=y,c=a,e=new w;e.start=0;e.end=c.length;for(var d=e,f=0,g=c.length;f<g;f++)if("{"===c[f]){d.rules||(d.rules=[]);var h=d,k=h.rules[h.rules.length-1]||null;d=new w;d.start=f+1;d.parent=h;d.previous=k;h.rules.push(d)}else"}"===c[f]&&(d.end=f+1,d=d.parent||e);return b(e,a)}
+function y(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=ca(c),c=c.replace(z," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=A:c.match(da)&&(a.type=B,a.keyframesName=a.selector.split(z).pop()):a.type=0===c.indexOf("--")?C:D);if(c=a.rules)for(var e=0,d=c.length,f=void 0;e<d&&(f=c[e]);e++)y(f,b);
+return a}function ca(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})}
+function E(a,b,c){c=void 0===c?"":c;var e="";if(a.cssText||a.rules){var d=a.rules,f;if(f=d)f=d[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var g=d.length,h=void 0;f<g&&(h=d[f]);f++)e=E(h,b,e)}else b?b=a.cssText:(b=a.cssText,b=b.replace(ea,"").replace(fa,""),b=b.replace(ha,"").replace(ia,"")),(e=b.trim())&&(e=" "+e+"\n")}e&&(a.selector&&(c+=a.selector+" {\n"),c+=e,a.selector&&(c+="}\n\n"));return c}
+var D=1,B=7,A=4,C=1E3,aa=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,ba=/@import[^;]*;/gim,ea=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,fa=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,ha=/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,ia=/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,da=/^@[^\s]*keyframes/,z=/\s+/g;var G=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,H=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,ja=/@media\s(.*)/;var I=new Set;function J(a){if(!a)return"";"string"===typeof a&&(a=x(a));return E(a,u)}function K(a){!a.__cssRules&&a.textContent&&(a.__cssRules=x(a.textContent));return a.__cssRules||null}function L(a,b,c,e){if(a){var d=!1,f=a.type;if(e&&f===A){var g=a.selector.match(ja);g&&(window.matchMedia(g[1]).matches||(d=!0))}f===D?b(a):c&&f===B?c(a):f===C&&(d=!0);if((a=a.rules)&&!d)for(d=0,f=a.length,g=void 0;d<f&&(g=a[d]);d++)L(g,b,c,e)}}
+function M(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");a:{var e=0;var d=c+3;for(var f=a.length;d<f;d++)if("("===a[d])e++;else if(")"===a[d]&&0===--e)break a;d=-1}e=a.substring(c+4,d);c=a.substring(0,c);a=M(a.substring(d+1),b);d=e.indexOf(",");return-1===d?b(c,e.trim(),"",a):b(c,e.substring(0,d).trim(),e.substring(d+1).trim(),a)}
+function N(a){if(void 0!==v)return v;if(void 0===a.__cssBuild){var b=a.getAttribute("css-build");if(b)a.__cssBuild=b;else{a:{b="template"===a.localName?a.content.firstChild:a.firstChild;if(b instanceof Comment&&(b=b.textContent.trim().split(":"),"css-build"===b[0])){b=b[1];break a}b=""}if(""!==b){var c="template"===a.localName?a.content.firstChild:a.firstChild;c.parentNode.removeChild(c)}a.__cssBuild=b}}return a.__cssBuild||""};var ka=/;\s*/m,la=/^\s*(initial)|(inherit)\s*$/,O=/\s*!important/;function P(){this.a={}}P.prototype.set=function(a,b){a=a.trim();this.a[a]={h:b,i:{}}};P.prototype.get=function(a){a=a.trim();return this.a[a]||null};var Q=null;function R(){this.b=this.c=null;this.a=new P}R.prototype.o=function(a){a=H.test(a)||G.test(a);H.lastIndex=0;G.lastIndex=0;return a};
+R.prototype.m=function(a,b){if(void 0===a._gatheredStyle){var c=[];for(var e=a.content.querySelectorAll("style"),d=0;d<e.length;d++){var f=e[d];if(f.hasAttribute("shady-unscoped")){if(!l){var g=f.textContent;I.has(g)||(I.add(g),g=f.cloneNode(!0),document.head.appendChild(g));f.parentNode.removeChild(f)}}else c.push(f.textContent),f.parentNode.removeChild(f)}(c=c.join("").trim())?(e=document.createElement("style"),e.textContent=c,a.content.insertBefore(e,a.content.firstChild),c=e):c=null;a._gatheredStyle=
+c}return(a=a._gatheredStyle)?this.j(a,b):null};R.prototype.j=function(a,b){b=void 0===b?"":b;var c=K(a);this.l(c,b);a.textContent=J(c);return c};R.prototype.f=function(a){var b=this,c=K(a);L(c,function(a){":root"===a.selector&&(a.selector="html");b.g(a)});a.textContent=J(c);return c};R.prototype.l=function(a,b){var c=this;this.c=b;L(a,function(a){c.g(a)});this.c=null};R.prototype.g=function(a){a.cssText=ma(this,a.parsedCssText,a);":root"===a.selector&&(a.selector=":host > *")};
+function ma(a,b,c){b=b.replace(G,function(b,d,f,g){return na(a,b,d,f,g,c)});return S(a,b,c)}function oa(a,b){for(var c=b;c.parent;)c=c.parent;var e={},d=!1;L(c,function(c){(d=d||c===b)||c.selector===b.selector&&Object.assign(e,T(a,c.parsedCssText))});return e}
+function S(a,b,c){for(var e;e=H.exec(b);){var d=e[0],f=e[1];e=e.index;var g=b.slice(0,e+d.indexOf("@apply"));b=b.slice(e+d.length);var h=c?oa(a,c):{};Object.assign(h,T(a,g));d=void 0;var k=a;f=f.replace(ka,"");var n=[];var m=k.a.get(f);m||(k.a.set(f,{}),m=k.a.get(f));if(m){k.c&&(m.i[k.c]=!0);var q=m.h;for(d in q)k=h&&h[d],m=[d,": var(",f,"_-_",d],k&&m.push(",",k.replace(O,"")),m.push(")"),O.test(q[d])&&m.push(" !important"),n.push(m.join(""))}d=n.join("; ");b=g+d+b;H.lastIndex=e+d.length}return b}
+function T(a,b,c){c=void 0===c?!1:c;b=b.split(";");for(var e,d,f={},g=0,h;g<b.length;g++)if(e=b[g])if(h=e.split(":"),1<h.length){e=h[0].trim();d=h.slice(1).join(":");if(c){var k=a;h=e;var n=la.exec(d);n&&(n[1]?(k.b||(k.b=document.createElement("meta"),k.b.setAttribute("apply-shim-measure",""),k.b.style.all="initial",document.head.appendChild(k.b)),h=window.getComputedStyle(k.b).getPropertyValue(h)):h="apply-shim-inherit",d=h)}f[e]=d}return f}function pa(a,b){if(Q)for(var c in b.i)c!==a.c&&Q(c)}
+function na(a,b,c,e,d,f){e&&M(e,function(b,c){c&&a.a.get(c)&&(d="@apply "+c+";")});if(!d)return b;var g=S(a,""+d,f);f=b.slice(0,b.indexOf("--"));var h=g=T(a,g,!0),k=a.a.get(c),n=k&&k.h;n?h=Object.assign(Object.create(n),g):a.a.set(c,h);var m=[],q,Z=!1;for(q in h){var F=g[q];void 0===F&&(F="initial");!n||q in n||(Z=!0);m.push(c+"_-_"+q+": "+F)}Z&&pa(a,k);k&&(k.h=h);e&&(f=b+";"+f);return f+m.join("; ")+";"}R.prototype.detectMixin=R.prototype.o;R.prototype.transformStyle=R.prototype.j;
+R.prototype.transformCustomStyle=R.prototype.f;R.prototype.transformRules=R.prototype.l;R.prototype.transformRule=R.prototype.g;R.prototype.transformTemplate=R.prototype.m;R.prototype._separator="_-_";Object.defineProperty(R.prototype,"invalidCallback",{get:function(){return Q},set:function(a){Q=a}});var U={};var qa=Promise.resolve();function ra(a){if(a=U[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function sa(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function ta(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a._validating||(a._validating=!0,qa.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a._validating=!1}))};var V=new R;function W(){this.a=null;V.invalidCallback=ra}function X(a){!a.a&&window.ShadyCSS.CustomStyleInterface&&(a.a=window.ShadyCSS.CustomStyleInterface,a.a.transformCallback=function(a){V.f(a)},a.a.validateCallback=function(){requestAnimationFrame(function(){a.a.enqueued&&a.flushCustomStyles()})})}W.prototype.prepareTemplate=function(a,b){X(this);""===N(a)&&(U[b]=a,b=V.m(a,b),a._styleAst=b)};
+W.prototype.flushCustomStyles=function(){X(this);if(this.a){var a=this.a.processStyles();if(this.a.enqueued){for(var b=0;b<a.length;b++){var c=this.a.getStyleForCustomStyle(a[b]);c&&V.f(c)}this.a.enqueued=!1}}};
+W.prototype.styleSubtree=function(a,b){X(this);if(b)for(var c in b)null===c?a.style.removeProperty(c):a.style.setProperty(c,b[c]);if(a.shadowRoot)for(this.styleElement(a),a=a.shadowRoot.children||a.shadowRoot.childNodes,b=0;b<a.length;b++)this.styleSubtree(a[b]);else for(a=a.children||a.childNodes,b=0;b<a.length;b++)this.styleSubtree(a[b])};
+W.prototype.styleElement=function(a){X(this);var b=a.localName,c;b?-1<b.indexOf("-")?c=b:c=a.getAttribute&&a.getAttribute("is")||"":c=a.is;b=U[c];if(!(b&&""!==N(b)||!b||sa(b))){if(sa(b)||b._applyShimValidatingVersion!==b._applyShimNextVersion)this.prepareTemplate(b,c),ta(b);if(a=a.shadowRoot)if(a=a.querySelector("style"))a.__cssRules=b._styleAst,a.textContent=J(b._styleAst)}};W.prototype.styleDocument=function(a){X(this);this.styleSubtree(document.body,a)};
+if(!window.ShadyCSS||!window.ShadyCSS.ScopingShim){var Y=new W,ua=window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface;window.ShadyCSS={prepareTemplate:function(a,b){Y.flushCustomStyles();Y.prepareTemplate(a,b)},prepareTemplateStyles:function(a,b,c){window.ShadyCSS.prepareTemplate(a,b,c)},prepareTemplateDom:function(){},styleSubtree:function(a,b){Y.flushCustomStyles();Y.styleSubtree(a,b)},styleElement:function(a){Y.flushCustomStyles();Y.styleElement(a)},styleDocument:function(a){Y.flushCustomStyles();
+Y.styleDocument(a)},getComputedStyleValue:function(a,b){return(a=window.getComputedStyle(a).getPropertyValue(b))?a.trim():""},flushCustomStyles:function(){Y.flushCustomStyles()},nativeCss:u,nativeShadow:l,cssBuild:v};ua&&(window.ShadyCSS.CustomStyleInterface=ua)}window.ShadyCSS.ApplyShim=V;}).call(this);
+
+//# sourceMappingURL=apply-shim.min.js.map
diff --git a/catapult/third_party/polymer/components/shadycss/apply-shim.min.js.map b/catapult/third_party/polymer/components/shadycss/apply-shim.min.js.map
new file mode 100644
index 00000000..f41ad4f0
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/apply-shim.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["src/style-settings.js","src/css-parse.js","src/common-regex.js","src/unscoped-style-handler.js","src/style-util.js","src/apply-shim.js","src/common-utils.js","src/template-map.js","src/apply-shim-utils.js","entrypoints/apply-shim.js"],"names":["nativeShadow","window","nativeCssVariables_","calcCssVariables","settings","navigator","userAgent","match","CSS","supports","cssBuild","ShadyCSS","undefined","nativeCss","nativeCssVariables","module$src$style_settings.cssBuild","constructor","StyleNode","parse","text","replace","RX$$module$src$css_parse.comments","RX$$module$src$css_parse.port","parseCss","root","length","n","i","l","OPEN_BRACE","p","previous","push","CLOSE_BRACE","node","t","substring","trim","ss","_expandUnicodeEscapes","RX$$module$src$css_parse.multipleSpaces","lastIndexOf","s","indexOf","AT_START","MEDIA_START","types$$module$src$css_parse.MEDIA_RULE","RX$$module$src$css_parse.keyframesRule","types$$module$src$css_parse.KEYFRAMES_RULE","split","pop","VAR_START","types$$module$src$css_parse.MIXIN_RULE","types$$module$src$css_parse.STYLE_RULE","r$","r","code","repeat","stringify","preserveProperties","cssText","rules","RX$$module$src$css_parse.customProp","RX$$module$src$css_parse.mixinProp","RX$$module$src$css_parse.mixinApply","RX$$module$src$css_parse.varApply","STYLE_RULE","KEYFRAMES_RULE","MEDIA_RULE","MIXIN_RULE","comments","port","customProp","mixinProp","mixinApply","varApply","keyframesRule","multipleSpaces","VAR_ASSIGN","MIXIN_MATCH","MEDIA_MATCH","styleTextSet","Set","toCssText","rulesForStyle","style","textContent","forEachRule","styleRuleCallback","keyframesRuleCallback","onlyActiveRules","skipRules","type","matchMedia","matches","processVariableAndFallback","str","callback","start","level","inner","end","prefix","suffix","comma","value","fallback","getCssBuild","element","__cssBuild","attrValue","getAttribute","CSS_BUILD_ATTR","buildComment","localName","content","firstChild","Comment","commentParts","parentNode","removeChild","APPLY_NAME_CLEAN","INITIAL_INHERIT","IMPORTANT","MixinMap","_map","set","name","props","properties","dependants","get","invalidCallback","ApplyShim","_measureElement","_currentElement","detectMixin","has","test","lastIndex","transformTemplate","template","elementName","_gatheredStyle","styleTextParts","styles","querySelectorAll","hasAttribute","scopingAttribute","add","newStyle","cloneNode","document","head","appendChild","gatherStyles","join","createElement","insertBefore","transformStyle","ast","transformRules","transformCustomStyle","rule","transformRule","transformCssText","matchText","propertyName","valueProperty","valueMixin","_produceCssProperties","_consumeCssProperties","_fallbacksFromPreviousRules","startRule","topRule","fallbacks","seenStartRule","Object","assign","_cssTextToMap","m","exec","mixinName","idx","index","textBeforeApply","slice","textAfterApply","defaults","_atApplyToCssProperties","vars","mixinEntry","f","parts","MIXIN_VAR_SEP","replacement","replaceInitialOrInherit","property","out","sp","_replaceInitialOrInherit","_getInitialValueForProperty","setAttribute","all","getComputedStyle","getPropertyValue","_invalidateMixinEntry","mixinAsProperties","combinedProps","mixinValues","oldProps","create","needToInvalidate","v","prototype","defineProperty","cb","templateMap","promise","Promise","resolve","invalidate","templateIsValid","startValidatingTemplate","_validating","then","applyShim","ApplyShimInterface","customStyleInterface","ensure","CustomStyleInterface","requestAnimationFrame","flushCustomStyles","prepareTemplate","styleSubtree","removeProperty","setProperty","shadowRoot","styleElement","shadowChildren","children","childNodes","is","querySelector","styleDocument","body","ScopingShim","applyShimInterface","prepareTemplateStyles","elementExtends","prepareTemplateDom","getComputedStyleValue"],"mappings":"A;;;;;;;;;aAYO,IAAMA,EAAe,EAAEC,MAAA,SAAF,EAAwBA,MAAA,SAAA,MAAxB,CAArB,CACHC,CAKJC,SAASA,EAAgB,CAACC,CAAD,CAAW,CAEhCF,CAAA,CADEE,CAAJ,EAAgBA,CAAA,kBAAhB,CACwB,CAAA,CADxB,CASwBJ,CATxB,EASwC,EAASK,SAAAC,UAAAC,MAAA,CAA0B,2BAA1B,CAAT,EACpCC,CAAAP,MAAAO,IADoC,EACtBC,CAAAD,GAAAC,SADsB,EACN,CAAAD,GAAAC,SAAA,CAAa,YAAb,CAA2B,kBAA3B,CADM,CAVN,CAgB7B,IAAIC,CACPT,OAAAU,SAAJ,EAAoDC,IAAAA,EAApD,GAAuBX,MAAAU,SAAAD,SAAvB,GACEA,CADF,CACaT,MAAAU,SAAAD,SADb,CAIIT,OAAAU,SAAJ,EAAqDC,IAAAA,EAArD,GAAuBX,MAAAU,SAAAE,UAAvB,CACEX,CADF,CACwBD,MAAAU,SAAAE,UADxB,CAEWZ,MAAAU,SAAJ,EACLR,CAAA,CAAiBF,MAAAU,SAAjB,CAEA,CAAAV,MAAAU,SAAA,CAAkBC,IAAAA,EAHb,EAKLT,CAAA,CAAiBF,MAAA,cAAjB,EAA4CA,MAAA,cAAA,MAA5C,CAMK;IAAMa,EAA4CZ,CAAlD,CAlBIa,EAAAL,C,CCfTM,QADIC,EACO,EAAG,CAIZ,IAAA,IAAA,CAFA,IAAA,MAEA,CAFgB,CAQhB,KAAA,MAAA,CAFA,IAAA,OAEA,CAJA,IAAA,SAIA,CAJmB,IAQnB,KAAA,QAAA,CAFA,IAAA,cAEA,CAFwB,EAIxB,KAAA,OAAA,CAAiB,CAAA,CAEjB,KAAA,KAAA,CAAe,CAMf,KAAA,eAAA,CAFA,IAAA,SAEA,CAJA,IAAA,cAIA,CAJwB,EApBZ;AAmCTC,QAASA,EAAK,CAACC,CAAD,CAAO,CAC1BA,CAAA,CAAaA,CAUNC,QAAA,CAAgBC,EAAhB,CAA6B,EAA7B,CAAAD,QAAA,CAAyCE,EAAzC,CAAkD,EAAlD,CATAC,KAAAA,EAAAA,CAAAA,CAAaJ,EAAAA,CAAbI,CAkBHC,EAAO,IAAIP,CACfO,EAAA,MAAA,CAAgB,CAChBA,EAAA,IAAA,CAAcL,CAAAM,OAEd,KADA,IAAIC,EAAIF,CAAR,CACSG,EAAI,CADb,CACgBC,EAAIT,CAAAM,OAApB,CAAiCE,CAAjC,CAAqCC,CAArC,CAAwCD,CAAA,EAAxC,CACE,GAuKeE,GAvKf,GAAIV,CAAA,CAAKQ,CAAL,CAAJ,CAA4B,CACrBD,CAAA,MAAL,GACEA,CAAA,MADF,CACe,EADf,CAGA,KAAII,EAAIJ,CAAR,CACIK,EAAWD,CAAA,MAAA,CAAWA,CAAA,MAAAL,OAAX,CAA+B,CAA/B,CAAXM,EAAgD,IACpDL,EAAA,CAAI,IAAIT,CACRS,EAAA,MAAA,CAAaC,CAAb,CAAiB,CACjBD,EAAA,OAAA,CAAcI,CACdJ,EAAA,SAAA,CAAgBK,CAChBD,EAAA,MAAAE,KAAA,CAAgBN,CAAhB,CAV0B,CAA5B,IAwKgBO,GA7JT,GAAId,CAAA,CAAKQ,CAAL,CAAJ,GACLD,CAAA,IACA,CADWC,CACX,CADe,CACf,CAAAD,CAAA,CAAIA,CAAA,OAAJ,EAAmBF,CAFd,CAlCT,OAAOD,EAAA,CAuCAC,CAvCA,CAAoBL,CAApB,CAFmB;AAkD5BI,QAASA,EAAQ,CAACW,CAAD,CAAOf,CAAP,CAAa,CAC5B,IAAIgB,EAAIhB,CAAAiB,UAAA,CAAeF,CAAA,MAAf,CAA8BA,CAAA,IAA9B,CAA4C,CAA5C,CACRA,EAAA,cAAA,CAAwBA,CAAA,QAAxB,CAA0CC,CAAAE,KAAA,EACtCH,EAAA,OAAJ,GAEEC,CASA,CATIhB,CAAAiB,UAAA,CADKF,CAAA,SAAAI,CAAmBJ,CAAA,SAAA,IAAnBI,CAA6CJ,CAAA,OAAA,MAClD,CAAmBA,CAAA,MAAnB,CAAmC,CAAnC,CASJ,CARAC,CAQA,CARII,EAAA,CAAsBJ,CAAtB,CAQJ,CAPAA,CAOA,CAPIA,CAAAf,QAAA,CAAUoB,CAAV,CAA6B,GAA7B,CAOJ,CAJAL,CAIA,CAJIA,CAAAC,UAAA,CAAYD,CAAAM,YAAA,CAAc,GAAd,CAAZ,CAAiC,CAAjC,CAIJ,CAHIC,CAGJ,CAHQR,CAAA,eAGR,CAHiCA,CAAA,SAGjC,CAHoDC,CAAAE,KAAA,EAGpD,CAFAH,CAAA,OAEA,CAF0C,CAE1C,GAFkBQ,CAAAC,QAAA,CAmJLC,GAnJK,CAElB,CAAIV,CAAA,OAAJ,CACiC,CAA/B,GAAIQ,CAAAC,QAAA,CA+IUE,QA/IV,CAAJ,CACEX,CAAA,KADF,CACiBY,CADjB,CAEWJ,CAAAnC,MAAA,CAAQwC,EAAR,CAFX,GAGEb,CAAA,KACA,CADec,CACf,CAAAd,CAAA,cAAA,CACEA,CAAA,SAAAe,MAAA,CAAuBT,CAAvB,CAAAU,IAAA,EALJ,CADF,CAUIhB,CAAA,KAVJ,CAS+B,CAA7B,GAAIQ,CAAAC,QAAA,CAsIQQ,IAtIR,CAAJ,CACiBC,CADjB,CAGiBC,CAvBrB,CA4BA,IADIC,CACJ,CADSpB,CAAA,MACT,CACE,IADM,IACGP,EAAI,CADP,CACUC,EAAI0B,CAAA7B,OADd,CACyB8B,EAAAA,IAAAA,EAA/B,CACG5B,CADH,CACOC,CADP,GACc2B,CADd,CACkBD,CAAA,CAAG3B,CAAH,CADlB,EAC0BA,CAAA,EAD1B,CAEEJ,CAAA,CAASgC,CAAT,CAAYpC,CAAZ,CAGJ;MAAOe,EArCqB,CA8C9BK,QAASA,GAAqB,CAACG,CAAD,CAAI,CAChC,MAAOA,EAAAtB,QAAA,CAAU,uBAAV,CAAmC,QAAQ,CAAA,CAAA,CAAA,CAAA,CAAG,CAC/CoC,CAAAA,CAAO,CAEX,KADEC,CACF,CADW,CACX,CADeD,CAAA/B,OACf,CAAOgC,CAAA,EAAP,CAAA,CACED,CAAA,CAAO,GAAP,CAAaA,CAEf,OAAO,IAAP,CAAcA,CANqC,CAA9C,CADyB;AAkB3BE,QAASA,EAAS,CAACxB,CAAD,CAAOyB,CAAP,CAA2BxC,CAA3B,CAAsC,CAAXA,CAAA,CAAA,IAAA,EAAA,GAAAA,CAAA,CAAO,EAAP,CAAAA,CAElD,KAAIyC,EAAU,EACd,IAAI1B,CAAA,QAAJ,EAAuBA,CAAA,MAAvB,CAAsC,CACpC,IAAIoB,EAAKpB,CAAA,MAAT,CACI,CAAA,IAAAoB,CAAA,CAAAA,CAAA,CAgCFC,CAhCQ,CAAAM,CAgCJ,CAAM,CAAN,CAhCI,CAAA,CAAA,CAAA,EAiCGN,CAjCH,EAiCiBA,CAAA,SAjCjB,EAiCwE,CAjCxE,GAiCmCA,CAAA,SAAAZ,QAAA,CAuD/BQ,IAvD+B,CAjCnC,CAAV,IAAI,CAAJ,CAA+B,CACpBxB,CAAAA,CAAI,CAAb,KAD6B,IACbC,EAAI0B,CAAA7B,OADS,CACE8B,EAAAA,IAAAA,EAA/B,CACG5B,CADH,CACOC,CADP,GACc2B,CADd,CACkBD,CAAA,CAAG3B,CAAH,CADlB,EAC0BA,CAAA,EAD1B,CAEEiC,CAAA,CAAUF,CAAA,CAAUH,CAAV,CAAaI,CAAb,CAAiCC,CAAjC,CAHiB,CAA/B,IAMYD,EAAA,CAAqB,CAArB,CAAqB,CAAA,QAArB,EACR,CAmCN,CAnCM,CAAA,QAmCN,CADAC,CACA,CADqCA,CAS9BxC,QAAA,CACI0C,EADJ,CACmB,EADnB,CAAA1C,QAAA,CAEI2C,EAFJ,CAEkB,EAFlB,CARP,CAAA,CAAA,CAA6BH,CAkBtBxC,QAAA,CACI4C,EADJ,CACmB,EADnB,CAAA5C,QAAA,CAEI6C,EAFJ,CAEiB,EAFjB,CAtDO,CAGV,EADAL,CACA,CAHUA,CAEAvB,KAAA,EACV,IACEuB,CADF,CACY,IADZ,CACmBA,CADnB,CAC6B,IAD7B,CAXkC,CAiBlCA,CAAJ,GACM1B,CAAA,SAIJ,GAHEf,CAGF,EAHUe,CAAA,SAGV,CAHgD,MAGhD,EADAf,CACA,EADQyC,CACR,CAAI1B,CAAA,SAAJ,GACEf,CADF,EACU,OADV,CALF,CASA,OAAOA,EA7BsD;AAwE7D+C,IAAAA,EAAYA,CAAZA,CACAC,EAAgBA,CADhBD,CAEAE,EAAYA,CAFZF,CAGAG,EAAYA,GAHZH,CAWAI,GAAUA,mCAXVJ,CAYAK,GAAMA,kBAZNL,CAaAM,GAAYA,mDAbZN,CAcAO,GAAWA,4DAdXP,CAeAQ,GAAYA,yCAfZR,CAgBAS,GAAUA,2CAhBVT,CAiBAU,GAAeA,mBAjBfV,CAkBAW,EAAgBA,M,CCxPX,IAAMC,EAAa,yHAAnB,CACMC,EAAc,sCADpB,CAIMC,GAAc,c,CCD3B,IAAMC,EAAe,IAAIC,G,CCSlBC,QAASA,EAAU,CAACtB,CAAD,CAAkB,CAC1C,GAAI,CAACA,CAAL,CACE,MAAO,EAEY,SAArB,GAAI,MAAOA,EAAX,GACEA,CADF,CACU3C,CAAA,CAAM2C,CAAN,CADV,CAMA,OAAOH,EAAA,CAAUG,CAAV,CAAiB/C,CAAjB,CAVmC,CAiBrCsE,QAASA,EAAa,CAACC,CAAD,CAAQ,CAC/B,CAACA,CAAA,WAAL,EAA4BA,CAAAC,YAA5B,GACED,CAAA,WADF,CACwBnE,CAAA,CAAMmE,CAAAC,YAAN,CADxB,CAGA,OAAOD,EAAA,WAAP,EAA8B,IAJK,CAyB9BE,QAASA,EAAW,CAACrD,CAAD,CAAOsD,CAAP,CAA0BC,CAA1B,CAAiDC,CAAjD,CAAkE,CAC3F,GAAKxD,CAAL,CAAA,CAGA,IAAIyD,EAAY,CAAA,CAAhB,CACIC,EAAO1D,CAAA,KACX,IAAIwD,CAAJ,EACME,CADN,GACe9C,CADf,CACiC,CAC7B,IAAI+C,EAAa3D,CAAA,SAAA3B,MAAA,CAAuByE,EAAvB,CACba,EAAJ,GAEO5F,MAAA4F,WAAA,CAAkBA,CAAA,CAAW,CAAX,CAAlB,CAAAC,QAFP,GAGIH,CAHJ,CAGgB,CAAA,CAHhB,EAF6B,CAU7BC,CAAJ,GAAavC,CAAb,CACEmC,CAAA,CAAkBtD,CAAlB,CADF,CAEWuD,CAAJ,EACLG,CADK,GACI5C,CADJ,CAELyC,CAAA,CAAsBvD,CAAtB,CAFK,CAGI0D,CAHJ,GAGaxC,CAHb,GAILuC,CAJK,CAIO,CAAA,CAJP,CAOP,KADIrC,CACJ,CADSpB,CAAA,MACT,GAAU,CAACyD,CAAX,CACE,IAAShE,CAAkB4B,CAAhB,CAAgBA,CAAb3B,CAAa2B,CAAXD,CAAA7B,OAAW8B,CAAAA,CAAAA,CAAAA,IAAAA,EAA3B,CAA+B5B,CAA/B,CAAiCC,CAAjC,GAAwC2B,CAAxC,CAA0CD,CAAA,CAAG3B,CAAH,CAA1C,EAAkDA,CAAA,EAAlD,CACE4D,CAAA,CAAYhC,CAAZ,CAAeiC,CAAf,CAAkCC,CAAlC,CAAyDC,CAAzD,CA3BJ,CAD2F;AAyItFK,QAASA,EAA0B,CAACC,CAAD,CAAMC,CAAN,CAAgB,CAExD,IAAIC,EAAQF,CAAArD,QAAA,CAAY,MAAZ,CACZ,IAAe,EAAf,GAAIuD,CAAJ,CAEE,MAAOD,EAAA,CAASD,CAAT,CAAc,EAAd,CAAkB,EAAlB,CAAsB,EAAtB,CAvBoC,EAAA,CAAA,CAC7C,IAAIG,EAAQ,CACHxE,KAAAA,EAwBwBuE,CAxBxBvE,CAwBgC,CAxBzC,KAAK,IAAaC,EAwBUoE,CAxBRvE,OAApB,CAAiCE,CAAjC,CAAqCC,CAArC,CAAwCD,CAAA,EAAxC,CACE,GAAgB,GAAhB,GAuB0BqE,CAvBtB,CAAKrE,CAAL,CAAJ,CACEwE,CAAA,EADF,KAEO,IAAgB,GAAhB,GAqBmBH,CArBf,CAAKrE,CAAL,CAAJ,EACW,CADX,GACD,EAAEwE,CADD,CAEH,MAAA,CAIN,EAAA,CAAQ,EAXqC,CA2BzCC,CAAAA,CAAQJ,CAAA5D,UAAA,CAAc8D,CAAd,CAAsB,CAAtB,CAAyBG,CAAzB,CACRC,EAAAA,CAASN,CAAA5D,UAAA,CAAc,CAAd,CAAiB8D,CAAjB,CAETK,EAAAA,CAASR,CAAA,CAA2BC,CAAA5D,UAAA,CAAciE,CAAd,CAAoB,CAApB,CAA3B,CAAmDJ,CAAnD,CACTO,EAAAA,CAAQJ,CAAAzD,QAAA,CAAc,GAAd,CAEZ,OAAe,EAAf,GAAI6D,CAAJ,CAESP,CAAA,CAASK,CAAT,CAAiBF,CAAA/D,KAAA,EAAjB,CAA+B,EAA/B,CAAmCkE,CAAnC,CAFT,CAOON,CAAA,CAASK,CAAT,CAFKF,CAAAhE,UAAA,CAAgB,CAAhB,CAAmBoE,CAAnB,CAAAnE,KAAAoE,EAEL,CADQL,CAAAhE,UAAA,CAAgBoE,CAAhB,CAAwB,CAAxB,CAAAnE,KAAAqE,EACR,CAAkCH,CAAlC,CAtBiD;AA+HnDI,QAASA,EAAW,CAACC,CAAD,CAAU,CACnC,GAAiBhG,IAAAA,EAAjB,GAAIG,CAAJ,CACE,MAA6BA,EAE/B,IAA2BH,IAAAA,EAA3B,GAAIgG,CAAAC,WAAJ,CAAsC,CAEpC,IAAMC,EAAYF,CAAAG,aAAA,CAdCC,WAcD,CAClB,IAAIF,CAAJ,CACEF,CAAAC,WAAA,CAAqBC,CADvB,KAEO,CAsC8B,CAAA,CAAA,CACjCG,CAAAA,CAAqC,UAAtB,GAtCoBL,CAsCpBM,UAAA,CAtCoBN,CAuCDO,QAAAC,WADnB,CAtCoBR,CAwCrCQ,WACJ,IAAIH,CAAJ,WAA4BI,QAA5B,GACQC,CACF,CADiBL,CAAA3B,YAAAjD,KAAA,EAAAY,MAAA,CAAsC,GAAtC,CACjB,CA7De+D,WA6Df,GAAAM,CAAA,CAAa,CAAb,CAFN,EAE0C,CACtC,CAAA,CAAOA,CAAA,CAAa,CAAb,CAAP,OAAA,CADsC,CAI1C,CAAA,CAAO,EAVgC,CApCnC,GAAqB,EAArB,GAAIL,CAAJ,CAAA,CAmEJ,IAAMA,EAAqC,UAAtB,GAjEIL,CAiEJM,UAAA,CAjEIN,CAkEeO,QAAAC,WADnB,CAjEIR,CAmErBQ,WACJH,EAAAM,WAAAC,YAAA,CAAoCP,CAApC,CAtEI,CAIAL,CAAAC,WAAA,CAAqBI,CANhB,CAL6B,CActC,MAAOL,EAAAC,WAAP,EAA6B,EAlBM,C,CC1PrC,IAAMY,GAAmB,OAAzB,CACMC,GAAkB,6BADxB,CAEMC,EAAY,eA0BhB3G,SADI4G,EACO,EAAG,CAEZ,IAAAC,EAAA,CAAY,EAFA,CAQdC,CAAAA,UAAAA,IAAAA,CAAAA,QAAGA,CAACC,CAADD,CAAOE,CAAPF,CAAcA,CACfC,CAAAD,CAAOC,CAAA1F,KAAAyF,EACPA,KAAAD,EAAAC,CAAUC,CAAVD,CAAAA,CAAkBA,CAChBG,EAAYD,CADIF,CAEhBI,EAAYJ,EAFIA,CAFHA,CAWjBK,EAAAA,UAAAA,IAAAA,CAAAA,QAAGA,CAACJ,CAADI,CAAOA,CACRJ,CAAAI,CAAOJ,CAAA1F,KAAA8F,EACPA,OAAOA,KAAAN,EAAAM,CAAUJ,CAAVI,CAAPA,EAA0BA,IAFlBA,CAUZ,KAAIC,EAAkB,IAIpBpH,SADIqH,EACO,EAAG,CAIZ,IAAAC,EAAA,CAFA,IAAAC,EAEA,CAFuB,IAGvB,KAAAV,EAAA,CAAY,IAAID,CALJ,CAYdY,CAAAA,UAAAA,EAAAA,CAAAA,QAAWA,CAAC5E,CAAD4E,CAAUA,CClGfC,CAAAA,CAAM1D,CAAA2D,KAAA,CDmGS9E,CCnGT,CAAN6E,EAAmC3D,CAAA4D,KAAA,CDmGpB9E,CCnGoB,CAEzCmB,EAAA4D,UAAA,CAAwB,CACxB7D,EAAA6D,UAAA,CAAuB,CDgGrBH,OC/FKC,ED8FcD,CAwBrBI;CAAAA,UAAAA,EAAAA,CAAAA,QAAiBA,CAACC,CAADD,CAAWE,CAAXF,CAAwBA,CACvCA,GAAgChI,IAAAA,EAAhCgI,GAAIC,CAAAE,eAAJH,CAA2CA,CDgGvCI,IAAAA,EAAiB,EAEvB,KADA,IAAMC,EChG0CJ,CAhBZ1B,QDgHuB+B,iBAAA,CAAyB,OAAzB,CAA3D,CACSvH,EAAI,CAAb,CAAgBA,CAAhB,CAAoBsH,CAAAxH,OAApB,CAAmCE,CAAA,EAAnC,CAAwC,CACtC,IAAM0D,EAAQ4D,CAAA,CAAOtH,CAAP,CACd,IAAoB0D,CD9Of8D,aAAA,CAvBuBC,gBAuBvB,CC8OL,CACE,IAAI,CAACpJ,CAAL,CAAmB,CD7PvB,IAAMmB,EC8PqBkE,CD9PdC,YACRL,EAAAwD,IAAA,CAAiBtH,CAAjB,CAAL,GACE8D,CAAAoE,IAAA,CAAiBlI,CAAjB,CAEA,CADMmI,CACN,CC0PyBjE,CD3PRkE,UAAA,CAAgB,CAAA,CAAhB,CACjB,CAAAC,QAAAC,KAAAC,YAAA,CAA0BJ,CAA1B,CAHF,CC8PMjE,EAAAkC,WAAAC,YAAA,CAA6BnC,CAA7B,CAFiB,CAAnB,CADF,IAME2D,EAAAhH,KAAA,CAAoBqD,CAAAC,YAApB,CACA,CAAAD,CAAAkC,WAAAC,YAAA,CAA6BnC,CAA7B,CAToC,CChHtCsE,CD4HF,CC5HEA,CD4HKX,CAAAY,KAAA,CAAoB,EAApB,CAAAvH,KAAA,EC5HLsH,GACQtE,CAGNsE,CAH+CH,QAAAK,cAAAF,CAAuBA,OAAvBA,CAG/CA,CAFAtE,CAAAC,YAEAqE,CALgBA,CAKhBA,CAW4Cd,CAZ5C1B,QAAA2C,aAAAH,CAA8BtE,CAA9BsE,CAY4Cd,CAZP1B,QAAAC,WAArCuC,CACAA,CAAAA,CAAAA,CAAOtE,CAJTsE,EAMAA,CANAA,CAMOA,IASLd,EAAAE,eAAAH;AAA0BA,CADeA,CAK3CA,MAAOA,CADDvD,CACCuD,CADOC,CAAAE,eACPH,EAAQA,IAAAmB,EAAAnB,CAAoBvD,CAApBuD,CAA2BE,CAA3BF,CAARA,CAAkDA,IANlBA,CAazCmB,EAAAA,UAAAA,EAAAA,CAAAA,QAAcA,CAAC1E,CAAD0E,CAAQjB,CAARiB,CAA0BA,CAAlBjB,CAAAiB,CAAAA,IAAAA,EAAAA,GAAAjB,CAAAiB,CAAcA,EAAdA,CAAAjB,CACpBiB,KAAIC,EAAM5E,CAAA2E,CAAc1E,CAAd0E,CACVA,KAAAE,EAAAF,CAAoBC,CAApBD,CAAyBjB,CAAzBiB,CACA1E,EAAAC,YAAAyE,CAAoB5E,CAAA4E,CAAUC,CAAVD,CACpBA,OAAOC,EAJ+BD,CAUxCG,EAAAA,UAAAA,EAAAA,CAAAA,QAAoBA,CAAC7E,CAAD6E,CAAQA,CAAAA,IAAAA,EAAAA,IAAAA,CACtBF,EAAM5E,CAAA8E,CAAc7E,CAAd6E,CACV3E,EAAA2E,CAAYF,CAAZE,CAAiBA,QAAAA,CAACC,CAADD,CAAUA,CACAA,OAAzBA,GAAIC,CAAAD,SAAJA,GACEC,CAAAD,SADFA,CACqBA,MADrBA,CAGAA,EAAAE,EAAAF,CAAmBC,CAAnBD,CAJyBA,CAA3BA,CAMA7E,EAAAC,YAAA4E,CAAoB/E,CAAA+E,CAAUF,CAAVE,CACpBA,OAAOF,EATmBE,CAe5BD,EAAAA,UAAAA,EAAAA,CAAAA,QAAcA,CAACpG,CAADoG,CAAQnB,CAARmB,CAAqBA,CAAAA,IAAAA,EAAAA,IACjCA,KAAA1B,EAAA0B,CAAuBnB,CACvBvD,EAAA0E,CAAYpG,CAAZoG,CAAmBA,QAAAA,CAAC1G,CAAD0G,CAAOA,CACxBA,CAAAG,EAAAH,CAAmB1G,CAAnB0G,CADwBA,CAA1BA,CAGAA,KAAA1B,EAAA0B,CAAuBA,IALUA,CAUnCG,EAAAA,UAAAA,EAAAA,CAAAA,QAAaA,CAACD,CAADC,CAAOA,CAClBD,CAAAC,QAAAA,CAAkBC,EAAAD,CAAAA,IAAAA,CAAsBD,CAAAC,cAAtBA,CAA6CD,CAA7CC,CAIOA,QAAzBA,GAAID,CAAAC,SAAJA,GACED,CAAAC,SADFA,CACqBA,WADrBA,CALkBA,CAcpBC;QAAAA,GAAgBA,CAAhBA,CAAgBA,CAACzG,CAADyG,CAAUF,CAAVE,CAAgBA,CAE9BzG,CAAAyG,CAAUzG,CAAAxC,QAAAiJ,CAAgBvF,CAAhBuF,CAA4BA,QAAAA,CAACC,CAADD,CAAYE,CAAZF,CAA0BG,CAA1BH,CAAyCI,CAAzCJ,CACpCA,CAAAA,MAAAK,GAAAL,CAH4BA,CAG5BA,CAA2BC,CAA3BD,CAAsCE,CAAtCF,CAAoDG,CAApDH,CAAmEI,CAAnEJ,CAA+EF,CAA/EE,CAAAA,CADQA,CAGVA,OAAOM,EAAAN,CAAAA,CAAAA,CAA2BzG,CAA3ByG,CAAoCF,CAApCE,CALuBA,CA0BhCO,QAAAA,GAA2BA,CAA3BA,CAA2BA,CAACC,CAADD,CAAYA,CAGrCA,IADAA,IAAIE,EAAUD,CACdD,CAAOE,CAAAF,OAAPA,CAAAA,CACEE,CAAAF,CAAUE,CAAAF,OAEZA,KAAMG,EAAYH,EAAlBA,CACII,EAAgBJ,CAAAA,CACpBrF,EAAAqF,CAAYE,CAAZF,CAAqBA,QAAAA,CAACrH,CAADqH,CAAOA,CAG1BA,CADAI,CACAJ,CADgBI,CAChBJ,EADiCrH,CACjCqH,GADuCC,CACvCD,GAOIrH,CAAAqH,SAPJA,GAOsBC,CAAAD,SAPtBA,EAQEK,MAAAC,OAAAN,CAAcG,CAAdH,CAAyBO,CAAAP,CAnBQA,CAmBRA,CAAmBrH,CAAAqH,cAAnBA,CAAzBA,CAXwBA,CAA5BA,CAcAA,OAAOG,EAtB8BH;AA8BvCD,QAAAA,EAAqBA,CAArBA,CAAqBA,CAACxJ,CAADwJ,CAAOR,CAAPQ,CAAaA,CAIhCA,IAFAA,IAAIS,CAEJT,CAAOS,CAAPT,CAAW5F,CAAAsG,KAAAV,CAAiBxJ,CAAjBwJ,CAAXA,CAAAA,CAAoCA,CAClCA,IAAIL,EAAYc,CAAAT,CAAEA,CAAFA,CAAhBA,CACIW,EAAYF,CAAAT,CAAEA,CAAFA,CACZY,EAAAA,CAAMH,CAAAI,MAMVb,KAAIc,EAAkBtK,CAAAuK,MAAAf,CAAWA,CAAXA,CAHPY,CAGOZ,CAHDL,CAAA3H,QAAAgI,CAAkBA,QAAlBA,CAGCA,CAClBgB,EAAAA,CAAiBxK,CAAAuK,MAAAf,CAHDY,CAGCZ,CAHKL,CAAA7I,OAGLkJ,CACrBA,KAAIiB,EAAWzB,CAAAQ,CAAOC,EAAAD,CAAAA,CAAAA,CAAiCR,CAAjCQ,CAAPA,CAAgDA,EAC/DM,OAAAC,OAAAP,CAAciB,CAAdjB,CAAwBQ,CAAAR,CAAAA,CAAAA,CAAmBc,CAAnBd,CAAxBA,CAiCI7I,EAAAA,CAAAA,IAAAA,EAhCc6I,KAAAA,EAAAA,CAmBpBW,EAAAO,CAAYP,CAAAlK,QAAAyK,CAAkBpE,EAAlBoE,CAAoCA,EAApCA,CACZA,KAAIC,EAAOD,EACPE,KAAAA,EAAaF,CAAAhE,EAAAM,IAAA0D,CAAcP,CAAdO,CAGZE,EAALF,GACEA,CAAAhE,EAAAC,IAAA+D,CAAcP,CAAdO,CAAyBA,EAAzBA,CACAA,CAAAE,CAAAF,CAAaA,CAAAhE,EAAAM,IAAA0D,CAAcP,CAAdO,CAFfA,CAIAA,IAAIE,CAAJF,CAAgBA,CACVA,CAAAtD,EAAJsD,GACEE,CAAA7D,EAAA2D,CAAsBA,CAAAtD,EAAtBsD,CADFA,CACgDA,CAAAA,CADhDA,CAIAA,KAAM5D,EAAa8D,CAAA9D,EACnB4D,KAAK/J,CAAL+J,GAAU5D,EAAV4D,CACEG,CASAH,CATId,CASJc,EATiBd,CAAAc,CAAU/J,CAAV+J,CASjBA,CARAI,CAQAJ,CARQA,CAAC/J,CAAD+J,CAAIA,QAAJA,CAAcP,CAAdO,CAtQMK,KAsQNL,CAAwC/J,CAAxC+J,CAQRA,CAPIG,CAOJH,EANEI,CAAAjK,KAAA6J,CAAWA,GAAXA,CAAgBG,CAAA5K,QAAAyK,CAAUlE,CAAVkE,CAAqBA,EAArBA,CAAhBA,CAMFA,CAJAI,CAAAjK,KAAA6J,CAAWA,GAAXA,CAIAA,CAHIlE,CAAAe,KAAAmD,CAAe5D,CAAA4D,CAAW/J,CAAX+J,CAAfA,CAGJA,EAFEI,CAAAjK,KAAA6J,CAAWA,aAAXA,CAEFA,CAAAC,CAAA9J,KAAA6J,CAAUI,CAAArC,KAAAiC,CAAWA,EAAXA,CAAVA,CAhBYA,CAmBhBA,CAAAA,CAAOC,CAAAlC,KAAAiC,CAAUA,IAAVA,CA7CL1K,EAAAwJ,CAAUc,CAAVd,CAA4BwB,CAA5BxB,CAA0CgB,CAE1C5G,EAAA4D,UAAAgC,CAAwBY,CAAxBZ,CAA8BwB,CAAA1K,OAjBIkJ,CAmBpCA,MAAOxJ,EAvByBwJ;AAkGlCQ,QAAAA,EAAaA,CAAbA,CAAaA,CAAChK,CAADgK,CAAOiB,CAAPjB,CAAwCA,CAAjCiB,CAAAjB,CAAAA,IAAAA,EAAAA,GAAAiB,CAAAjB,CAA0BA,CAAAA,CAA1BA,CAAAiB,CACdpE,EAAAA,CAAQ7G,CAAA8B,MAAAkI,CAAWA,GAAXA,CAGZA,KAJmDA,IAE/CkB,CAF+ClB,CAErC1E,CAFqC0E,CAG/CmB,EAAMnB,EAHyCA,CAI1CxJ,EAAIwJ,CAJsCA,CAIhCoB,CAAnBpB,CAAuBxJ,CAAvBwJ,CAA2BnD,CAAAvG,OAA3B0J,CAAyCxJ,CAAAwJ,EAAzCA,CAEEA,GADArJ,CACAqJ,CADInD,CAAAmD,CAAMxJ,CAANwJ,CACJA,CAGEA,GAFAoB,CAEIpB,CAFCrJ,CAAAmB,MAAAkI,CAAQA,GAARA,CAEDA,CAAYA,CAAZA,CAAAoB,CAAA9K,OAAJ0J,CAAmBA,CACjBkB,CAAAlB,CAAWoB,CAAApB,CAAGA,CAAHA,CAAA9I,KAAA8I,EAEX1E,EAAA0E,CAAQoB,CAAAb,MAAAP,CAASA,CAATA,CAAAvB,KAAAuB,CAAiBA,GAAjBA,CACRA,IAAIiB,CAAJjB,CAAAA,CACUA,IAAAA,EAAAA,CAA8BkB,EAAAA,CAAAA,CAvC9CG,KAAIjM,EAAQmH,EAAA2D,KAAAmB,CAAqB/F,CAArB+F,CACRjM,EAAJiM,GACMjM,CAAAiM,CAAMA,CAANA,CAAJA,EAvHGC,CAAAnE,EAMLmE,GALEA,CAAAnE,EAGAmE,CAHsDjD,QAAAK,cAAA4C,CAAuBA,MAAvBA,CAGtDA,CAFAA,CAAAnE,EAAAoE,aAAAD,CAAkCA,oBAAlCA,CAAwDA,EAAxDA,CAEAA,CADAA,CAAAnE,EAAAjD,MAAAsH,IACAF,CADiCA,SACjCA,CAAAjD,QAAAC,KAAAC,YAAA+C,CAA0BA,CAAAnE,EAA1BmE,CAEFA,EAAAA,CAAAA,CAAOxM,MAAA2M,iBAAAH,CAAwBA,CAAAnE,EAAxBmE,CAAAI,iBAAAJ,CAA+DJ,CAA/DI,CAiHLD,EAUUA,CAVVA,CAUUA,oBAPRA,CAAA/F,CAAA+F,CAHFA,CADFA,CAqCMrB,CAGAmB,CAAAnB,CAAIkB,CAAJlB,CAAAA,CAAgB1E,CAPC0E,CAWvBA,MAAOmB,EApB4CnB,CA0BrD2B,QAAAA,GAAqBA,CAArBA,CAAqBA,CAACf,CAADe,CAAaA,CAChCA,GAAK1E,CAAL0E,CAGAA,IAAKA,IAAIhE,CAATgE,GAAwBf,EAAA7D,EAAxB4E,CACMhE,CAAJgE,GAAoBA,CAAAvE,EAApBuE,EACE1E,CAAA0E,CAAgBhE,CAAhBgE,CAN4BA;AAmBlCpC,QAAAA,GAAqBA,CAArBA,CAAqBA,CAACJ,CAADI,CAAYH,CAAZG,CAA0BF,CAA1BE,CAAyCD,CAAzCC,CAAqDP,CAArDO,CAA2DA,CAE1EF,CAAJE,EAEE3E,CAAA2E,CAA2BF,CAA3BE,CAA0CA,QAAAA,CAACpE,CAADoE,CAASjE,CAATiE,CAAmBA,CACvDjE,CAAJiE,EAL0EA,CAK7D7C,EAAAM,IAAAuC,CAAcjE,CAAdiE,CAAbA,GACED,CADFC,CACeA,SADfA,CACyBjE,CADzBiE,CAC8BA,GAD9BA,CAD2DA,CAA7DA,CAMFA,IAAIA,CAACD,CAALC,CACEA,MAAOJ,EAETI,KAAIqC,EAAoBpC,CAAAD,CAAAA,CAAAA,CAA2BA,EAA3BA,CAAgCD,CAAhCC,CAA4CP,CAA5CO,CACpBpE,EAAAA,CAASgE,CAAAoB,MAAAhB,CAAgBA,CAAhBA,CAAmBJ,CAAA3H,QAAA+H,CAAkBA,IAAlBA,CAAnBA,CAKbA,KAAIsC,EADAC,CACAD,CADc7B,CAAAT,CAAAA,CAAAA,CAAmBqC,CAAnBrC,CAAsCA,CAAAA,CAAtCA,CAClBA,CACIqB,EAAarB,CAAA7C,EAAAM,IAAAuC,CAAcH,CAAdG,CADjBA,CAEIwC,EAAWnB,CAAXmB,EAAyBnB,CAAA9D,EACzBiF,EAAJxC,CAGEsC,CAHFtC,CAGkBO,MAAAC,OAAAR,CAAcO,MAAAkC,OAAAzC,CAAcwC,CAAdxC,CAAdA,CAAuCuC,CAAvCvC,CAHlBA,CAKEA,CAAA7C,EAAAC,IAAA4C,CAAcH,CAAdG,CAA4BsC,CAA5BtC,CAEFA,KAAI4B,EAAM5B,EAAVA,CACI5I,CADJ4I,CAGI0C,EAAmB1C,CAAAA,CACvBA,KAAK5I,CAAL4I,GAAUsC,EAAVtC,CAAyBA,CACvBA,IAAA2C,EAAIJ,CAAAvC,CAAY5I,CAAZ4I,CAEM9J,KAAAA,EAAV8J,GAAI2C,CAAJ3C,GACE2C,CADF3C,CACMA,SADNA,CAGIwC,EAAAA,CAAJxC,EAAkB5I,CAAlB4I,GAAuBwC,EAAvBxC,GACE0C,CADF1C,CACqBA,CAAAA,CADrBA,CAGA4B,EAAAtK,KAAA0I,CAAYH,CAAZG,CA1YgBwB,KA0YhBxB,CAA2C5I,CAA3C4I,CAA4CA,IAA5CA,CAAiD2C,CAAjD3C,CATuBA,CAWrB0C,CAAJ1C,EACEoC,EAAApC,CAAAA,CAAAA,CAA2BqB,CAA3BrB,CAEEqB,EAAJrB,GACEqB,CAAA9D,EADFyC,CAC0BsC,CAD1BtC,CAaIF,EAAJE,GACEpE,CADFoE,CACcJ,CADdI,CACuBA,GADvBA,CAC2BpE,CAD3BoE,CAGAA,OAAUpE,EAAVoE,CAAmB4B,CAAA1C,KAAAc,CAASA,IAATA,CAAnBA,CAAiCA,GA/D6CA,CAqElFrC,CAAAiF,UAAA,YAAA,CAAqCjF,CAAAiF,UAAA9E,EACrCH,EAAAiF,UAAA,eAAA,CAAwCjF,CAAAiF,UAAAvD,EACxC1B;CAAAiF,UAAA,qBAAA,CAA8CjF,CAAAiF,UAAApD,EAC9C7B,EAAAiF,UAAA,eAAA,CAAwCjF,CAAAiF,UAAArD,EACxC5B,EAAAiF,UAAA,cAAA,CAAuCjF,CAAAiF,UAAAlD,EACvC/B,EAAAiF,UAAA,kBAAA,CAA2CjF,CAAAiF,UAAA1E,EAC3CP,EAAAiF,UAAA,WAAA,CA3asBpB,KA6atBjB,OAAAsC,eAAA,CAAsBlF,CAAAiF,UAAtB,CAA2C,iBAA3C,CAA8D,CAE5D,IAAAnF,QAAG,EAAG,CACJ,MAAOC,EADH,CAFsD,CAM5D,IAAAN,QAAG,CAAC0F,CAAD,CAAK,CACNpF,CAAA,CAAkBoF,CADZ,CANoD,CAA9D,C,CElfA,IAAMC,EAAc,E,CCkBpB,IAAMC,GAAUC,OAAAC,QAAA,EAKTC,SAASA,GAAU,CAAC/E,CAAD,CAAa,CAErC,GADID,CACJ,CDxBa4E,CCuBE,CAAY3E,CAAZ,CACf,CACqBD,CAerB,yBAIA,CAnBqBA,CAeO,yBAI5B,EAJyD,CAIzD,CAnBqBA,CAiBrB,4BAEA,CAnBqBA,CAiBU,4BAE/B,EAF+D,CAE/D,CAnBqBA,CAmBrB,sBAAA,EAnBqBA,CAmBK,sBAA1B,EAAoD,CAApD,EAAyD,CAtBpB,CAyChCiF,QAASA,GAAe,CAACjF,CAAD,CAAW,CACxC,MAAOA,EAAA,yBAAP,GAAqCA,CAAA,sBADG,CA4CnCkF,QAASA,GAAuB,CAAClF,CAAD,CAAW,CAEhDA,CAAA,4BAAA,CAA+BA,CAAA,sBAE1BA,EAAAmF,YAAL,GACEnF,CAAAmF,YACA,CADuB,CAAA,CACvB,CAAAN,EAAAO,KAAA,CAAa,QAAQ,EAAG,CAEtBpF,CAAA,yBAAA,CAA4BA,CAAA,sBAC5BA,EAAAmF,YAAA,CAAuB,CAAA,CAHD,CAAxB,CAFF,CAJgD,C,CCtGlD,IAAME,EAAY,IJufH7F,CIpfbrH,SADImN,EACO,EAAG,CAEZ,IAAAC,EAAA,CAA4B,IAC5BF,EAAA,gBAAA,CAA8CL,EAHlC,CAKdQ,QAAAA,EAAMA,CAANA,CAAMA,CAAGA,CACHD,CAAAC,CAAAD,EAAJC,EAGIpO,MAAAU,SAAA2N,qBAHJD,GAIEA,CAAAD,EAMAC,CAJQpO,MAAAU,SAAA2N,qBAIRD,CAHAA,CAAAD,EAAAC,kBAGAA,CAHiDA,QAAAA,CAAChJ,CAADgJ,CAAWA,CAC1DH,CAAAhE,EAAAmE,CAA+BhJ,CAA/BgJ,CAD0DA,CAG5DA,CAAAA,CAAAD,EAAAC,iBAAAA,CAAgDA,QAAAA,EAAMA,CACpDE,qBAAAF,CAAsBA,QAAAA,EAAMA,CAZzBA,CAaGD,EAAAC,SAAJA,EAbCA,CAcCG,kBAAAH,EAFwBA,CAA5BA,CADoDA,CAVxDA,CADOA,CAwBTI,CAAAA,UAAAA,gBAAAA,CAAAA,QAAeA,CAAC5F,CAAD4F,CAAW3F,CAAX2F,CAAwBA,CACrCJ,CAAAI,CAAAA,IAAAA,CLuT8B,GKtT9BA,GLsTK9H,CAAA,CKtTkBkC,CLsTlB,CKtTL4F,GFvCWhB,CE0CXgB,CAAY3F,CAAZ2F,CAGAA,CAH2B5F,CAG3B4F,CAFIzE,CAEJyE,CAFUP,CAAAtF,EAAA6F,CAA4B5F,CAA5B4F,CAAsC3F,CAAtC2F,CAEVA,CAAA5F,CAAA4F,UAAAA,CAAwBzE,CANxByE,CAFqCA,CAUvCD;CAAAA,UAAAA,kBAAAA,CAAAA,QAAiBA,EAAGA,CAClBH,CAAAG,CAAAA,IAAAA,CACAA,IAAKA,IAAAJ,EAALI,CAAAA,CAGAA,IAAIvF,EAASuF,IAAAJ,EAAAI,cAAAA,EACbA,IAAKA,IAAAJ,EAAAI,SAALA,CAAAA,CAGAA,IAAKA,IAAI7M,EAAI6M,CAAbA,CAAgB7M,CAAhB6M,CAAoBvF,CAAAxH,OAApB+M,CAAmC7M,CAAA6M,EAAnCA,CAAyCA,CAEvCA,IAAInJ,EAAQmJ,IAAAJ,EAAAI,uBAAAA,CADHvF,CAAAuF,CAAO7M,CAAP6M,CACGA,CACRnJ,EAAJmJ,EACEN,CAAAhE,EAAAsE,CAA+BnJ,CAA/BmJ,CAJqCA,CAOzCA,IAAAJ,EAAAI,SAAAA,CAAwCA,CAAAA,CAVxCA,CAJAA,CAFkBA,CAsBpBE;CAAAA,UAAAA,aAAAA,CAAAA,QAAYA,CAAC9H,CAAD8H,CAAUzG,CAAVyG,CAAsBA,CAChCL,CAAAK,CAAAA,IAAAA,CACAA,IAAIzG,CAAJyG,CHnEF,IAAK5M,IAAIA,CAAT,GGoEoCmG,EHpEpC,CAEY,IAAV,GAAInG,CAAJ,CGkEyB8E,CHjEvBvB,MAAAsJ,eAAA,CAA6B7M,CAA7B,CADF,CGkEyB8E,CH/DvBvB,MAAAuJ,YAAA,CAA0B9M,CAA1B,CG+DgCmG,CH/DH,CAAWnG,CAAX,CAA7B,CGiEF4M,IAAI9H,CAAAiI,WAAJH,CAKEA,IAJAA,IAAAI,aAAAJ,CAAkB9H,CAAlB8H,CAIS/M,CAHLoN,CAGKpN,CAFuBiF,CAAAiI,WAADG,SAEtBrN,EADLiF,CAAAiI,WAAAI,WACKtN,CAAAA,CAAAA,CAAI+M,CAAbA,CAAgB/M,CAAhB+M,CAAoBK,CAAAtN,OAApBiN,CAA2C/M,CAAA+M,EAA3CA,CACEA,IAAAA,aAAAA,CAA6CK,CAAAL,CAAe/M,CAAf+M,CAA7CA,CANJA,KAUEA,KADIM,CACKrN,CADMiF,CAAAoI,SACNrN,EAD0BiF,CAAAqI,WAC1BtN,CAAAA,CAAAA,CAAI+M,CAAbA,CAAgB/M,CAAhB+M,CAAoBM,CAAAvN,OAApBiN,CAAqC/M,CAAA+M,EAArCA,CACEA,IAAAA,aAAAA,CAA6CM,CAAAN,CAAS/M,CAAT+M,CAA7CA,CAhB4BA,CAuBlCI;CAAAA,UAAAA,aAAAA,CAAAA,QAAYA,CAAClI,CAADkI,CAAUA,CACpBT,CAAAS,CAAAA,IAAAA,CLyIF,KAAI5H,EKxIsBN,CLwIV,UAAhB,CACIsI,CAKAhI,EAAJ,CACgC,EAA9B,CAAIA,CAAAvE,QAAA,CAAkB,GAAlB,CAAJ,CACEuM,CADF,CACOhI,CADP,CAIEgI,CAJF,CK/IwBtI,CLmJhBG,aAJR,EK/IwBH,CLmJQG,aAAA,CAAqB,IAArB,CAJhC,EAI+D,EALjE,CAQEmI,CARF,CK9I0BtI,CLsJHsI,GKrJjBrG,EAAAA,CF/FO4E,CE+FIqB,CAAYI,CAAZJ,CACfA,IAAIA,EAAAjG,CAAAiG,EL6P0B,EK7P1BA,GL6PCnI,CAAA,CK7P8BkC,CL6P9B,CK7PDiG,EAGAjG,CAAAA,CAHAiG,EAG4BA,EAAfA,CAA+BjG,CAA/BiG,CAHbA,CAAJA,CAG2DA,CAEzDA,GDfIhB,EAAA,CCeqCjF,CDfrC,CCeJiG,EAAyCjG,CDfR,4BCejCiG,GAAyCjG,CDfyB,sBCelEiG,CACEA,IAAAL,gBAAAK,CAAqBjG,CAArBiG,CAA+BI,CAA/BJ,CACAA,CAAeA,EAAfA,CAAuCjG,CAAvCiG,CAIFA,IADItN,CACJsN,CADWlI,CAAAiI,WACXC,CAEEA,GADIzJ,CACJyJ,CAD4CtN,CAAA2N,cAAAL,CAAmBA,OAAnBA,CAC5CA,CAEEzJ,CAAAyJ,WACAA,CADsBjG,CAAAiG,UACtBA,CAAAzJ,CAAAC,YAAAwJ,CAAoB3J,CAAA2J,CAAUjG,CAAAiG,UAAVA,CAbiCA,CAPvCA,CA4BtBM,EAAAA,UAAAA,cAAAA,CAAAA,QAAaA,CAACnH,CAADmH,CAAaA,CACxBf,CAAAe,CAAAA,IAAAA,CACAA,KAAAV,aAAAU,CAAkB5F,QAAA6F,KAAlBD,CAAiCnH,CAAjCmH,CAFwBA,CAM5B;GAAI,CAACnP,MAAAU,SAAL,EAAwB,CAACV,MAAAU,SAAA2O,YAAzB,CAAsD,CACpD,IAAMC,EAAqB,IAAIpB,CAA/B,CACIG,GAAuBrO,MAAAU,SAAvB2N,EAA0CrO,MAAAU,SAAA2N,qBAG9CrO,OAAAU,SAAA,CAAkB,CAMhB,gBAAA8N,QAAe,CAAC5F,CAAD,CAAWC,CAAX,CAAwC,CACrDyG,CAAAf,kBAAA,EACAe,EAAAd,gBAAA,CAAmC5F,CAAnC,CAA6CC,CAA7C,CAFqD,CANvC,CAgBhB,sBAAA0G,QAAqB,CAAC3G,CAAD,CAAWC,CAAX,CAAwB2G,CAAxB,CAAwC,CAC3DxP,MAAAU,SAAA8N,gBAAA,CAAgC5F,CAAhC,CAA0CC,CAA1C,CAAuD2G,CAAvD,CAD2D,CAhB7C,CAwBhB,mBAAAC,QAAkB,EAAwB,EAxB1B,CA8BhB,aAAAhB,QAAY,CAAC9H,CAAD,CAAUqB,CAAV,CAAsB,CAChCsH,CAAAf,kBAAA,EACAe,EAAAb,aAAA,CAAgC9H,CAAhC,CAAyCqB,CAAzC,CAFgC,CA9BlB,CAsChB,aAAA6G,QAAY,CAAClI,CAAD,CAAU,CACpB2I,CAAAf,kBAAA,EACAe,EAAAT,aAAA,CAAgClI,CAAhC,CAFoB,CAtCN,CA8ChB,cAAAwI,QAAa,CAACnH,CAAD,CAAa,CACxBsH,CAAAf,kBAAA,EACAe;CAAAH,cAAA,CAAiCnH,CAAjC,CAFwB,CA9CV,CAwDhB,sBAAA0H,QAAqB,CAAC/I,CAAD,CAAUyF,CAAV,CAAoB,CACvC,MHpKJ,CADM5F,CACN,CADcxG,MAAA2M,iBAAA,CGqKmBhG,CHrKnB,CAAAiG,iBAAA,CGqK4BR,CHrK5B,CACd,EAGS5F,CAAApE,KAAA,EAHT,CACS,EGkKkC,CAxDzB,CA4DhB,kBAAAmM,QAAiB,EAAG,CAClBe,CAAAf,kBAAA,EADkB,CA5DJ,CAgEhB3N,UAAWC,CAhEK,CAiEhBd,aAAcA,CAjEE,CAkEhBU,SAAUK,CAlEM,CAqEduN,GAAJ,GACErO,MAAAU,SAAA2N,qBADF,CACyCA,EADzC,CA1EoD,CA+EtDrO,MAAAU,SAAA0H,UAAA,CAA4B6F","file":"apply-shim.min.js","sourcesContent":["/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nexport const nativeShadow = !(window['ShadyDOM'] && window['ShadyDOM']['inUse']);\nlet nativeCssVariables_;\n\n/**\n * @param {(ShadyCSSOptions | ShadyCSSInterface)=} settings\n */\nfunction calcCssVariables(settings) {\n if (settings && settings['shimcssproperties']) {\n nativeCssVariables_ = false;\n } else {\n // chrome 49 has semi-working css vars, check if box-shadow works\n // safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782\n // However, shim css custom properties are only supported with ShadyDOM enabled,\n // so fall back on native if we do not detect ShadyDOM\n // Edge 15: custom properties used in ::before and ::after will also be used in the parent element\n // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12414257/\n nativeCssVariables_ = nativeShadow || Boolean(!navigator.userAgent.match(/AppleWebKit\\/601|Edge\\/15/) &&\n window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)'));\n }\n}\n\n/** @type {string | undefined} */\nexport let cssBuild;\nif (window.ShadyCSS && window.ShadyCSS.cssBuild !== undefined) {\n cssBuild = window.ShadyCSS.cssBuild;\n}\n\nif (window.ShadyCSS && window.ShadyCSS.nativeCss !== undefined) {\n nativeCssVariables_ = window.ShadyCSS.nativeCss;\n} else if (window.ShadyCSS) {\n calcCssVariables(window.ShadyCSS);\n // reset window variable to let ShadyCSS API take its place\n window.ShadyCSS = undefined;\n} else {\n calcCssVariables(window['WebComponents'] && window['WebComponents']['flags']);\n}\n\n// Hack for type error under new type inference which doesn't like that\n// nativeCssVariables is updated in a function and assigns the type\n// `function(): ?` instead of `boolean`.\nexport const nativeCssVariables = /** @type {boolean} */(nativeCssVariables_);","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n/*\nExtremely simple css parser. Intended to be not more than what we need\nand definitely not necessarily correct =).\n*/\n\n'use strict';\n\n/** @unrestricted */\nclass StyleNode {\n constructor() {\n /** @type {number} */\n this['start'] = 0;\n /** @type {number} */\n this['end'] = 0;\n /** @type {StyleNode} */\n this['previous'] = null;\n /** @type {StyleNode} */\n this['parent'] = null;\n /** @type {Array<StyleNode>} */\n this['rules'] = null;\n /** @type {string} */\n this['parsedCssText'] = '';\n /** @type {string} */\n this['cssText'] = '';\n /** @type {boolean} */\n this['atRule'] = false;\n /** @type {number} */\n this['type'] = 0;\n /** @type {string} */\n this['keyframesName'] = '';\n /** @type {string} */\n this['selector'] = '';\n /** @type {string} */\n this['parsedSelector'] = '';\n }\n}\n\nexport {StyleNode}\n\n// given a string of css, return a simple rule tree\n/**\n * @param {string} text\n * @return {StyleNode}\n */\nexport function parse(text) {\n text = clean(text);\n return parseCss(lex(text), text);\n}\n\n// remove stuff we don't care about that may hinder parsing\n/**\n * @param {string} cssText\n * @return {string}\n */\nfunction clean(cssText) {\n return cssText.replace(RX.comments, '').replace(RX.port, '');\n}\n\n// super simple {...} lexer that returns a node tree\n/**\n * @param {string} text\n * @return {StyleNode}\n */\nfunction lex(text) {\n let root = new StyleNode();\n root['start'] = 0;\n root['end'] = text.length\n let n = root;\n for (let i = 0, l = text.length; i < l; i++) {\n if (text[i] === OPEN_BRACE) {\n if (!n['rules']) {\n n['rules'] = [];\n }\n let p = n;\n let previous = p['rules'][p['rules'].length - 1] || null;\n n = new StyleNode();\n n['start'] = i + 1;\n n['parent'] = p;\n n['previous'] = previous;\n p['rules'].push(n);\n } else if (text[i] === CLOSE_BRACE) {\n n['end'] = i + 1;\n n = n['parent'] || root;\n }\n }\n return root;\n}\n\n// add selectors/cssText to node tree\n/**\n * @param {StyleNode} node\n * @param {string} text\n * @return {StyleNode}\n */\nfunction parseCss(node, text) {\n let t = text.substring(node['start'], node['end'] - 1);\n node['parsedCssText'] = node['cssText'] = t.trim();\n if (node['parent']) {\n let ss = node['previous'] ? node['previous']['end'] : node['parent']['start'];\n t = text.substring(ss, node['start'] - 1);\n t = _expandUnicodeEscapes(t);\n t = t.replace(RX.multipleSpaces, ' ');\n // TODO(sorvell): ad hoc; make selector include only after last ;\n // helps with mixin syntax\n t = t.substring(t.lastIndexOf(';') + 1);\n let s = node['parsedSelector'] = node['selector'] = t.trim();\n node['atRule'] = (s.indexOf(AT_START) === 0);\n // note, support a subset of rule types...\n if (node['atRule']) {\n if (s.indexOf(MEDIA_START) === 0) {\n node['type'] = types.MEDIA_RULE;\n } else if (s.match(RX.keyframesRule)) {\n node['type'] = types.KEYFRAMES_RULE;\n node['keyframesName'] =\n node['selector'].split(RX.multipleSpaces).pop();\n }\n } else {\n if (s.indexOf(VAR_START) === 0) {\n node['type'] = types.MIXIN_RULE;\n } else {\n node['type'] = types.STYLE_RULE;\n }\n }\n }\n let r$ = node['rules'];\n if (r$) {\n for (let i = 0, l = r$.length, r;\n (i < l) && (r = r$[i]); i++) {\n parseCss(r, text);\n }\n }\n return node;\n}\n\n/**\n * conversion of sort unicode escapes with spaces like `\\33 ` (and longer) into\n * expanded form that doesn't require trailing space `\\000033`\n * @param {string} s\n * @return {string}\n */\nfunction _expandUnicodeEscapes(s) {\n return s.replace(/\\\\([0-9a-f]{1,6})\\s/gi, function() {\n let code = arguments[1],\n repeat = 6 - code.length;\n while (repeat--) {\n code = '0' + code;\n }\n return '\\\\' + code;\n });\n}\n\n/**\n * stringify parsed css.\n * @param {StyleNode} node\n * @param {boolean=} preserveProperties\n * @param {string=} text\n * @return {string}\n */\nexport function stringify(node, preserveProperties, text = '') {\n // calc rule cssText\n let cssText = '';\n if (node['cssText'] || node['rules']) {\n let r$ = node['rules'];\n if (r$ && !_hasMixinRules(r$)) {\n for (let i = 0, l = r$.length, r;\n (i < l) && (r = r$[i]); i++) {\n cssText = stringify(r, preserveProperties, cssText);\n }\n } else {\n cssText = preserveProperties ? node['cssText'] :\n removeCustomProps(node['cssText']);\n cssText = cssText.trim();\n if (cssText) {\n cssText = ' ' + cssText + '\\n';\n }\n }\n }\n // emit rule if there is cssText\n if (cssText) {\n if (node['selector']) {\n text += node['selector'] + ' ' + OPEN_BRACE + '\\n';\n }\n text += cssText;\n if (node['selector']) {\n text += CLOSE_BRACE + '\\n\\n';\n }\n }\n return text;\n}\n\n/**\n * @param {Array<StyleNode>} rules\n * @return {boolean}\n */\nfunction _hasMixinRules(rules) {\n let r = rules[0];\n return Boolean(r) && Boolean(r['selector']) && r['selector'].indexOf(VAR_START) === 0;\n}\n\n/**\n * @param {string} cssText\n * @return {string}\n */\nfunction removeCustomProps(cssText) {\n cssText = removeCustomPropAssignment(cssText);\n return removeCustomPropApply(cssText);\n}\n\n/**\n * @param {string} cssText\n * @return {string}\n */\nexport function removeCustomPropAssignment(cssText) {\n return cssText\n .replace(RX.customProp, '')\n .replace(RX.mixinProp, '');\n}\n\n/**\n * @param {string} cssText\n * @return {string}\n */\nfunction removeCustomPropApply(cssText) {\n return cssText\n .replace(RX.mixinApply, '')\n .replace(RX.varApply, '');\n}\n\n/** @enum {number} */\nexport const types = {\n STYLE_RULE: 1,\n KEYFRAMES_RULE: 7,\n MEDIA_RULE: 4,\n MIXIN_RULE: 1000\n}\n\nconst OPEN_BRACE = '{';\nconst CLOSE_BRACE = '}';\n\n// helper regexp's\nconst RX = {\n comments: /\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//gim,\n port: /@import[^;]*;/gim,\n customProp: /(?:^[^;\\-\\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\\n]|$)/gim,\n mixinProp: /(?:^[^;\\-\\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\\n]|$)?/gim,\n mixinApply: /@apply\\s*\\(?[^);]*\\)?\\s*(?:[;\\n]|$)?/gim,\n varApply: /[^;:]*?:[^;]*?var\\([^;]*\\)(?:[;\\n]|$)?/gim,\n keyframesRule: /^@[^\\s]*keyframes/,\n multipleSpaces: /\\s+/g\n}\n\nconst VAR_START = '--';\nconst MEDIA_START = '@media';\nconst AT_START = '@';\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\nexport const VAR_ASSIGN = /(?:^|[;\\s{]\\s*)(--[\\w-]*?)\\s*:\\s*(?:((?:'(?:\\\\'|.)*?'|\"(?:\\\\\"|.)*?\"|\\([^)]*?\\)|[^};{])+)|\\{([^}]*)\\}(?:(?=[;\\s}])|$))/gi;\nexport const MIXIN_MATCH = /(?:^|\\W+)@apply\\s*\\(?([^);\\n]*)\\)?/gi;\nexport const VAR_CONSUMED = /(--[\\w-]+)\\s*([:,;)]|$)/gi;\nexport const ANIMATION_MATCH = /(animation\\s*:)|(animation-name\\s*:)/;\nexport const MEDIA_MATCH = /@media\\s(.*)/;\nexport const IS_VAR = /^--/;\nexport const BRACKETED = /\\{[^}]*\\}/g;\nexport const HOST_PREFIX = '(?:^|[^.#[:])';\nexport const HOST_SUFFIX = '($|[.:[\\\\s>+~])';\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\n/** @type {!Set<string>} */\nconst styleTextSet = new Set();\n\nexport const scopingAttribute = 'shady-unscoped';\n\n/**\n * Add a specifically-marked style to the document directly, and only one copy of that style.\n *\n * @param {!HTMLStyleElement} style\n * @return {undefined}\n */\nexport function processUnscopedStyle(style) {\n const text = style.textContent;\n if (!styleTextSet.has(text)) {\n styleTextSet.add(text);\n const newStyle = style.cloneNode(true);\n document.head.appendChild(newStyle);\n }\n}\n\n/**\n * Check if a style is supposed to be unscoped\n * @param {!HTMLStyleElement} style\n * @return {boolean} true if the style has the unscoping attribute\n */\nexport function isUnscopedStyle(style) {\n return style.hasAttribute(scopingAttribute);\n}","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {nativeShadow, nativeCssVariables, cssBuild} from './style-settings.js';\nimport {parse, stringify, types, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\nimport {MEDIA_MATCH} from './common-regex.js';\nimport {processUnscopedStyle, isUnscopedStyle} from './unscoped-style-handler.js';\n\n/**\n * @param {string|StyleNode} rules\n * @param {function(StyleNode)=} callback\n * @return {string}\n */\nexport function toCssText (rules, callback) {\n if (!rules) {\n return '';\n }\n if (typeof rules === 'string') {\n rules = parse(rules);\n }\n if (callback) {\n forEachRule(rules, callback);\n }\n return stringify(rules, nativeCssVariables);\n}\n\n/**\n * @param {HTMLStyleElement} style\n * @return {StyleNode}\n */\nexport function rulesForStyle(style) {\n if (!style['__cssRules'] && style.textContent) {\n style['__cssRules'] = parse(style.textContent);\n }\n return style['__cssRules'] || null;\n}\n\n// Tests if a rule is a keyframes selector, which looks almost exactly\n// like a normal selector but is not (it has nothing to do with scoping\n// for example).\n/**\n * @param {StyleNode} rule\n * @return {boolean}\n */\nexport function isKeyframesSelector(rule) {\n return Boolean(rule['parent']) &&\n rule['parent']['type'] === types.KEYFRAMES_RULE;\n}\n\n/**\n * @param {StyleNode} node\n * @param {Function=} styleRuleCallback\n * @param {Function=} keyframesRuleCallback\n * @param {boolean=} onlyActiveRules\n */\nexport function forEachRule(node, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) {\n if (!node) {\n return;\n }\n let skipRules = false;\n let type = node['type'];\n if (onlyActiveRules) {\n if (type === types.MEDIA_RULE) {\n let matchMedia = node['selector'].match(MEDIA_MATCH);\n if (matchMedia) {\n // if rule is a non matching @media rule, skip subrules\n if (!window.matchMedia(matchMedia[1]).matches) {\n skipRules = true;\n }\n }\n }\n }\n if (type === types.STYLE_RULE) {\n styleRuleCallback(node);\n } else if (keyframesRuleCallback &&\n type === types.KEYFRAMES_RULE) {\n keyframesRuleCallback(node);\n } else if (type === types.MIXIN_RULE) {\n skipRules = true;\n }\n let r$ = node['rules'];\n if (r$ && !skipRules) {\n for (let i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {\n forEachRule(r, styleRuleCallback, keyframesRuleCallback, onlyActiveRules);\n }\n }\n}\n\n// add a string of cssText to the document.\n/**\n * @param {string} cssText\n * @param {string} moniker\n * @param {Node} target\n * @param {Node} contextNode\n * @return {HTMLStyleElement}\n */\nexport function applyCss(cssText, moniker, target, contextNode) {\n let style = createScopeStyle(cssText, moniker);\n applyStyle(style, target, contextNode);\n return style;\n}\n\n/**\n * @param {string} cssText\n * @param {string} moniker\n * @return {HTMLStyleElement}\n */\nexport function createScopeStyle(cssText, moniker) {\n let style = /** @type {HTMLStyleElement} */(document.createElement('style'));\n if (moniker) {\n style.setAttribute('scope', moniker);\n }\n style.textContent = cssText;\n return style;\n}\n\n/**\n * Track the position of the last added style for placing placeholders\n * @type {Node}\n */\nlet lastHeadApplyNode = null;\n\n// insert a comment node as a styling position placeholder.\n/**\n * @param {string} moniker\n * @return {!Comment}\n */\nexport function applyStylePlaceHolder(moniker) {\n let placeHolder = document.createComment(' Shady DOM styles for ' +\n moniker + ' ');\n let after = lastHeadApplyNode ?\n lastHeadApplyNode['nextSibling'] : null;\n let scope = document.head;\n scope.insertBefore(placeHolder, after || scope.firstChild);\n lastHeadApplyNode = placeHolder;\n return placeHolder;\n}\n\n/**\n * @param {HTMLStyleElement} style\n * @param {?Node} target\n * @param {?Node} contextNode\n */\nexport function applyStyle(style, target, contextNode) {\n target = target || document.head;\n let after = (contextNode && contextNode.nextSibling) ||\n target.firstChild;\n target.insertBefore(style, after);\n if (!lastHeadApplyNode) {\n lastHeadApplyNode = style;\n } else {\n // only update lastHeadApplyNode if the new style is inserted after the old lastHeadApplyNode\n let position = style.compareDocumentPosition(lastHeadApplyNode);\n if (position === Node.DOCUMENT_POSITION_PRECEDING) {\n lastHeadApplyNode = style;\n }\n }\n}\n\n/**\n * @param {string} buildType\n * @return {boolean}\n */\nexport function isTargetedBuild(buildType) {\n return nativeShadow ? buildType === 'shadow' : buildType === 'shady';\n}\n\n/**\n * Walk from text[start] matching parens and\n * returns position of the outer end paren\n * @param {string} text\n * @param {number} start\n * @return {number}\n */\nexport function findMatchingParen(text, start) {\n let level = 0;\n for (let i=start, l=text.length; i < l; i++) {\n if (text[i] === '(') {\n level++;\n } else if (text[i] === ')') {\n if (--level === 0) {\n return i;\n }\n }\n }\n return -1;\n}\n\n/**\n * @param {string} str\n * @param {function(string, string, string, string)} callback\n */\nexport function processVariableAndFallback(str, callback) {\n // find 'var('\n let start = str.indexOf('var(');\n if (start === -1) {\n // no var?, everything is prefix\n return callback(str, '', '', '');\n }\n //${prefix}var(${inner})${suffix}\n let end = findMatchingParen(str, start + 3);\n let inner = str.substring(start + 4, end);\n let prefix = str.substring(0, start);\n // suffix may have other variables\n let suffix = processVariableAndFallback(str.substring(end + 1), callback);\n let comma = inner.indexOf(',');\n // value and fallback args should be trimmed to match in property lookup\n if (comma === -1) {\n // variable, no fallback\n return callback(prefix, inner.trim(), '', suffix);\n }\n // var(${value},${fallback})\n let value = inner.substring(0, comma).trim();\n let fallback = inner.substring(comma + 1).trim();\n return callback(prefix, value, fallback, suffix);\n}\n\n/**\n * @param {Element} element\n * @param {string} value\n */\nexport function setElementClassRaw(element, value) {\n // use native setAttribute provided by ShadyDOM when setAttribute is patched\n if (nativeShadow) {\n element.setAttribute('class', value);\n } else {\n window['ShadyDOM']['nativeMethods']['setAttribute'].call(element, 'class', value);\n }\n}\n\nexport const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] || ((node) => node);\n\n/**\n * @param {Element | {is: string, extends: string}} element\n * @return {{is: string, typeExtension: string}}\n */\nexport function getIsExtends(element) {\n let localName = element['localName'];\n let is = '', typeExtension = '';\n /*\n NOTE: technically, this can be wrong for certain svg elements\n with `-` in the name like `<font-face>`\n */\n if (localName) {\n if (localName.indexOf('-') > -1) {\n is = localName;\n } else {\n typeExtension = localName;\n is = (element.getAttribute && element.getAttribute('is')) || '';\n }\n } else {\n is = /** @type {?} */(element).is;\n typeExtension = /** @type {?} */(element).extends;\n }\n return {is, typeExtension};\n}\n\n/**\n * @param {Element|DocumentFragment} element\n * @return {string}\n */\nexport function gatherStyleText(element) {\n /** @type {!Array<string>} */\n const styleTextParts = [];\n const styles = /** @type {!NodeList<!HTMLStyleElement>} */(element.querySelectorAll('style'));\n for (let i = 0; i < styles.length; i++) {\n const style = styles[i];\n if (isUnscopedStyle(style)) {\n if (!nativeShadow) {\n processUnscopedStyle(style);\n style.parentNode.removeChild(style);\n }\n } else {\n styleTextParts.push(style.textContent);\n style.parentNode.removeChild(style);\n }\n }\n return styleTextParts.join('').trim();\n}\n\n/**\n * Split a selector separated by commas into an array in a smart way\n * @param {string} selector\n * @return {!Array<string>}\n */\nexport function splitSelectorList(selector) {\n const parts = [];\n let part = '';\n for (let i = 0; i >= 0 && i < selector.length; i++) {\n // A selector with parentheses will be one complete part\n if (selector[i] === '(') {\n // find the matching paren\n const end = findMatchingParen(selector, i);\n // push the paren block into the part\n part += selector.slice(i, end + 1);\n // move the index to after the paren block\n i = end;\n } else if (selector[i] === ',') {\n parts.push(part);\n part = '';\n } else {\n part += selector[i];\n }\n }\n // catch any pieces after the last comma\n if (part) {\n parts.push(part);\n }\n return parts;\n}\n\nconst CSS_BUILD_ATTR = 'css-build';\n\n/**\n * Return the polymer-css-build \"build type\" applied to this element\n *\n * @param {!HTMLElement} element\n * @return {string} Can be \"\", \"shady\", or \"shadow\"\n */\nexport function getCssBuild(element) {\n if (cssBuild !== undefined) {\n return /** @type {string} */(cssBuild);\n }\n if (element.__cssBuild === undefined) {\n // try attribute first, as it is the common case\n const attrValue = element.getAttribute(CSS_BUILD_ATTR);\n if (attrValue) {\n element.__cssBuild = attrValue;\n } else {\n const buildComment = getBuildComment(element);\n if (buildComment !== '') {\n // remove build comment so it is not needlessly copied into every element instance\n removeBuildComment(element);\n }\n element.__cssBuild = buildComment;\n }\n }\n return element.__cssBuild || '';\n}\n\n/**\n * Check if the given element, either a <template> or <style>, has been processed\n * by polymer-css-build.\n *\n * If so, then we can make a number of optimizations:\n * - polymer-css-build will decompose mixins into individual CSS Custom Properties,\n * so the ApplyShim can be skipped entirely.\n * - Under native ShadowDOM, the style text can just be copied into each instance\n * without modification\n * - If the build is \"shady\" and ShadyDOM is in use, the styling does not need\n * scoping beyond the shimming of CSS Custom Properties\n *\n * @param {!HTMLElement} element\n * @return {boolean}\n */\nexport function elementHasBuiltCss(element) {\n return getCssBuild(element) !== '';\n}\n\n/**\n * For templates made with tagged template literals, polymer-css-build will\n * insert a comment of the form `<!--css-build:shadow-->`\n *\n * @param {!HTMLElement} element\n * @return {string}\n */\nexport function getBuildComment(element) {\n const buildComment = element.localName === 'template' ?\n /** @type {!HTMLTemplateElement} */ (element).content.firstChild :\n element.firstChild;\n if (buildComment instanceof Comment) {\n const commentParts = buildComment.textContent.trim().split(':');\n if (commentParts[0] === CSS_BUILD_ATTR) {\n return commentParts[1];\n }\n }\n return '';\n}\n\n/**\n * Check if the css build status is optimal, and do no unneeded work.\n *\n * @param {string=} cssBuild CSS build status\n * @return {boolean} css build is optimal or not\n */\nexport function isOptimalCssBuild(cssBuild = '') {\n // CSS custom property shim always requires work\n if (cssBuild === '' || !nativeCssVariables) {\n return false;\n }\n return nativeShadow ? cssBuild === 'shadow' : cssBuild === 'shady';\n}\n\n/**\n * @param {!HTMLElement} element\n */\nfunction removeBuildComment(element) {\n const buildComment = element.localName === 'template' ?\n /** @type {!HTMLTemplateElement} */ (element).content.firstChild :\n element.firstChild;\n buildComment.parentNode.removeChild(buildComment);\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n/*\n * The apply shim simulates the behavior of `@apply` proposed at\n * https://tabatkins.github.io/specs/css-apply-rule/.\n * The approach is to convert a property like this:\n *\n * --foo: {color: red; background: blue;}\n *\n * to this:\n *\n * --foo_-_color: red;\n * --foo_-_background: blue;\n *\n * Then where `@apply --foo` is used, that is converted to:\n *\n * color: var(--foo_-_color);\n * background: var(--foo_-_background);\n *\n * This approach generally works but there are some issues and limitations.\n * Consider, for example, that somewhere *between* where `--foo` is set and used,\n * another element sets it to:\n *\n * --foo: { border: 2px solid red; }\n *\n * We must now ensure that the color and background from the previous setting\n * do not apply. This is accomplished by changing the property set to this:\n *\n * --foo_-_border: 2px solid red;\n * --foo_-_color: initial;\n * --foo_-_background: initial;\n *\n * This works but introduces one new issue.\n * Consider this setup at the point where the `@apply` is used:\n *\n * background: orange;\n * `@apply` --foo;\n *\n * In this case the background will be unset (initial) rather than the desired\n * `orange`. We address this by altering the property set to use a fallback\n * value like this:\n *\n * color: var(--foo_-_color);\n * background: var(--foo_-_background, orange);\n * border: var(--foo_-_border);\n *\n * Note that the default is retained in the property set and the `background` is\n * the desired `orange`. This leads us to a limitation.\n *\n * Limitation 1:\n\n * Only properties in the rule where the `@apply`\n * is used are considered as default values.\n * If another rule matches the element and sets `background` with\n * less specificity than the rule in which `@apply` appears,\n * the `background` will not be set.\n *\n * Limitation 2:\n *\n * When using Polymer's `updateStyles` api, new properties may not be set for\n * `@apply` properties.\n\n*/\n\n'use strict';\n\nimport {forEachRule, processVariableAndFallback, rulesForStyle, toCssText, gatherStyleText} from './style-util.js';\nimport {MIXIN_MATCH, VAR_ASSIGN} from './common-regex.js';\nimport {detectMixin} from './common-utils.js';\nimport {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\n\nconst APPLY_NAME_CLEAN = /;\\s*/m;\nconst INITIAL_INHERIT = /^\\s*(initial)|(inherit)\\s*$/;\nconst IMPORTANT = /\\s*!important/;\n\n// separator used between mixin-name and mixin-property-name when producing properties\n// NOTE: plain '-' may cause collisions in user styles\nconst MIXIN_VAR_SEP = '_-_';\n\n/**\n * @typedef {!Object<string, string>}\n */\nlet PropertyEntry; // eslint-disable-line no-unused-vars\n\n/**\n * @typedef {!Object<string, boolean>}\n */\nlet DependantsEntry; // eslint-disable-line no-unused-vars\n\n/** @typedef {{\n * properties: PropertyEntry,\n * dependants: DependantsEntry\n * }}\n */\nlet MixinMapEntry; // eslint-disable-line no-unused-vars\n\n// map of mixin to property names\n// --foo: {border: 2px} -> {properties: {(--foo, ['border'])}, dependants: {'element-name': proto}}\nclass MixinMap {\n constructor() {\n /** @type {!Object<string, !MixinMapEntry>} */\n this._map = {};\n }\n /**\n * @param {string} name\n * @param {!PropertyEntry} props\n */\n set(name, props) {\n name = name.trim();\n this._map[name] = {\n properties: props,\n dependants: {}\n }\n }\n /**\n * @param {string} name\n * @return {MixinMapEntry}\n */\n get(name) {\n name = name.trim();\n return this._map[name] || null;\n }\n}\n\n/**\n * Callback for when an element is marked invalid\n * @type {?function(string)}\n */\nlet invalidCallback = null;\n\n/** @unrestricted */\nclass ApplyShim {\n constructor() {\n /** @type {?string} */\n this._currentElement = null;\n /** @type {HTMLMetaElement} */\n this._measureElement = null;\n this._map = new MixinMap();\n }\n /**\n * return true if `cssText` contains a mixin definition or consumption\n * @param {string} cssText\n * @return {boolean}\n */\n detectMixin(cssText) {\n return detectMixin(cssText);\n }\n\n /**\n * Gather styles into one style for easier processing\n * @param {!HTMLTemplateElement} template\n * @return {HTMLStyleElement}\n */\n gatherStyles(template) {\n const styleText = gatherStyleText(template.content);\n if (styleText) {\n const style = /** @type {!HTMLStyleElement} */(document.createElement('style'));\n style.textContent = styleText;\n template.content.insertBefore(style, template.content.firstChild);\n return style;\n }\n return null;\n }\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @return {StyleNode}\n */\n transformTemplate(template, elementName) {\n if (template._gatheredStyle === undefined) {\n template._gatheredStyle = this.gatherStyles(template);\n }\n /** @type {HTMLStyleElement} */\n const style = template._gatheredStyle;\n return style ? this.transformStyle(style, elementName) : null;\n }\n /**\n * @param {!HTMLStyleElement} style\n * @param {string} elementName\n * @return {StyleNode}\n */\n transformStyle(style, elementName = '') {\n let ast = rulesForStyle(style);\n this.transformRules(ast, elementName);\n style.textContent = toCssText(ast);\n return ast;\n }\n /**\n * @param {!HTMLStyleElement} style\n * @return {StyleNode}\n */\n transformCustomStyle(style) {\n let ast = rulesForStyle(style);\n forEachRule(ast, (rule) => {\n if (rule['selector'] === ':root') {\n rule['selector'] = 'html';\n }\n this.transformRule(rule);\n })\n style.textContent = toCssText(ast);\n return ast;\n }\n /**\n * @param {StyleNode} rules\n * @param {string} elementName\n */\n transformRules(rules, elementName) {\n this._currentElement = elementName;\n forEachRule(rules, (r) => {\n this.transformRule(r);\n });\n this._currentElement = null;\n }\n /**\n * @param {!StyleNode} rule\n */\n transformRule(rule) {\n rule['cssText'] = this.transformCssText(rule['parsedCssText'], rule);\n // :root was only used for variable assignment in property shim,\n // but generates invalid selectors with real properties.\n // replace with `:host > *`, which serves the same effect\n if (rule['selector'] === ':root') {\n rule['selector'] = ':host > *';\n }\n }\n /**\n * @param {string} cssText\n * @param {!StyleNode} rule\n * @return {string}\n */\n transformCssText(cssText, rule) {\n // produce variables\n cssText = cssText.replace(VAR_ASSIGN, (matchText, propertyName, valueProperty, valueMixin) =>\n this._produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule));\n // consume mixins\n return this._consumeCssProperties(cssText, rule);\n }\n /**\n * @param {string} property\n * @return {string}\n */\n _getInitialValueForProperty(property) {\n if (!this._measureElement) {\n this._measureElement = /** @type {HTMLMetaElement} */(document.createElement('meta'));\n this._measureElement.setAttribute('apply-shim-measure', '');\n this._measureElement.style.all = 'initial';\n document.head.appendChild(this._measureElement);\n }\n return window.getComputedStyle(this._measureElement).getPropertyValue(property);\n }\n /**\n * Walk over all rules before this rule to find fallbacks for mixins\n *\n * @param {!StyleNode} startRule\n * @return {!Object}\n */\n _fallbacksFromPreviousRules(startRule) {\n // find the \"top\" rule\n let topRule = startRule;\n while (topRule['parent']) {\n topRule = topRule['parent'];\n }\n const fallbacks = {};\n let seenStartRule = false;\n forEachRule(topRule, (r) => {\n // stop when we hit the input rule\n seenStartRule = seenStartRule || r === startRule;\n if (seenStartRule) {\n return;\n }\n // NOTE: Only matching selectors are \"safe\" for this fallback processing\n // It would be prohibitive to run `matchesSelector()` on each selector,\n // so we cheat and only check if the same selector string is used, which\n // guarantees things like specificity matching\n if (r['selector'] === startRule['selector']) {\n Object.assign(fallbacks, this._cssTextToMap(r['parsedCssText']));\n }\n });\n return fallbacks;\n }\n /**\n * replace mixin consumption with variable consumption\n * @param {string} text\n * @param {!StyleNode=} rule\n * @return {string}\n */\n _consumeCssProperties(text, rule) {\n /** @type {Array} */\n let m = null;\n // loop over text until all mixins with defintions have been applied\n while((m = MIXIN_MATCH.exec(text))) {\n let matchText = m[0];\n let mixinName = m[1];\n let idx = m.index;\n // collect properties before apply to be \"defaults\" if mixin might override them\n // match includes a \"prefix\", so find the start and end positions of @apply\n let applyPos = idx + matchText.indexOf('@apply');\n let afterApplyPos = idx + matchText.length;\n // find props defined before this @apply\n let textBeforeApply = text.slice(0, applyPos);\n let textAfterApply = text.slice(afterApplyPos);\n let defaults = rule ? this._fallbacksFromPreviousRules(rule) : {};\n Object.assign(defaults, this._cssTextToMap(textBeforeApply));\n let replacement = this._atApplyToCssProperties(mixinName, defaults);\n // use regex match position to replace mixin, keep linear processing time\n text = `${textBeforeApply}${replacement}${textAfterApply}`;\n // move regex search to _after_ replacement\n MIXIN_MATCH.lastIndex = idx + replacement.length;\n }\n return text;\n }\n /**\n * produce variable consumption at the site of mixin consumption\n * `@apply` --foo; -> for all props (${propname}: var(--foo_-_${propname}, ${fallback[propname]}}))\n * Example:\n * border: var(--foo_-_border); padding: var(--foo_-_padding, 2px)\n *\n * @param {string} mixinName\n * @param {Object} fallbacks\n * @return {string}\n */\n _atApplyToCssProperties(mixinName, fallbacks) {\n mixinName = mixinName.replace(APPLY_NAME_CLEAN, '');\n let vars = [];\n let mixinEntry = this._map.get(mixinName);\n // if we depend on a mixin before it is created\n // make a sentinel entry in the map to add this element as a dependency for when it is defined.\n if (!mixinEntry) {\n this._map.set(mixinName, {});\n mixinEntry = this._map.get(mixinName);\n }\n if (mixinEntry) {\n if (this._currentElement) {\n mixinEntry.dependants[this._currentElement] = true;\n }\n let p, parts, f;\n const properties = mixinEntry.properties;\n for (p in properties) {\n f = fallbacks && fallbacks[p];\n parts = [p, ': var(', mixinName, MIXIN_VAR_SEP, p];\n if (f) {\n parts.push(',', f.replace(IMPORTANT, ''));\n }\n parts.push(')');\n if (IMPORTANT.test(properties[p])) {\n parts.push(' !important');\n }\n vars.push(parts.join(''));\n }\n }\n return vars.join('; ');\n }\n\n /**\n * @param {string} property\n * @param {string} value\n * @return {string}\n */\n _replaceInitialOrInherit(property, value) {\n let match = INITIAL_INHERIT.exec(value);\n if (match) {\n if (match[1]) {\n // initial\n // replace `initial` with the concrete initial value for this property\n value = this._getInitialValueForProperty(property);\n } else {\n // inherit\n // with this purposfully illegal value, the variable will be invalid at\n // compute time (https://www.w3.org/TR/css-variables/#invalid-at-computed-value-time)\n // and for inheriting values, will behave similarly\n // we cannot support the same behavior for non inheriting values like 'border'\n value = 'apply-shim-inherit';\n }\n }\n return value;\n }\n\n /**\n * \"parse\" a mixin definition into a map of properties and values\n * cssTextToMap('border: 2px solid black') -> ('border', '2px solid black')\n * @param {string} text\n * @param {boolean=} replaceInitialOrInherit\n * @return {!Object<string, string>}\n */\n _cssTextToMap(text, replaceInitialOrInherit = false) {\n let props = text.split(';');\n let property, value;\n let out = {};\n for (let i = 0, p, sp; i < props.length; i++) {\n p = props[i];\n if (p) {\n sp = p.split(':');\n // ignore lines that aren't definitions like @media\n if (sp.length > 1) {\n property = sp[0].trim();\n // some properties may have ':' in the value, like data urls\n value = sp.slice(1).join(':');\n if (replaceInitialOrInherit) {\n value = this._replaceInitialOrInherit(property, value);\n }\n out[property] = value;\n }\n }\n }\n return out;\n }\n\n /**\n * @param {MixinMapEntry} mixinEntry\n */\n _invalidateMixinEntry(mixinEntry) {\n if (!invalidCallback) {\n return;\n }\n for (let elementName in mixinEntry.dependants) {\n if (elementName !== this._currentElement) {\n invalidCallback(elementName);\n }\n }\n }\n\n /**\n * @param {string} matchText\n * @param {string} propertyName\n * @param {?string} valueProperty\n * @param {?string} valueMixin\n * @param {!StyleNode} rule\n * @return {string}\n */\n _produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule) {\n // handle case where property value is a mixin\n if (valueProperty) {\n // form: --mixin2: var(--mixin1), where --mixin1 is in the map\n processVariableAndFallback(valueProperty, (prefix, value) => {\n if (value && this._map.get(value)) {\n valueMixin = `@apply ${value};`\n }\n });\n }\n if (!valueMixin) {\n return matchText;\n }\n let mixinAsProperties = this._consumeCssProperties('' + valueMixin, rule);\n let prefix = matchText.slice(0, matchText.indexOf('--'));\n // `initial` and `inherit` as properties in a map should be replaced because\n // these keywords are eagerly evaluated when the mixin becomes CSS Custom Properties,\n // and would set the variable value, rather than carry the keyword to the `var()` usage.\n let mixinValues = this._cssTextToMap(mixinAsProperties, true);\n let combinedProps = mixinValues;\n let mixinEntry = this._map.get(propertyName);\n let oldProps = mixinEntry && mixinEntry.properties;\n if (oldProps) {\n // NOTE: since we use mixin, the map of properties is updated here\n // and this is what we want.\n combinedProps = Object.assign(Object.create(oldProps), mixinValues);\n } else {\n this._map.set(propertyName, combinedProps);\n }\n let out = [];\n let p, v;\n // set variables defined by current mixin\n let needToInvalidate = false;\n for (p in combinedProps) {\n v = mixinValues[p];\n // if property not defined by current mixin, set initial\n if (v === undefined) {\n v = 'initial';\n }\n if (oldProps && !(p in oldProps)) {\n needToInvalidate = true;\n }\n out.push(`${propertyName}${MIXIN_VAR_SEP}${p}: ${v}`);\n }\n if (needToInvalidate) {\n this._invalidateMixinEntry(mixinEntry);\n }\n if (mixinEntry) {\n mixinEntry.properties = combinedProps;\n }\n // because the mixinMap is global, the mixin might conflict with\n // a different scope's simple variable definition:\n // Example:\n // some style somewhere:\n // --mixin1:{ ... }\n // --mixin2: var(--mixin1);\n // some other element:\n // --mixin1: 10px solid red;\n // --foo: var(--mixin1);\n // In this case, we leave the original variable definition in place.\n if (valueProperty) {\n prefix = `${matchText};${prefix}`;\n }\n return `${prefix}${out.join('; ')};`;\n }\n}\n\n/* exports */\n/* eslint-disable no-self-assign */\nApplyShim.prototype['detectMixin'] = ApplyShim.prototype.detectMixin;\nApplyShim.prototype['transformStyle'] = ApplyShim.prototype.transformStyle;\nApplyShim.prototype['transformCustomStyle'] = ApplyShim.prototype.transformCustomStyle;\nApplyShim.prototype['transformRules'] = ApplyShim.prototype.transformRules;\nApplyShim.prototype['transformRule'] = ApplyShim.prototype.transformRule;\nApplyShim.prototype['transformTemplate'] = ApplyShim.prototype.transformTemplate;\nApplyShim.prototype['_separator'] = MIXIN_VAR_SEP;\n/* eslint-enable no-self-assign */\nObject.defineProperty(ApplyShim.prototype, 'invalidCallback', {\n /** @return {?function(string)} */\n get() {\n return invalidCallback;\n },\n /** @param {?function(string)} cb */\n set(cb) {\n invalidCallback = cb;\n }\n});\n\nexport default ApplyShim;\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport { MIXIN_MATCH, VAR_ASSIGN } from './common-regex.js';\n\n/**\n * @param {Element} element\n * @param {Object=} properties\n */\nexport function updateNativeProperties(element, properties) {\n // remove previous properties\n for (let p in properties) {\n // NOTE: for bc with shim, don't apply null values.\n if (p === null) {\n element.style.removeProperty(p);\n } else {\n element.style.setProperty(p, properties[p]);\n }\n }\n}\n\n/**\n * @param {Element} element\n * @param {string} property\n * @return {string}\n */\nexport function getComputedStyleValue(element, property) {\n /**\n * @const {string}\n */\n const value = window.getComputedStyle(element).getPropertyValue(property);\n if (!value) {\n return '';\n } else {\n return value.trim();\n }\n}\n\n/**\n * return true if `cssText` contains a mixin definition or consumption\n * @param {string} cssText\n * @return {boolean}\n */\nexport function detectMixin(cssText) {\n const has = MIXIN_MATCH.test(cssText) || VAR_ASSIGN.test(cssText);\n // reset state of the regexes\n MIXIN_MATCH.lastIndex = 0;\n VAR_ASSIGN.lastIndex = 0;\n return has;\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\n/**\n * @const {!Object<string, !HTMLTemplateElement>}\n */\nconst templateMap = {};\nexport default templateMap;\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\nimport templateMap from './template-map.js';\nimport {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\n\n/*\n * Utilities for handling invalidating apply-shim mixins for a given template.\n *\n * The invalidation strategy involves keeping track of the \"current\" version of a template's mixins, and updating that count when a mixin is invalidated.\n * The template\n */\n\n/** @const {string} */\nconst CURRENT_VERSION = '_applyShimCurrentVersion';\n\n/** @const {string} */\nconst NEXT_VERSION = '_applyShimNextVersion';\n\n/** @const {string} */\nconst VALIDATING_VERSION = '_applyShimValidatingVersion';\n\n/**\n * @const {Promise<void>}\n */\nconst promise = Promise.resolve();\n\n/**\n * @param {string} elementName\n */\nexport function invalidate(elementName){\n let template = templateMap[elementName];\n if (template) {\n invalidateTemplate(template);\n }\n}\n\n/**\n * This function can be called multiple times to mark a template invalid\n * and signal that the style inside must be regenerated.\n *\n * Use `startValidatingTemplate` to begin an asynchronous validation cycle.\n * During that cycle, call `templateIsValidating` to see if the template must\n * be revalidated\n * @param {HTMLTemplateElement} template\n */\nexport function invalidateTemplate(template) {\n // default the current version to 0\n template[CURRENT_VERSION] = template[CURRENT_VERSION] || 0;\n // ensure the \"validating for\" flag exists\n template[VALIDATING_VERSION] = template[VALIDATING_VERSION] || 0;\n // increment the next version\n template[NEXT_VERSION] = (template[NEXT_VERSION] || 0) + 1;\n}\n\n/**\n * @param {string} elementName\n * @return {boolean}\n */\nexport function isValid(elementName) {\n let template = templateMap[elementName];\n if (template) {\n return templateIsValid(template);\n }\n return true;\n}\n\n/**\n * @param {HTMLTemplateElement} template\n * @return {boolean}\n */\nexport function templateIsValid(template) {\n return template[CURRENT_VERSION] === template[NEXT_VERSION];\n}\n\n/**\n * @param {string} elementName\n * @return {boolean}\n */\nexport function isValidating(elementName) {\n let template = templateMap[elementName];\n if (template) {\n return templateIsValidating(template);\n }\n return false;\n}\n\n/**\n * Returns true if the template is currently invalid and `startValidating` has been called since the last invalidation.\n * If false, the template must be validated.\n * @param {HTMLTemplateElement} template\n * @return {boolean}\n */\nexport function templateIsValidating(template) {\n return !templateIsValid(template) && template[VALIDATING_VERSION] === template[NEXT_VERSION];\n}\n\n/**\n * the template is marked as `validating` for one microtask so that all instances\n * found in the tree crawl of `applyStyle` will update themselves,\n * but the template will only be updated once.\n * @param {string} elementName\n*/\nexport function startValidating(elementName) {\n let template = templateMap[elementName];\n startValidatingTemplate(template);\n}\n\n/**\n * Begin an asynchronous invalidation cycle.\n * This should be called after every validation of a template\n *\n * After one microtask, the template will be marked as valid until the next call to `invalidateTemplate`\n * @param {HTMLTemplateElement} template\n */\nexport function startValidatingTemplate(template) {\n // remember that the current \"next version\" is the reason for this validation cycle\n template[VALIDATING_VERSION] = template[NEXT_VERSION];\n // however, there only needs to be one async task to clear the counters\n if (!template._validating) {\n template._validating = true;\n promise.then(function() {\n // sync the current version to let future invalidations cause a refresh cycle\n template[CURRENT_VERSION] = template[NEXT_VERSION];\n template._validating = false;\n });\n }\n}\n\n/**\n * @return {boolean}\n */\nexport function elementsAreInvalid() {\n for (let elementName in templateMap) {\n let template = templateMap[elementName];\n if (!templateIsValid(template)) {\n return true;\n }\n }\n return false;\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport ApplyShim from '../src/apply-shim.js';\nimport templateMap from '../src/template-map.js';\nimport {getIsExtends, toCssText, elementHasBuiltCss} from '../src/style-util.js';\nimport * as ApplyShimUtils from '../src/apply-shim-utils.js';\nimport {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js';\nimport {CustomStyleInterfaceInterface} from '../src/custom-style-interface.js'; // eslint-disable-line no-unused-vars\nimport {nativeCssVariables, nativeShadow, cssBuild} from '../src/style-settings.js';\n\n/** @const {ApplyShim} */\nconst applyShim = new ApplyShim();\n\nclass ApplyShimInterface {\n constructor() {\n /** @type {?CustomStyleInterfaceInterface} */\n this.customStyleInterface = null;\n applyShim['invalidCallback'] = ApplyShimUtils.invalidate;\n }\n ensure() {\n if (this.customStyleInterface) {\n return;\n }\n if (window.ShadyCSS.CustomStyleInterface) {\n this.customStyleInterface =\n /** @type {!CustomStyleInterfaceInterface} */ (\n window.ShadyCSS.CustomStyleInterface);\n this.customStyleInterface['transformCallback'] = (style) => {\n applyShim.transformCustomStyle(style);\n };\n this.customStyleInterface['validateCallback'] = () => {\n requestAnimationFrame(() => {\n if (this.customStyleInterface['enqueued']) {\n this.flushCustomStyles();\n }\n });\n }\n }\n }\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n */\n prepareTemplate(template, elementName) {\n this.ensure();\n if (elementHasBuiltCss(template)) {\n return;\n }\n templateMap[elementName] = template;\n let ast = applyShim.transformTemplate(template, elementName);\n // save original style ast to use for revalidating instances\n template['_styleAst'] = ast;\n }\n flushCustomStyles() {\n this.ensure();\n if (!this.customStyleInterface) {\n return;\n }\n let styles = this.customStyleInterface['processStyles']();\n if (!this.customStyleInterface['enqueued']) {\n return;\n }\n for (let i = 0; i < styles.length; i++ ) {\n let cs = styles[i];\n let style = this.customStyleInterface['getStyleForCustomStyle'](cs);\n if (style) {\n applyShim.transformCustomStyle(style);\n }\n }\n this.customStyleInterface['enqueued'] = false;\n }\n /**\n * @param {HTMLElement} element\n * @param {Object=} properties\n */\n styleSubtree(element, properties) {\n this.ensure();\n if (properties) {\n updateNativeProperties(element, properties);\n }\n if (element.shadowRoot) {\n this.styleElement(element);\n let shadowChildren =\n /** @type {!ParentNode} */ (element.shadowRoot).children ||\n element.shadowRoot.childNodes;\n for (let i = 0; i < shadowChildren.length; i++) {\n this.styleSubtree(/** @type {HTMLElement} */(shadowChildren[i]));\n }\n } else {\n let children = element.children || element.childNodes;\n for (let i = 0; i < children.length; i++) {\n this.styleSubtree(/** @type {HTMLElement} */(children[i]));\n }\n }\n }\n /**\n * @param {HTMLElement} element\n */\n styleElement(element) {\n this.ensure();\n let {is} = getIsExtends(element);\n let template = templateMap[is];\n if (template && elementHasBuiltCss(template)) {\n return;\n }\n if (template && !ApplyShimUtils.templateIsValid(template)) {\n // only revalidate template once\n if (!ApplyShimUtils.templateIsValidating(template)) {\n this.prepareTemplate(template, is);\n ApplyShimUtils.startValidatingTemplate(template);\n }\n // update this element instance\n let root = element.shadowRoot;\n if (root) {\n let style = /** @type {HTMLStyleElement} */(root.querySelector('style'));\n if (style) {\n // reuse the template's style ast, it has all the original css text\n style['__cssRules'] = template['_styleAst'];\n style.textContent = toCssText(template['_styleAst'])\n }\n }\n }\n }\n /**\n * @param {Object=} properties\n */\n styleDocument(properties) {\n this.ensure();\n this.styleSubtree(document.body, properties);\n }\n}\n\nif (!window.ShadyCSS || !window.ShadyCSS.ScopingShim) {\n const applyShimInterface = new ApplyShimInterface();\n let CustomStyleInterface = window.ShadyCSS && window.ShadyCSS.CustomStyleInterface;\n\n /** @suppress {duplicate} */\n window.ShadyCSS = {\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} elementExtends\n */\n prepareTemplate(template, elementName, elementExtends) { // eslint-disable-line no-unused-vars\n applyShimInterface.flushCustomStyles();\n applyShimInterface.prepareTemplate(template, elementName);\n },\n\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} elementExtends\n */\n prepareTemplateStyles(template, elementName, elementExtends) {\n window.ShadyCSS.prepareTemplate(template, elementName, elementExtends);\n },\n\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n */\n prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars\n\n /**\n * @param {!HTMLElement} element\n * @param {Object=} properties\n */\n styleSubtree(element, properties) {\n applyShimInterface.flushCustomStyles();\n applyShimInterface.styleSubtree(element, properties);\n },\n\n /**\n * @param {!HTMLElement} element\n */\n styleElement(element) {\n applyShimInterface.flushCustomStyles();\n applyShimInterface.styleElement(element);\n },\n\n /**\n * @param {Object=} properties\n */\n styleDocument(properties) {\n applyShimInterface.flushCustomStyles();\n applyShimInterface.styleDocument(properties);\n },\n\n /**\n * @param {Element} element\n * @param {string} property\n * @return {string}\n */\n getComputedStyleValue(element, property) {\n return getComputedStyleValue(element, property);\n },\n\n flushCustomStyles() {\n applyShimInterface.flushCustomStyles();\n },\n\n nativeCss: nativeCssVariables,\n nativeShadow: nativeShadow,\n cssBuild: cssBuild\n };\n\n if (CustomStyleInterface) {\n window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;\n }\n}\n\nwindow.ShadyCSS.ApplyShim = applyShim;\n"]} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/custom-style-interface.html b/catapult/third_party/polymer/components/shadycss/custom-style-interface.html
new file mode 100644
index 00000000..a3919fad
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/custom-style-interface.html
@@ -0,0 +1,10 @@
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<script src="custom-style-interface.min.js"></script>
diff --git a/catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js b/catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js
new file mode 100644
index 00000000..2e7fd885
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js
@@ -0,0 +1,15 @@
+(function(){/*
+
+Copyright (c) 2017 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
+*/
+'use strict';var c=null,f=window.HTMLImports&&window.HTMLImports.whenReady||null,g;function h(a){requestAnimationFrame(function(){f?f(a):(c||(c=new Promise(function(a){g=a}),"complete"===document.readyState?g():document.addEventListener("readystatechange",function(){"complete"===document.readyState&&g()})),c.then(function(){a&&a()}))})};var k=null,l=null;function m(){this.customStyles=[];this.enqueued=!1;h(function(){window.ShadyCSS.flushCustomStyles&&window.ShadyCSS.flushCustomStyles()})}function n(a){!a.enqueued&&l&&(a.enqueued=!0,h(l))}m.prototype.c=function(a){a.__seenByShadyCSS||(a.__seenByShadyCSS=!0,this.customStyles.push(a),n(this))};m.prototype.b=function(a){if(a.__shadyCSSCachedStyle)return a.__shadyCSSCachedStyle;var b;a.getStyle?b=a.getStyle():b=a;return b};
+m.prototype.a=function(){for(var a=this.customStyles,b=0;b<a.length;b++){var d=a[b];if(!d.__shadyCSSCachedStyle){var e=this.b(d);e&&(e=e.__appliedElement||e,k&&k(e),d.__shadyCSSCachedStyle=e)}}return a};m.prototype.addCustomStyle=m.prototype.c;m.prototype.getStyleForCustomStyle=m.prototype.b;m.prototype.processStyles=m.prototype.a;
+Object.defineProperties(m.prototype,{transformCallback:{get:function(){return k},set:function(a){k=a}},validateCallback:{get:function(){return l},set:function(a){var b=!1;l||(b=!0);l=a;b&&n(this)}}});function p(a,b){for(var d in b)null===d?a.style.removeProperty(d):a.style.setProperty(d,b[d])};var q=!(window.ShadyDOM&&window.ShadyDOM.inUse),r;function t(a){r=a&&a.shimcssproperties?!1:q||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var u;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(u=window.ShadyCSS.cssBuild);window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?r=window.ShadyCSS.nativeCss:window.ShadyCSS?(t(window.ShadyCSS),window.ShadyCSS=void 0):t(window.WebComponents&&window.WebComponents.flags);
+var v=r,w=u;var x=new m;window.ShadyCSS||(window.ShadyCSS={prepareTemplate:function(){},prepareTemplateDom:function(){},prepareTemplateStyles:function(){},styleSubtree:function(a,b){x.a();p(a,b)},styleElement:function(){x.a()},styleDocument:function(a){x.a();p(document.body,a)},getComputedStyleValue:function(a,b){return(a=window.getComputedStyle(a).getPropertyValue(b))?a.trim():""},flushCustomStyles:function(){},nativeCss:v,nativeShadow:q,cssBuild:w});window.ShadyCSS.CustomStyleInterface=x;}).call(this);
+
+//# sourceMappingURL=custom-style-interface.min.js.map
diff --git a/catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js.map b/catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js.map
new file mode 100644
index 00000000..722c51ee
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/custom-style-interface.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["src/document-wait.js","src/custom-style-interface.js","src/common-utils.js","src/style-settings.js","entrypoints/custom-style-interface.js"],"names":["readyPromise","whenReady","window","resolveFn","documentWait","callback","requestAnimationFrame","Promise","resolve","document","readyState","addEventListener","then","transformFn","validateFn","constructor","CustomStyleInterface","enqueueDocumentValidation","addCustomStyle","style","push","getStyleForCustomStyle","customStyle","processStyles","cs","i","length","styleToTransform","prototype","Object","defineProperties","get","set","fn","needsEnqueue","updateNativeProperties","element","properties","p","removeProperty","setProperty","nativeShadow","nativeCssVariables_","calcCssVariables","settings","navigator","userAgent","match","CSS","supports","cssBuild","ShadyCSS","undefined","nativeCss","nativeCssVariables","module$src$style_settings.cssBuild","customStyleInterface","prepareTemplate","prepareTemplateDom","prepareTemplateStyles","styleSubtree","styleElement","styleDocument","body","getComputedStyleValue","property","value","getComputedStyle","getPropertyValue","trim","flushCustomStyles"],"mappings":"A;;;;;;;;;aAaA,IAAIA,EAAe,IAAnB,CAGIC,EAAYC,MAAA,YAAZD,EAAqCC,MAAA,YAAA,UAArCD,EAA2E,IAH/E,CAMIE,CAKWC,SAASA,EAAY,CAACC,CAAD,CAAW,CAC7CC,qBAAA,CAAsB,QAAQ,EAAG,CAC3BL,CAAJ,CACEA,CAAA,CAAUI,CAAV,CADF,EAGOL,CAYL,GAXEA,CACA,CADe,IAAIO,OAAJ,CAAY,QAAA,CAACC,CAAD,CAAa,CAACL,CAAA,CAAYK,CAAb,CAAzB,CACf,CAA4B,UAA5B,GAAIC,QAAAC,WAAJ,CACEP,CAAA,EADF,CAGEM,QAAAE,iBAAA,CAA0B,kBAA1B,CAA8C,QAAA,EAAM,CACtB,UAA5B,GAAIF,QAAAC,WAAJ,EACEP,CAAA,EAFgD,CAApD,CAOJ,EAAAH,CAAAY,KAAA,CAAkB,QAAQ,EAAE,CAAEP,CAAA,EAAYA,CAAA,EAAd,CAA5B,CAfF,CAD+B,CAAjC,CAD6C,C,CCD/C,IAAIQ,EAAc,IAAlB,CAGIC,EAAa,IAiBfC,SADmBC,EACR,EAAG,CAEZ,IAAA,aAAA,CAAuB,EACvB,KAAA,SAAA,CAAmB,CAAA,CAEnBZ,EAAA,CAAa,QAAA,EAAM,CACbF,MAAA,SAAA,kBAAJ,EACEA,MAAA,SAAA,kBAAA,EAFe,CAAnB,CALY,CAcde,QAAAA,EAAyBA,CAAzBA,CAAyBA,CAAGA,CACtBA,CAAAA,CAAAA,SAAJA,EAAyBH,CAAzBG,GAGAA,CAAAA,SACAA,CADmBA,CAAAA,CACnBA,CAAAb,CAAAa,CAAaH,CAAbG,CAJAA,CAD0BA,CAU5BC,CAAAA,UAAAA,EAAAA,CAAAA,QAAcA,CAACC,CAADD,CAAQA,CACfC,CAAAD,iBAALA,GACEC,CAAAD,iBAEAA,CAFqBA,CAAAA,CAErBA,CADAA,IAAAA,aAAAE,KAAAF,CAA0BC,CAA1BD,CACAA,CAAAD,CAAAC,CAAAA,IAAAA,CAHFA,CADoBA,CAWtBG,EAAAA,UAAAA,EAAAA,CAAAA,QAAsBA,CAACC,CAADD,CAAcA,CAClCA,GAAIC,CAAAD,sBAAJA,CACEA,MAAOC,EAAAD,sBAETA,KAAIF,CACAG,EAAAD,SAAJA,CACEF,CADFE,CACUC,CAAAD,SAAAA,EADVA,CAGEF,CAHFE,CAGUC,CAEVD,OAAOF,EAV2BE,CAepCE;CAAAA,UAAAA,EAAAA,CAAAA,QAAaA,EAAGA,CAEdA,IADAA,IAAMC,EAAKD,IAAAA,aAAXA,CACSE,EAAIF,CAAbA,CAAgBE,CAAhBF,CAAoBC,CAAAE,OAApBH,CAA+BE,CAAAF,EAA/BA,CAAoCA,CAClCA,IAAMD,EAAcE,CAAAD,CAAGE,CAAHF,CACpBA,IAAIA,CAAAD,CAAAC,sBAAJA,CAAAA,CAGAA,IAAMJ,EAAQI,IAAAF,EAAAE,CAA4BD,CAA5BC,CACVJ,EAAJI,GAGQI,CAINJ,CAJ0DJ,CAAAI,iBAI1DA,EAJuFJ,CAIvFI,CAHIV,CAGJU,EAFEV,CAAAU,CAAYI,CAAZJ,CAEFA,CAAAD,CAAAC,sBAAAA,CAA4BI,CAP9BJ,CAJAA,CAFkCA,CAgBpCA,MAAOC,EAlBOD,CAuBlBP,EAAAY,UAAA,eAAA,CAAmDZ,CAAAY,UAAAV,EACnDF,EAAAY,UAAA,uBAAA,CAA2DZ,CAAAY,UAAAP,EAC3DL,EAAAY,UAAA,cAAA,CAAkDZ,CAAAY,UAAAL,EAGlDM;MAAAC,iBAAA,CAAwBd,CAAAY,UAAxB,CAAwD,CACtD,kBAAqB,CAEnB,IAAAG,QAAG,EAAG,CACJ,MAAOlB,EADH,CAFa,CAMnB,IAAAmB,QAAG,CAACC,CAAD,CAAK,CACNpB,CAAA,CAAcoB,CADR,CANW,CADiC,CAWtD,iBAAoB,CAElB,IAAAF,QAAG,EAAG,CACJ,MAAOjB,EADH,CAFY,CASlB,IAAAkB,QAAG,CAACC,CAAD,CAAK,CACN,IAAIC,EAAe,CAAA,CACdpB,EAAL,GACEoB,CADF,CACiB,CAAA,CADjB,CAGApB,EAAA,CAAamB,CACTC,EAAJ,EACEjB,CAAA,CAAAA,IAAA,CAPI,CATU,CAXkC,CAAxD,C,CCvGOkB,QAASA,EAAsB,CAACC,CAAD,CAAUC,CAAV,CAAsB,CAE1D,IAAKC,IAAIA,CAAT,GAAcD,EAAd,CAEY,IAAV,GAAIC,CAAJ,CACEF,CAAAjB,MAAAoB,eAAA,CAA6BD,CAA7B,CADF,CAGEF,CAAAjB,MAAAqB,YAAA,CAA0BF,CAA1B,CAA6BD,CAAA,CAAWC,CAAX,CAA7B,CAPsD,C,CCNrD,IAAMG,EAAe,EAAEvC,MAAA,SAAF,EAAwBA,MAAA,SAAA,MAAxB,CAArB,CACHwC,CAKJC,SAASA,EAAgB,CAACC,CAAD,CAAW,CAEhCF,CAAA,CADEE,CAAJ,EAAgBA,CAAA,kBAAhB,CACwB,CAAA,CADxB,CASwBH,CATxB,EASwC,EAASI,SAAAC,UAAAC,MAAA,CAA0B,2BAA1B,CAAT,EACpCC,CAAA9C,MAAA8C,IADoC,EACtBC,CAAAD,GAAAC,SADsB,EACN,CAAAD,GAAAC,SAAA,CAAa,YAAb,CAA2B,kBAA3B,CADM,CAVN,CAgB7B,IAAIC,CACPhD,OAAAiD,SAAJ,EAAoDC,IAAAA,EAApD,GAAuBlD,MAAAiD,SAAAD,SAAvB,GACEA,CADF,CACahD,MAAAiD,SAAAD,SADb,CAIIhD,OAAAiD,SAAJ,EAAqDC,IAAAA,EAArD,GAAuBlD,MAAAiD,SAAAE,UAAvB,CACEX,CADF,CACwBxC,MAAAiD,SAAAE,UADxB,CAEWnD,MAAAiD,SAAJ,EACLR,CAAA,CAAiBzC,MAAAiD,SAAjB,CAEA,CAAAjD,MAAAiD,SAAA,CAAkBC,IAAAA,EAHb,EAKLT,CAAA,CAAiBzC,MAAA,cAAjB,EAA4CA,MAAA,cAAA,MAA5C,CAMK;IAAMoD,EAA4CZ,CAAlD,CAlBIa,EAAAL,C,CClBX,IAAMM,EAAuB,IAAIxC,CAE5Bd,OAAAiD,SAAL,GACEjD,MAAAiD,SADF,CACoB,CAMhB,gBAAAM,QAAe,EAAwC,EANvC,CAYhB,mBAAAC,QAAkB,EAAwB,EAZ1B,CAmBhB,sBAAAC,QAAqB,EAAwC,EAnB7C,CAyBhB,aAAAC,QAAY,CAACxB,CAAD,CAAUC,CAAV,CAAsB,CAChCmB,CAAAjC,EAAA,EACAY,EAAA,CAAuBC,CAAvB,CAAgCC,CAAhC,CAFgC,CAzBlB,CAiChB,aAAAwB,QAAY,EAAU,CACpBL,CAAAjC,EAAA,EADoB,CAjCN,CAwChB,cAAAuC,QAAa,CAACzB,CAAD,CAAa,CACxBmB,CAAAjC,EAAA,EACAY,EAAA,CAAuB1B,QAAAsD,KAAvB,CAAsC1B,CAAtC,CAFwB,CAxCV,CAkDhB,sBAAA2B,QAAqB,CAAC5B,CAAD,CAAU6B,CAAV,CAAoB,CACvC,MF9BJ,CADMC,CACN,CADchE,MAAAiE,iBAAA,CE+BmB/B,CF/BnB,CAAAgC,iBAAA,CE+B4BH,CF/B5B,CACd,EAGSC,CAAAG,KAAA,EAHT,CACS,EE4BkC,CAlDzB,CAsDhB,kBAAAC,QAAiB,EAAG,EAtDJ,CAuDhBjB,UAAWC,CAvDK,CAwDhBb,aAAcA,CAxDE,CAyDhBS,SAAUK,CAzDM,CADpB,CA8DArD,OAAAiD,SAAAnC,qBAAA,CAAuCwC","file":"custom-style-interface.min.js","sourcesContent":["/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\n/** @type {Promise<void>} */\nlet readyPromise = null;\n\n/** @type {?function(?function())} */\nlet whenReady = window['HTMLImports'] && window['HTMLImports']['whenReady'] || null;\n\n/** @type {function()} */\nlet resolveFn;\n\n/**\n * @param {?function()} callback\n */\nexport default function documentWait(callback) {\n requestAnimationFrame(function() {\n if (whenReady) {\n whenReady(callback)\n } else {\n if (!readyPromise) {\n readyPromise = new Promise((resolve) => {resolveFn = resolve});\n if (document.readyState === 'complete') {\n resolveFn();\n } else {\n document.addEventListener('readystatechange', () => {\n if (document.readyState === 'complete') {\n resolveFn();\n }\n });\n }\n }\n readyPromise.then(function(){ callback && callback(); });\n }\n });\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport documentWait from './document-wait.js';\n\n/**\n * @typedef {HTMLStyleElement | {getStyle: function():HTMLStyleElement}}\n */\nexport let CustomStyleProvider;\n\nconst SEEN_MARKER = '__seenByShadyCSS';\nconst CACHED_STYLE = '__shadyCSSCachedStyle';\n\n/** @type {?function(!HTMLStyleElement)} */\nlet transformFn = null;\n\n/** @type {?function()} */\nlet validateFn = null;\n\n/**\nThis interface is provided to add document-level <style> elements to ShadyCSS for processing.\nThese styles must be processed by ShadyCSS to simulate ShadowRoot upper-bound encapsulation from outside styles\nIn addition, these styles may also need to be processed for @apply rules and CSS Custom Properties\n\nTo add document-level styles to ShadyCSS, one can call `ShadyCSS.addDocumentStyle(styleElement)` or `ShadyCSS.addDocumentStyle({getStyle: () => styleElement})`\n\nIn addition, if the process used to discover document-level styles can be synchronously flushed, one should set `ShadyCSS.documentStyleFlush`.\nThis function will be called when calculating styles.\n\nAn example usage of the document-level styling api can be found in `examples/document-style-lib.js`\n\n@unrestricted\n*/\nexport default class CustomStyleInterface {\n constructor() {\n /** @type {!Array<!CustomStyleProvider>} */\n this['customStyles'] = [];\n this['enqueued'] = false;\n // NOTE(dfreedm): use quotes here to prevent closure inlining to `function(){}`;\n documentWait(() => {\n if (window['ShadyCSS']['flushCustomStyles']) {\n window['ShadyCSS']['flushCustomStyles']();\n }\n })\n }\n /**\n * Queue a validation for new custom styles to batch style recalculations\n */\n enqueueDocumentValidation() {\n if (this['enqueued'] || !validateFn) {\n return;\n }\n this['enqueued'] = true;\n documentWait(validateFn);\n }\n /**\n * @param {!HTMLStyleElement} style\n */\n addCustomStyle(style) {\n if (!style[SEEN_MARKER]) {\n style[SEEN_MARKER] = true;\n this['customStyles'].push(style);\n this.enqueueDocumentValidation();\n }\n }\n /**\n * @param {!CustomStyleProvider} customStyle\n * @return {HTMLStyleElement}\n */\n getStyleForCustomStyle(customStyle) {\n if (customStyle[CACHED_STYLE]) {\n return customStyle[CACHED_STYLE];\n }\n let style;\n if (customStyle['getStyle']) {\n style = customStyle['getStyle']();\n } else {\n style = customStyle;\n }\n return style;\n }\n /**\n * @return {!Array<!CustomStyleProvider>}\n */\n processStyles() {\n const cs = this['customStyles'];\n for (let i = 0; i < cs.length; i++) {\n const customStyle = cs[i];\n if (customStyle[CACHED_STYLE]) {\n continue;\n }\n const style = this.getStyleForCustomStyle(customStyle);\n if (style) {\n // HTMLImports polyfill may have cloned the style into the main document,\n // which is referenced with __appliedElement.\n const styleToTransform = /** @type {!HTMLStyleElement} */(style['__appliedElement'] || style);\n if (transformFn) {\n transformFn(styleToTransform);\n }\n customStyle[CACHED_STYLE] = styleToTransform;\n }\n }\n return cs;\n }\n}\n\n/* eslint-disable no-self-assign */\nCustomStyleInterface.prototype['addCustomStyle'] = CustomStyleInterface.prototype.addCustomStyle;\nCustomStyleInterface.prototype['getStyleForCustomStyle'] = CustomStyleInterface.prototype.getStyleForCustomStyle;\nCustomStyleInterface.prototype['processStyles'] = CustomStyleInterface.prototype.processStyles;\n/* eslint-enable no-self-assign */\n\nObject.defineProperties(CustomStyleInterface.prototype, {\n 'transformCallback': {\n /** @return {?function(!HTMLStyleElement)} */\n get() {\n return transformFn;\n },\n /** @param {?function(!HTMLStyleElement)} fn */\n set(fn) {\n transformFn = fn;\n }\n },\n 'validateCallback': {\n /** @return {?function()} */\n get() {\n return validateFn;\n },\n /**\n * @param {?function()} fn\n * @this {CustomStyleInterface}\n */\n set(fn) {\n let needsEnqueue = false;\n if (!validateFn) {\n needsEnqueue = true;\n }\n validateFn = fn;\n if (needsEnqueue) {\n this.enqueueDocumentValidation();\n }\n },\n }\n})\n\n/** @typedef {{\n * customStyles: !Array<!CustomStyleProvider>,\n * addCustomStyle: function(!CustomStyleProvider),\n * getStyleForCustomStyle: function(!CustomStyleProvider): HTMLStyleElement,\n * findStyles: function(),\n * transformCallback: ?function(!HTMLStyleElement),\n * validateCallback: ?function()\n * }}\n */\nexport const CustomStyleInterfaceInterface = {};\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport { MIXIN_MATCH, VAR_ASSIGN } from './common-regex.js';\n\n/**\n * @param {Element} element\n * @param {Object=} properties\n */\nexport function updateNativeProperties(element, properties) {\n // remove previous properties\n for (let p in properties) {\n // NOTE: for bc with shim, don't apply null values.\n if (p === null) {\n element.style.removeProperty(p);\n } else {\n element.style.setProperty(p, properties[p]);\n }\n }\n}\n\n/**\n * @param {Element} element\n * @param {string} property\n * @return {string}\n */\nexport function getComputedStyleValue(element, property) {\n /**\n * @const {string}\n */\n const value = window.getComputedStyle(element).getPropertyValue(property);\n if (!value) {\n return '';\n } else {\n return value.trim();\n }\n}\n\n/**\n * return true if `cssText` contains a mixin definition or consumption\n * @param {string} cssText\n * @return {boolean}\n */\nexport function detectMixin(cssText) {\n const has = MIXIN_MATCH.test(cssText) || VAR_ASSIGN.test(cssText);\n // reset state of the regexes\n MIXIN_MATCH.lastIndex = 0;\n VAR_ASSIGN.lastIndex = 0;\n return has;\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nexport const nativeShadow = !(window['ShadyDOM'] && window['ShadyDOM']['inUse']);\nlet nativeCssVariables_;\n\n/**\n * @param {(ShadyCSSOptions | ShadyCSSInterface)=} settings\n */\nfunction calcCssVariables(settings) {\n if (settings && settings['shimcssproperties']) {\n nativeCssVariables_ = false;\n } else {\n // chrome 49 has semi-working css vars, check if box-shadow works\n // safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782\n // However, shim css custom properties are only supported with ShadyDOM enabled,\n // so fall back on native if we do not detect ShadyDOM\n // Edge 15: custom properties used in ::before and ::after will also be used in the parent element\n // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12414257/\n nativeCssVariables_ = nativeShadow || Boolean(!navigator.userAgent.match(/AppleWebKit\\/601|Edge\\/15/) &&\n window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)'));\n }\n}\n\n/** @type {string | undefined} */\nexport let cssBuild;\nif (window.ShadyCSS && window.ShadyCSS.cssBuild !== undefined) {\n cssBuild = window.ShadyCSS.cssBuild;\n}\n\nif (window.ShadyCSS && window.ShadyCSS.nativeCss !== undefined) {\n nativeCssVariables_ = window.ShadyCSS.nativeCss;\n} else if (window.ShadyCSS) {\n calcCssVariables(window.ShadyCSS);\n // reset window variable to let ShadyCSS API take its place\n window.ShadyCSS = undefined;\n} else {\n calcCssVariables(window['WebComponents'] && window['WebComponents']['flags']);\n}\n\n// Hack for type error under new type inference which doesn't like that\n// nativeCssVariables is updated in a function and assigns the type\n// `function(): ?` instead of `boolean`.\nexport const nativeCssVariables = /** @type {boolean} */(nativeCssVariables_);","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport CustomStyleInterface from '../src/custom-style-interface.js';\nimport {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js';\nimport {nativeCssVariables, nativeShadow, cssBuild} from '../src/style-settings.js';\n\nconst customStyleInterface = new CustomStyleInterface();\n\nif (!window.ShadyCSS) {\n window.ShadyCSS = {\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} elementExtends\n */\n prepareTemplate(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars\n\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n */\n prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars\n\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} elementExtends\n */\n prepareTemplateStyles(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars\n\n /**\n * @param {Element} element\n * @param {Object=} properties\n */\n styleSubtree(element, properties) {\n customStyleInterface.processStyles();\n updateNativeProperties(element, properties);\n },\n\n /**\n * @param {Element} element\n */\n styleElement(element) { // eslint-disable-line no-unused-vars\n customStyleInterface.processStyles();\n },\n\n /**\n * @param {Object=} properties\n */\n styleDocument(properties) {\n customStyleInterface.processStyles();\n updateNativeProperties(document.body, properties);\n },\n\n /**\n * @param {Element} element\n * @param {string} property\n * @return {string}\n */\n getComputedStyleValue(element, property) {\n return getComputedStyleValue(element, property);\n },\n\n flushCustomStyles() {},\n nativeCss: nativeCssVariables,\n nativeShadow: nativeShadow,\n cssBuild: cssBuild\n }\n}\n\nwindow.ShadyCSS.CustomStyleInterface = customStyleInterface;"]} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/entrypoints/apply-shim.js b/catapult/third_party/polymer/components/shadycss/entrypoints/apply-shim.js
new file mode 100644
index 00000000..4921bec9
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/entrypoints/apply-shim.js
@@ -0,0 +1,222 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import ApplyShim from '../src/apply-shim.js';
+import templateMap from '../src/template-map.js';
+import {getIsExtends, toCssText, elementHasBuiltCss} from '../src/style-util.js';
+import * as ApplyShimUtils from '../src/apply-shim-utils.js';
+import {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js';
+import {CustomStyleInterfaceInterface} from '../src/custom-style-interface.js'; // eslint-disable-line no-unused-vars
+import {nativeCssVariables, nativeShadow, cssBuild} from '../src/style-settings.js';
+
+/** @const {ApplyShim} */
+const applyShim = new ApplyShim();
+
+class ApplyShimInterface {
+ constructor() {
+ /** @type {?CustomStyleInterfaceInterface} */
+ this.customStyleInterface = null;
+ applyShim['invalidCallback'] = ApplyShimUtils.invalidate;
+ }
+ ensure() {
+ if (this.customStyleInterface) {
+ return;
+ }
+ if (window.ShadyCSS.CustomStyleInterface) {
+ this.customStyleInterface =
+ /** @type {!CustomStyleInterfaceInterface} */ (
+ window.ShadyCSS.CustomStyleInterface);
+ this.customStyleInterface['transformCallback'] = (style) => {
+ applyShim.transformCustomStyle(style);
+ };
+ this.customStyleInterface['validateCallback'] = () => {
+ requestAnimationFrame(() => {
+ if (this.customStyleInterface['enqueued']) {
+ this.flushCustomStyles();
+ }
+ });
+ }
+ }
+ }
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ */
+ prepareTemplate(template, elementName) {
+ this.ensure();
+ if (elementHasBuiltCss(template)) {
+ return;
+ }
+ templateMap[elementName] = template;
+ let ast = applyShim.transformTemplate(template, elementName);
+ // save original style ast to use for revalidating instances
+ template['_styleAst'] = ast;
+ }
+ flushCustomStyles() {
+ this.ensure();
+ if (!this.customStyleInterface) {
+ return;
+ }
+ let styles = this.customStyleInterface['processStyles']();
+ if (!this.customStyleInterface['enqueued']) {
+ return;
+ }
+ for (let i = 0; i < styles.length; i++ ) {
+ let cs = styles[i];
+ let style = this.customStyleInterface['getStyleForCustomStyle'](cs);
+ if (style) {
+ applyShim.transformCustomStyle(style);
+ }
+ }
+ this.customStyleInterface['enqueued'] = false;
+ }
+ /**
+ * @param {HTMLElement} element
+ * @param {Object=} properties
+ */
+ styleSubtree(element, properties) {
+ this.ensure();
+ if (properties) {
+ updateNativeProperties(element, properties);
+ }
+ if (element.shadowRoot) {
+ this.styleElement(element);
+ let shadowChildren =
+ /** @type {!ParentNode} */ (element.shadowRoot).children ||
+ element.shadowRoot.childNodes;
+ for (let i = 0; i < shadowChildren.length; i++) {
+ this.styleSubtree(/** @type {HTMLElement} */(shadowChildren[i]));
+ }
+ } else {
+ let children = element.children || element.childNodes;
+ for (let i = 0; i < children.length; i++) {
+ this.styleSubtree(/** @type {HTMLElement} */(children[i]));
+ }
+ }
+ }
+ /**
+ * @param {HTMLElement} element
+ */
+ styleElement(element) {
+ this.ensure();
+ let {is} = getIsExtends(element);
+ let template = templateMap[is];
+ if (template && elementHasBuiltCss(template)) {
+ return;
+ }
+ if (template && !ApplyShimUtils.templateIsValid(template)) {
+ // only revalidate template once
+ if (!ApplyShimUtils.templateIsValidating(template)) {
+ this.prepareTemplate(template, is);
+ ApplyShimUtils.startValidatingTemplate(template);
+ }
+ // update this element instance
+ let root = element.shadowRoot;
+ if (root) {
+ let style = /** @type {HTMLStyleElement} */(root.querySelector('style'));
+ if (style) {
+ // reuse the template's style ast, it has all the original css text
+ style['__cssRules'] = template['_styleAst'];
+ style.textContent = toCssText(template['_styleAst'])
+ }
+ }
+ }
+ }
+ /**
+ * @param {Object=} properties
+ */
+ styleDocument(properties) {
+ this.ensure();
+ this.styleSubtree(document.body, properties);
+ }
+}
+
+if (!window.ShadyCSS || !window.ShadyCSS.ScopingShim) {
+ const applyShimInterface = new ApplyShimInterface();
+ let CustomStyleInterface = window.ShadyCSS && window.ShadyCSS.CustomStyleInterface;
+
+ /** @suppress {duplicate} */
+ window.ShadyCSS = {
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} elementExtends
+ */
+ prepareTemplate(template, elementName, elementExtends) { // eslint-disable-line no-unused-vars
+ applyShimInterface.flushCustomStyles();
+ applyShimInterface.prepareTemplate(template, elementName);
+ },
+
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} elementExtends
+ */
+ prepareTemplateStyles(template, elementName, elementExtends) {
+ window.ShadyCSS.prepareTemplate(template, elementName, elementExtends);
+ },
+
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ */
+ prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars
+
+ /**
+ * @param {!HTMLElement} element
+ * @param {Object=} properties
+ */
+ styleSubtree(element, properties) {
+ applyShimInterface.flushCustomStyles();
+ applyShimInterface.styleSubtree(element, properties);
+ },
+
+ /**
+ * @param {!HTMLElement} element
+ */
+ styleElement(element) {
+ applyShimInterface.flushCustomStyles();
+ applyShimInterface.styleElement(element);
+ },
+
+ /**
+ * @param {Object=} properties
+ */
+ styleDocument(properties) {
+ applyShimInterface.flushCustomStyles();
+ applyShimInterface.styleDocument(properties);
+ },
+
+ /**
+ * @param {Element} element
+ * @param {string} property
+ * @return {string}
+ */
+ getComputedStyleValue(element, property) {
+ return getComputedStyleValue(element, property);
+ },
+
+ flushCustomStyles() {
+ applyShimInterface.flushCustomStyles();
+ },
+
+ nativeCss: nativeCssVariables,
+ nativeShadow: nativeShadow,
+ cssBuild: cssBuild
+ };
+
+ if (CustomStyleInterface) {
+ window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;
+ }
+}
+
+window.ShadyCSS.ApplyShim = applyShim;
diff --git a/catapult/third_party/polymer/components/shadycss/entrypoints/custom-style-interface.js b/catapult/third_party/polymer/components/shadycss/entrypoints/custom-style-interface.js
new file mode 100644
index 00000000..6e9d96f6
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/entrypoints/custom-style-interface.js
@@ -0,0 +1,81 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import CustomStyleInterface from '../src/custom-style-interface.js';
+import {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js';
+import {nativeCssVariables, nativeShadow, cssBuild} from '../src/style-settings.js';
+
+const customStyleInterface = new CustomStyleInterface();
+
+if (!window.ShadyCSS) {
+ window.ShadyCSS = {
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} elementExtends
+ */
+ prepareTemplate(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars
+
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ */
+ prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars
+
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} elementExtends
+ */
+ prepareTemplateStyles(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars
+
+ /**
+ * @param {Element} element
+ * @param {Object=} properties
+ */
+ styleSubtree(element, properties) {
+ customStyleInterface.processStyles();
+ updateNativeProperties(element, properties);
+ },
+
+ /**
+ * @param {Element} element
+ */
+ styleElement(element) { // eslint-disable-line no-unused-vars
+ customStyleInterface.processStyles();
+ },
+
+ /**
+ * @param {Object=} properties
+ */
+ styleDocument(properties) {
+ customStyleInterface.processStyles();
+ updateNativeProperties(document.body, properties);
+ },
+
+ /**
+ * @param {Element} element
+ * @param {string} property
+ * @return {string}
+ */
+ getComputedStyleValue(element, property) {
+ return getComputedStyleValue(element, property);
+ },
+
+ flushCustomStyles() {},
+ nativeCss: nativeCssVariables,
+ nativeShadow: nativeShadow,
+ cssBuild: cssBuild
+ }
+}
+
+window.ShadyCSS.CustomStyleInterface = customStyleInterface; \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/entrypoints/scoping-shim.js b/catapult/third_party/polymer/components/shadycss/entrypoints/scoping-shim.js
new file mode 100644
index 00000000..8e35a17e
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/entrypoints/scoping-shim.js
@@ -0,0 +1,106 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import ScopingShim from '../src/scoping-shim.js';
+import {nativeCssVariables, nativeShadow, cssBuild} from '../src/style-settings.js';
+
+/** @const {ScopingShim} */
+const scopingShim = new ScopingShim();
+
+let ApplyShim, CustomStyleInterface;
+
+if (window['ShadyCSS']) {
+ ApplyShim = window['ShadyCSS']['ApplyShim'];
+ CustomStyleInterface = window['ShadyCSS']['CustomStyleInterface'];
+}
+
+window.ShadyCSS = {
+ ScopingShim: scopingShim,
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} elementExtends
+ */
+ prepareTemplate(template, elementName, elementExtends) {
+ scopingShim.flushCustomStyles();
+ scopingShim.prepareTemplate(template, elementName, elementExtends)
+ },
+
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ */
+ prepareTemplateDom(template, elementName) {
+ scopingShim.prepareTemplateDom(template, elementName);
+ },
+
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} elementExtends
+ */
+ prepareTemplateStyles(template, elementName, elementExtends) {
+ scopingShim.flushCustomStyles();
+ scopingShim.prepareTemplateStyles(template, elementName, elementExtends)
+ },
+ /**
+ * @param {!HTMLElement} element
+ * @param {Object=} properties
+ */
+ styleSubtree(element, properties) {
+ scopingShim.flushCustomStyles();
+ scopingShim.styleSubtree(element, properties);
+ },
+
+ /**
+ * @param {!HTMLElement} element
+ */
+ styleElement(element) {
+ scopingShim.flushCustomStyles();
+ scopingShim.styleElement(element);
+ },
+
+ /**
+ * @param {Object=} properties
+ */
+ styleDocument(properties) {
+ scopingShim.flushCustomStyles();
+ scopingShim.styleDocument(properties);
+ },
+
+ flushCustomStyles() {
+ scopingShim.flushCustomStyles();
+ },
+
+ /**
+ * @param {Element} element
+ * @param {string} property
+ * @return {string}
+ */
+ getComputedStyleValue(element, property) {
+ return scopingShim.getComputedStyleValue(element, property);
+ },
+
+ nativeCss: nativeCssVariables,
+
+ nativeShadow: nativeShadow,
+
+ cssBuild: cssBuild
+};
+
+if (ApplyShim) {
+ window.ShadyCSS.ApplyShim = ApplyShim;
+}
+
+if (CustomStyleInterface) {
+ window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/examples/custom-style-element.js b/catapult/third_party/polymer/components/shadycss/examples/custom-style-element.js
new file mode 100644
index 00000000..0542e8a1
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/examples/custom-style-element.js
@@ -0,0 +1,42 @@
+/**
+@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
+*/
+
+/*
+Wrapper over <style> elements to co-operate with ShadyCSS
+
+Example:
+<custom-style>
+ <style>
+ ...
+ </style>
+</custom-style>
+*/
+(function() {
+ 'use strict';
+
+ const CustomStyleInterface = window.ShadyCSS.CustomStyleInterface;
+
+ class CustomStyle extends HTMLElement {
+ constructor() {
+ super();
+ this._style = null;
+ CustomStyleInterface.addCustomStyle(this);
+ }
+ getStyle() {
+ if (!this._style) {
+ this._style = this.querySelector('style');
+ }
+ return this._style;
+ }
+ }
+
+ window.CustomStyle = CustomStyle;
+ window.customElements.define('custom-style', CustomStyle);
+})(); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/examples/document-style-lib.js b/catapult/third_party/polymer/components/shadycss/examples/document-style-lib.js
new file mode 100644
index 00000000..a1c032c0
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/examples/document-style-lib.js
@@ -0,0 +1,54 @@
+/**
+@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
+*/
+
+/*
+Example library for adding document-level styles to ShadyCSS
+
+After DOMContentLoaded, synchronously add all document document level styles.
+Then, start a MutationObserver for dynamically added styles.
+
+Caveat: ShadyCSS will add a `scope` attribute to styles it controls, so do not add those styles.
+*/
+(function() {
+ 'use strict';
+
+ const CustomStyleInterface = window.ShadyCSS.CustomStyleInterface;
+
+ function shouldAddDocumentStyle(n) {
+ return n.nodeType === Node.ELEMENT_NODE && n.localName === 'style' && !n.hasAttribute('scope');
+ }
+
+ function handler(mxns) {
+ for (let i = 0; i < mxns.length; i++) {
+ let mxn = mxns[i];
+ for (let j = 0; j < mxn.addedNodes.length; j++) {
+ let n = mxn.addedNodes[j];
+ if (shouldAddDocumentStyle(n)) {
+ CustomStyleInterface.addCustomStyle(n);
+ }
+ }
+ }
+ }
+
+ const observer = new MutationObserver(handler);
+
+ document.addEventListener('DOMContentLoaded', () => {
+ const candidates = document.querySelectorAll('custom-style');
+ for (let i = 0; i < candidates.length; i++) {
+ const candidate = candidates[i];
+ if (shouldAddDocumentStyle(candidate)) {
+ CustomStyleInterface.addCustomStyle(candidate);
+ }
+ }
+ observer.observe(document, {childList: true, subtree: true});
+ });
+
+ window.documentStyleFlush = () => {handler(observer.takeRecords())};
+})(); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/externs/shadycss-externs.js b/catapult/third_party/polymer/components/shadycss/externs/shadycss-externs.js
new file mode 100644
index 00000000..85b30589
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/externs/shadycss-externs.js
@@ -0,0 +1,64 @@
+/** @externs */
+
+/** @typedef {{
+ * styleElement: function(!HTMLElement),
+ * styleSubtree: function(!HTMLElement, Object<string, string>=),
+ * prepareTemplate: function(!HTMLTemplateElement, string, string=),
+ * prepareTemplateStyles: function(!HTMLTemplateElement, string, string=),
+ * prepareTemplateDom: function(!HTMLTemplateElement, string),
+ * styleDocument: function(Object<string, string>=),
+ * flushCustomStyles: function(),
+ * getComputedStyleValue: function(!Element, string): string,
+ * ScopingShim: (Object|undefined),
+ * ApplyShim: (Object|undefined),
+ * CustomStyleInterface: (Object|undefined),
+ * nativeCss: boolean,
+ * nativeShadow: boolean,
+ * cssBuild: (string | undefined),
+ * }}
+ */
+let ShadyCSSInterface; //eslint-disable-line no-unused-vars
+
+/**
+ * @typedef {{
+ * shimcssproperties: (boolean | undefined),
+ * shimshadow: (boolean | undefined),
+ * cssBuild: (string | undefined),
+ * }}
+ */
+let ShadyCSSOptions; //eslint-disable-line no-unused-vars
+
+/** @type {(ShadyCSSInterface | ShadyCSSOptions | undefined)} */
+window.ShadyCSS;
+
+/** @type {string|undefined} */
+Element.prototype.extends;
+
+/** @type {?Element|undefined} */
+Element.prototype._element;
+
+/** @type {string|undefined} */
+Element.prototype.__cssBuild;
+
+/** @type {boolean|undefined} */
+HTMLTemplateElement.prototype._validating;
+
+/** @type {boolean|undefined} */
+HTMLTemplateElement.prototype._prepared;
+
+/** @type {boolean|undefined} */
+HTMLTemplateElement.prototype._domPrepared;
+
+/** @type {?DocumentFragment|undefined} */
+HTMLTemplateElement.prototype._content;
+
+/** @type {?HTMLStyleElement|undefined} */
+HTMLTemplateElement.prototype._gatheredStyle;
+
+/** @type {?HTMLStyleElement|undefined} */
+HTMLTemplateElement.prototype._style;
+
+/**
+ * @type {string | undefined}
+ */
+DOMTokenList.prototype.value; \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/gulpfile.js b/catapult/third_party/polymer/components/shadycss/gulpfile.js
new file mode 100644
index 00000000..14517423
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/gulpfile.js
@@ -0,0 +1,107 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+
+'use strict';
+
+/* eslint-env node */
+/* eslint-disable no-console */
+
+const gulp = require('gulp');
+const sourcemaps = require('gulp-sourcemaps');
+const del = require('del');
+const rename = require('gulp-rename');
+const rollup = require('rollup-stream');
+const buffer = require('vinyl-buffer');
+const source = require('vinyl-source-stream');
+const closure = require('google-closure-compiler').gulp();
+const size = require('gulp-size');
+
+const modules = [
+ 'css-parse',
+ 'custom-style-element',
+ 'make-element',
+ 'svg-in-shadow',
+ 'style-util',
+ 'style-transformer',
+ 'style-settings'
+];
+
+const moduleTasks = modules.map((m) => {
+ gulp.task(`test-module-${m}`, () => {
+ return rollup({
+ entry: `tests/module/${m}.js`,
+ format: 'iife',
+ moduleName: m.replace(/-/g, '_')
+ })
+ .pipe(source(`${m}.js`, 'tests/module'))
+ .pipe(gulp.dest('./tests/module/generated'))
+ });
+ return `test-module-${m}`;
+});
+
+gulp.task('clean-test-modules', () => del(['tests/module/generated']));
+
+gulp.task('test-modules', gulp.series(['clean-test-modules', ...moduleTasks]));
+
+function closurify(entry) {
+ gulp.task(`closure-${entry}`, () => {
+ return gulp.src(['src/*.js', 'entrypoints/*.js'], {base: './'})
+ .pipe(sourcemaps.init())
+ .pipe(closure({
+ compilation_level: 'ADVANCED',
+ language_in: 'ES6_STRICT',
+ language_out: 'ES5_STRICT',
+ isolation_mode: 'IIFE',
+ assume_function_wrapper: true,
+ js_output_file: `${entry}.min.js`,
+ entry_point: `./entrypoints/${entry}.js`,
+ dependency_mode: 'STRICT',
+ warning_level: 'VERBOSE',
+ rewrite_polyfills: false,
+ externs: 'externs/shadycss-externs.js'
+ }))
+ .pipe(size({showFiles: true, showTotal: false, gzip: true}))
+ .pipe(sourcemaps.write('.'))
+ .pipe(gulp.dest('.'))
+ });
+ return `closure-${entry}`;
+}
+
+function debugify(entry) {
+ gulp.task(`debug-${entry}`, () => {
+ return rollup({
+ entry: `entrypoints/${entry}.js`,
+ format: 'iife',
+ moduleName: `${entry}`.replace(/-/g, '_'),
+ })
+ .pipe(source(`${entry}.js`, 'entrypoints'))
+ .pipe(buffer())
+ .pipe(sourcemaps.init({loadMaps: true}))
+ .pipe(rename(`${entry}.min.js`))
+ .pipe(size({showFiles: true, showTotal: false, gzip: true}))
+ .pipe(gulp.dest('./'))
+ });
+ return `debug-${entry}`;
+}
+
+const entrypoints = [
+ 'scoping-shim',
+ 'apply-shim',
+ 'custom-style-interface'
+]
+
+let closureTasks = entrypoints.map((e) => closurify(e));
+let debugTasks = entrypoints.map((e) => debugify(e));
+
+gulp.task('closure', gulp.series([...closureTasks]));
+
+gulp.task('default', gulp.series('closure', 'test-modules'));
+
+gulp.task('debug', gulp.series([...debugTasks]));
diff --git a/catapult/third_party/polymer/components/shadycss/package-lock.json b/catapult/third_party/polymer/components/shadycss/package-lock.json
new file mode 100644
index 00000000..bfdb2fe2
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/package-lock.json
@@ -0,0 +1,12144 @@
+{
+ "name": "@webcomponents/shadycss",
+ "version": "1.7.1",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
+ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.0.0"
+ }
+ },
+ "@babel/core": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.2.tgz",
+ "integrity": "sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/generator": "^7.1.2",
+ "@babel/helpers": "^7.1.2",
+ "@babel/parser": "^7.1.2",
+ "@babel/template": "^7.1.2",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.1.2",
+ "convert-source-map": "^1.1.0",
+ "debug": "^3.1.0",
+ "json5": "^0.5.0",
+ "lodash": "^4.17.10",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.3.tgz",
+ "integrity": "sha512-ZoCZGcfIJFJuZBqxcY9OjC1KW2lWK64qrX1o4UYL3yshVhwKFYgzpWZ0vvtGMNJdTlvkw0W+HR1VnYN8q3QPFQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.1.3",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.10",
+ "source-map": "^0.5.0",
+ "trim-right": "^1.0.1"
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz",
+ "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz",
+ "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-explode-assignable-expression": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-call-delegate": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz",
+ "integrity": "sha512-YEtYZrw3GUK6emQHKthltKNZwszBcHK58Ygcis+gVUrF4/FmTVr5CCqQNSfmvg2y+YDEANyYoaLz/SHsnusCwQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.0.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-define-map": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz",
+ "integrity": "sha512-yPPcW8dc3gZLN+U1mhYV91QU3n5uTbx7DUdf8NnPbjS0RMwBuHi9Xt2MUgppmNz7CJxTBWsGczTiEp1CSOTPRg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/types": "^7.0.0",
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/helper-explode-assignable-expression": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz",
+ "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz",
+ "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.0.0",
+ "@babel/template": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz",
+ "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0.tgz",
+ "integrity": "sha512-Ggv5sldXUeSKsuzLkddtyhyHe2YantsxWKNi7A+7LeD12ExRDWTRk29JCXpaHPAbMaIPZSil7n+lq78WY2VY7w==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz",
+ "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
+ "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz",
+ "integrity": "sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-simple-access": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.0.0",
+ "@babel/template": "^7.1.0",
+ "@babel/types": "^7.0.0",
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz",
+ "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz",
+ "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==",
+ "dev": true
+ },
+ "@babel/helper-regex": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.0.0.tgz",
+ "integrity": "sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz",
+ "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-wrap-function": "^7.1.0",
+ "@babel/template": "^7.1.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.1.0.tgz",
+ "integrity": "sha512-BvcDWYZRWVuDeXTYZWxekQNO5D4kO55aArwZOTFXw6rlLQA8ZaDicJR1sO47h+HrnCiDFiww0fSPV0d713KBGQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz",
+ "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz",
+ "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz",
+ "integrity": "sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/template": "^7.1.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.2.tgz",
+ "integrity": "sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.1.2",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.1.2"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
+ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.3.tgz",
+ "integrity": "sha512-gqmspPZOMW3MIRb9HlrnbZHXI1/KHTOroBwN1NcLL6pWxzqzEKGvRTq0W/PxS45OtQGbaFikSQpkS5zbnsQm2w==",
+ "dev": true
+ },
+ "@babel/plugin-external-helpers": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.0.0.tgz",
+ "integrity": "sha512-tZKTMdhZvTy0KCEX5EGQQm1RHr7jUa36q/yax1baEA0yZapVYmu10yW7LTqijITgSq416gPVjrcexiA6y4pJlA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz",
+ "integrity": "sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-remap-async-to-generator": "^7.1.0",
+ "@babel/plugin-syntax-async-generators": "^7.0.0"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz",
+ "integrity": "sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-async-generators": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz",
+ "integrity": "sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-dynamic-import": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.0.0.tgz",
+ "integrity": "sha512-Gt9xNyRrCHCiyX/ZxDGOcBnlJl0I3IWicpZRC4CdC0P5a/I07Ya2OAMEBU+J7GmRFVmIetqEYRko6QYRuKOESw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-import-meta": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.0.0.tgz",
+ "integrity": "sha512-FEoGvhXVAiWzpDjyZIlBGzKyNk/lnRPy7aPke3PjVkiAY0QFsvFfkjUg5diRwVfowBA8SJqvFt0ZoXNSjl70hQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz",
+ "integrity": "sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-arrow-functions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz",
+ "integrity": "sha512-2EZDBl1WIO/q4DIkIp4s86sdp4ZifL51MoIviLY/gG/mLSuOIEg7J8o6mhbxOTvUJkaN50n+8u41FVsr5KLy/w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz",
+ "integrity": "sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-remap-async-to-generator": "^7.1.0"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0.tgz",
+ "integrity": "sha512-AOBiyUp7vYTqz2Jibe1UaAWL0Hl9JUXEgjFvvvcSc9MVDItv46ViXFw2F7SVt1B5k+KWjl44eeXOAk3UDEaJjQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0.tgz",
+ "integrity": "sha512-GWEMCrmHQcYWISilUrk9GDqH4enf3UmhOEbNbNrlNAX1ssH3MsS1xLOS6rdjRVPgA7XXVPn87tRkdTEoA/dxEg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.1.0.tgz",
+ "integrity": "sha512-rNaqoD+4OCBZjM7VaskladgqnZ1LO6o2UxuWSDzljzW21pN1KXkB7BstAVweZdxQkHAujps5QMNOTWesBciKFg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-define-map": "^7.1.0",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.0.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0.tgz",
+ "integrity": "sha512-ubouZdChNAv4AAWAgU7QKbB93NU5sHwInEWfp+/OzJKA02E6Woh9RVoX4sZrbRwtybky/d7baTUqwFx+HgbvMA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.1.3.tgz",
+ "integrity": "sha512-Mb9M4DGIOspH1ExHOUnn2UUXFOyVTiX84fXCd+6B5iWrQg/QMeeRmSwpZ9lnjYLSXtZwiw80ytVMr3zue0ucYw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0.tgz",
+ "integrity": "sha512-w2vfPkMqRkdxx+C71ATLJG30PpwtTpW7DDdLqYt2acXU7YjztzeWW2Jk1T6hKqCLYCcEA5UQM/+xTAm+QCSnuQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz",
+ "integrity": "sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0.tgz",
+ "integrity": "sha512-TlxKecN20X2tt2UEr2LNE6aqA0oPeMT1Y3cgz8k4Dn1j5ObT8M3nl9aA37LLklx0PBZKETC9ZAf9n/6SujTuXA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.1.0.tgz",
+ "integrity": "sha512-VxOa1TMlFMtqPW2IDYZQaHsFrq/dDoIjgN098NowhexhZcz3UGlvPgZXuE1jEvNygyWyxRacqDpCZt+par1FNg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-instanceof": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-instanceof/-/plugin-transform-instanceof-7.0.0.tgz",
+ "integrity": "sha512-Dv6MtJZOjjGjnHlSwQVpYlwZBkPzaWX/1zoHUW82fmKmUNOp+XnYA1lCYCB+7RXkX8rBa6IuNZ9Y8u3MLJCxuQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0.tgz",
+ "integrity": "sha512-1NTDBWkeNXgpUcyoVFxbr9hS57EpZYXpje92zv0SUzjdu3enaRwF/l3cmyRnXLtIdyJASyiS6PtybK+CgKf7jA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.1.0.tgz",
+ "integrity": "sha512-wt8P+xQ85rrnGNr2x1iV3DW32W8zrB6ctuBkYBbf5/ZzJY99Ob4MFgsZDFgczNU76iy9PWsy4EuxOliDjdKw6A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.1.0.tgz",
+ "integrity": "sha512-/O02Je1CRTSk2SSJaq0xjwQ8hG4zhZGNjE8psTsSNPXyLRCODv7/PBozqT5AmQMzp7MI3ndvMhGdqp9c96tTEw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.1.0"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.1.0.tgz",
+ "integrity": "sha512-vHV7oxkEJ8IHxTfRr3hNGzV446GAb+0hgbA7o/0Jd76s+YzccdWuTU296FOCOl/xweU4t/Ya4g41yWz80RFCRw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-call-delegate": "^7.1.0",
+ "@babel/helper-get-function-arity": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz",
+ "integrity": "sha512-sj2qzsEx8KDVv1QuJc/dEfilkg3RRPvPYx/VnKLtItVQRWt1Wqf5eVCOLZm29CiGFfYYsA3VPjfizTCV0S0Dlw==",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "^0.13.3"
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz",
+ "integrity": "sha512-g/99LI4vm5iOf5r1Gdxq5Xmu91zvjhEG5+yZDJW268AZELAu4J1EiFLnkSG3yuUsZyOipVOVUKoGPYwfsTymhw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0.tgz",
+ "integrity": "sha512-L702YFy2EvirrR4shTj0g2xQp7aNwZoWNCkNu2mcoU0uyzMl0XRwDSwzB/xp6DSUFiBmEXuyAyEN16LsgVqGGQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0.tgz",
+ "integrity": "sha512-LFUToxiyS/WD+XEWpkx/XJBrUXKewSZpzX68s+yEOtIbdnsRjpryDw9U06gYc6klYEij/+KQVRnD3nz3AoKmjw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0.tgz",
+ "integrity": "sha512-vA6rkTCabRZu7Nbl9DfLZE1imj4tzdWcg5vtdQGvj+OH9itNNB6hxuRMHuIY8SGnEt1T9g5foqs9LnrHzsqEFg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0.tgz",
+ "integrity": "sha512-1r1X5DO78WnaAIvs5uC48t41LLckxsYklJrZjNKcevyz83sF2l4RHbw29qrCPr/6ksFsdfRpT/ZgxNWHXRnffg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0.tgz",
+ "integrity": "sha512-uJBrJhBOEa3D033P95nPHu3nbFwFE9ZgXsfEitzoIXIwqAZWk7uXcg06yFKXz9FSxBH5ucgU/cYdX0IV8ldHKw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.0.0",
+ "regexpu-core": "^4.1.3"
+ }
+ },
+ "@babel/template": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz",
+ "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/parser": "^7.1.2",
+ "@babel/types": "^7.1.2"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.4.tgz",
+ "integrity": "sha512-my9mdrAIGdDiSVBuMjpn/oXYpva0/EZwWL3sm3Wcy/AVWO2eXnsoZruOT9jOGNRXU8KbCIu5zsKnXcAJ6PcV6Q==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/generator": "^7.1.3",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.0.0",
+ "@babel/parser": "^7.1.3",
+ "@babel/types": "^7.1.3",
+ "debug": "^3.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/types": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz",
+ "integrity": "sha512-RpPOVfK+yatXyn8n4PB1NW6k9qjinrXrRR8ugBN8fD6hCy5RXI6PSbVqpOJBO9oSaY7Nom4ohj35feb0UR9hSA==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.10",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@gulp-sourcemaps/identity-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.1.tgz",
+ "integrity": "sha1-z6I7xYQPkQTOMqZedNt+epdLvuE=",
+ "dev": true,
+ "requires": {
+ "acorn": "^5.0.3",
+ "css": "^2.2.1",
+ "normalize-path": "^2.1.1",
+ "source-map": "^0.5.6",
+ "through2": "^2.0.3"
+ }
+ },
+ "@gulp-sourcemaps/map-sources": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz",
+ "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^2.0.1",
+ "through2": "^2.0.3"
+ }
+ },
+ "@polymer/esm-amd-loader": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@polymer/esm-amd-loader/-/esm-amd-loader-1.0.3.tgz",
+ "integrity": "sha512-j7fDIkNeR8cekqK2WRH5YV1VtIdPwqrgb5OzAN5QUIWEBkFj5OsxTvrkJiSaFgEMdKZoozs/vz+oq83Qehp4wA==",
+ "dev": true
+ },
+ "@polymer/polymer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@polymer/polymer/-/polymer-3.0.5.tgz",
+ "integrity": "sha512-Zbmhtr5vZ3NoHWwFYLKI4ff7yfE6DZopI8vaS7HvmUIuNqsv/EpEDXfNEYjqePQmkMX5LU9OIKV1eX/+9aveow==",
+ "dev": true,
+ "requires": {
+ "@webcomponents/shadycss": "^1.2.0"
+ }
+ },
+ "@polymer/sinonjs": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/@polymer/sinonjs/-/sinonjs-1.17.1.tgz",
+ "integrity": "sha512-/U8F/cOTrbF2iVVYgINYmvKbtbexs+89Q3v8AaHADRYabTg7aOZGOb0RyWpOI+sUJt04kj63U4FwMhzW5r4wZA==",
+ "dev": true
+ },
+ "@polymer/test-fixture": {
+ "version": "3.0.0-pre.21",
+ "resolved": "https://registry.npmjs.org/@polymer/test-fixture/-/test-fixture-3.0.0-pre.21.tgz",
+ "integrity": "sha512-IxzUe6YzaORzUksafHAXHprV29YncOJgr0+1zNAifl0/f+cb5iAd4IWUrnsnVFHG5UGTLjvis5RgV6vvIZPDrA==",
+ "dev": true
+ },
+ "@types/babel-generator": {
+ "version": "6.25.2",
+ "resolved": "https://registry.npmjs.org/@types/babel-generator/-/babel-generator-6.25.2.tgz",
+ "integrity": "sha512-W7PQkeDlYOqJblfNeqZARwj4W8nO+ZhQQZksU8+wbaKuHeUdIVUAdREO/Qb0FfNr3CY5Sq1gNtqsyFeZfS3iSw==",
+ "dev": true,
+ "requires": {
+ "@types/babel-types": "*"
+ }
+ },
+ "@types/babel-traverse": {
+ "version": "6.25.4",
+ "resolved": "https://registry.npmjs.org/@types/babel-traverse/-/babel-traverse-6.25.4.tgz",
+ "integrity": "sha512-+/670NaZE7qPvdh8EtGds32/2uHFKE5JeS+7ePH6nGwF8Wj8r671/RkTiJQP2k22nFntWEb9xQ11MFj7xEqI0g==",
+ "dev": true,
+ "requires": {
+ "@types/babel-types": "*"
+ }
+ },
+ "@types/babel-types": {
+ "version": "6.25.2",
+ "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-6.25.2.tgz",
+ "integrity": "sha512-+3bMuktcY4a70a0KZc8aPJlEOArPuAKQYHU5ErjkOqGJdx8xuEEVK6nWogqigBOJ8nKPxRpyCUDTCPmZ3bUxGA==",
+ "dev": true
+ },
+ "@types/babylon": {
+ "version": "6.16.3",
+ "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.3.tgz",
+ "integrity": "sha512-lyJ8sW1PbY3uwuvpOBZ9zMYKshMnQpXmeDHh8dj9j2nJm/xrW0FgB5gLSYOArj5X0IfaXnmhFoJnhS4KbqIMug==",
+ "dev": true,
+ "requires": {
+ "@types/babel-types": "*"
+ }
+ },
+ "@types/bluebird": {
+ "version": "3.5.24",
+ "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.24.tgz",
+ "integrity": "sha512-YeQoDpq4Lm8ppSBqAnAeF/xy1cYp/dMTif2JFcvmAbETMRlvKHT2iLcWu+WyYiJO3b3Ivokwo7EQca/xfLVJmg==",
+ "dev": true
+ },
+ "@types/body-parser": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz",
+ "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==",
+ "dev": true,
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/chai": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz",
+ "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==",
+ "dev": true
+ },
+ "@types/chai-subset": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.1.tgz",
+ "integrity": "sha512-Aof+FLfWzBPzDgJ2uuBuPNOBHVx9Siyw4vmOcsMgsuxX1nfUWSlzpq4pdvQiaBgGjGS7vP/Oft5dpJbX4krT1A==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "*"
+ }
+ },
+ "@types/chalk": {
+ "version": "0.4.31",
+ "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz",
+ "integrity": "sha1-ox10JBprHtu5c8822XooloNKUfk=",
+ "dev": true
+ },
+ "@types/clean-css": {
+ "version": "3.4.30",
+ "resolved": "http://registry.npmjs.org/@types/clean-css/-/clean-css-3.4.30.tgz",
+ "integrity": "sha1-AFLBNvUkgAJCjjY4s33ko5gYZB0=",
+ "dev": true
+ },
+ "@types/clone": {
+ "version": "0.1.30",
+ "resolved": "http://registry.npmjs.org/@types/clone/-/clone-0.1.30.tgz",
+ "integrity": "sha1-5zZWSMG0ITalnH1QQGN7O1yDthQ=",
+ "dev": true
+ },
+ "@types/compression": {
+ "version": "0.0.33",
+ "resolved": "http://registry.npmjs.org/@types/compression/-/compression-0.0.33.tgz",
+ "integrity": "sha1-ldxzOiM5qoRjgdfxN3eS0lU9wn0=",
+ "dev": true,
+ "requires": {
+ "@types/express": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.32",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz",
+ "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/content-type": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.2.tgz",
+ "integrity": "sha512-w2d7fBCYbCBUBTGtkC4JfX1FicTtgEmq7wTTjc7rC5RA/JdB1Bi7o88nKzUqAnIIBXJVmq0n4tTmF3PJN8QqCg==",
+ "dev": true
+ },
+ "@types/cssbeautify": {
+ "version": "0.3.1",
+ "resolved": "http://registry.npmjs.org/@types/cssbeautify/-/cssbeautify-0.3.1.tgz",
+ "integrity": "sha1-jgvuj33suVIlDaDK6+BeMFkcF+8=",
+ "dev": true
+ },
+ "@types/doctrine": {
+ "version": "0.0.1",
+ "resolved": "http://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.1.tgz",
+ "integrity": "sha1-uZny2fe0PKvgoaLzm8IDvH3K2p0=",
+ "dev": true
+ },
+ "@types/escape-html": {
+ "version": "0.0.20",
+ "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-0.0.20.tgz",
+ "integrity": "sha512-6dhZJLbA7aOwkYB2GDGdIqJ20wmHnkDzaxV9PJXe7O02I2dSFTERzRB6JrX6cWKaS+VqhhY7cQUMCbO5kloFUw==",
+ "dev": true
+ },
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "@types/events": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
+ "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==",
+ "dev": true
+ },
+ "@types/express": {
+ "version": "4.16.0",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz",
+ "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==",
+ "dev": true,
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.16.0",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz",
+ "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==",
+ "dev": true,
+ "requires": {
+ "@types/events": "*",
+ "@types/node": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/freeport": {
+ "version": "1.0.21",
+ "resolved": "https://registry.npmjs.org/@types/freeport/-/freeport-1.0.21.tgz",
+ "integrity": "sha1-c/ZUPtZ9PKP/+XuYVZFZi3CSBm8=",
+ "dev": true,
+ "optional": true
+ },
+ "@types/glob": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
+ "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
+ "dev": true,
+ "requires": {
+ "@types/events": "*",
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/glob-stream": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-6.1.0.tgz",
+ "integrity": "sha512-RHv6ZQjcTncXo3thYZrsbAVwoy4vSKosSWhuhuQxLOTv74OJuFQxXkmUuZCr3q9uNBEVCvIzmZL/FeRNbHZGUg==",
+ "dev": true,
+ "requires": {
+ "@types/glob": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/gulp-if": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/@types/gulp-if/-/gulp-if-0.0.33.tgz",
+ "integrity": "sha512-J5lzff21X7r1x/4hSzn02GgIUEyjCqYIXZ9GgGBLhbsD3RiBdqwnkFWgF16/0jO5rcVZ52Zp+6MQMQdvIsWuKg==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/vinyl": "*"
+ }
+ },
+ "@types/html-minifier": {
+ "version": "3.5.2",
+ "resolved": "http://registry.npmjs.org/@types/html-minifier/-/html-minifier-3.5.2.tgz",
+ "integrity": "sha512-yikK28/KlVyf8g9i/k+TDFlteLuZ6QQTUdVqvKtzEB+8DSLCTjxfh6IK45KnW4rYFI3Y8T4LWpYJMTmfJleWaQ==",
+ "dev": true,
+ "requires": {
+ "@types/clean-css": "*",
+ "@types/relateurl": "*",
+ "@types/uglify-js": "*"
+ }
+ },
+ "@types/is-windows": {
+ "version": "0.2.0",
+ "resolved": "http://registry.npmjs.org/@types/is-windows/-/is-windows-0.2.0.tgz",
+ "integrity": "sha1-byTuSHMdMRaOpRBhDW3RXl/Jxv8=",
+ "dev": true
+ },
+ "@types/launchpad": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@types/launchpad/-/launchpad-0.6.0.tgz",
+ "integrity": "sha1-NylhCbfyd/bmxf1+DAcGvJGPu1E=",
+ "dev": true,
+ "optional": true
+ },
+ "@types/mime": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz",
+ "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==",
+ "dev": true
+ },
+ "@types/minimatch": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
+ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
+ "dev": true
+ },
+ "@types/mz": {
+ "version": "0.0.29",
+ "resolved": "http://registry.npmjs.org/@types/mz/-/mz-0.0.29.tgz",
+ "integrity": "sha1-vCRyjGSZc/HHhR6QM/nOUlZowns=",
+ "dev": true,
+ "requires": {
+ "@types/bluebird": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/node": {
+ "version": "10.12.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.1.tgz",
+ "integrity": "sha512-i1sl+WCX2OCHeUi9oi7PiCNUtYFrpWhpcx878vpeq/tlZTKzcFdHePlyFHVbWqeuKN0SRPl/9ZFDSTsfv9h7VQ==",
+ "dev": true
+ },
+ "@types/opn": {
+ "version": "3.0.28",
+ "resolved": "http://registry.npmjs.org/@types/opn/-/opn-3.0.28.tgz",
+ "integrity": "sha1-CX0NHJtXSVc6XZbfEyOHu20CEYo=",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/parse5": {
+ "version": "2.2.34",
+ "resolved": "http://registry.npmjs.org/@types/parse5/-/parse5-2.2.34.tgz",
+ "integrity": "sha1-44cKEOgnNacg9i1x3NGDunjvOp0=",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/path-is-inside": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npmjs.org/@types/path-is-inside/-/path-is-inside-1.0.0.tgz",
+ "integrity": "sha512-hfnXRGugz+McgX2jxyy5qz9sB21LRzlGn24zlwN2KEgoPtEvjzNRrLtUkOOebPDPZl3Rq7ywKxYvylVcEZDnEw==",
+ "dev": true
+ },
+ "@types/pem": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/@types/pem/-/pem-1.9.3.tgz",
+ "integrity": "sha512-+hHbGi9PAyHVeRdMJN6yNuMWoshJ+7oTqYuhBB1/vHq0Tfu46ucbvgxmhwBfe0GCiJZvCa20VHhHsA0mY5W6hQ==",
+ "dev": true
+ },
+ "@types/range-parser": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz",
+ "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==",
+ "dev": true
+ },
+ "@types/relateurl": {
+ "version": "0.2.28",
+ "resolved": "http://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz",
+ "integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y=",
+ "dev": true
+ },
+ "@types/resolve": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.6.tgz",
+ "integrity": "sha512-g+Rg8uMWY76oYTyaL+m7ZcblqF/oj7pE6uEUyACluJx4zcop1Lk14qQiocdEkEVMDFm6DmKpxJhsER+ZuTwG3g==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/serve-static": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz",
+ "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==",
+ "dev": true,
+ "requires": {
+ "@types/express-serve-static-core": "*",
+ "@types/mime": "*"
+ }
+ },
+ "@types/spdy": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/@types/spdy/-/spdy-3.4.4.tgz",
+ "integrity": "sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/ua-parser-js": {
+ "version": "0.7.32",
+ "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
+ "integrity": "sha512-+z7Q72Mlnq6SFkQYHzLg2Z70pIsgRVzgx1b5PV8eUv5uaZ/zoqIs45XnhtToW4gTeX4FbjIP49nhIjyvPF4rPg==",
+ "dev": true
+ },
+ "@types/uglify-js": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz",
+ "integrity": "sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "@types/uuid": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz",
+ "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/vinyl": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.2.tgz",
+ "integrity": "sha512-2iYpNuOl98SrLPBZfEN9Mh2JCJ2EI9HU35SfgBEb51DcmaHkhp8cKMblYeBqMQiwXMgAD3W60DbQ4i/UdLiXhw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/vinyl-fs": {
+ "version": "2.4.9",
+ "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.9.tgz",
+ "integrity": "sha512-Q0EXd6c1fORjiOuK4ZaKdfFcMyFzJlTi56dqktwaWVLIDAzE49wUs3bKnYbZwzyMWoH+NcMWnRuR73S9A0jnRA==",
+ "dev": true,
+ "requires": {
+ "@types/events": "*",
+ "@types/glob-stream": "*",
+ "@types/node": "*",
+ "@types/vinyl": "*"
+ }
+ },
+ "@types/whatwg-url": {
+ "version": "6.4.0",
+ "resolved": "http://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-6.4.0.tgz",
+ "integrity": "sha512-tonhlcbQ2eho09am6RHnHOgvtDfDYINd5rgxD+2YSkKENooVCFsWizJz139MQW/PV8FfClyKrNe9ZbdHrSCxGg==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@types/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-ZrJDWpvg75LTGX4XwuneY9s6bF3OeZcGTpoGh3zDV9ytzcHMFsRrMIaLBRJZQMBoGyKs6unBQfVdrLZiYfb1zQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@types/winston": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/@types/winston/-/winston-2.4.4.tgz",
+ "integrity": "sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw==",
+ "dev": true,
+ "requires": {
+ "winston": "*"
+ }
+ },
+ "@webcomponents/custom-elements": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.2.1.tgz",
+ "integrity": "sha512-flmTp4rVbBkcUIF3eBO3LNoAaYvleTdhPZKzdzr6iztWLLrxCctcK+7MAQeC3/SPjc3JDdC3jYLMRF4R6C3f9g==",
+ "dev": true
+ },
+ "@webcomponents/html-imports": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@webcomponents/html-imports/-/html-imports-1.2.0.tgz",
+ "integrity": "sha512-EIy8RqSfsoJ5Tk2wsfaSX4IKhXg8oYCq/xVEz8LEELXhv5tup/iZ/SInoz8SLtZGFP+vjUh2oRsgkUMhbrr0GQ==",
+ "dev": true
+ },
+ "@webcomponents/shadycss": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.5.2.tgz",
+ "integrity": "sha512-0OyrmVc7S+INtzoqP2ofAo+OdVn2Nj0Qvq4wD9FEGN7nMmLRxaD2mzy6hD6EslzxUSuGH302CDU4KXiY66SEqg==",
+ "dev": true
+ },
+ "@webcomponents/shadydom": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@webcomponents/shadydom/-/shadydom-1.1.2.tgz",
+ "integrity": "sha512-au9UMKTgs881UITNZfo6RqoIh4q7K3k/CeXAwcK6qbsb/0NF52X4SWVG/Yeb95hbTrDvH0IlVp6kaGJlpn4P3g==",
+ "dev": true
+ },
+ "@webcomponents/template": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@webcomponents/template/-/template-1.4.0.tgz",
+ "integrity": "sha512-HJfhAxCD+DZmtm8oCALtvyOL9JlisSDqwE/4FWfaxq4SK3gUIp/2eUjLE6zqt9n6VHeo1zQjMTOA4fKKF6qSQg==",
+ "dev": true
+ },
+ "@webcomponents/webcomponents-platform": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@webcomponents/webcomponents-platform/-/webcomponents-platform-1.0.0.tgz",
+ "integrity": "sha1-iHkUY4DdvsuGF+MiSX1Z53w/9Gg=",
+ "dev": true
+ },
+ "@webcomponents/webcomponentsjs": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-1.2.7.tgz",
+ "integrity": "sha512-011DyXjpQoZ7f6oMCpYTYgrzsWJ7+0fEbt6Y8KcfZZa3ZdJ/ttoMgeH75SqHDe7aNdolfMhCvrSNNgh9wcsgpA==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
+ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.18",
+ "negotiator": "0.6.1"
+ }
+ },
+ "accessibility-developer-tools": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/accessibility-developer-tools/-/accessibility-developer-tools-2.12.0.tgz",
+ "integrity": "sha1-PaDM6dbsY3OWS4TzXbfPw996tRQ=",
+ "dev": true
+ },
+ "acorn": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
+ "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.0.tgz",
+ "integrity": "sha512-XkB50fn0MURDyww9+UYL3c1yLbOBz0ZFvrdYlGB8l+Ije1oSC75qAqrzSPjYQbdnQUzhlUGNKuesryAv0gxZOg==",
+ "dev": true
+ },
+ "adm-zip": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz",
+ "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==",
+ "dev": true,
+ "optional": true
+ },
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+ "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ },
+ "dependencies": {
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ }
+ }
+ },
+ "ajv": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz",
+ "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+ "dev": true
+ },
+ "ansi-align": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
+ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.0.0"
+ }
+ },
+ "ansi-colors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "^0.1.0"
+ }
+ },
+ "ansi-cyan": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
+ "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-escapes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
+ "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
+ "dev": true
+ },
+ "ansi-gray": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+ "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-red": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
+ "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "ansi-wrap": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
+ "dev": true
+ },
+ "any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
+ "dev": true
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "append-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz",
+ "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=",
+ "dev": true,
+ "requires": {
+ "buffer-equal": "^1.0.0"
+ }
+ },
+ "append-field": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+ "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=",
+ "dev": true
+ },
+ "archiver": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-2.1.1.tgz",
+ "integrity": "sha1-/2YrSnggFJSj7lRNOjP+dJZQnrw=",
+ "dev": true,
+ "requires": {
+ "archiver-utils": "^1.3.0",
+ "async": "^2.0.0",
+ "buffer-crc32": "^0.2.1",
+ "glob": "^7.0.0",
+ "lodash": "^4.8.0",
+ "readable-stream": "^2.0.0",
+ "tar-stream": "^1.5.0",
+ "zip-stream": "^1.2.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ }
+ }
+ },
+ "archiver-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz",
+ "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.0",
+ "graceful-fs": "^4.1.0",
+ "lazystream": "^1.0.0",
+ "lodash": "^4.8.0",
+ "normalize-path": "^2.0.0",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "archy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "argv-tools": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/argv-tools/-/argv-tools-0.1.1.tgz",
+ "integrity": "sha512-Cc0dBvx4dvrjjKpyDA6w8RlNAw8Su30NvZbWl/Tv9ZALEVlLVkWQiHMi84Q0xNfpVuSaiQbYkdmWK8g1PLGhKw==",
+ "dev": true,
+ "requires": {
+ "array-back": "^2.0.0",
+ "find-replace": "^2.0.1"
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-filter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz",
+ "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=",
+ "dev": true,
+ "requires": {
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-map": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
+ "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=",
+ "dev": true,
+ "requires": {
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-back": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
+ "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==",
+ "dev": true,
+ "requires": {
+ "typical": "^2.6.1"
+ }
+ },
+ "array-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
+ "dev": true
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+ "dev": true
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+ "dev": true
+ },
+ "array-initial": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
+ "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=",
+ "dev": true,
+ "requires": {
+ "array-slice": "^1.0.0",
+ "is-number": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true
+ }
+ }
+ },
+ "array-last": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz",
+ "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==",
+ "dev": true,
+ "requires": {
+ "is-number": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true
+ }
+ }
+ },
+ "array-slice": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+ "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+ "dev": true
+ },
+ "array-sort": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz",
+ "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==",
+ "dev": true,
+ "requires": {
+ "default-compare": "^1.0.0",
+ "get-value": "^2.0.6",
+ "kind-of": "^5.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
+ },
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "async-done": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz",
+ "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.2",
+ "process-nextick-args": "^1.0.7",
+ "stream-exhaust": "^1.0.1"
+ },
+ "dependencies": {
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+ "dev": true
+ }
+ }
+ },
+ "async-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
+ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+ "dev": true
+ },
+ "async-settle": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
+ "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=",
+ "dev": true,
+ "requires": {
+ "async-done": "^1.2.2"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz",
+ "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+ "dev": true
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "babel-generator": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
+ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+ "dev": true,
+ "requires": {
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "detect-indent": "^4.0.0",
+ "jsesc": "^1.3.0",
+ "lodash": "^4.17.4",
+ "source-map": "^0.5.7",
+ "trim-right": "^1.0.1"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+ "dev": true
+ }
+ }
+ },
+ "babel-helper-evaluate-path": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.4.3.tgz",
+ "integrity": "sha1-ComvcCwGshcCf6NxkI3UmJ0+Yz8=",
+ "dev": true
+ },
+ "babel-helper-flip-expressions": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz",
+ "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=",
+ "dev": true
+ },
+ "babel-helper-is-nodes-equiv": {
+ "version": "0.0.1",
+ "resolved": "http://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz",
+ "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=",
+ "dev": true
+ },
+ "babel-helper-is-void-0": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz",
+ "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=",
+ "dev": true
+ },
+ "babel-helper-mark-eval-scopes": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz",
+ "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=",
+ "dev": true
+ },
+ "babel-helper-remove-or-void": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz",
+ "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=",
+ "dev": true
+ },
+ "babel-helper-to-multiple-sequence-expressions": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.4.3.tgz",
+ "integrity": "sha1-W1GLESf0ezA4dzOGoVYaK0jmMrY=",
+ "dev": true
+ },
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-minify-builtins": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.4.3.tgz",
+ "integrity": "sha1-nqPVn0rEp7uVjXEtKVVqH4b3+B4=",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-constant-folding": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.4.3.tgz",
+ "integrity": "sha1-MA+d6N2ghEoXaxk2U5YOJK0z4ZE=",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-dead-code-elimination": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.4.3.tgz",
+ "integrity": "sha1-c2KCZYZPkAjQAnUG9Yq+s8HQLZg=",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.4.3",
+ "babel-helper-mark-eval-scopes": "^0.4.3",
+ "babel-helper-remove-or-void": "^0.4.3",
+ "lodash.some": "^4.6.0"
+ }
+ },
+ "babel-plugin-minify-flip-comparisons": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz",
+ "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=",
+ "dev": true,
+ "requires": {
+ "babel-helper-is-void-0": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-guarded-expressions": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.1.tgz",
+ "integrity": "sha1-ylpZoGvBwi3Vz9mWpnUWOm9hm30=",
+ "dev": true,
+ "requires": {
+ "babel-helper-flip-expressions": "^0.4.1"
+ }
+ },
+ "babel-plugin-minify-infinity": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz",
+ "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=",
+ "dev": true
+ },
+ "babel-plugin-minify-mangle-names": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.4.3.tgz",
+ "integrity": "sha1-FvG/90t6fJPfwkHngx3V+0sCPvc=",
+ "dev": true,
+ "requires": {
+ "babel-helper-mark-eval-scopes": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-numeric-literals": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz",
+ "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=",
+ "dev": true
+ },
+ "babel-plugin-minify-replace": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.4.3.tgz",
+ "integrity": "sha1-nSifS6FdTmAR6HmfpfG6d+yBIZ0=",
+ "dev": true
+ },
+ "babel-plugin-minify-simplify": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.4.3.tgz",
+ "integrity": "sha1-N3VthcYURktLCSfytOQXGR1Vc4o=",
+ "dev": true,
+ "requires": {
+ "babel-helper-flip-expressions": "^0.4.3",
+ "babel-helper-is-nodes-equiv": "^0.0.1",
+ "babel-helper-to-multiple-sequence-expressions": "^0.4.3"
+ }
+ },
+ "babel-plugin-minify-type-constructors": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz",
+ "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=",
+ "dev": true,
+ "requires": {
+ "babel-helper-is-void-0": "^0.4.3"
+ }
+ },
+ "babel-plugin-transform-inline-consecutive-adds": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz",
+ "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=",
+ "dev": true
+ },
+ "babel-plugin-transform-member-expression-literals": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-Jy69Ki1DQbhsJNzYQ3SuWqNwKHQ=",
+ "dev": true
+ },
+ "babel-plugin-transform-merge-sibling-variables": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-SKMw0oKT4xjQcXXCYMdIWec5i0M=",
+ "dev": true
+ },
+ "babel-plugin-transform-minify-booleans": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-He72nCITUDipHeH10T11njRkxDw=",
+ "dev": true
+ },
+ "babel-plugin-transform-property-literals": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-NxJ6qgQSXD0Iv5XNtajx0uRMpFM=",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "babel-plugin-transform-regexp-constructors": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz",
+ "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=",
+ "dev": true
+ },
+ "babel-plugin-transform-remove-console": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-xXF6+fdpGLKCHPrvRNgkXU6pQiw=",
+ "dev": true
+ },
+ "babel-plugin-transform-remove-debugger": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-H8NcKcfAh4zzDlWKczZRkG6IjkQ=",
+ "dev": true
+ },
+ "babel-plugin-transform-remove-undefined": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.4.3.tgz",
+ "integrity": "sha1-1AsNp/kcCMBsxyt2dHTAHEiU3gI=",
+ "dev": true,
+ "requires": {
+ "babel-helper-evaluate-path": "^0.4.3"
+ }
+ },
+ "babel-plugin-transform-simplify-comparison-operators": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-9UmabcPtaGvaUzY4ZrZ92ndMW+0=",
+ "dev": true
+ },
+ "babel-plugin-transform-undefined-to-void": {
+ "version": "6.10.0-alpha.f95869d4",
+ "resolved": "http://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.10.0-alpha.f95869d4.tgz",
+ "integrity": "sha1-F1oaMJDmFkA/jIGc3Ooa7LZlI7I=",
+ "dev": true
+ },
+ "babel-preset-minify": {
+ "version": "0.4.0-alpha.caaefb4c",
+ "resolved": "http://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.4.0-alpha.caaefb4c.tgz",
+ "integrity": "sha1-pQUsWVXdl9JGmbKB/amjAuqMGHE=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-minify-builtins": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-constant-folding": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-dead-code-elimination": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-flip-comparisons": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-guarded-expressions": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-infinity": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-mangle-names": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-numeric-literals": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-replace": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-simplify": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-minify-type-constructors": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-transform-inline-consecutive-adds": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-transform-member-expression-literals": "^6.10.0-alpha.caaefb4c",
+ "babel-plugin-transform-merge-sibling-variables": "^6.10.0-alpha.caaefb4c",
+ "babel-plugin-transform-minify-booleans": "^6.10.0-alpha.caaefb4c",
+ "babel-plugin-transform-property-literals": "^6.10.0-alpha.caaefb4c",
+ "babel-plugin-transform-regexp-constructors": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-transform-remove-console": "^6.10.0-alpha.caaefb4c",
+ "babel-plugin-transform-remove-debugger": "^6.10.0-alpha.caaefb4c",
+ "babel-plugin-transform-remove-undefined": "^0.4.0-alpha.caaefb4c",
+ "babel-plugin-transform-simplify-comparison-operators": "^6.10.0-alpha.caaefb4c",
+ "babel-plugin-transform-undefined-to-void": "^6.10.0-alpha.caaefb4c",
+ "lodash.isplainobject": "^4.0.6"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "dev": true,
+ "requires": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
+ },
+ "dependencies": {
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true
+ }
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ },
+ "dependencies": {
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+ "dev": true
+ }
+ }
+ },
+ "babylon": {
+ "version": "7.0.0-beta.47",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
+ "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==",
+ "dev": true
+ },
+ "bach": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
+ "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=",
+ "dev": true,
+ "requires": {
+ "arr-filter": "^1.1.1",
+ "arr-flatten": "^1.0.1",
+ "arr-map": "^2.0.0",
+ "array-each": "^1.0.0",
+ "array-initial": "^1.0.0",
+ "array-last": "^1.1.1",
+ "async-done": "^1.2.2",
+ "async-settle": "^1.0.0",
+ "now-and-later": "^2.0.0"
+ }
+ },
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz",
+ "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=",
+ "dev": true
+ },
+ "base64id": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
+ "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+ "dev": true,
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
+ "binary-extensions": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz",
+ "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==",
+ "dev": true
+ },
+ "bl": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
+ "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.5"
+ }
+ },
+ "blob": {
+ "version": "0.0.4",
+ "resolved": "http://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
+ "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=",
+ "dev": true
+ },
+ "body-parser": {
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
+ "dev": true,
+ "requires": {
+ "bytes": "3.0.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "~1.6.3",
+ "iconv-lite": "0.4.23",
+ "on-finished": "~2.3.0",
+ "qs": "6.5.2",
+ "raw-body": "2.3.3",
+ "type-is": "~1.6.16"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "boom": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
+ "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ },
+ "bower-config": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/bower-config/-/bower-config-1.4.1.tgz",
+ "integrity": "sha1-hf2d82fCuNu9DKpMXyutQM2Ewsw=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.3",
+ "mout": "^1.0.0",
+ "optimist": "^0.6.1",
+ "osenv": "^0.1.3",
+ "untildify": "^2.1.0"
+ }
+ },
+ "boxen": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
+ "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
+ "dev": true,
+ "requires": {
+ "ansi-align": "^2.0.0",
+ "camelcase": "^4.0.0",
+ "chalk": "^2.0.1",
+ "cli-boxes": "^1.0.0",
+ "string-width": "^2.0.0",
+ "term-size": "^1.2.0",
+ "widest-line": "^2.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+ "dev": true
+ }
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz",
+ "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "kind-of": "^6.0.2",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "browser-capabilities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/browser-capabilities/-/browser-capabilities-1.1.2.tgz",
+ "integrity": "sha512-T9BTu9Lmdrh9XZe0XnUY3jGiBlB0jAkl4M9qvt+1SszqlckgcUTzJuBwD6HNNKjdiDA+18KfiIUJEVxTY2W24g==",
+ "dev": true,
+ "requires": {
+ "@types/ua-parser-js": "^0.7.31",
+ "ua-parser-js": "^0.7.15"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
+ "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
+ "dev": true
+ },
+ "browserstack": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.1.tgz",
+ "integrity": "sha512-O8VMT64P9NOLhuIoD4YngyxBURefaSdR4QdhG8l6HZ9VxtU7jc3m6jLufFwKA5gaf7fetfB2TnRJnMxyob+heg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
+ "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "buffer-alloc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc-unsafe": "^1.1.0",
+ "buffer-fill": "^1.0.0"
+ }
+ },
+ "buffer-alloc-unsafe": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
+ "dev": true
+ },
+ "buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
+ "dev": true
+ },
+ "buffer-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
+ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=",
+ "dev": true
+ },
+ "buffer-fill": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "busboy": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+ "dev": true,
+ "requires": {
+ "dicer": "0.2.5",
+ "readable-stream": "1.1.x"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ }
+ }
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "dev": true
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "caller-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+ "dev": true,
+ "requires": {
+ "callsites": "^0.2.0"
+ }
+ },
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
+ "dev": true
+ },
+ "callsites": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+ "dev": true
+ },
+ "camel-case": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
+ "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+ "dev": true,
+ "requires": {
+ "no-case": "^2.2.0",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+ "dev": true
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^2.0.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "cancel-token": {
+ "version": "0.1.1",
+ "resolved": "http://registry.npmjs.org/cancel-token/-/cancel-token-0.1.1.tgz",
+ "integrity": "sha1-wYGXZ0uxyEwdaTPr8V2NWlznm08=",
+ "dev": true,
+ "requires": {
+ "@types/node": "^4.0.30"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-4.9.0.tgz",
+ "integrity": "sha512-xUFkZ+er9gUGw0x9qyfmr/Th0LuX6IB0m7HrRMB6sO6vcBVRFZ/3YV1EeiOC2fG50RX09avDfKwGBHOnPVxFeg==",
+ "dev": true
+ }
+ }
+ },
+ "capture-stack-trace": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz",
+ "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "chai": {
+ "version": "3.5.0",
+ "resolved": "http://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
+ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.0.1",
+ "deep-eql": "^0.1.3",
+ "type-detect": "^1.0.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
+ "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.0",
+ "braces": "^2.3.0",
+ "fsevents": "^1.2.2",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.1",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "lodash.debounce": "^4.0.8",
+ "normalize-path": "^2.1.1",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.0.0",
+ "upath": "^1.0.5"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "is-glob": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
+ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ }
+ }
+ },
+ "ci-info": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
+ "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
+ "dev": true
+ },
+ "circular-json": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+ "dev": true
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "clean-css": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
+ "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "cleankill": {
+ "version": "2.0.0",
+ "resolved": "http://registry.npmjs.org/cleankill/-/cleankill-2.0.0.tgz",
+ "integrity": "sha1-WYMN/ItBHVPccq0J1Fp46jMWGpE=",
+ "dev": true
+ },
+ "cli-boxes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wrap-ansi": "^2.0.0"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "clone": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
+ "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=",
+ "dev": true
+ },
+ "clone-buffer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
+ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
+ "dev": true
+ },
+ "clone-stats": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
+ "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
+ "dev": true
+ },
+ "cloneable-readable": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz",
+ "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "process-nextick-args": "^2.0.0",
+ "readable-stream": "^2.3.5"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true
+ },
+ "collection-map": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
+ "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=",
+ "dev": true,
+ "requires": {
+ "arr-map": "^2.0.2",
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
+ "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.1",
+ "color-string": "^1.5.2"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "color-string": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
+ "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true
+ },
+ "colornames": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz",
+ "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz",
+ "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==",
+ "dev": true
+ },
+ "colorspace": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz",
+ "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==",
+ "dev": true,
+ "requires": {
+ "color": "3.0.x",
+ "text-hex": "1.0.x"
+ }
+ },
+ "combined-stream": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
+ "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "command-line-args": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.0.2.tgz",
+ "integrity": "sha512-/qPcbL8zpqg53x4rAaqMFlRV4opN3pbla7I7k9x8kyOBMQoGT6WltjN6sXZuxOXw6DgdK7Ad+ijYS5gjcr7vlA==",
+ "dev": true,
+ "requires": {
+ "argv-tools": "^0.1.1",
+ "array-back": "^2.0.0",
+ "find-replace": "^2.0.1",
+ "lodash.camelcase": "^4.3.0",
+ "typical": "^2.6.1"
+ }
+ },
+ "command-line-usage": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-5.0.5.tgz",
+ "integrity": "sha512-d8NrGylA5oCXSbGoKz05FkehDAzSmIm4K03S5VDh4d5lZAtTWfc3D1RuETtuQCn8129nYfJfDdF7P/lwcz1BlA==",
+ "dev": true,
+ "requires": {
+ "array-back": "^2.0.0",
+ "chalk": "^2.4.1",
+ "table-layout": "^0.4.3",
+ "typical": "^2.6.1"
+ }
+ },
+ "commander": {
+ "version": "2.9.0",
+ "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
+ "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+ "dev": true,
+ "requires": {
+ "graceful-readlink": ">= 1.0.0"
+ }
+ },
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
+ "dev": true
+ },
+ "compress-commons": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz",
+ "integrity": "sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8=",
+ "dev": true,
+ "requires": {
+ "buffer-crc32": "^0.2.1",
+ "crc32-stream": "^2.0.0",
+ "normalize-path": "^2.0.0",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "compressible": {
+ "version": "2.0.15",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz",
+ "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==",
+ "dev": true,
+ "requires": {
+ "mime-db": ">= 1.36.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
+ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.14",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.1",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.1.tgz",
+ "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "configstore": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
+ "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
+ "dev": true,
+ "requires": {
+ "dot-prop": "^4.1.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^1.0.0",
+ "unique-string": "^1.0.0",
+ "write-file-atomic": "^2.0.0",
+ "xdg-basedir": "^3.0.0"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
+ "dev": true
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
+ "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=",
+ "dev": true
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+ "dev": true
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+ "dev": true
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "copy-props": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz",
+ "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==",
+ "dev": true,
+ "requires": {
+ "each-props": "^1.3.0",
+ "is-plain-object": "^2.0.1"
+ }
+ },
+ "core-js": {
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
+ "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "crc": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz",
+ "integrity": "sha1-XZyPt3okXNXsopHl0tAFM0urAII=",
+ "dev": true
+ },
+ "crc32-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz",
+ "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=",
+ "dev": true,
+ "requires": {
+ "crc": "^3.4.4",
+ "readable-stream": "^2.0.0"
+ },
+ "dependencies": {
+ "crc": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
+ "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
+ "dev": true,
+ "requires": {
+ "buffer": "^5.1.0"
+ }
+ }
+ }
+ },
+ "create-error-class": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
+ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+ "dev": true,
+ "requires": {
+ "capture-stack-trace": "^1.0.0"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=",
+ "dev": true
+ },
+ "cryptiles": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
+ "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
+ "dev": true,
+ "requires": {
+ "boom": "5.x.x"
+ },
+ "dependencies": {
+ "boom": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
+ "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ }
+ }
+ },
+ "crypto-random-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
+ "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
+ "dev": true
+ },
+ "css": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css/-/css-2.2.1.tgz",
+ "integrity": "sha1-c6TIHehdtmTU7mdPfUcIXjstVdw=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "source-map": "^0.1.38",
+ "source-map-resolve": "^0.3.0",
+ "urix": "^0.1.0"
+ },
+ "dependencies": {
+ "atob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.3.tgz",
+ "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.1.43",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ },
+ "source-map-resolve": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz",
+ "integrity": "sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E=",
+ "dev": true,
+ "requires": {
+ "atob": "~1.1.0",
+ "resolve-url": "~0.2.1",
+ "source-map-url": "~0.3.0",
+ "urix": "~0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz",
+ "integrity": "sha1-fsrxO1e80J2opAxdJp2zN5nUqvk=",
+ "dev": true
+ }
+ }
+ },
+ "css-slam": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/css-slam/-/css-slam-2.1.2.tgz",
+ "integrity": "sha512-cObrY+mhFEmepWpua6EpMrgRNTQ0eeym+kvR0lukI6hDEzK7F8himEDS4cJ9+fPHCoArTzVrrR0d+oAUbTR1NA==",
+ "dev": true,
+ "requires": {
+ "command-line-args": "^5.0.2",
+ "command-line-usage": "^5.0.5",
+ "dom5": "^3.0.0",
+ "parse5": "^4.0.0",
+ "shady-css-parser": "^0.1.0"
+ }
+ },
+ "cssbeautify": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cssbeautify/-/cssbeautify-0.3.1.tgz",
+ "integrity": "sha1-Et0fc0A1wub6ymfcvc73TkKBE5c=",
+ "dev": true
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "dev": true,
+ "requires": {
+ "array-find-index": "^1.0.1"
+ }
+ },
+ "cycle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+ "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=",
+ "dev": true
+ },
+ "d": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
+ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
+ "dev": true,
+ "requires": {
+ "es5-ext": "^0.10.9"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "debug-fabulous": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.0.0.tgz",
+ "integrity": "sha512-dsd50qQ1atDeurcxL7XOjPp4nZCGZzWIONDujDXzl1atSyC3hMbZD+v6440etw+Vt0Pr8ce4TQzHfX3KZM05Mw==",
+ "dev": true,
+ "requires": {
+ "debug": "3.X",
+ "memoizee": "0.4.X",
+ "object-assign": "4.X"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
+ "deep-eql": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+ "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+ "dev": true,
+ "requires": {
+ "type-detect": "0.1.1"
+ },
+ "dependencies": {
+ "type-detect": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
+ "dev": true
+ }
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "default-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
+ "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^5.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "default-resolution": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz",
+ "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ }
+ },
+ "del": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
+ "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=",
+ "dev": true,
+ "requires": {
+ "globby": "^6.1.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "p-map": "^1.1.1",
+ "pify": "^3.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+ "dev": true
+ },
+ "detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+ "dev": true
+ },
+ "detect-indent": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "detect-newline": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
+ "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
+ "dev": true
+ },
+ "detect-node": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
+ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
+ "dev": true
+ },
+ "diagnostics": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz",
+ "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==",
+ "dev": true,
+ "requires": {
+ "colorspace": "1.1.x",
+ "enabled": "1.0.x",
+ "kuler": "1.0.x"
+ }
+ },
+ "dicer": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+ "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "1.1.x",
+ "streamsearch": "0.1.2"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ }
+ }
+ },
+ "diff": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz",
+ "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=",
+ "dev": true
+ },
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "~1.1.1",
+ "entities": "~1.1.1"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
+ "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
+ "dev": true
+ }
+ }
+ },
+ "dom-urls": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz",
+ "integrity": "sha1-AB3fgWKM0ecGElxxdvU8zsVdkY4=",
+ "dev": true,
+ "requires": {
+ "urijs": "^1.16.1"
+ }
+ },
+ "dom5": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dom5/-/dom5-3.0.1.tgz",
+ "integrity": "sha512-JPFiouQIr16VQ4dX6i0+Hpbg3H2bMKPmZ+WZgBOSSvOPx9QHwwY8sPzeM2baUtViESYto6wC2nuZOMC/6gulcA==",
+ "dev": true,
+ "requires": {
+ "@types/parse5": "^2.2.34",
+ "clone": "^2.1.0",
+ "parse5": "^4.0.0"
+ }
+ },
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
+ "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+ "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "dot-prop": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
+ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
+ "dev": true,
+ "requires": {
+ "is-obj": "^1.0.0"
+ }
+ },
+ "duplexer": {
+ "version": "0.1.1",
+ "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+ "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
+ "dev": true
+ },
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "duplexer3": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
+ "dev": true
+ },
+ "duplexify": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
+ "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ },
+ "dependencies": {
+ "end-of-stream": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ }
+ }
+ },
+ "each-props": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
+ "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.1",
+ "object.defaults": "^1.1.0"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "emitter-component": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz",
+ "integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY=",
+ "dev": true
+ },
+ "enabled": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
+ "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=",
+ "dev": true,
+ "requires": {
+ "env-variable": "0.0.x"
+ }
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "end-of-stream": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "engine.io": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.0.tgz",
+ "integrity": "sha512-mRbgmAtQ4GAlKwuPnnAvXXwdPhEx+jkc0OBCLrXuD/CRvwNK3AxRSnqK4FSqmAMRRHryVJP8TopOvmEaA64fKw==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "1.0.0",
+ "cookie": "0.3.1",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.0",
+ "ws": "~3.3.1"
+ }
+ },
+ "engine.io-client": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+ "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.1",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "ws": "~3.3.1",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz",
+ "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==",
+ "dev": true,
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.5",
+ "blob": "0.0.4",
+ "has-binary2": "~1.0.2"
+ }
+ },
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ },
+ "env-variable": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz",
+ "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es5-ext": {
+ "version": "0.10.40",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.40.tgz",
+ "integrity": "sha512-S9Fh3oya5OOvYSNGvPZJ+vyrs6VYpe1IXPowVe3N1OhaiwVaGlwfn3Zf5P5klYcWOA0toIwYQW8XEv/QqhdHvQ==",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.1"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
+ "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.1.tgz",
+ "integrity": "sha512-J3ZkwbEnnO+fGAKrjVpeUAnZshAdfZvbhQpqfIH9kSAspReRC4nJnu8ewm55b4y9ElyeuhCTzJD0XiH8Tsbhlw==",
+ "dev": true
+ },
+ "es6-symbol": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "~0.10.14"
+ }
+ },
+ "es6-weak-map": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
+ "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.14",
+ "es6-iterator": "^2.0.1",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz",
+ "integrity": "sha512-zYCeFQahsxffGl87U2aJ7DPyH8CbWgxBC213Y8+TCanhUTf2gEvfq3EKpHmEcozTLyPmGe9LZdMAwC/CpJBM5A==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.5.3",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^2.1.0",
+ "eslint-scope": "^4.0.0",
+ "eslint-utils": "^1.3.1",
+ "eslint-visitor-keys": "^1.0.0",
+ "espree": "^4.0.0",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^2.0.0",
+ "functional-red-black-tree": "^1.0.1",
+ "glob": "^7.1.2",
+ "globals": "^11.7.0",
+ "ignore": "^4.0.6",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^6.1.0",
+ "is-resolvable": "^1.1.0",
+ "js-yaml": "^3.12.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.5",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.2",
+ "path-is-inside": "^1.0.2",
+ "pluralize": "^7.0.0",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "require-uncached": "^1.0.3",
+ "semver": "^5.5.1",
+ "strip-ansi": "^4.0.0",
+ "strip-json-comments": "^2.0.1",
+ "table": "^5.0.2",
+ "text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
+ "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-html": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-4.0.6.tgz",
+ "integrity": "sha512-nj6A9oK+7BKnMm0E7dMRH3r75BfpkXtcVIb3pFC4AcDdBTNyg2NGxHXyFNT1emW4VsR7P2SZvRXXQtUR+kY08w==",
+ "dev": true,
+ "requires": {
+ "htmlparser2": "^3.8.2"
+ }
+ },
+ "eslint-scope": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
+ "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
+ "dev": true
+ },
+ "eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+ "dev": true
+ },
+ "espree": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz",
+ "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==",
+ "dev": true,
+ "requires": {
+ "acorn": "^6.0.2",
+ "acorn-jsx": "^5.0.0",
+ "eslint-visitor-keys": "^1.0.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.2.tgz",
+ "integrity": "sha512-GXmKIvbrN3TV7aVqAzVFaMW8F8wzVX7voEBRO3bDA64+EX37YSayggRJP5Xig6HYHBkWKpFg9W5gg6orklubhg==",
+ "dev": true
+ }
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "dev": true
+ },
+ "event-emitter": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "~0.10.14"
+ }
+ },
+ "eventemitter3": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
+ "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==",
+ "dev": true
+ },
+ "execa": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ }
+ }
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "expand-range": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
+ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
+ "dev": true,
+ "requires": {
+ "fill-range": "^2.1.0"
+ },
+ "dependencies": {
+ "fill-range": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
+ "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
+ "dev": true,
+ "requires": {
+ "is-number": "^2.1.0",
+ "isobject": "^2.0.0",
+ "randomatic": "^3.0.0",
+ "repeat-element": "^1.1.2",
+ "repeat-string": "^1.5.2"
+ }
+ },
+ "is-number": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "express": {
+ "version": "4.16.4",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
+ "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.18.3",
+ "content-disposition": "0.5.2",
+ "content-type": "~1.0.4",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.1.1",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.4",
+ "qs": "6.5.2",
+ "range-parser": "~1.2.0",
+ "safe-buffer": "5.1.2",
+ "send": "0.16.2",
+ "serve-static": "1.13.2",
+ "setprototypeof": "1.1.0",
+ "statuses": "~1.4.0",
+ "type-is": "~1.6.16",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ }
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "dev": true
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "external-editor": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+ "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "eyes": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+ "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=",
+ "dev": true
+ },
+ "fancy-log": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz",
+ "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=",
+ "dev": true,
+ "requires": {
+ "ansi-gray": "^0.1.1",
+ "color-support": "^1.1.3",
+ "time-stamp": "^1.0.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "fast-safe-stringify": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz",
+ "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==",
+ "dev": true
+ },
+ "fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
+ "fecha": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
+ "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==",
+ "dev": true
+ },
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^1.2.1",
+ "object-assign": "^4.0.1"
+ }
+ },
+ "filename-regex": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
+ "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.1",
+ "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.4.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "dev": true
+ }
+ }
+ },
+ "find-port": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/find-port/-/find-port-1.0.1.tgz",
+ "integrity": "sha1-2whKbL+ZVk2Zhprnn73s9m6KGFw=",
+ "dev": true,
+ "requires": {
+ "async": "~0.2.9"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.2.10",
+ "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
+ "dev": true
+ }
+ }
+ },
+ "find-replace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-2.0.1.tgz",
+ "integrity": "sha512-LzDo3Fpa30FLIBsh6DCDnMN1KW2g4QKkqKmejlImgWY67dDFPX/x9Kh/op/GK522DchQXEvDi/wD48HKW49XOQ==",
+ "dev": true,
+ "requires": {
+ "array-back": "^2.0.0",
+ "test-value": "^3.0.0"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "findup-sync": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^3.1.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ }
+ },
+ "fined": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz",
+ "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "is-plain-object": "^2.0.3",
+ "object.defaults": "^1.1.0",
+ "object.pick": "^1.2.0",
+ "parse-filepath": "^1.0.1"
+ }
+ },
+ "first-chunk-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz",
+ "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=",
+ "dev": true
+ },
+ "flagged-respawn": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz",
+ "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=",
+ "dev": true
+ },
+ "flat-cache": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
+ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
+ "dev": true,
+ "requires": {
+ "circular-json": "^0.3.1",
+ "del": "^2.0.2",
+ "graceful-fs": "^4.1.2",
+ "write": "^0.2.1"
+ },
+ "dependencies": {
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true,
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "flush-write-stream": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
+ "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.4"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.5.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.9.tgz",
+ "integrity": "sha512-Bh65EZI/RU8nx0wbYF9shkFZlqLP+6WT/5FnA3cE/djNSuKNHJEinGGZgu/cQEkeeb2GdFOgenAmn8qaqYke2w==",
+ "dev": true,
+ "requires": {
+ "debug": "=3.1.0"
+ }
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "for-own": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.1"
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "fork-stream": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz",
+ "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "formatio": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz",
+ "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=",
+ "dev": true,
+ "requires": {
+ "samsam": "~1.1"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
+ "dev": true
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "freeport": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/freeport/-/freeport-1.0.5.tgz",
+ "integrity": "sha1-JV6KuEFwwzuoXZkOghrl9KGpvF0=",
+ "dev": true,
+ "optional": true
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "dev": true
+ },
+ "fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true
+ },
+ "fs-mkdirp-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
+ "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "through2": "^2.0.3"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
+ "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "nan": "^2.9.2",
+ "node-pre-gyp": "^0.10.0"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "deep-extend": {
+ "version": "0.5.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.21",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true,
+ "dev": true
+ },
+ "minipass": {
+ "version": "2.2.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.1",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "needle": {
+ "version": "2.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^2.1.2",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.10.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.0",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.1.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.1.10",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.7",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.5.1",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.0.5"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.5.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.0.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.2.4",
+ "minizlib": "^1.1.0",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.1",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "3.0.0",
+ "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+ "dev": true
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-base": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
+ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
+ "dev": true,
+ "requires": {
+ "glob-parent": "^2.0.0",
+ "is-glob": "^2.0.0"
+ },
+ "dependencies": {
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ }
+ }
+ },
+ "glob-parent": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^2.0.0"
+ },
+ "dependencies": {
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ }
+ }
+ },
+ "glob-stream": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
+ "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "glob": "^7.1.1",
+ "glob-parent": "^3.1.0",
+ "is-negated-glob": "^1.0.0",
+ "ordered-read-streams": "^1.0.0",
+ "pumpify": "^1.3.5",
+ "readable-stream": "^2.1.5",
+ "remove-trailing-separator": "^1.0.1",
+ "to-absolute-glob": "^2.0.0",
+ "unique-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ }
+ },
+ "to-absolute-glob": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
+ "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
+ "dev": true,
+ "requires": {
+ "is-absolute": "^1.0.0",
+ "is-negated-glob": "^1.0.0"
+ }
+ }
+ }
+ },
+ "glob-watcher": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.1.tgz",
+ "integrity": "sha512-fK92r2COMC199WCyGUblrZKhjra3cyVMDiypDdqg1vsSDmexnbYivK1kNR4QItiNXLKmGlqan469ks67RtNa2g==",
+ "dev": true,
+ "requires": {
+ "async-done": "^1.2.0",
+ "chokidar": "^2.0.0",
+ "just-debounce": "^1.0.0",
+ "object.defaults": "^1.1.0"
+ }
+ },
+ "global-dirs": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+ "dev": true,
+ "requires": {
+ "ini": "^1.3.4"
+ }
+ },
+ "global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ }
+ },
+ "global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ }
+ },
+ "globals": {
+ "version": "11.7.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz",
+ "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==",
+ "dev": true
+ },
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "glogg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz",
+ "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==",
+ "dev": true,
+ "requires": {
+ "sparkles": "^1.0.0"
+ }
+ },
+ "google-closure-compiler": {
+ "version": "20180805.0.0",
+ "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20180805.0.0.tgz",
+ "integrity": "sha512-MV9JTTQDO0tYOAaJmqd3MMIjCLxHkhZcj7hN6gUJdjQV7wYmH2wqwj56teIK22o9pSJvhKg89F/WCguaAZhSUA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "google-closure-compiler-linux": "^20180805.0.0",
+ "google-closure-compiler-osx": "^20180805.0.0",
+ "minimist": "^1.2.0",
+ "vinyl": "^2.0.1",
+ "vinyl-sourcemaps-apply": "^0.2.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "google-closure-compiler-linux": {
+ "version": "20180805.0.0",
+ "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20180805.0.0.tgz",
+ "integrity": "sha512-Y53/dehGj5/HoAEnxoYFWLpLf8gHq8QaQSOw8ulnh6VkpHPdcAuhRqIVLcLeJ9rVW3xteOMQ2WFyRfFMayVZcA==",
+ "dev": true,
+ "optional": true
+ },
+ "google-closure-compiler-osx": {
+ "version": "20180805.0.0",
+ "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20180805.0.0.tgz",
+ "integrity": "sha512-Zt558FPdkpcZulaT4uH7qNiO5wy3DT2Eaq+JPb4LjuS3KQSo8GSWx9caYWGZ1OdKULTv+v/flkHMtoHu5XP2bw==",
+ "dev": true,
+ "optional": true
+ },
+ "got": {
+ "version": "6.7.1",
+ "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz",
+ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+ "dev": true,
+ "requires": {
+ "create-error-class": "^3.0.0",
+ "duplexer3": "^0.1.4",
+ "get-stream": "^3.0.0",
+ "is-redirect": "^1.0.0",
+ "is-retry-allowed": "^1.0.0",
+ "is-stream": "^1.0.0",
+ "lowercase-keys": "^1.0.0",
+ "safe-buffer": "^5.0.1",
+ "timed-out": "^4.0.0",
+ "unzip-response": "^2.0.1",
+ "url-parse-lax": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+ "dev": true
+ },
+ "graceful-readlink": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
+ "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
+ "dev": true
+ },
+ "gulp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz",
+ "integrity": "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y=",
+ "dev": true,
+ "requires": {
+ "glob-watcher": "^5.0.0",
+ "gulp-cli": "^2.0.0",
+ "undertaker": "^1.0.0",
+ "vinyl-fs": "^3.0.0"
+ },
+ "dependencies": {
+ "gulp-cli": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.0.1.tgz",
+ "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^1.0.1",
+ "archy": "^1.0.0",
+ "array-sort": "^1.0.0",
+ "color-support": "^1.1.3",
+ "concat-stream": "^1.6.0",
+ "copy-props": "^2.0.1",
+ "fancy-log": "^1.3.2",
+ "gulplog": "^1.0.0",
+ "interpret": "^1.1.0",
+ "isobject": "^3.0.1",
+ "liftoff": "^2.5.0",
+ "matchdep": "^2.0.0",
+ "mute-stdout": "^1.0.0",
+ "pretty-hrtime": "^1.0.0",
+ "replace-homedir": "^1.0.0",
+ "semver-greatest-satisfied-range": "^1.1.0",
+ "v8flags": "^3.0.1",
+ "yargs": "^7.1.0"
+ }
+ }
+ }
+ },
+ "gulp-if": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-2.0.2.tgz",
+ "integrity": "sha1-pJe351cwBQQcqivIt92jyARE1ik=",
+ "dev": true,
+ "requires": {
+ "gulp-match": "^1.0.3",
+ "ternary-stream": "^2.0.1",
+ "through2": "^2.0.1"
+ }
+ },
+ "gulp-match": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.0.3.tgz",
+ "integrity": "sha1-kcfA1/Kb7NZgbVfYCn+Hdqh6uo4=",
+ "dev": true,
+ "requires": {
+ "minimatch": "^3.0.3"
+ }
+ },
+ "gulp-rename": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz",
+ "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==",
+ "dev": true
+ },
+ "gulp-size": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/gulp-size/-/gulp-size-3.0.0.tgz",
+ "integrity": "sha1-yxrI5rqD3t5SQwxH/QOTJPAD/4I=",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.3.0",
+ "fancy-log": "^1.3.2",
+ "gzip-size": "^4.1.0",
+ "plugin-error": "^0.1.2",
+ "pretty-bytes": "^4.0.2",
+ "stream-counter": "^1.0.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "gulp-sourcemaps": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.4.tgz",
+ "integrity": "sha1-y7IAhFCxvM5s0jv5gze+dRv24wo=",
+ "dev": true,
+ "requires": {
+ "@gulp-sourcemaps/identity-map": "1.X",
+ "@gulp-sourcemaps/map-sources": "1.X",
+ "acorn": "5.X",
+ "convert-source-map": "1.X",
+ "css": "2.X",
+ "debug-fabulous": "1.X",
+ "detect-newline": "2.X",
+ "graceful-fs": "4.X",
+ "source-map": "~0.6.0",
+ "strip-bom-string": "1.X",
+ "through2": "2.X"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "gulplog": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
+ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
+ "dev": true,
+ "requires": {
+ "glogg": "^1.0.0"
+ }
+ },
+ "gzip-size": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz",
+ "integrity": "sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw=",
+ "dev": true,
+ "requires": {
+ "duplexer": "^0.1.1",
+ "pify": "^3.0.0"
+ }
+ },
+ "handle-thing": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
+ "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=",
+ "dev": true
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
+ "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ajv": "^5.3.0",
+ "har-schema": "^2.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "co": "^4.6.0",
+ "fast-deep-equal": "^1.0.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.3.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
+ "dev": true,
+ "optional": true
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "dev": true,
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "has-color": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
+ "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=",
+ "dev": true
+ },
+ "has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hawk": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
+ "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
+ "dev": true,
+ "requires": {
+ "boom": "4.x.x",
+ "cryptiles": "3.x.x",
+ "hoek": "4.x.x",
+ "sntp": "2.x.x"
+ }
+ },
+ "he": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+ "dev": true
+ },
+ "hoek": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
+ "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==",
+ "dev": true
+ },
+ "homedir-polyfill": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
+ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
+ "dev": true,
+ "requires": {
+ "parse-passwd": "^1.0.0"
+ }
+ },
+ "hosted-git-info": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+ "dev": true
+ },
+ "hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ }
+ },
+ "html-minifier": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz",
+ "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==",
+ "dev": true,
+ "requires": {
+ "camel-case": "3.0.x",
+ "clean-css": "4.2.x",
+ "commander": "2.17.x",
+ "he": "1.2.x",
+ "param-case": "2.1.x",
+ "relateurl": "0.2.x",
+ "uglify-js": "3.4.x"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "dev": true
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ }
+ }
+ },
+ "htmlparser2": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz",
+ "integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.0",
+ "domhandler": "^2.3.0",
+ "domutils": "^1.5.1",
+ "entities": "^1.1.1",
+ "inherits": "^2.0.1",
+ "readable-stream": "^3.0.6"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz",
+ "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "http-deceiver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "http-proxy": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
+ "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^3.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "http-proxy-middleware": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz",
+ "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=",
+ "dev": true,
+ "requires": {
+ "http-proxy": "^1.16.2",
+ "is-glob": "^3.1.0",
+ "lodash": "^4.17.2",
+ "micromatch": "^2.3.11"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.0.1"
+ }
+ },
+ "array-unique": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "dev": true
+ },
+ "braces": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+ "dev": true,
+ "requires": {
+ "expand-range": "^1.8.1",
+ "preserve": "^0.2.0",
+ "repeat-element": "^1.1.2"
+ }
+ },
+ "expand-brackets": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+ "dev": true,
+ "requires": {
+ "is-posix-bracket": "^0.1.0"
+ }
+ },
+ "extglob": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "micromatch": {
+ "version": "2.3.11",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^2.0.0",
+ "array-unique": "^0.2.1",
+ "braces": "^1.8.2",
+ "expand-brackets": "^0.1.4",
+ "extglob": "^0.3.1",
+ "filename-regex": "^2.0.0",
+ "is-extglob": "^1.0.0",
+ "is-glob": "^2.0.1",
+ "kind-of": "^3.0.2",
+ "normalize-path": "^2.0.1",
+ "object.omit": "^2.0.0",
+ "parse-glob": "^3.0.4",
+ "regex-cache": "^0.4.2"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ }
+ }
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
+ "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "agent-base": "^4.1.0",
+ "debug": "^3.1.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
+ "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-lazy": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
+ "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
+ "dev": true
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "indent": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/indent/-/indent-0.0.2.tgz",
+ "integrity": "sha1-jHnwgBkFWbaHA0uEx676l9WpEdk=",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz",
+ "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.0",
+ "cli-cursor": "^2.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.0",
+ "figures": "^2.0.0",
+ "lodash": "^4.17.10",
+ "mute-stream": "0.0.7",
+ "run-async": "^2.2.0",
+ "rxjs": "^6.1.0",
+ "string-width": "^2.1.0",
+ "strip-ansi": "^4.0.0",
+ "through": "^2.3.6"
+ }
+ },
+ "interpret": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
+ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=",
+ "dev": true
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "invert-kv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
+ "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=",
+ "dev": true
+ },
+ "is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+ "dev": true,
+ "requires": {
+ "is-relative": "^1.0.0",
+ "is-windows": "^1.0.1"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^1.0.0"
+ }
+ },
+ "is-ci": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
+ "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^1.5.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "is-dotfile": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
+ "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
+ "dev": true
+ },
+ "is-equal-shallow": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
+ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
+ "dev": true,
+ "requires": {
+ "is-primitive": "^2.0.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ },
+ "is-installed-globally": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
+ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
+ "dev": true,
+ "requires": {
+ "global-dirs": "^0.1.0",
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-negated-glob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
+ "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=",
+ "dev": true
+ },
+ "is-npm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
+ "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=",
+ "dev": true
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
+ "dev": true
+ },
+ "is-odd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz",
+ "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true
+ }
+ }
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
+ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-posix-bracket": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
+ "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
+ "dev": true
+ },
+ "is-primitive": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
+ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
+ "dev": true
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
+ "is-redirect": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+ "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
+ "dev": true
+ },
+ "is-relative": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+ "dev": true,
+ "requires": {
+ "is-unc-path": "^1.0.0"
+ }
+ },
+ "is-resolvable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
+ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+ "dev": true
+ },
+ "is-retry-allowed": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
+ "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-unc-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+ "dev": true,
+ "requires": {
+ "unc-path-regex": "^0.1.2"
+ }
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "is-valid-glob": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz",
+ "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=",
+ "dev": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
+ "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz",
+ "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=",
+ "dev": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+ "dev": true,
+ "requires": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "json3": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
+ "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+ "dev": true
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "jsonschema": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.4.tgz",
+ "integrity": "sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==",
+ "dev": true
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "just-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz",
+ "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ },
+ "kuler": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz",
+ "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==",
+ "dev": true,
+ "requires": {
+ "colornames": "^1.1.1"
+ }
+ },
+ "last-run": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
+ "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=",
+ "dev": true,
+ "requires": {
+ "default-resolution": "^2.0.0",
+ "es6-weak-map": "^2.0.1"
+ }
+ },
+ "latest-version": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
+ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
+ "dev": true,
+ "requires": {
+ "package-json": "^4.0.0"
+ }
+ },
+ "launchpad": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/launchpad/-/launchpad-0.7.0.tgz",
+ "integrity": "sha1-9CfTwOFehp7hVROCi6/vwW+ce8M=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "async": "^2.0.1",
+ "browserstack": "^1.2.0",
+ "debug": "^2.2.0",
+ "plist": "^2.0.1",
+ "q": "^1.4.1",
+ "underscore": "^1.8.3"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "underscore": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
+ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "lazystream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+ "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.5"
+ }
+ },
+ "lcid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+ "dev": true,
+ "requires": {
+ "invert-kv": "^1.0.0"
+ }
+ },
+ "lead": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz",
+ "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=",
+ "dev": true,
+ "requires": {
+ "flush-write-stream": "^1.0.2"
+ }
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "liftoff": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
+ "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "findup-sync": "^2.0.0",
+ "fined": "^1.0.1",
+ "flagged-respawn": "^1.0.0",
+ "is-plain-object": "^2.0.4",
+ "object.map": "^1.0.0",
+ "rechoir": "^0.6.2",
+ "resolve": "^1.1.7"
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
+ "lodash._baseassign": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
+ "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=",
+ "dev": true,
+ "requires": {
+ "lodash._basecopy": "^3.0.0",
+ "lodash.keys": "^3.0.0"
+ }
+ },
+ "lodash._basecopy": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
+ "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
+ "dev": true
+ },
+ "lodash._basecreate": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz",
+ "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=",
+ "dev": true
+ },
+ "lodash._getnative": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
+ "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
+ "dev": true
+ },
+ "lodash._isiterateecall": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
+ "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
+ "dev": true
+ },
+ "lodash._reinterpolate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
+ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
+ "dev": true
+ },
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
+ "dev": true
+ },
+ "lodash.create": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz",
+ "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=",
+ "dev": true,
+ "requires": {
+ "lodash._baseassign": "^3.0.0",
+ "lodash._basecreate": "^3.0.0",
+ "lodash._isiterateecall": "^3.0.0"
+ }
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
+ "dev": true
+ },
+ "lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=",
+ "dev": true
+ },
+ "lodash.isarguments": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+ "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
+ "dev": true
+ },
+ "lodash.isarray": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
+ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
+ "dev": true
+ },
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
+ "dev": true
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
+ "dev": true
+ },
+ "lodash.keys": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
+ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
+ "dev": true,
+ "requires": {
+ "lodash._getnative": "^3.0.0",
+ "lodash.isarguments": "^3.0.0",
+ "lodash.isarray": "^3.0.0"
+ }
+ },
+ "lodash.padend": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
+ "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=",
+ "dev": true
+ },
+ "lodash.some": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
+ "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=",
+ "dev": true
+ },
+ "lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+ "dev": true
+ },
+ "lodash.template": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
+ "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=",
+ "dev": true,
+ "requires": {
+ "lodash._reinterpolate": "~3.0.0",
+ "lodash.templatesettings": "^4.0.0"
+ }
+ },
+ "lodash.templatesettings": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz",
+ "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=",
+ "dev": true,
+ "requires": {
+ "lodash._reinterpolate": "~3.0.0"
+ }
+ },
+ "logform": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz",
+ "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==",
+ "dev": true,
+ "requires": {
+ "colors": "^1.2.1",
+ "fast-safe-stringify": "^2.0.4",
+ "fecha": "^2.3.3",
+ "ms": "^2.1.1",
+ "triple-beam": "^1.2.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "lolex": {
+ "version": "1.3.2",
+ "resolved": "http://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz",
+ "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "dev": true,
+ "requires": {
+ "currently-unhandled": "^0.4.1",
+ "signal-exit": "^3.0.0"
+ }
+ },
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
+ "dev": true
+ },
+ "lowercase-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz",
+ "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "lru-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
+ "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=",
+ "dev": true,
+ "requires": {
+ "es5-ext": "~0.10.2"
+ }
+ },
+ "magic-string": {
+ "version": "0.22.5",
+ "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
+ "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
+ "dev": true,
+ "requires": {
+ "vlq": "^0.2.2"
+ }
+ },
+ "make-dir": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "make-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.2"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "matchdep": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
+ "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=",
+ "dev": true,
+ "requires": {
+ "findup-sync": "^2.0.0",
+ "micromatch": "^3.0.4",
+ "resolve": "^1.4.0",
+ "stack-trace": "0.0.10"
+ }
+ },
+ "matcher": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz",
+ "integrity": "sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.4"
+ }
+ },
+ "math-random": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
+ "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=",
+ "dev": true
+ },
+ "md5": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
+ "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
+ "dev": true,
+ "requires": {
+ "charenc": "~0.0.1",
+ "crypt": "~0.0.1",
+ "is-buffer": "~1.1.1"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "dev": true
+ },
+ "memoizee": {
+ "version": "0.4.12",
+ "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.12.tgz",
+ "integrity": "sha512-sprBu6nwxBWBvBOh5v2jcsGqiGLlL2xr2dLub3vR8dnE8YB17omwtm/0NSHl8jjNbcsJd5GMWJAnTSVe/O0Wfg==",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.30",
+ "es6-weak-map": "^2.0.2",
+ "event-emitter": "^0.3.5",
+ "is-promise": "^2.1",
+ "lru-queue": "0.1",
+ "next-tick": "1",
+ "timers-ext": "^0.1.2"
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "dev": true,
+ "requires": {
+ "camelcase-keys": "^2.0.0",
+ "decamelize": "^1.1.2",
+ "loud-rejection": "^1.0.0",
+ "map-obj": "^1.0.1",
+ "minimist": "^1.1.3",
+ "normalize-package-data": "^2.3.4",
+ "object-assign": "^4.0.1",
+ "read-pkg-up": "^1.0.1",
+ "redent": "^1.0.0",
+ "trim-newlines": "^1.0.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "dev": true
+ },
+ "merge-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
+ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.9.tgz",
+ "integrity": "sha512-SlIz6sv5UPaAVVFRKodKjCg48EbNoIhgetzfK/Cy0v5U52Z6zB136M8tp0UC9jM53LYbmIRihJszvvqpKkfm9g==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "mime": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.37.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
+ "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.21",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
+ "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.37.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+ "dev": true
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimatch-all": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/minimatch-all/-/minimatch-all-1.1.0.tgz",
+ "integrity": "sha1-QMSWonouEo0Zv3WOdrsBoMcUV4c=",
+ "dev": true,
+ "requires": {
+ "minimatch": "^3.0.2"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mixin-deep": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
+ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "mocha": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz",
+ "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==",
+ "dev": true,
+ "requires": {
+ "browser-stdout": "1.3.0",
+ "commander": "2.9.0",
+ "debug": "2.6.8",
+ "diff": "3.2.0",
+ "escape-string-regexp": "1.0.5",
+ "glob": "7.1.1",
+ "growl": "1.9.2",
+ "he": "1.1.1",
+ "json3": "3.3.2",
+ "lodash.create": "3.1.1",
+ "mkdirp": "0.5.1",
+ "supports-color": "3.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.8",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
+ "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+ "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+ "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz",
+ "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=",
+ "dev": true,
+ "requires": {
+ "has-flag": "^1.0.0"
+ }
+ }
+ }
+ },
+ "mout": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mout/-/mout-1.1.0.tgz",
+ "integrity": "sha512-XsP0vf4As6BfqglxZqbqQ8SR6KQot2AgxvR0gG+WtUkf90vUXchMOZQtPf/Hml1rEffJupqL/tIrU6EYhsUQjw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "multer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.1.tgz",
+ "integrity": "sha512-zzOLNRxzszwd+61JFuAo0fxdQfvku12aNJgnla0AQ+hHxFmfc/B7jBVuPr5Rmvu46Jze/iJrFpSOsD7afO8SDw==",
+ "dev": true,
+ "requires": {
+ "append-field": "^1.0.0",
+ "busboy": "^0.2.11",
+ "concat-stream": "^1.5.2",
+ "mkdirp": "^0.5.1",
+ "object-assign": "^4.1.1",
+ "on-finished": "^2.3.0",
+ "type-is": "^1.6.4",
+ "xtend": "^4.0.0"
+ }
+ },
+ "multipipe": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-1.0.2.tgz",
+ "integrity": "sha1-zBPv2DPJzamfIk+GhGG44aP9k50=",
+ "dev": true,
+ "requires": {
+ "duplexer2": "^0.1.2",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "mute-stdout": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
+ "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+ "dev": true
+ },
+ "mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "nan": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz",
+ "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==",
+ "dev": true,
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz",
+ "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-odd": "^2.0.0",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "native-promise-only": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
+ "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "negotiator": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
+ "dev": true
+ },
+ "next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "no-case": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
+ "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
+ "dev": true,
+ "requires": {
+ "lower-case": "^1.1.1"
+ }
+ },
+ "nomnom": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
+ "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=",
+ "dev": true,
+ "requires": {
+ "chalk": "~0.4.0",
+ "underscore": "~1.6.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
+ "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "0.4.0",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
+ "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "~1.0.0",
+ "has-color": "~0.1.0",
+ "strip-ansi": "~0.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "0.1.1",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
+ "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=",
+ "dev": true
+ }
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "is-builtin-module": "^1.0.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ },
+ "now-and-later": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.0.tgz",
+ "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.2"
+ }
+ },
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "dev": true,
+ "requires": {
+ "path-key": "^2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "optional": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-keys": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
+ "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
+ "dev": true
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "object.defaults": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+ "dev": true,
+ "requires": {
+ "array-each": "^1.0.1",
+ "array-slice": "^1.0.0",
+ "for-own": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+ "dev": true,
+ "requires": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "object.omit": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
+ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
+ "dev": true,
+ "requires": {
+ "for-own": "^0.1.4",
+ "is-extendable": "^0.1.1"
+ },
+ "dependencies": {
+ "for-own": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
+ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.1"
+ }
+ }
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "object.reduce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz",
+ "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=",
+ "dev": true,
+ "requires": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
+ "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "one-time": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz",
+ "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=",
+ "dev": true
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "opn": {
+ "version": "3.0.3",
+ "resolved": "http://registry.npmjs.org/opn/-/opn-3.0.3.tgz",
+ "integrity": "sha1-ttmec5n3jWXDuq/+8fsojpuFJDo=",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "dev": true,
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "dev": true
+ }
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ }
+ },
+ "ordered-read-streams": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
+ "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true
+ },
+ "os-locale": {
+ "version": "1.4.0",
+ "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "dev": true,
+ "requires": {
+ "lcid": "^1.0.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "dev": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+ "dev": true
+ },
+ "p-map": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
+ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
+ "dev": true
+ },
+ "package-json": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
+ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
+ "dev": true,
+ "requires": {
+ "got": "^6.7.1",
+ "registry-auth-token": "^3.0.1",
+ "registry-url": "^3.0.3",
+ "semver": "^5.1.0"
+ }
+ },
+ "param-case": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
+ "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
+ "dev": true,
+ "requires": {
+ "no-case": "^2.2.0"
+ }
+ },
+ "parse-filepath": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+ "dev": true,
+ "requires": {
+ "is-absolute": "^1.0.0",
+ "map-cache": "^0.2.0",
+ "path-root": "^0.1.1"
+ }
+ },
+ "parse-glob": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
+ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
+ "dev": true,
+ "requires": {
+ "glob-base": "^0.3.0",
+ "is-dotfile": "^1.0.0",
+ "is-extglob": "^1.0.0",
+ "is-glob": "^2.0.0"
+ },
+ "dependencies": {
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ }
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+ "dev": true
+ },
+ "parse5": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+ "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
+ "dev": true
+ },
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
+ "dev": true
+ },
+ "path-root": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+ "dev": true,
+ "requires": {
+ "path-root-regex": "^0.1.0"
+ }
+ },
+ "path-root-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "pem": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/pem/-/pem-1.13.2.tgz",
+ "integrity": "sha512-MPJWuEb/r6AG+GpZi2JnfNtGAZDeL/8+ERKwXEWRuST5i+4lq/Uy36B352OWIUSPQGH+HR1HEDcIDi+8cKxXNg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^6.0.0",
+ "md5": "^2.2.1",
+ "os-tmpdir": "^1.0.1",
+ "which": "^1.3.1"
+ },
+ "dependencies": {
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+ "dev": true,
+ "optional": true
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "plist": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz",
+ "integrity": "sha1-V8zbeggh3yGDEhejytVOPhRqECU=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "base64-js": "1.2.0",
+ "xmlbuilder": "8.2.2",
+ "xmldom": "0.1.x"
+ }
+ },
+ "plugin-error": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
+ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=",
+ "dev": true,
+ "requires": {
+ "ansi-cyan": "^0.1.1",
+ "ansi-red": "^0.1.1",
+ "arr-diff": "^1.0.1",
+ "arr-union": "^2.0.1",
+ "extend-shallow": "^1.1.2"
+ },
+ "dependencies": {
+ "arr-diff": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
+ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.0.1",
+ "array-slice": "^0.2.3"
+ }
+ },
+ "arr-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
+ "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=",
+ "dev": true
+ },
+ "array-slice": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
+ "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
+ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^1.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
+ "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
+ "dev": true
+ }
+ }
+ },
+ "pluralize": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+ "dev": true
+ },
+ "plylog": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/plylog/-/plylog-1.1.0.tgz",
+ "integrity": "sha512-/QnY5aSVaP54va6hruzNtAj02HpsLlAt7V5EndMrtq6ZUTZJKUja43rgiUtGXqm95yrSJjbZoPW0yQQQwLpoJA==",
+ "dev": true,
+ "requires": {
+ "logform": "^1.9.1",
+ "winston": "^3.0.0",
+ "winston-transport": "^4.2.0"
+ }
+ },
+ "polymer-analyzer": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/polymer-analyzer/-/polymer-analyzer-3.1.3.tgz",
+ "integrity": "sha512-PU2gp7I4PT9/oNweNaDOS/g8mnfxarDSvUCHJ/ThJSH/gkIeqtDr6v/35Eky8G/5NjE7rPHbY+l+PGXlTKAC9w==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.0.0-beta.42",
+ "@babel/traverse": "^7.0.0-beta.42",
+ "@babel/types": "^7.0.0-beta.42",
+ "@types/babel-generator": "^6.25.1",
+ "@types/babel-traverse": "^6.25.2",
+ "@types/babel-types": "^6.25.1",
+ "@types/babylon": "^6.16.2",
+ "@types/chai-subset": "^1.3.0",
+ "@types/chalk": "^0.4.30",
+ "@types/clone": "^0.1.30",
+ "@types/cssbeautify": "^0.3.1",
+ "@types/doctrine": "^0.0.1",
+ "@types/is-windows": "^0.2.0",
+ "@types/minimatch": "^3.0.1",
+ "@types/parse5": "^2.2.34",
+ "@types/path-is-inside": "^1.0.0",
+ "@types/resolve": "0.0.6",
+ "@types/whatwg-url": "^6.4.0",
+ "babylon": "^7.0.0-beta.42",
+ "cancel-token": "^0.1.1",
+ "chalk": "^1.1.3",
+ "clone": "^2.0.0",
+ "cssbeautify": "^0.3.1",
+ "doctrine": "^2.0.2",
+ "dom5": "^3.0.0",
+ "indent": "0.0.2",
+ "is-windows": "^1.0.2",
+ "jsonschema": "^1.1.0",
+ "minimatch": "^3.0.4",
+ "parse5": "^4.0.0",
+ "path-is-inside": "^1.0.2",
+ "resolve": "^1.5.0",
+ "shady-css-parser": "^0.1.0",
+ "stable": "^0.1.6",
+ "strip-indent": "^2.0.0",
+ "vscode-uri": "^1.0.1",
+ "whatwg-url": "^6.4.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "polymer-build": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/polymer-build/-/polymer-build-3.1.0.tgz",
+ "integrity": "sha512-DwSiOtd1ERpGPfVCqi7SdSjW97yg4oGeUBtg2tnD/ZyEANNFSBCrkEtOFchKm/H5gCCjSEpLqqchTPrqZAYNcw==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.0.0",
+ "@babel/plugin-external-helpers": "^7.0.0",
+ "@babel/plugin-proposal-async-generator-functions": "^7.0.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
+ "@babel/plugin-syntax-async-generators": "^7.0.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+ "@babel/plugin-syntax-import-meta": "^7.0.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
+ "@babel/plugin-transform-arrow-functions": "^7.0.0",
+ "@babel/plugin-transform-async-to-generator": "^7.0.0",
+ "@babel/plugin-transform-block-scoped-functions": "^7.0.0",
+ "@babel/plugin-transform-block-scoping": "^7.0.0",
+ "@babel/plugin-transform-classes": "^7.0.0",
+ "@babel/plugin-transform-computed-properties": "^7.0.0",
+ "@babel/plugin-transform-destructuring": "^7.0.0",
+ "@babel/plugin-transform-duplicate-keys": "^7.0.0",
+ "@babel/plugin-transform-exponentiation-operator": "^7.0.0",
+ "@babel/plugin-transform-for-of": "^7.0.0",
+ "@babel/plugin-transform-function-name": "^7.0.0",
+ "@babel/plugin-transform-instanceof": "^7.0.0",
+ "@babel/plugin-transform-literals": "^7.0.0",
+ "@babel/plugin-transform-modules-amd": "^7.0.0",
+ "@babel/plugin-transform-object-super": "^7.0.0",
+ "@babel/plugin-transform-parameters": "^7.0.0",
+ "@babel/plugin-transform-regenerator": "^7.0.0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0",
+ "@babel/plugin-transform-spread": "^7.0.0",
+ "@babel/plugin-transform-sticky-regex": "^7.0.0",
+ "@babel/plugin-transform-template-literals": "^7.0.0",
+ "@babel/plugin-transform-typeof-symbol": "^7.0.0",
+ "@babel/plugin-transform-unicode-regex": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@polymer/esm-amd-loader": "^1.0.0",
+ "@types/babel-types": "^6.25.1",
+ "@types/babylon": "^6.16.2",
+ "@types/gulp-if": "0.0.33",
+ "@types/html-minifier": "^3.5.1",
+ "@types/is-windows": "^0.2.0",
+ "@types/mz": "0.0.31",
+ "@types/parse5": "^2.2.34",
+ "@types/resolve": "0.0.7",
+ "@types/uuid": "^3.4.3",
+ "@types/vinyl": "^2.0.0",
+ "@types/vinyl-fs": "^2.4.8",
+ "babel-plugin-minify-guarded-expressions": "=0.4.1",
+ "babel-preset-minify": "=0.4.0-alpha.caaefb4c",
+ "babylon": "^7.0.0-beta.42",
+ "css-slam": "^2.1.2",
+ "dom5": "^3.0.0",
+ "gulp-if": "^2.0.2",
+ "html-minifier": "^3.5.10",
+ "matcher": "^1.1.0",
+ "multipipe": "^1.0.2",
+ "mz": "^2.6.0",
+ "parse5": "^4.0.0",
+ "plylog": "^1.0.0",
+ "polymer-analyzer": "^3.1.3",
+ "polymer-bundler": "^4.0.3",
+ "polymer-project-config": "^4.0.0",
+ "regenerator-runtime": "^0.11.1",
+ "stream": "0.0.2",
+ "sw-precache": "^5.1.1",
+ "uuid": "^3.2.1",
+ "vinyl": "^1.2.0",
+ "vinyl-fs": "^2.4.4"
+ },
+ "dependencies": {
+ "@types/mz": {
+ "version": "0.0.31",
+ "resolved": "https://registry.npmjs.org/@types/mz/-/mz-0.0.31.tgz",
+ "integrity": "sha1-pNgMCC/v5x5Ap8DwfR5lVbu8e1I=",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/resolve": {
+ "version": "0.0.7",
+ "resolved": "http://registry.npmjs.org/@types/resolve/-/resolve-0.0.7.tgz",
+ "integrity": "sha512-GPewdjkb0Q76o459qgp6pBLzJj/bD3oveS2kfLhIkZ9U3t3AFKtl5DlFB6lGTw0iZmcmxoGC8lpLW3NNJKrN9A==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "arr-diff": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
+ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.0.1"
+ }
+ },
+ "array-unique": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "dev": true
+ },
+ "braces": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
+ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
+ "dev": true,
+ "requires": {
+ "expand-range": "^1.8.1",
+ "preserve": "^0.2.0",
+ "repeat-element": "^1.1.2"
+ }
+ },
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
+ "dev": true
+ },
+ "clone-stats": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz",
+ "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=",
+ "dev": true
+ },
+ "expand-brackets": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
+ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
+ "dev": true,
+ "requires": {
+ "is-posix-bracket": "^0.1.0"
+ }
+ },
+ "extglob": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
+ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ }
+ },
+ "glob-stream": {
+ "version": "5.3.5",
+ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz",
+ "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "glob": "^5.0.3",
+ "glob-parent": "^3.0.0",
+ "micromatch": "^2.3.7",
+ "ordered-read-streams": "^0.3.0",
+ "through2": "^0.6.0",
+ "to-absolute-glob": "^0.1.1",
+ "unique-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "through2": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
+ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
+ "dev": true,
+ "requires": {
+ "readable-stream": ">=1.0.33-1 <1.1.0-0",
+ "xtend": ">=4.0.0 <4.1.0-0"
+ }
+ }
+ }
+ },
+ "gulp-sourcemaps": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz",
+ "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "^1.1.1",
+ "graceful-fs": "^4.1.2",
+ "strip-bom": "^2.0.0",
+ "through2": "^2.0.0",
+ "vinyl": "^1.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "micromatch": {
+ "version": "2.3.11",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
+ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^2.0.0",
+ "array-unique": "^0.2.1",
+ "braces": "^1.8.2",
+ "expand-brackets": "^0.1.4",
+ "extglob": "^0.3.1",
+ "filename-regex": "^2.0.0",
+ "is-extglob": "^1.0.0",
+ "is-glob": "^2.0.1",
+ "kind-of": "^3.0.2",
+ "normalize-path": "^2.0.1",
+ "object.omit": "^2.0.0",
+ "parse-glob": "^3.0.4",
+ "regex-cache": "^0.4.2"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^1.0.0"
+ }
+ }
+ }
+ },
+ "ordered-read-streams": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz",
+ "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=",
+ "dev": true,
+ "requires": {
+ "is-stream": "^1.0.1",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "replace-ext": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz",
+ "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "vinyl": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz",
+ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=",
+ "dev": true,
+ "requires": {
+ "clone": "^1.0.0",
+ "clone-stats": "^0.0.1",
+ "replace-ext": "0.0.1"
+ }
+ },
+ "vinyl-fs": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz",
+ "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=",
+ "dev": true,
+ "requires": {
+ "duplexify": "^3.2.0",
+ "glob-stream": "^5.3.2",
+ "graceful-fs": "^4.0.0",
+ "gulp-sourcemaps": "1.6.0",
+ "is-valid-glob": "^0.3.0",
+ "lazystream": "^1.0.0",
+ "lodash.isequal": "^4.0.0",
+ "merge-stream": "^1.0.0",
+ "mkdirp": "^0.5.0",
+ "object-assign": "^4.0.0",
+ "readable-stream": "^2.0.4",
+ "strip-bom": "^2.0.0",
+ "strip-bom-stream": "^1.0.0",
+ "through2": "^2.0.0",
+ "through2-filter": "^2.0.0",
+ "vali-date": "^1.0.0",
+ "vinyl": "^1.0.0"
+ }
+ }
+ }
+ },
+ "polymer-bundler": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/polymer-bundler/-/polymer-bundler-4.0.4.tgz",
+ "integrity": "sha512-XrTn//eNxushnirhM/+mLpUYEHGwZRh0w79J8rnFjocdoAttGvEK74G2oYkSAIWJYKGfpwqUZGrNUsNXvf/EvQ==",
+ "dev": true,
+ "requires": {
+ "@types/babel-generator": "^6.25.1",
+ "@types/babel-traverse": "^6.25.3",
+ "babel-generator": "^6.26.1",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "clone": "^2.1.0",
+ "command-line-args": "^5.0.2",
+ "command-line-usage": "^5.0.5",
+ "dom5": "^3.0.0",
+ "espree": "^3.5.2",
+ "magic-string": "^0.22.4",
+ "mkdirp": "^0.5.1",
+ "parse5": "^4.0.0",
+ "polymer-analyzer": "^3.1.3",
+ "rollup": "^0.64.1",
+ "source-map": "^0.5.6",
+ "vscode-uri": "^1.0.1"
+ },
+ "dependencies": {
+ "acorn-jsx": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+ "dev": true,
+ "requires": {
+ "acorn": "^3.0.4"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "3.3.0",
+ "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+ "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+ "dev": true
+ }
+ }
+ },
+ "espree": {
+ "version": "3.5.4",
+ "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
+ "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
+ "dev": true,
+ "requires": {
+ "acorn": "^5.5.0",
+ "acorn-jsx": "^3.0.0"
+ }
+ },
+ "rollup": {
+ "version": "0.64.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.64.1.tgz",
+ "integrity": "sha512-+ThdVXrvonJdOTzyybMBipP0uz605Z8AnzWVY3rf+cSGnLO7uNkJBlN+9jXqWOomkvumXfm/esmBpA5d53qm7g==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "@types/node": "*"
+ }
+ }
+ }
+ },
+ "polymer-project-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/polymer-project-config/-/polymer-project-config-4.0.2.tgz",
+ "integrity": "sha512-nnxLkbpYYPIVBYooeercovQIWqq4coHzQ5PwK2+NxNpVWUJ5tW3OCDt46dqw3CtJNe4r/qIOkPgBJdVwXAAEmw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "^9.6.4",
+ "browser-capabilities": "^1.0.0",
+ "jsonschema": "^1.1.1",
+ "minimatch-all": "^1.1.0",
+ "plylog": "^0.5.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "9.6.35",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.35.tgz",
+ "integrity": "sha512-h5zvHS8wXHGa+Gcqs9K8vqCgOtqjr0+NqG/DDJmQIX1wpR9HivAfgV8bjcD3mGM4bPfQw5Aneb2Pn8355L83jA==",
+ "dev": true
+ },
+ "async": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz",
+ "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
+ "dev": true
+ },
+ "plylog": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/plylog/-/plylog-0.5.0.tgz",
+ "integrity": "sha1-yXbrodgNLdmRAF18EQ2vh0FUeI8=",
+ "dev": true,
+ "requires": {
+ "@types/node": "^4.2.3",
+ "@types/winston": "^2.2.0",
+ "winston": "^2.2.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-4.9.0.tgz",
+ "integrity": "sha512-xUFkZ+er9gUGw0x9qyfmr/Th0LuX6IB0m7HrRMB6sO6vcBVRFZ/3YV1EeiOC2fG50RX09avDfKwGBHOnPVxFeg==",
+ "dev": true
+ }
+ }
+ },
+ "winston": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz",
+ "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==",
+ "dev": true,
+ "requires": {
+ "async": "~1.0.0",
+ "colors": "1.0.x",
+ "cycle": "1.0.x",
+ "eyes": "0.1.x",
+ "isstream": "0.1.x",
+ "stack-trace": "0.0.x"
+ }
+ }
+ }
+ },
+ "polyserve": {
+ "version": "0.27.13",
+ "resolved": "https://registry.npmjs.org/polyserve/-/polyserve-0.27.13.tgz",
+ "integrity": "sha512-9YlIJRx4TJ07ywVHpK/+W4tEPMcoJ4oyJWHkSDrPck5jmT5bitCme8dONCSxERZrE4J8hagHE3oOPa5U9iFfYg==",
+ "dev": true,
+ "requires": {
+ "@types/compression": "^0.0.33",
+ "@types/content-type": "^1.1.0",
+ "@types/escape-html": "0.0.20",
+ "@types/express": "^4.0.36",
+ "@types/mime": "^2.0.0",
+ "@types/mz": "0.0.29",
+ "@types/opn": "^3.0.28",
+ "@types/parse5": "^2.2.34",
+ "@types/pem": "^1.8.1",
+ "@types/resolve": "0.0.6",
+ "@types/serve-static": "^1.7.31",
+ "@types/spdy": "^3.4.1",
+ "bower-config": "^1.4.1",
+ "browser-capabilities": "^1.0.0",
+ "command-line-args": "^5.0.2",
+ "command-line-usage": "^5.0.5",
+ "compression": "^1.6.2",
+ "content-type": "^1.0.2",
+ "escape-html": "^1.0.3",
+ "express": "^4.8.5",
+ "find-port": "^1.0.1",
+ "http-proxy-middleware": "^0.17.2",
+ "lru-cache": "^4.0.2",
+ "mime": "^2.3.1",
+ "mz": "^2.4.0",
+ "opn": "^3.0.2",
+ "pem": "^1.8.3",
+ "polymer-build": "^3.1.0",
+ "polymer-project-config": "^4.0.0",
+ "requirejs": "^2.3.4",
+ "resolve": "^1.5.0",
+ "send": "^0.16.2",
+ "spdy": "^3.3.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "mime": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz",
+ "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==",
+ "dev": true
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
+ "dev": true
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "dev": true
+ }
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "prepend-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+ "dev": true
+ },
+ "preserve": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
+ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
+ "dev": true
+ },
+ "pretty-bytes": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
+ "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=",
+ "dev": true
+ },
+ "pretty-hrtime": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
+ "dev": true
+ },
+ "private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
+ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+ "dev": true
+ },
+ "proxy-addr": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
+ "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
+ "dev": true,
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.8.0"
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.1.29",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
+ "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==",
+ "dev": true,
+ "optional": true
+ },
+ "pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "dev": true,
+ "requires": {
+ "duplexify": "^3.6.0",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+ "dev": true,
+ "optional": true
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ },
+ "randomatic": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
+ "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==",
+ "dev": true,
+ "requires": {
+ "is-number": "^4.0.0",
+ "kind-of": "^6.0.0",
+ "math-random": "^1.0.1"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true
+ }
+ }
+ },
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+ "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.0.0",
+ "http-errors": "1.6.3",
+ "iconv-lite": "0.4.23",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
+ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.0.3",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ }
+ }
+ },
+ "rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "dev": true,
+ "requires": {
+ "indent-string": "^2.1.0",
+ "strip-indent": "^1.0.1"
+ },
+ "dependencies": {
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1"
+ }
+ }
+ }
+ },
+ "reduce-flatten": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz",
+ "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=",
+ "dev": true
+ },
+ "regenerate": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
+ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
+ "dev": true
+ },
+ "regenerate-unicode-properties": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz",
+ "integrity": "sha512-s5NGghCE4itSlUS+0WUj88G6cfMVMmH8boTPNvABf8od+2dhT9WDlWu8n01raQAJZMOK8Ch6jSexaRO7swd6aw==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.3.tgz",
+ "integrity": "sha512-5ipTrZFSq5vU2YoGoww4uaRVAK4wyYC4TSICibbfEPOruUu8FFP7ErV0BjmbIOEpn3O/k9na9UEdYR/3m7N6uA==",
+ "dev": true,
+ "requires": {
+ "private": "^0.1.6"
+ }
+ },
+ "regex-cache": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
+ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
+ "dev": true,
+ "requires": {
+ "is-equal-shallow": "^0.1.3"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "regexpp": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+ "dev": true
+ },
+ "regexpu-core": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.2.0.tgz",
+ "integrity": "sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0",
+ "regenerate-unicode-properties": "^7.0.0",
+ "regjsgen": "^0.4.0",
+ "regjsparser": "^0.3.0",
+ "unicode-match-property-ecmascript": "^1.0.4",
+ "unicode-match-property-value-ecmascript": "^1.0.2"
+ }
+ },
+ "registry-auth-token": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
+ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
+ "dev": true,
+ "requires": {
+ "rc": "^1.1.6",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "registry-url": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+ "dev": true,
+ "requires": {
+ "rc": "^1.0.1"
+ }
+ },
+ "regjsgen": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.4.0.tgz",
+ "integrity": "sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.3.0.tgz",
+ "integrity": "sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA==",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+ "dev": true
+ }
+ }
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
+ "dev": true
+ },
+ "remove-bom-buffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
+ "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5",
+ "is-utf8": "^0.2.1"
+ }
+ },
+ "remove-bom-stream": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz",
+ "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=",
+ "dev": true,
+ "requires": {
+ "remove-bom-buffer": "^3.0.0",
+ "safe-buffer": "^5.1.0",
+ "through2": "^2.0.3"
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "repeat-element": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
+ "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
+ "replace-ext": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
+ "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
+ "dev": true
+ },
+ "replace-homedir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
+ "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1",
+ "is-absolute": "^1.0.0",
+ "remove-trailing-separator": "^1.1.0"
+ }
+ },
+ "request": {
+ "version": "2.88.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.0",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.4.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true,
+ "optional": true
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "dev": true
+ },
+ "require-uncached": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+ "dev": true,
+ "requires": {
+ "caller-path": "^0.1.0",
+ "resolve-from": "^1.0.0"
+ }
+ },
+ "requirejs": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
+ "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
+ "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.5"
+ }
+ },
+ "resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+ "dev": true
+ },
+ "resolve-options": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
+ "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=",
+ "dev": true,
+ "requires": {
+ "value-or-function": "^3.0.0"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.5"
+ }
+ },
+ "rollup": {
+ "version": "0.45.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.45.2.tgz",
+ "integrity": "sha512-2+bq5GQSrocdhr+M92mOQRmF1evtLRzv9NdmEC2wo7BILvTG8irHCtD0q+zg8ikNu63iJicdN5IzyxAXRTFKOQ==",
+ "dev": true,
+ "requires": {
+ "source-map-support": "^0.4.0"
+ }
+ },
+ "rollup-stream": {
+ "version": "1.23.1",
+ "resolved": "https://registry.npmjs.org/rollup-stream/-/rollup-stream-1.23.1.tgz",
+ "integrity": "sha512-niUbTM3sqckz1FNebsSiN+koCR7RdgrRZ2HCcR4V2DT9PSs53tB4z1xzdTGxrX6bo3QT00R2sQA5n1jr/to69Q==",
+ "dev": true,
+ "requires": {
+ "rollup": "^0.45.1"
+ }
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "run-sequence": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz",
+ "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "fancy-log": "^1.3.2",
+ "plugin-error": "^0.1.2"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "rxjs": {
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "samsam": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz",
+ "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=",
+ "dev": true
+ },
+ "sauce-connect-launcher": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sauce-connect-launcher/-/sauce-connect-launcher-1.2.4.tgz",
+ "integrity": "sha512-X2vfwulR6brUGiicXKxPm1GJ7dBEeP1II450Uv4bHGrcGOapZNgzJvn9aioea5IC5BPp/7qjKdE3xbbTBIVXMA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "adm-zip": "~0.4.3",
+ "async": "^2.1.2",
+ "https-proxy-agent": "^2.2.1",
+ "lodash": "^4.16.6",
+ "rimraf": "^2.5.4"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ }
+ }
+ },
+ "select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+ "dev": true
+ },
+ "selenium-standalone": {
+ "version": "6.15.3",
+ "resolved": "https://registry.npmjs.org/selenium-standalone/-/selenium-standalone-6.15.3.tgz",
+ "integrity": "sha512-BFzdXRB8yYPfCRcLxpJDBLWM0akTBP/x0hB0g+8AR7N/PEvbW39dM/hq0Yp1R0hihVQTPI3KkAJpW6h/f41S4g==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "async": "^2.1.4",
+ "commander": "^2.9.0",
+ "cross-spawn": "^6.0.0",
+ "debug": "^4.0.0",
+ "lodash": "^4.17.4",
+ "minimist": "^1.2.0",
+ "mkdirp": "^0.5.1",
+ "progress": "2.0.0",
+ "request": "2.88.0",
+ "tar-stream": "1.6.1",
+ "urijs": "^1.18.4",
+ "which": "^1.2.12",
+ "yauzl": "^2.5.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ },
+ "debug": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
+ "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true,
+ "optional": true
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "semver": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+ "dev": true
+ },
+ "semver-diff": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+ "dev": true,
+ "requires": {
+ "semver": "^5.0.3"
+ }
+ },
+ "semver-greatest-satisfied-range": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz",
+ "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=",
+ "dev": true,
+ "requires": {
+ "sver-compat": "^1.5.0"
+ }
+ },
+ "send": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.11.1.tgz",
+ "integrity": "sha1-G+q/1C+eJwn5kCivMHisErRwktU=",
+ "dev": true,
+ "requires": {
+ "debug": "~2.1.1",
+ "depd": "~1.0.0",
+ "destroy": "1.0.3",
+ "escape-html": "1.0.1",
+ "etag": "~1.5.1",
+ "fresh": "0.2.4",
+ "mime": "1.2.11",
+ "ms": "0.7.0",
+ "on-finished": "~2.2.0",
+ "range-parser": "~1.0.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.1.3",
+ "resolved": "http://registry.npmjs.org/debug/-/debug-2.1.3.tgz",
+ "integrity": "sha1-zoqxte6PvuK/o7Yzyrk9NmtjQY4=",
+ "dev": true,
+ "requires": {
+ "ms": "0.7.0"
+ }
+ },
+ "depd": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npmjs.org/depd/-/depd-1.0.1.tgz",
+ "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=",
+ "dev": true
+ },
+ "destroy": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz",
+ "integrity": "sha1-tDO0ck5x/YVR2YhRdIUcX8N34sk=",
+ "dev": true
+ },
+ "ee-first": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz",
+ "integrity": "sha1-ag18YiHkkP7v2S7D9EHJzozQl/Q=",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz",
+ "integrity": "sha1-GBoobq05ejmpKFfPsdQwUuNWv/A=",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.5.1.tgz",
+ "integrity": "sha1-VMUN4E7kJpVWKSWsVmWIKRvn6eo=",
+ "dev": true,
+ "requires": {
+ "crc": "3.2.1"
+ }
+ },
+ "fresh": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz",
+ "integrity": "sha1-NYJJkgbJcjcUGQ7ddLRgT+tKYUw=",
+ "dev": true
+ },
+ "mime": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
+ "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=",
+ "dev": true
+ },
+ "ms": {
+ "version": "0.7.0",
+ "resolved": "http://registry.npmjs.org/ms/-/ms-0.7.0.tgz",
+ "integrity": "sha1-hlvpTC5zl62KV9pqYzpuLzB5i4M=",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.1.tgz",
+ "integrity": "sha1-XIXBzDYpn3gCllP2Z/J7a5nrwCk=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz",
+ "integrity": "sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU=",
+ "dev": true
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.2",
+ "send": "0.16.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ }
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "dev": true
+ }
+ }
+ },
+ "server-destroy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
+ "integrity": "sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0=",
+ "dev": true
+ },
+ "serviceworker-cache-polyfill": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz",
+ "integrity": "sha1-3hnuc77yGrPAdAo3sz22JGS6ves=",
+ "dev": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
+ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ },
+ "shady-css-parser": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/shady-css-parser/-/shady-css-parser-0.1.0.tgz",
+ "integrity": "sha512-irfJUUkEuDlNHKZNAp2r7zOyMlmbfVJ+kWSfjlCYYUx/7dJnANLCyTzQZsuxy5NJkvtNwSxY5Gj8MOlqXUQPyA==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.3.1"
+ },
+ "dependencies": {
+ "is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "dev": true
+ }
+ }
+ },
+ "sinon": {
+ "version": "1.17.7",
+ "resolved": "http://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz",
+ "integrity": "sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=",
+ "dev": true,
+ "requires": {
+ "formatio": "1.1.1",
+ "lolex": "1.3.2",
+ "samsam": "1.1.2",
+ "util": ">=0.10.3 <1"
+ }
+ },
+ "sinon-chai": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.14.0.tgz",
+ "integrity": "sha512-9stIF1utB0ywNHNT7RgiXbdmen8QDCRsrTjw+G9TgKt1Yexjiv8TOWZ6WHsTPz57Yky3DIswZvEqX8fpuHNDtQ==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
+ "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "sntp": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
+ "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
+ "dev": true,
+ "requires": {
+ "hoek": "4.x.x"
+ }
+ },
+ "socket.io": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
+ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
+ "dev": true,
+ "requires": {
+ "debug": "~3.1.0",
+ "engine.io": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "socket.io-adapter": "~1.1.0",
+ "socket.io-client": "2.1.1",
+ "socket.io-parser": "~3.2.0"
+ }
+ },
+ "socket.io-adapter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
+ "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=",
+ "dev": true
+ },
+ "socket.io-client": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
+ "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "dev": true,
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "engine.io-client": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.2.0",
+ "to-array": "0.1.4"
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+ "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz",
+ "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.0.0",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+ "dev": true
+ },
+ "sparkles": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz",
+ "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
+ "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
+ "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
+ "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==",
+ "dev": true
+ },
+ "spdy": {
+ "version": "3.4.7",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz",
+ "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.8",
+ "handle-thing": "^1.2.5",
+ "http-deceiver": "^1.2.7",
+ "safe-buffer": "^5.0.1",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^2.0.18"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "spdy-transport": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz",
+ "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.8",
+ "detect-node": "^2.0.3",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.1",
+ "readable-stream": "^2.2.9",
+ "safe-buffer": "^5.0.1",
+ "wbuf": "^1.7.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz",
+ "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "stable": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+ "dev": true
+ },
+ "stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+ "dev": true
+ },
+ "stacky": {
+ "version": "1.3.1",
+ "resolved": "http://registry.npmjs.org/stacky/-/stacky-1.3.1.tgz",
+ "integrity": "sha1-PxF+UYe5pz0j+HbWnwXIWxGAShI=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.1",
+ "lodash": "^3.0.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+ "dev": true
+ },
+ "stream": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz",
+ "integrity": "sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8=",
+ "dev": true,
+ "requires": {
+ "emitter-component": "^1.1.1"
+ }
+ },
+ "stream-counter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stream-counter/-/stream-counter-1.0.0.tgz",
+ "integrity": "sha1-kc8lac5NxQYf6816yyY5SloRR1E=",
+ "dev": true
+ },
+ "stream-exhaust": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
+ "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==",
+ "dev": true
+ },
+ "stream-shift": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+ "dev": true
+ },
+ "streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "stringstream": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz",
+ "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ }
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-bom-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz",
+ "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=",
+ "dev": true,
+ "requires": {
+ "first-chunk-stream": "^1.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "strip-bom-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
+ "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=",
+ "dev": true
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+ "dev": true
+ },
+ "strip-indent": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
+ "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "sver-compat": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz",
+ "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "^2.0.1",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "sw-precache": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.1.tgz",
+ "integrity": "sha512-8FAy+BP/FXE+ILfiVTt+GQJ6UEf4CVHD9OfhzH0JX+3zoy2uFk7Vn9EfXASOtVmmIVbL3jE/W8Z66VgPSZcMhw==",
+ "dev": true,
+ "requires": {
+ "dom-urls": "^1.1.0",
+ "es6-promise": "^4.0.5",
+ "glob": "^7.1.1",
+ "lodash.defaults": "^4.2.0",
+ "lodash.template": "^4.4.0",
+ "meow": "^3.7.0",
+ "mkdirp": "^0.5.1",
+ "pretty-bytes": "^4.0.2",
+ "sw-toolbox": "^3.4.0",
+ "update-notifier": "^2.3.0"
+ }
+ },
+ "sw-toolbox": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz",
+ "integrity": "sha1-Jt8dHHA0hljk3qKIQxkUm3sxg7U=",
+ "dev": true,
+ "requires": {
+ "path-to-regexp": "^1.0.1",
+ "serviceworker-cache-polyfill": "^4.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+ "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+ "dev": true,
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ }
+ }
+ },
+ "table": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.1.0.tgz",
+ "integrity": "sha512-e542in22ZLhD/fOIuXs/8yDZ9W61ltF8daM88rkRNtgTIct+vI2fTnAyu/Db2TCfEcI8i7mjZz6meLq0nW7TYg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.5.3",
+ "lodash": "^4.17.10",
+ "slice-ansi": "1.0.0",
+ "string-width": "^2.1.1"
+ }
+ },
+ "table-layout": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.4.tgz",
+ "integrity": "sha512-uNaR3SRMJwfdp9OUr36eyEi6LLsbcTqTO/hfTsNviKsNeyMBPICJCC7QXRF3+07bAP6FRwA8rczJPBqXDc0CkQ==",
+ "dev": true,
+ "requires": {
+ "array-back": "^2.0.0",
+ "deep-extend": "~0.6.0",
+ "lodash.padend": "^4.6.1",
+ "typical": "^2.6.1",
+ "wordwrapjs": "^3.0.0"
+ }
+ },
+ "tar-stream": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz",
+ "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==",
+ "dev": true,
+ "requires": {
+ "bl": "^1.0.0",
+ "buffer-alloc": "^1.1.0",
+ "end-of-stream": "^1.0.0",
+ "fs-constants": "^1.0.0",
+ "readable-stream": "^2.3.0",
+ "to-buffer": "^1.1.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "temp": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
+ "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "os-tmpdir": "^1.0.0",
+ "rimraf": "~2.2.6"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "term-size": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
+ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+ "dev": true,
+ "requires": {
+ "execa": "^0.7.0"
+ }
+ },
+ "ternary-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-2.0.1.tgz",
+ "integrity": "sha1-Bk5Im0tb9gumpre8fy9cJ07Pgmk=",
+ "dev": true,
+ "requires": {
+ "duplexify": "^3.5.0",
+ "fork-stream": "^0.0.4",
+ "merge-stream": "^1.0.0",
+ "through2": "^2.0.1"
+ }
+ },
+ "test-value": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz",
+ "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==",
+ "dev": true,
+ "requires": {
+ "array-back": "^2.0.0",
+ "typical": "^2.6.1"
+ }
+ },
+ "text-encoding": {
+ "version": "0.6.4",
+ "resolved": "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
+ "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
+ "dev": true
+ },
+ "text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+ "dev": true
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "thenify": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
+ "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+ "dev": true,
+ "requires": {
+ "thenify": ">= 3.1.0 < 4"
+ }
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
+ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.1.5",
+ "xtend": "~4.0.1"
+ }
+ },
+ "through2-filter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz",
+ "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=",
+ "dev": true,
+ "requires": {
+ "through2": "~2.0.0",
+ "xtend": "~4.0.0"
+ }
+ },
+ "time-stamp": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
+ "dev": true
+ },
+ "timed-out": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
+ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
+ "dev": true
+ },
+ "timers-ext": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz",
+ "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==",
+ "dev": true,
+ "requires": {
+ "es5-ext": "~0.10.14",
+ "next-tick": "1"
+ }
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-absolute-glob": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz",
+ "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=",
+ "dev": true
+ },
+ "to-buffer": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
+ "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "to-through": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz",
+ "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=",
+ "dev": true,
+ "requires": {
+ "through2": "^2.0.3"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "psl": "^1.1.24",
+ "punycode": "^1.4.1"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "tr46": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+ "dev": true
+ },
+ "trim-right": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+ "dev": true
+ },
+ "triple-beam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-detect": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
+ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.16",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
+ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.18"
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "typical": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
+ "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=",
+ "dev": true
+ },
+ "ua-parser-js": {
+ "version": "0.7.19",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
+ "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "3.4.9",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
+ "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
+ "dev": true,
+ "requires": {
+ "commander": "~2.17.1",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
+ "dev": true
+ },
+ "unc-path-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+ "dev": true
+ },
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+ "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=",
+ "dev": true
+ },
+ "undertaker": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz",
+ "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.0.1",
+ "arr-map": "^2.0.0",
+ "bach": "^1.0.0",
+ "collection-map": "^1.0.0",
+ "es6-weak-map": "^2.0.1",
+ "last-run": "^1.1.0",
+ "object.defaults": "^1.0.0",
+ "object.reduce": "^1.0.0",
+ "undertaker-registry": "^1.0.0"
+ }
+ },
+ "undertaker-registry": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
+ "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=",
+ "dev": true
+ },
+ "unicode-canonical-property-names-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==",
+ "dev": true
+ },
+ "unicode-match-property-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
+ "dev": true,
+ "requires": {
+ "unicode-canonical-property-names-ecmascript": "^1.0.4",
+ "unicode-property-aliases-ecmascript": "^1.0.4"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz",
+ "integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==",
+ "dev": true
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==",
+ "dev": true
+ },
+ "union-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
+ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^0.4.3"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "set-value": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
+ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.1",
+ "to-object-path": "^0.3.0"
+ }
+ }
+ }
+ },
+ "unique-stream": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz",
+ "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=",
+ "dev": true,
+ "requires": {
+ "json-stable-stringify": "^1.0.0",
+ "through2-filter": "^2.0.0"
+ }
+ },
+ "unique-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
+ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+ "dev": true,
+ "requires": {
+ "crypto-random-string": "^1.0.0"
+ }
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ }
+ }
+ },
+ "untildify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz",
+ "integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=",
+ "dev": true,
+ "requires": {
+ "os-homedir": "^1.0.0"
+ }
+ },
+ "unzip-response": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
+ "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=",
+ "dev": true
+ },
+ "upath": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
+ "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
+ "dev": true
+ },
+ "update-notifier": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
+ "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
+ "dev": true,
+ "requires": {
+ "boxen": "^1.2.1",
+ "chalk": "^2.0.1",
+ "configstore": "^3.0.0",
+ "import-lazy": "^2.1.0",
+ "is-ci": "^1.0.10",
+ "is-installed-globally": "^0.1.0",
+ "is-npm": "^1.0.0",
+ "latest-version": "^3.0.0",
+ "semver-diff": "^2.0.0",
+ "xdg-basedir": "^3.0.0"
+ }
+ },
+ "upper-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+ "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urijs": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.1.tgz",
+ "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==",
+ "dev": true
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "url-parse-lax": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
+ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+ "dev": true,
+ "requires": {
+ "prepend-http": "^1.0.1"
+ }
+ },
+ "use": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz",
+ "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.2"
+ }
+ },
+ "util": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.11.0.tgz",
+ "integrity": "sha512-5n12uMzKCjvB2HPFHnbQSjaqAa98L5iIXmHrZCLavuZVe0qe/SJGbDGWlpaHk5lnBkWRDO+dRu1/PgmUYKPPTw==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+ "dev": true
+ },
+ "v8flags": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.1.tgz",
+ "integrity": "sha512-iw/1ViSEaff8NJ3HLyEjawk/8hjJib3E7pvG4pddVXfUg1983s3VGsiClDjhK64MQVDGqc1Q8r18S4VKQZS9EQ==",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "vali-date": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
+ "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
+ "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "value-or-function": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
+ "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
+ "dev": true
+ },
+ "vargs": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/vargs/-/vargs-0.1.0.tgz",
+ "integrity": "sha1-a2GE2mUgzDIEzhtAfKwm2SYJ6/8=",
+ "dev": true
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "vinyl": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz",
+ "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.1",
+ "clone-buffer": "^1.0.0",
+ "clone-stats": "^1.0.0",
+ "cloneable-readable": "^1.0.0",
+ "remove-trailing-separator": "^1.0.1",
+ "replace-ext": "^1.0.0"
+ }
+ },
+ "vinyl-buffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz",
+ "integrity": "sha1-lsGjR5uMU5JULGEgKQE7Wyf4i78=",
+ "dev": true,
+ "requires": {
+ "bl": "^1.2.1",
+ "through2": "^2.0.3"
+ }
+ },
+ "vinyl-fs": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
+ "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==",
+ "dev": true,
+ "requires": {
+ "fs-mkdirp-stream": "^1.0.0",
+ "glob-stream": "^6.1.0",
+ "graceful-fs": "^4.0.0",
+ "is-valid-glob": "^1.0.0",
+ "lazystream": "^1.0.0",
+ "lead": "^1.0.0",
+ "object.assign": "^4.0.4",
+ "pumpify": "^1.3.5",
+ "readable-stream": "^2.3.3",
+ "remove-bom-buffer": "^3.0.0",
+ "remove-bom-stream": "^1.2.0",
+ "resolve-options": "^1.1.0",
+ "through2": "^2.0.0",
+ "to-through": "^2.0.0",
+ "value-or-function": "^3.0.0",
+ "vinyl": "^2.0.0",
+ "vinyl-sourcemap": "^1.1.0"
+ },
+ "dependencies": {
+ "is-valid-glob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
+ "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=",
+ "dev": true
+ }
+ }
+ },
+ "vinyl-source-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz",
+ "integrity": "sha1-84pa+53R6Ttl1VBGmsYYKsT1S44=",
+ "dev": true,
+ "requires": {
+ "through2": "^2.0.3",
+ "vinyl": "^2.1.0"
+ }
+ },
+ "vinyl-sourcemap": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz",
+ "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=",
+ "dev": true,
+ "requires": {
+ "append-buffer": "^1.0.2",
+ "convert-source-map": "^1.5.0",
+ "graceful-fs": "^4.1.6",
+ "normalize-path": "^2.1.1",
+ "now-and-later": "^2.0.0",
+ "remove-bom-buffer": "^3.0.0",
+ "vinyl": "^2.0.0"
+ }
+ },
+ "vinyl-sourcemaps-apply": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz",
+ "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.1"
+ }
+ },
+ "vlq": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
+ "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==",
+ "dev": true
+ },
+ "vscode-uri": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.6.tgz",
+ "integrity": "sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww==",
+ "dev": true
+ },
+ "wbuf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "dev": true,
+ "requires": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "wct-browser-legacy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wct-browser-legacy/-/wct-browser-legacy-1.0.2.tgz",
+ "integrity": "sha512-23rbZwBh/DxWU36htJN9lsyBq3NxgVbuyMUq7fgFP6ZVTel+uFWO6LPXPoZQ6VyvXvlUYLE5PxY+ZdJ88a4COw==",
+ "dev": true,
+ "requires": {
+ "@polymer/polymer": "^3.0.0",
+ "@polymer/sinonjs": "^1.14.1",
+ "@polymer/test-fixture": "^3.0.0-pre.1",
+ "@webcomponents/webcomponentsjs": "^2.0.0",
+ "accessibility-developer-tools": "^2.12.0",
+ "async": "^1.5.2",
+ "chai": "^3.5.0",
+ "lodash": "^3.10.1",
+ "mocha": "^3.4.2",
+ "sinon": "^1.17.1",
+ "sinon-chai": "^2.10.0",
+ "stacky": "^1.3.1"
+ },
+ "dependencies": {
+ "@webcomponents/webcomponentsjs": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.1.3.tgz",
+ "integrity": "sha512-0UHJNY88lR3pnEYtBVT7F8cuuxOiITQGWJa0LxoELqkBSB7IabzJFOj5K99PajD3CGAsWpjB0CAeijfe376Y1w==",
+ "dev": true
+ },
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+ "dev": true
+ }
+ }
+ },
+ "wct-local": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/wct-local/-/wct-local-2.1.3.tgz",
+ "integrity": "sha512-pOGyT07Bh6TAJVk7E3P+n5RybjtYBqm745fCfY5vuhQd069mN1WUlivMgZzWfJuvuXVpKFkAERrN/+tTjbmgmQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@types/express": "^4.0.30",
+ "@types/freeport": "^1.0.19",
+ "@types/launchpad": "^0.6.0",
+ "@types/which": "^1.3.1",
+ "chalk": "^2.3.0",
+ "cleankill": "^2.0.0",
+ "freeport": "^1.0.4",
+ "launchpad": "^0.7.0",
+ "selenium-standalone": "^6.7.0",
+ "which": "^1.0.8"
+ }
+ },
+ "wct-sauce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wct-sauce/-/wct-sauce-2.1.0.tgz",
+ "integrity": "sha512-c3R4PJcbpS7Gxv2vZ4HDAqpXV6cT9peslAWMU7hHH9PMhKDPbn8RNa6E4DVL0tOmZznB+3cRmtZ6+vJ/aDwu1A==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "cleankill": "^2.0.0",
+ "lodash": "^4.17.10",
+ "request": "^2.85.0",
+ "sauce-connect-launcher": "^1.0.0",
+ "temp": "^0.8.1",
+ "uuid": "^3.2.1"
+ }
+ },
+ "wd": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/wd/-/wd-1.11.0.tgz",
+ "integrity": "sha512-h2EBfJvmsWocIjOOg5BsHh9IJKrqZDG4Az4jEZhFugEH7sOPcX6feZQ30aFuktqDI0jquarZJmNpA6V0A0Q7Mg==",
+ "dev": true,
+ "requires": {
+ "archiver": "2.1.1",
+ "async": "2.0.1",
+ "lodash": "4.17.10",
+ "mkdirp": "^0.5.1",
+ "q": "1.4.1",
+ "request": "2.85.0",
+ "vargs": "0.1.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "dev": true,
+ "requires": {
+ "co": "^4.6.0",
+ "fast-deep-equal": "^1.0.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.3.0"
+ }
+ },
+ "async": {
+ "version": "2.0.1",
+ "resolved": "http://registry.npmjs.org/async/-/async-2.0.1.tgz",
+ "integrity": "sha1-twnMAoCpw28J9FNr6CPIOKkEniU=",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.8.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
+ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+ "dev": true,
+ "requires": {
+ "ajv": "^5.1.0",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+ "dev": true
+ },
+ "lodash": {
+ "version": "4.17.10",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
+ "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+ "dev": true
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "q": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+ "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
+ "dev": true
+ },
+ "request": {
+ "version": "2.85.0",
+ "resolved": "http://registry.npmjs.org/request/-/request-2.85.0.tgz",
+ "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.6.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.5",
+ "extend": "~3.0.1",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.1",
+ "har-validator": "~5.0.3",
+ "hawk": "~6.0.2",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.17",
+ "oauth-sign": "~0.8.2",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.1",
+ "safe-buffer": "^5.1.1",
+ "stringstream": "~0.0.5",
+ "tough-cookie": "~2.3.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.1.0"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
+ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
+ "dev": true,
+ "requires": {
+ "punycode": "^1.4.1"
+ }
+ }
+ }
+ },
+ "web-component-tester": {
+ "version": "6.9.0",
+ "resolved": "https://registry.npmjs.org/web-component-tester/-/web-component-tester-6.9.0.tgz",
+ "integrity": "sha512-l5KzzhlHJQ+I2qtVlo5cdUZdXenfb70mNJdHdOXc9YdgpUdkT1kQ9cRKWguaVpXQmphcpWjw8KOgkf5oUkafUw==",
+ "dev": true,
+ "requires": {
+ "@polymer/sinonjs": "^1.14.1",
+ "@polymer/test-fixture": "^0.0.3",
+ "@webcomponents/webcomponentsjs": "^1.0.7",
+ "accessibility-developer-tools": "^2.12.0",
+ "async": "^2.4.1",
+ "body-parser": "^1.17.2",
+ "bower-config": "^1.4.0",
+ "chalk": "^1.1.3",
+ "cleankill": "^2.0.0",
+ "express": "^4.15.3",
+ "findup-sync": "^2.0.0",
+ "glob": "^7.1.2",
+ "lodash": "^3.10.1",
+ "multer": "^1.3.0",
+ "nomnom": "^1.8.1",
+ "polyserve": "^0.27.13",
+ "resolve": "^1.5.0",
+ "semver": "^5.3.0",
+ "send": "^0.11.1",
+ "server-destroy": "^1.0.1",
+ "sinon": "^2.3.5",
+ "sinon-chai": "^2.10.0",
+ "socket.io": "^2.0.3",
+ "stacky": "^1.3.1",
+ "update-notifier": "^2.2.0",
+ "wct-local": "^2.1.1",
+ "wct-sauce": "^2.0.2",
+ "wd": "^1.2.0"
+ },
+ "dependencies": {
+ "@polymer/test-fixture": {
+ "version": "0.0.3",
+ "resolved": "http://registry.npmjs.org/@polymer/test-fixture/-/test-fixture-0.0.3.tgz",
+ "integrity": "sha1-REN1JpfU2Sk7vEEuoLXk00HxSdk=",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ }
+ }
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "formatio": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz",
+ "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=",
+ "dev": true,
+ "requires": {
+ "samsam": "1.x"
+ }
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+ "dev": true
+ },
+ "lolex": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz",
+ "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+ "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+ "dev": true,
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ },
+ "samsam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz",
+ "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==",
+ "dev": true
+ },
+ "sinon": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.4.1.tgz",
+ "integrity": "sha512-vFTrO9Wt0ECffDYIPSP/E5bBugt0UjcBQOfQUMh66xzkyPEnhl/vM2LRZi2ajuTdkH07sA6DzrM6KvdvGIH8xw==",
+ "dev": true,
+ "requires": {
+ "diff": "^3.1.0",
+ "formatio": "1.2.0",
+ "lolex": "^1.6.0",
+ "native-promise-only": "^0.8.1",
+ "path-to-regexp": "^1.7.0",
+ "samsam": "^1.1.3",
+ "text-encoding": "0.6.4",
+ "type-detect": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ }
+ }
+ },
+ "webidl-conversions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
+ "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
+ "dev": true,
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "which": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
+ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+ "dev": true
+ },
+ "widest-line": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
+ "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1"
+ }
+ },
+ "winston": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz",
+ "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.0",
+ "diagnostics": "^1.1.1",
+ "is-stream": "^1.1.0",
+ "logform": "^1.9.1",
+ "one-time": "0.0.4",
+ "readable-stream": "^2.3.6",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.2.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "winston-transport": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.2.0.tgz",
+ "integrity": "sha512-0R1bvFqxSlK/ZKTH86nymOuKv/cT1PQBMuDdA7k7f0S9fM44dNH6bXnuxwXPrN8lefJgtZq08BKdyZ0DZIy/rg==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.3.6",
+ "triple-beam": "^1.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ },
+ "wordwrapjs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz",
+ "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==",
+ "dev": true,
+ "requires": {
+ "reduce-flatten": "^1.0.1",
+ "typical": "^2.6.1"
+ }
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "write-file-atomic": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
+ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ }
+ },
+ "xdg-basedir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
+ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
+ "dev": true
+ },
+ "xmlbuilder": {
+ "version": "8.2.2",
+ "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
+ "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=",
+ "dev": true,
+ "optional": true
+ },
+ "xmldom": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
+ "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=",
+ "dev": true,
+ "optional": true
+ },
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+ "dev": true
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
+ "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^3.0.0",
+ "cliui": "^3.2.0",
+ "decamelize": "^1.1.1",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^1.4.0",
+ "read-pkg-up": "^1.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^1.0.2",
+ "which-module": "^1.0.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "^5.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
+ "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^3.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+ "dev": true
+ }
+ }
+ },
+ "yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
+ "dev": true
+ },
+ "zip-stream": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz",
+ "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=",
+ "dev": true,
+ "requires": {
+ "archiver-utils": "^1.3.0",
+ "compress-commons": "^1.2.0",
+ "lodash": "^4.8.0",
+ "readable-stream": "^2.0.0"
+ }
+ }
+ }
+}
diff --git a/catapult/third_party/polymer/components/shadycss/package.json b/catapult/third_party/polymer/components/shadycss/package.json
new file mode 100644
index 00000000..3d36d67e
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/package.json
@@ -0,0 +1,71 @@
+{
+ "name": "@webcomponents/shadycss",
+ "version": "1.7.1",
+ "description": "Styling helpers for ShadyDOM",
+ "main": "shadycss.min.js",
+ "directories": {
+ "test": "tests"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/webcomponents/shadycss.git"
+ },
+ "author": "The Polymer Project Authors (https://polymer.github.io/AUTHORS.txt)",
+ "license": "BSD-3-Clause",
+ "keywords": [
+ "shady-css",
+ "shadycss",
+ "shadow-css",
+ "shadowcss",
+ "web-components",
+ "webcomponents",
+ "polyfill",
+ "shim"
+ ],
+ "bugs": {
+ "url": "https://github.com/webcomponents/shadycss/issues"
+ },
+ "scripts": {
+ "build": "gulp",
+ "debug": "gulp debug",
+ "lint": "eslint src tests entrypoints",
+ "test": "npm run lint && gulp && wct",
+ "prepack": "gulp closure"
+ },
+ "files": [
+ "apply-shim.html",
+ "apply-shim.min.js*",
+ "custom-style-interface.html",
+ "custom-style-interface.min.js*",
+ "scoping-shim.min.js*",
+ "entrypoints/**/*.js",
+ "src/**/*.js",
+ "externs/**/*.js"
+ ],
+ "homepage": "https://webcomponents.org/polyfills",
+ "devDependencies": {
+ "@webcomponents/custom-elements": "^1.2.1",
+ "@webcomponents/html-imports": "^1.2.0",
+ "@webcomponents/shadydom": "1.1.2",
+ "@webcomponents/template": "^1.4.0",
+ "@webcomponents/webcomponents-platform": "^1.0.0",
+ "del": "^3.0.0",
+ "es6-promise": "^4.2.5",
+ "eslint": "^5.7.0",
+ "eslint-plugin-html": "^4.0.6",
+ "google-closure-compiler": "^20180805.0.0",
+ "gulp": "^4.0.0",
+ "gulp-rename": "^1.4.0",
+ "gulp-size": "^3.0.0",
+ "gulp-sourcemaps": "^2.6.4",
+ "rollup-stream": "=1.23.1",
+ "run-sequence": "^2.2.1",
+ "vinyl-buffer": "^1.0.1",
+ "vinyl-source-stream": "^2.0.0",
+ "wct-browser-legacy": "^1.0.2",
+ "web-component-tester": "^6.9.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/catapult/third_party/polymer/components/shadycss/scoping-shim.min.js b/catapult/third_party/polymer/components/shadycss/scoping-shim.min.js
new file mode 100644
index 00000000..61794b41
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/scoping-shim.min.js
@@ -0,0 +1,58 @@
+(function(){/*
+
+Copyright (c) 2017 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
+*/
+'use strict';var k,aa="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function n(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""}
+function p(a){a=a.replace(ba,"").replace(ca,"");var b=da,c=a,e=new n;e.start=0;e.end=c.length;for(var d=e,f=0,g=c.length;f<g;f++)if("{"===c[f]){d.rules||(d.rules=[]);var h=d,l=h.rules[h.rules.length-1]||null;d=new n;d.start=f+1;d.parent=h;d.previous=l;h.rules.push(d)}else"}"===c[f]&&(d.end=f+1,d=d.parent||e);return b(e,a)}
+function da(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=ea(c),c=c.replace(fa," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=ha:c.match(ia)&&(a.type=q,a.keyframesName=a.selector.split(fa).pop()):a.type=0===c.indexOf("--")?ja:ka);if(c=a.rules)for(var e=0,d=c.length,f=void 0;e<d&&(f=c[e]);e++)da(f,
+b);return a}function ea(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})}
+function la(a,b,c){c=void 0===c?"":c;var e="";if(a.cssText||a.rules){var d=a.rules,f;if(f=d)f=d[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var g=d.length,h=void 0;f<g&&(h=d[f]);f++)e=la(h,b,e)}else b?b=a.cssText:(b=a.cssText,b=b.replace(ma,"").replace(na,""),b=b.replace(oa,"").replace(pa,"")),(e=b.trim())&&(e=" "+e+"\n")}e&&(a.selector&&(c+=a.selector+" {\n"),c+=e,a.selector&&(c+="}\n\n"));return c}
+var ka=1,q=7,ha=4,ja=1E3,ba=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,ca=/@import[^;]*;/gim,ma=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,na=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,oa=/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,pa=/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,ia=/^@[^\s]*keyframes/,fa=/\s+/g;var r=!(window.ShadyDOM&&window.ShadyDOM.inUse),t;function qa(a){t=a&&a.shimcssproperties?!1:r||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var ra;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(ra=window.ShadyCSS.cssBuild);window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?t=window.ShadyCSS.nativeCss:window.ShadyCSS?(qa(window.ShadyCSS),window.ShadyCSS=void 0):qa(window.WebComponents&&window.WebComponents.flags);
+var u=t,v=ra;var w=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,x=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,sa=/(--[\w-]+)\s*([:,;)]|$)/gi,ta=/(animation\s*:)|(animation-name\s*:)/,ua=/@media\s(.*)/,va=/\{[^}]*\}/g;var wa=new Set;function y(a,b){if(!a)return"";"string"===typeof a&&(a=p(a));b&&z(a,b);return la(a,u)}function A(a){!a.__cssRules&&a.textContent&&(a.__cssRules=p(a.textContent));return a.__cssRules||null}function xa(a){return!!a.parent&&a.parent.type===q}function z(a,b,c,e){if(a){var d=!1,f=a.type;if(e&&f===ha){var g=a.selector.match(ua);g&&(window.matchMedia(g[1]).matches||(d=!0))}f===ka?b(a):c&&f===q?c(a):f===ja&&(d=!0);if((a=a.rules)&&!d)for(d=0,f=a.length,g=void 0;d<f&&(g=a[d]);d++)z(g,b,c,e)}}
+function B(a,b,c,e){var d=document.createElement("style");b&&d.setAttribute("scope",b);d.textContent=a;ya(d,c,e);return d}var C=null;function za(a){a=document.createComment(" Shady DOM styles for "+a+" ");var b=document.head;b.insertBefore(a,(C?C.nextSibling:null)||b.firstChild);return C=a}function ya(a,b,c){b=b||document.head;b.insertBefore(a,c&&c.nextSibling||b.firstChild);C?a.compareDocumentPosition(C)===Node.DOCUMENT_POSITION_PRECEDING&&(C=a):C=a}
+function D(a,b){for(var c=0,e=a.length;b<e;b++)if("("===a[b])c++;else if(")"===a[b]&&0===--c)return b;return-1}function Aa(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");var e=D(a,c+3),d=a.substring(c+4,e);c=a.substring(0,c);a=Aa(a.substring(e+1),b);e=d.indexOf(",");return-1===e?b(c,d.trim(),"",a):b(c,d.substring(0,e).trim(),d.substring(e+1).trim(),a)}function E(a,b){r?a.setAttribute("class",b):window.ShadyDOM.nativeMethods.setAttribute.call(a,"class",b)}
+var F=window.ShadyDOM&&window.ShadyDOM.wrap||function(a){return a};function G(a){var b=a.localName,c="";b?-1<b.indexOf("-")||(c=b,b=a.getAttribute&&a.getAttribute("is")||""):(b=a.is,c=a.extends);return{is:b,s:c}}function Ba(a){for(var b=[],c="",e=0;0<=e&&e<a.length;e++)if("("===a[e]){var d=D(a,e);c+=a.slice(e,d+1);e=d}else","===a[e]?(b.push(c),c=""):c+=a[e];c&&b.push(c);return b}
+function H(a){if(void 0!==v)return v;if(void 0===a.__cssBuild){var b=a.getAttribute("css-build");if(b)a.__cssBuild=b;else{a:{b="template"===a.localName?a.content.firstChild:a.firstChild;if(b instanceof Comment&&(b=b.textContent.trim().split(":"),"css-build"===b[0])){b=b[1];break a}b=""}if(""!==b){var c="template"===a.localName?a.content.firstChild:a.firstChild;c.parentNode.removeChild(c)}a.__cssBuild=b}}return a.__cssBuild||""}
+function Ca(a){a=void 0===a?"":a;return""!==a&&u?r?"shadow"===a:"shady"===a:!1};function I(){}function Da(a,b){J(K,a,function(a){L(a,b||"")})}function J(a,b,c){b.nodeType===Node.ELEMENT_NODE&&c(b);var e;"template"===b.localName?e=(b.content||b._content||b).childNodes:e=b.children||b.childNodes;if(e)for(b=0;b<e.length;b++)J(a,e[b],c)}
+function L(a,b,c){if(b)if(a.classList)c?(a.classList.remove("style-scope"),a.classList.remove(b)):(a.classList.add("style-scope"),a.classList.add(b));else if(a.getAttribute){var e=a.getAttribute("class");c?e&&(b=e.replace("style-scope","").replace(b,""),E(a,b)):E(a,(e?e+" ":"")+"style-scope "+b)}}function Ea(a,b,c){J(K,a,function(a){L(a,b,!0);L(a,c)})}function Fa(a,b){J(K,a,function(a){L(a,b||"",!0)})}
+function M(a,b,c,e,d){var f=K;d=void 0===d?"":d;""===d&&(r||"shady"===(void 0===e?"":e)?d=y(b,c):(a=G(a),d=Ga(f,b,a.is,a.s,c)+"\n\n"));return d.trim()}function Ga(a,b,c,e,d){var f=Ha(c,e);c=c?"."+c:"";return y(b,function(b){b.c||(b.selector=b.g=Ia(a,b,a.b,c,f),b.c=!0);d&&d(b,c,f)})}function Ha(a,b){return b?"[is="+a+"]":a}function Ia(a,b,c,e,d){var f=Ba(b.selector);if(!xa(b)){b=0;for(var g=f.length,h=void 0;b<g&&(h=f[b]);b++)f[b]=c.call(a,h,e,d)}return f.filter(function(a){return!!a}).join(",")}
+function Ja(a){return a.replace(Ka,function(a,c,e){-1<e.indexOf("+")?e=e.replace(/\+/g,"___"):-1<e.indexOf("___")&&(e=e.replace(/___/g,"+"));return":"+c+"("+e+")"})}function La(a){for(var b=[],c;c=a.match(Ma);){var e=c.index,d=D(a,e);if(-1===d)throw Error(c.input+" selector missing ')'");c=a.slice(e,d+1);a=a.replace(c,"\ue000");b.push(c)}return{A:a,matches:b}}function Na(a,b){var c=a.split("\ue000");return b.reduce(function(a,b,f){return a+b+c[f+1]},c[0])}
+I.prototype.b=function(a,b,c){var e=!1;a=a.trim();var d=Ka.test(a);d&&(a=a.replace(Ka,function(a,b,c){return":"+b+"("+c.replace(/\s/g,"")+")"}),a=Ja(a));var f=Ma.test(a);if(f){var g=La(a);a=g.A;g=g.matches}a=a.replace(Oa,":host $1");a=a.replace(Pa,function(a,d,f){e||(a=Qa(f,d,b,c),e=e||a.stop,d=a.G,f=a.value);return d+f});f&&(a=Na(a,g));d&&(a=Ja(a));return a};
+function Qa(a,b,c,e){var d=a.indexOf("::slotted");0<=a.indexOf(":host")?a=Ra(a,e):0!==d&&(a=c?Sa(a,c):a);c=!1;0<=d&&(b="",c=!0);if(c){var f=!0;c&&(a=a.replace(Ta,function(a,b){return" > "+b}))}a=a.replace(Ua,function(a,b,c){return'[dir="'+c+'"] '+b+", "+b+'[dir="'+c+'"]'});return{value:a,G:b,stop:f}}function Sa(a,b){a=a.split(/(\[.+?\])/);for(var c=[],e=0;e<a.length;e++)if(1===e%2)c.push(a[e]);else{var d=a[e];if(""!==d||e!==a.length-1)d=d.split(":"),d[0]+=b,c.push(d.join(":"))}return c.join("")}
+function Ra(a,b){var c=a.match(Va);return(c=c&&c[2].trim()||"")?c[0].match(Wa)?a.replace(Va,function(a,c,f){return b+f}):c.split(Wa)[0]===b?c:"should_not_match":a.replace(":host",b)}function Xa(a){":root"===a.selector&&(a.selector="html")}I.prototype.c=function(a){return a.match(":host")?"":a.match("::slotted")?this.b(a,":not(.style-scope)"):Sa(a.trim(),":not(.style-scope)")};aa.Object.defineProperties(I.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"style-scope"}}});
+var Ka=/:(nth[-\w]+)\(([^)]+)\)/,Pa=/(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g,Wa=/[[.:#*]/,Oa=/^(::slotted)/,Va=/(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Ta=/(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Ua=/(.*):dir\((?:(ltr|rtl))\)/,Ma=/:(?:matches|any|-(?:webkit|moz)-any)/,K=new I;function N(a,b,c,e,d){this.m=a||null;this.b=b||null;this.w=c||[];this.o=null;this.cssBuild=d||"";this.s=e||"";this.a=this.i=this.l=null}function P(a){return a?a.__styleInfo:null}function Ya(a,b){return a.__styleInfo=b}N.prototype.c=function(){return this.m};N.prototype._getStyleRules=N.prototype.c;function Za(a){var b=this.matches||this.matchesSelector||this.mozMatchesSelector||this.msMatchesSelector||this.oMatchesSelector||this.webkitMatchesSelector;return b&&b.call(this,a)}var $a=navigator.userAgent.match("Trident");function ab(){}function bb(a){var b={},c=[],e=0;z(a,function(a){Q(a);a.index=e++;a=a.f.cssText;for(var c;c=sa.exec(a);){var d=c[1];":"!==c[2]&&(b[d]=!0)}},function(a){c.push(a)});a.b=c;a=[];for(var d in b)a.push(d);return a}
+function Q(a){if(!a.f){var b={},c={};R(a,c)&&(b.j=c,a.rules=null);b.cssText=a.parsedCssText.replace(va,"").replace(w,"");a.f=b}}function R(a,b){var c=a.f;if(c){if(c.j)return Object.assign(b,c.j),!0}else{c=a.parsedCssText;for(var e;a=w.exec(c);){e=(a[2]||a[3]).trim();if("inherit"!==e||"unset"!==e)b[a[1].trim()]=e;e=!0}return e}}
+function S(a,b,c){b&&(b=0<=b.indexOf(";")?cb(a,b,c):Aa(b,function(b,d,f,g){if(!d)return b+g;(d=S(a,c[d],c))&&"initial"!==d?"apply-shim-inherit"===d&&(d="inherit"):d=S(a,c[f]||f,c)||f;return b+(d||"")+g}));return b&&b.trim()||""}
+function cb(a,b,c){b=b.split(";");for(var e=0,d,f;e<b.length;e++)if(d=b[e]){x.lastIndex=0;if(f=x.exec(d))d=S(a,c[f[1]],c);else if(f=d.indexOf(":"),-1!==f){var g=d.substring(f);g=g.trim();g=S(a,g,c)||g;d=d.substring(0,f)+g}b[e]=d&&d.lastIndexOf(";")===d.length-1?d.slice(0,-1):d||""}return b.join(";")}
+function db(a,b){var c={},e=[];z(a,function(a){a.f||Q(a);var d=a.g||a.parsedSelector;b&&a.f.j&&d&&Za.call(b,d)&&(R(a,c),a=a.index,d=parseInt(a/32,10),e[d]=(e[d]||0)|1<<a%32)},null,!0);return{j:c,key:e}}
+function eb(a,b,c,e){b.f||Q(b);if(b.f.j){var d=G(a);a=d.is;d=d.s;d=a?Ha(a,d):"html";var f=b.parsedSelector,g=":host > *"===f||"html"===f,h=0===f.indexOf(":host")&&!g;"shady"===c&&(g=f===d+" > *."+d||-1!==f.indexOf("html"),h=!g&&0===f.indexOf(d));if(g||h)c=d,h&&(b.g||(b.g=Ia(K,b,K.b,a?"."+a:"",d)),c=b.g||d),e({A:c,K:h,S:g})}}function fb(a,b,c){var e={},d={};z(b,function(b){eb(a,b,c,function(c){Za.call(a._element||a,c.A)&&(c.K?R(b,e):R(b,d))})},null,!0);return{L:d,J:e}}
+function gb(a,b,c,e){var d=G(b),f=Ha(d.is,d.s),g=new RegExp("(?:^|[^.#[:])"+(b.extends?"\\"+f.slice(0,-1)+"\\]":f)+"($|[.:[\\s>+~])"),h=P(b);d=h.m;h=h.cssBuild;var l=hb(d,e);return M(b,d,function(b){var d="";b.f||Q(b);b.f.cssText&&(d=cb(a,b.f.cssText,c));b.cssText=d;if(!r&&!xa(b)&&b.cssText){var h=d=b.cssText;null==b.C&&(b.C=ta.test(d));if(b.C)if(null==b.u){b.u=[];for(var m in l)h=l[m],h=h(d),d!==h&&(d=h,b.u.push(m))}else{for(m=0;m<b.u.length;++m)h=l[b.u[m]],d=h(d);h=d}b.cssText=h;b.g=b.g||b.selector;
+d="."+e;m=Ba(b.g);h=0;for(var xb=m.length,O=void 0;h<xb&&(O=m[h]);h++)m[h]=O.match(g)?O.replace(f,d):d+" "+O;b.selector=m.join(",")}},h)}function hb(a,b){a=a.b;var c={};if(!r&&a)for(var e=0,d=a[e];e<a.length;d=a[++e]){var f=d,g=b;f.h=new RegExp("\\b"+f.keyframesName+"(?!\\B|-)","g");f.a=f.keyframesName+"-"+g;f.g=f.g||f.selector;f.selector=f.g.replace(f.keyframesName,f.a);c[d.keyframesName]=ib(d)}return c}function ib(a){return function(b){return b.replace(a.h,a.a)}}
+function jb(a,b){var c=T,e=A(a);a.textContent=y(e,function(a){var d=a.cssText=a.parsedCssText;a.f&&a.f.cssText&&(d=d.replace(ma,"").replace(na,""),a.cssText=cb(c,d,b))})}aa.Object.defineProperties(ab.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"x-scope"}}});var T=new ab;var U={},V=window.customElements;if(V&&!r){var kb=V.define;V.define=function(a,b,c){U[a]||(U[a]=za(a));kb.call(V,a,b,c)}};function lb(){this.cache={}}lb.prototype.store=function(a,b,c,e){var d=this.cache[a]||[];d.push({j:b,styleElement:c,i:e});100<d.length&&d.shift();this.cache[a]=d};function mb(){}var nb=new RegExp(K.a+"\\s*([^\\s]*)");function ob(a){return(a=(a.classList&&a.classList.value?a.classList.value:a.getAttribute("class")||"").match(nb))?a[1]:""}function pb(a){var b=F(a).getRootNode();return b===a||b===a.ownerDocument?"":(a=b.host)?G(a).is:""}
+function qb(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.target!==document.documentElement&&c.target!==document.head)for(var e=0;e<c.addedNodes.length;e++){var d=c.addedNodes[e];if(d.nodeType===Node.ELEMENT_NODE){var f=d.getRootNode(),g=ob(d);if(g&&f===d.ownerDocument&&("style"!==d.localName&&"template"!==d.localName||""===H(d)))Fa(d,g);else if(f instanceof ShadowRoot)for(f=pb(d),f!==g&&Ea(d,g,f),d=window.ShadyDOM.nativeMethods.querySelectorAll.call(d,":not(."+K.a+")"),g=0;g<d.length;g++){f=d[g];
+var h=pb(f);h&&L(f,h)}}}}}
+if(!(r||window.ShadyDOM&&window.ShadyDOM.handlesDynamicScoping)){var rb=new MutationObserver(qb),sb=function(a){rb.observe(a,{childList:!0,subtree:!0})};if(window.customElements&&!window.customElements.polyfillWrapFlushCallback)sb(document);else{var tb=function(){sb(document.body)};window.HTMLImports?window.HTMLImports.whenReady(tb):requestAnimationFrame(function(){if("loading"===document.readyState){var a=function(){tb();document.removeEventListener("readystatechange",a)};document.addEventListener("readystatechange",
+a)}else tb()})}mb=function(){qb(rb.takeRecords())}}var ub=mb;var W={};var vb=Promise.resolve();function wb(a){if(a=W[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function yb(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function zb(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a._validating||(a._validating=!0,vb.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a._validating=!1}))};var Ab=new lb;function X(){this.B={};this.c=document.documentElement;var a=new n;a.rules=[];this.h=Ya(this.c,new N(a));this.v=!1;this.b=this.a=null}k=X.prototype;k.flush=function(){ub()};k.I=function(a){return A(a)};k.P=function(a){return y(a)};k.prepareTemplate=function(a,b,c){this.prepareTemplateDom(a,b);this.prepareTemplateStyles(a,b,c)};
+k.prepareTemplateStyles=function(a,b,c){if(!a._prepared){r||U[b]||(U[b]=za(b));a._prepared=!0;a.name=b;a.extends=c;W[b]=a;var e=H(a),d=Ca(e);c={is:b,extends:c};var f=[];for(var g=a.content.querySelectorAll("style"),h=0;h<g.length;h++){var l=g[h];if(l.hasAttribute("shady-unscoped")){if(!r){var m=l.textContent;wa.has(m)||(wa.add(m),m=l.cloneNode(!0),document.head.appendChild(m));l.parentNode.removeChild(l)}}else f.push(l.textContent),l.parentNode.removeChild(l)}f=f.join("").trim();Y(this);if(!d){if(g=
+!e)g=x.test(f)||w.test(f),x.lastIndex=0,w.lastIndex=0;h=p(f);g&&u&&this.a&&this.a.transformRules(h,b);a._styleAst=h}g=[];u||(g=bb(a._styleAst));if(!g.length||u)h=r?a.content:null,b=U[b]||null,e=M(c,a._styleAst,null,e,d?f:""),e=e.length?B(e,c.is,h,b):null,a._style=e;a.a=g}};k.prepareTemplateDom=function(a,b){var c=H(a);r||"shady"===c||a._domPrepared||(a._domPrepared=!0,Da(a.content,b))};
+function Bb(a){var b=G(a),c=b.is;b=b.s;var e=U[c]||null,d=W[c];if(d){c=d._styleAst;var f=d.a;d=H(d);b=new N(c,e,f,b,d);Ya(a,b);return b}}function Cb(a){!a.b&&window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface&&(a.b=window.ShadyCSS.CustomStyleInterface,a.b.transformCallback=function(b){a.D(b)},a.b.validateCallback=function(){requestAnimationFrame(function(){(a.b.enqueued||a.v)&&a.flushCustomStyles()})})}
+function Y(a){!a.a&&window.ShadyCSS&&window.ShadyCSS.ApplyShim&&(a.a=window.ShadyCSS.ApplyShim,a.a.invalidCallback=wb);Cb(a)}
+k.flushCustomStyles=function(){Y(this);if(this.b){var a=this.b.processStyles();if(this.b.enqueued&&!Ca(this.h.cssBuild)){if(u){if(!this.h.cssBuild)for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);if(c&&u&&this.a){var e=A(c);Y(this);this.a.transformRules(e);c.textContent=y(e)}}}else{Db(this,this.c,this.h);for(b=0;b<a.length;b++)(c=this.b.getStyleForCustomStyle(a[b]))&&jb(c,this.h.l);this.v&&this.styleDocument()}this.b.enqueued=!1}}};
+k.styleElement=function(a,b){var c=P(a)||Bb(a);if(c)if(a!==this.c&&(this.v=!0),b&&(c.o=c.o||{},Object.assign(c.o,b)),u){b=G(a).is;if(c.o){var e=c.o,d;for(d in e)null===d?a.style.removeProperty(d):a.style.setProperty(d,e[d])}if(!(!(d=W[b])&&a!==this.c||d&&""!==H(d))&&d&&d._style&&!yb(d)){if(yb(d)||d._applyShimValidatingVersion!==d._applyShimNextVersion)Y(this),this.a&&this.a.transformRules(d._styleAst,b),d._style.textContent=M(a,c.m),zb(d);r&&(b=a.shadowRoot)&&(b=b.querySelector("style"))&&(b.textContent=
+M(a,c.m));c.m=d._styleAst}}else if(this.flush(),Db(this,a,c),c.w&&c.w.length){b=G(a).is;a:{if(d=Ab.cache[b])for(e=d.length-1;0<=e;e--){var f=d[e];b:{var g=c.w;for(var h=0;h<g.length;h++){var l=g[h];if(f.j[l]!==c.l[l]){g=!1;break b}}g=!0}if(g){d=f;break a}}d=void 0}g=d?d.styleElement:null;e=c.i;(f=d&&d.i)||(f=this.B[b]=(this.B[b]||0)+1,f=b+"-"+f);c.i=f;f=c.i;h=T;h=g?g.textContent||"":gb(h,a,c.l,f);l=P(a);var m=l.a;m&&!r&&m!==g&&(m._useCount--,0>=m._useCount&&m.parentNode&&m.parentNode.removeChild(m));
+r?l.a?(l.a.textContent=h,g=l.a):h&&(g=B(h,f,a.shadowRoot,l.b)):g?g.parentNode||($a&&-1<h.indexOf("@media")&&(g.textContent=h),ya(g,null,l.b)):h&&(g=B(h,f,null,l.b));g&&(g._useCount=g._useCount||0,l.a!=g&&g._useCount++,l.a=g);f=g;r||(g=c.i,l=h=a.getAttribute("class")||"",e&&(l=h.replace(new RegExp("\\s*x-scope\\s*"+e+"\\s*","g")," ")),l+=(l?" ":"")+"x-scope "+g,h!==l&&E(a,l));d||Ab.store(b,c.l,f,c.i)}};function Eb(a,b){return(b=F(b).getRootNode().host)?P(b)||Bb(b)?b:Eb(a,b):a.c}
+function Db(a,b,c){var e=Eb(a,b),d=P(e),f=d.l;e===a.c||f||(Db(a,e,d),f=d.l);a=Object.create(f||null);e=fb(b,c.m,c.cssBuild);b=db(d.m,b).j;Object.assign(a,e.J,b,e.L);b=c.o;for(var g in b)if((d=b[g])||0===d)a[g]=d;g=T;b=Object.getOwnPropertyNames(a);for(d=0;d<b.length;d++)e=b[d],a[e]=S(g,a[e],a);c.l=a}k.styleDocument=function(a){this.styleSubtree(this.c,a)};
+k.styleSubtree=function(a,b){var c=a.shadowRoot;(c||a===this.c)&&this.styleElement(a,b);if(b=c&&(c.children||c.childNodes))for(a=0;a<b.length;a++)this.styleSubtree(b[a]);else if(a=a.children||a.childNodes)for(b=0;b<a.length;b++)this.styleSubtree(a[b])};
+k.D=function(a){var b=this,c=H(a);c!==this.h.cssBuild&&(this.h.cssBuild=c);if(!Ca(c)){var e=A(a);z(e,function(a){if(r)Xa(a);else{var d=K;a.selector=a.parsedSelector;Xa(a);a.selector=a.g=Ia(d,a,d.c,void 0,void 0)}u&&""===c&&(Y(b),b.a&&b.a.transformRule(a))});u?a.textContent=y(e):this.h.m.rules.push(e)}};k.getComputedStyleValue=function(a,b){var c;u||(c=(P(a)||P(Eb(this,a))).l[b]);return(c=c||window.getComputedStyle(a).getPropertyValue(b))?c.trim():""};
+k.O=function(a,b){var c=F(a).getRootNode();b=b?b.split(/\s/):[];c=c.host&&c.host.localName;if(!c){var e=a.getAttribute("class");if(e){e=e.split(/\s/);for(var d=0;d<e.length;d++)if(e[d]===K.a){c=e[d+1];break}}}c&&b.push(K.a,c);u||(c=P(a))&&c.i&&b.push(T.a,c.i);E(a,b.join(" "))};k.F=function(a){return P(a)};k.N=function(a,b){L(a,b)};k.R=function(a,b){L(a,b,!0)};k.M=function(a){return pb(a)};k.H=function(a){return ob(a)};X.prototype.flush=X.prototype.flush;X.prototype.prepareTemplate=X.prototype.prepareTemplate;
+X.prototype.styleElement=X.prototype.styleElement;X.prototype.styleDocument=X.prototype.styleDocument;X.prototype.styleSubtree=X.prototype.styleSubtree;X.prototype.getComputedStyleValue=X.prototype.getComputedStyleValue;X.prototype.setElementClass=X.prototype.O;X.prototype._styleInfoForNode=X.prototype.F;X.prototype.transformCustomStyleForDocument=X.prototype.D;X.prototype.getStyleAst=X.prototype.I;X.prototype.styleAstToString=X.prototype.P;X.prototype.flushCustomStyles=X.prototype.flushCustomStyles;
+X.prototype.scopeNode=X.prototype.N;X.prototype.unscopeNode=X.prototype.R;X.prototype.scopeForNode=X.prototype.M;X.prototype.currentScopeForNode=X.prototype.H;Object.defineProperties(X.prototype,{nativeShadow:{get:function(){return r}},nativeCss:{get:function(){return u}}});var Z=new X,Fb,Gb;window.ShadyCSS&&(Fb=window.ShadyCSS.ApplyShim,Gb=window.ShadyCSS.CustomStyleInterface);
+window.ShadyCSS={ScopingShim:Z,prepareTemplate:function(a,b,c){Z.flushCustomStyles();Z.prepareTemplate(a,b,c)},prepareTemplateDom:function(a,b){Z.prepareTemplateDom(a,b)},prepareTemplateStyles:function(a,b,c){Z.flushCustomStyles();Z.prepareTemplateStyles(a,b,c)},styleSubtree:function(a,b){Z.flushCustomStyles();Z.styleSubtree(a,b)},styleElement:function(a){Z.flushCustomStyles();Z.styleElement(a)},styleDocument:function(a){Z.flushCustomStyles();Z.styleDocument(a)},flushCustomStyles:function(){Z.flushCustomStyles()},
+getComputedStyleValue:function(a,b){return Z.getComputedStyleValue(a,b)},nativeCss:u,nativeShadow:r,cssBuild:v};Fb&&(window.ShadyCSS.ApplyShim=Fb);Gb&&(window.ShadyCSS.CustomStyleInterface=Gb);}).call(this);
+
+//# sourceMappingURL=scoping-shim.min.js.map
diff --git a/catapult/third_party/polymer/components/shadycss/scoping-shim.min.js.map b/catapult/third_party/polymer/components/shadycss/scoping-shim.min.js.map
new file mode 100644
index 00000000..2389dcd1
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/scoping-shim.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["src/css-parse.js"," [synthetic:util/global] ","src/style-settings.js","src/common-regex.js","src/unscoped-style-handler.js","src/style-util.js","src/style-transformer.js","src/scoping-shim.js","src/document-watcher.js","src/style-properties.js","src/style-info.js","src/style-placeholder.js","src/style-cache.js","src/template-map.js","src/apply-shim-utils.js","src/common-utils.js","entrypoints/scoping-shim.js"],"names":["$jscomp.global","constructor","StyleNode","parse","text","replace","RX$$module$src$css_parse.comments","RX$$module$src$css_parse.port","parseCss","root","length","n","i","l","OPEN_BRACE","p","previous","push","CLOSE_BRACE","node","t","substring","trim","ss","_expandUnicodeEscapes","RX$$module$src$css_parse.multipleSpaces","lastIndexOf","s","indexOf","AT_START","MEDIA_START","types$$module$src$css_parse.MEDIA_RULE","match","RX$$module$src$css_parse.keyframesRule","types$$module$src$css_parse.KEYFRAMES_RULE","split","pop","VAR_START","types$$module$src$css_parse.MIXIN_RULE","types$$module$src$css_parse.STYLE_RULE","r$","r","code","repeat","stringify","preserveProperties","cssText","rules","RX$$module$src$css_parse.customProp","RX$$module$src$css_parse.mixinProp","RX$$module$src$css_parse.mixinApply","RX$$module$src$css_parse.varApply","STYLE_RULE","KEYFRAMES_RULE","MEDIA_RULE","MIXIN_RULE","comments","port","customProp","mixinProp","mixinApply","varApply","keyframesRule","multipleSpaces","nativeShadow","window","nativeCssVariables_","calcCssVariables","settings","navigator","userAgent","CSS","supports","cssBuild","ShadyCSS","undefined","nativeCss","nativeCssVariables","module$src$style_settings.cssBuild","VAR_ASSIGN","MIXIN_MATCH","VAR_CONSUMED","ANIMATION_MATCH","MEDIA_MATCH","BRACKETED","styleTextSet","Set","toCssText","callback","forEachRule","rulesForStyle","style","textContent","isKeyframesSelector","rule","styleRuleCallback","keyframesRuleCallback","onlyActiveRules","skipRules","type","matchMedia","matches","applyCss","moniker","target","contextNode","document","createElement","setAttribute","applyStyle","lastHeadApplyNode","applyStylePlaceHolder","placeHolder","createComment","scope","head","insertBefore","after","firstChild","nextSibling","compareDocumentPosition","position","Node","DOCUMENT_POSITION_PRECEDING","findMatchingParen","start","level","processVariableAndFallback","str","end","inner","prefix","suffix","comma","value","fallback","setElementClassRaw","element","call","wrap","getIsExtends","localName","typeExtension","is","getAttribute","extends","splitSelectorList","selector","parts","part","slice","getCssBuild","__cssBuild","attrValue","CSS_BUILD_ATTR","buildComment","content","Comment","commentParts","parentNode","removeChild","isOptimalCssBuild","StyleTransformer","domAddScope","_transformDom","fn","startNode","transformer","nodeType","ELEMENT_NODE","c$","childNodes","_content","children","shouldRemoveScope","classList","remove","SCOPE_NAME","add","c","CLASS","newValue","domReplaceScope","oldScope","newScope","domRemoveScope","elementStyles","styleRules","css","ext","hostScope","_calcHostScope","_calcElementScope","CSS_CLASS_PREFIX","isScoped","_transformRule","transformedSelector","_transformRuleCss","_transformComplexSelector","p$","filter","join","COMPLEX_SELECTOR_SEP","_twiddleNthPlus","NTH","m","inside","_preserveMatchesPseudo","MATCHES","index","Error","input","MATCHES_REPLACEMENT","_replaceMatchesPseudo","reduce","acc","cur","idx","stop","isNth","test","isMatches","SLOTTED_START","SIMPLE_SELECTOR_SEP","info","_transformCompoundSelector","combinator","slottedIndex","SLOTTED","HOST","_transformHostSelector","_transformSimpleSelector","slotted","SLOTTED_PAREN","paren","DIR_PAREN","before","dir","attributes","output","PSEUDO_PREFIX","HOST_PAREN","SIMPLE_SELECTOR_PREFIX","host","SELECTOR_NO_MATCH","normalizeRootSelector","ROOT","_transformDocumentSelector","SCOPE_DOC_SELECTOR","$jscomp.global.Object.defineProperties","$jscompDefaultExport","StyleInfo","ast","placeholder","ownStylePropertyNames","overrideStyleProperties","customStyle","scopeSelector","styleProperties","get","set","styleInfo","_getStyleRules","prototype","matchesSelector","method","mozMatchesSelector","msMatchesSelector","oMatchesSelector","webkitMatchesSelector","IS_IE","StyleProperties","decorateStyles","props","keyframes","ruleIndex","decorateRule","propertyInfo","collectPropertiesInCssText","exec","name","onKeyframesRule","_keyframes","names","properties","collectProperties","collectCssText","collectConsumingCssText","Object","assign","any","valueForProperty","property","valueForProperties","propertyValue","lastIndex","colon","pp","propertyDataFromStyles","o","selectorToMatch","parseInt","key","whenHostOrRootRule","parsedSelector","isRoot","isHost","hostAndRootPropertiesForScope","hostProps","rootProps","_element","transformStyles","hostSelector","hostRx","RegExp","StyleInfo$$module$src$style_info.get","keyframeTransforms","_elementKeyframeTransforms","applyProperties","applyKeyframeTransforms","hasAnimations","keyframeNamesToTransform","keyframe","transform","_scopeSelector","keyframesRules","keyframesNameRx","_scopeKeyframes","transformedKeyframesName","scopeId","_keyframesRuleTransformer","applyCustomStyle","XSCOPE_NAME","placeholderMap","ce","origDefine","wrappedDefine","clazz","options","StyleCache","cache","store","tagname","styleElement","list","typeMax","shift","flush","scopeRegExp","getCurrentScope","getOwnerScope","ownerRoot","getRootNode","ownerDocument","handler","mxns","x","mxn","documentElement","addedNodes","currentScope","ShadowRoot","j","unscopedNodes","unscopedNode","scopeForPreviouslyUnscopedNode","observer","MutationObserver","observe","childList","subtree","delayedStart","body","requestAnimationFrame","readyState","listener","removeEventListener","addEventListener","takeRecords","module$src$document_watcher.flush","templateMap","promise","Promise","resolve","invalidate","elementName","template","templateIsValid","startValidatingTemplate","_validating","then","styleCache","ScopingShim","_scopeCounter","_documentOwner","_documentOwnerStyleInfo","StyleInfo$$module$src$style_info.set","_elementsHaveApplied","_customStyleInterface","_applyShim","flush.prototype","getStyleAst.prototype","getStyleAst","styleAstToString.prototype","styleAstToString","prepareTemplate.prototype","prepareTemplate","prepareTemplateDom","prepareTemplateStyles","prepareTemplateStyles.prototype","_prepared","optimalBuild","styleTextParts","styles","querySelectorAll","hasAttribute","scopingAttribute","has","newStyle","cloneNode","appendChild","_ensure","hasMixins","ownPropertyNames","_generateStaticStyle","shadowroot","_style","_ownPropertyNames","prepareTemplateDom.prototype","_domPrepared","_prepareHost","_ensureCustomStyleInterface","CustomStyleInterface","transformCustomStyleForDocument","flushCustomStyles","_ensureApplyShim","ApplyShim","flushCustomStyles.prototype","customStyles","_revalidateCustomStyleApplyShim","_revalidateApplyShim","_updateProperties","_applyCustomStyles","styleDocument","styleElement.prototype","overrideProps","styleElementNativeVariables","removeProperty","setProperty","shadowRoot","querySelector","styleElementShimVariables","_applyStyleProperties","fetch","entry","_validate","pn","cachedStyle","cacheEntry","oldScopeSelector","id","_generateScopeSelector","applyElementStyle","applyElementScopeSelector","v","_styleOwnerForNode","owner","ownerStyleInfo","ownerProperties","create","hostAndRootProps","propertiesMatchingHost","_mixinOverrideStyles","overrides","reify","getOwnPropertyNames","styleDocument.prototype","styleSubtree","styleSubtree.prototype","shadowChildren","transformCustomStyleForDocument.prototype","documentRule","getComputedStyleValue.prototype","getComputedStyleValue","getComputedStyle","getPropertyValue","setElementClass.prototype","setElementClass","classString","classes","scopeName","classAttr","k$","_styleInfoForNode.prototype","_styleInfoForNode","scopeNode.prototype","scopeNode","unscopeNode.prototype","unscopeNode","scopeForNode.prototype","scopeForNode","currentScopeForNode.prototype","currentScopeForNode","defineProperties","scopingShim","elementExtends"],"mappings":"A;;;;;;;;;aAeA,IAAA,CAAA,CC4BAA,GAb2B,WAAlB,EAAC,MAAO,OAAR,EAAiC,MAAjC,GAa0B,IAb1B,CAa0B,IAb1B,CAEe,WAAlB,EAAC,MAAO,OAAR,EAA2C,IAA3C,EAAiC,MAAjC,CAAmD,MAAnD,CAW6B,IDxBjCC,SADIC,EACO,EAAG,CAIZ,IAAA,IAAA,CAFA,IAAA,MAEA,CAFgB,CAQhB,KAAA,MAAA,CAFA,IAAA,OAEA,CAJA,IAAA,SAIA,CAJmB,IAQnB,KAAA,QAAA,CAFA,IAAA,cAEA,CAFwB,EAIxB,KAAA,OAAA,CAAiB,CAAA,CAEjB,KAAA,KAAA,CAAe,CAMf,KAAA,eAAA,CAFA,IAAA,SAEA,CAJA,IAAA,cAIA,CAJwB,EApBZ;AAmCTC,QAASA,EAAK,CAACC,CAAD,CAAO,CAC1BA,CAAA,CAAaA,CAUNC,QAAA,CAAgBC,EAAhB,CAA6B,EAA7B,CAAAD,QAAA,CAAyCE,EAAzC,CAAkD,EAAlD,CATAC,KAAAA,EAAAA,EAAAA,CAAaJ,EAAAA,CAAbI,CAkBHC,EAAO,IAAIP,CACfO,EAAA,MAAA,CAAgB,CAChBA,EAAA,IAAA,CAAcL,CAAAM,OAEd,KADA,IAAIC,EAAIF,CAAR,CACSG,EAAI,CADb,CACgBC,EAAIT,CAAAM,OAApB,CAAiCE,CAAjC,CAAqCC,CAArC,CAAwCD,CAAA,EAAxC,CACE,GAuKeE,GAvKf,GAAIV,CAAA,CAAKQ,CAAL,CAAJ,CAA4B,CACrBD,CAAA,MAAL,GACEA,CAAA,MADF,CACe,EADf,CAGA,KAAII,EAAIJ,CAAR,CACIK,EAAWD,CAAA,MAAA,CAAWA,CAAA,MAAAL,OAAX,CAA+B,CAA/B,CAAXM,EAAgD,IACpDL,EAAA,CAAI,IAAIT,CACRS,EAAA,MAAA,CAAaC,CAAb,CAAiB,CACjBD,EAAA,OAAA,CAAcI,CACdJ,EAAA,SAAA,CAAgBK,CAChBD,EAAA,MAAAE,KAAA,CAAgBN,CAAhB,CAV0B,CAA5B,IAwKgBO,GA7JT,GAAId,CAAA,CAAKQ,CAAL,CAAJ,GACLD,CAAA,IACA,CADWC,CACX,CADe,CACf,CAAAD,CAAA,CAAIA,CAAA,OAAJ,EAAmBF,CAFd,CAlCT,OAAOD,EAAA,CAuCAC,CAvCA,CAAoBL,CAApB,CAFmB;AAkD5BI,QAASA,GAAQ,CAACW,CAAD,CAAOf,CAAP,CAAa,CAC5B,IAAIgB,EAAIhB,CAAAiB,UAAA,CAAeF,CAAA,MAAf,CAA8BA,CAAA,IAA9B,CAA4C,CAA5C,CACRA,EAAA,cAAA,CAAwBA,CAAA,QAAxB,CAA0CC,CAAAE,KAAA,EACtCH,EAAA,OAAJ,GAEEC,CASA,CATIhB,CAAAiB,UAAA,CADKF,CAAA,SAAAI,CAAmBJ,CAAA,SAAA,IAAnBI,CAA6CJ,CAAA,OAAA,MAClD,CAAmBA,CAAA,MAAnB,CAAmC,CAAnC,CASJ,CARAC,CAQA,CARII,EAAA,CAAsBJ,CAAtB,CAQJ,CAPAA,CAOA,CAPIA,CAAAf,QAAA,CAAUoB,EAAV,CAA6B,GAA7B,CAOJ,CAJAL,CAIA,CAJIA,CAAAC,UAAA,CAAYD,CAAAM,YAAA,CAAc,GAAd,CAAZ,CAAiC,CAAjC,CAIJ,CAHIC,CAGJ,CAHQR,CAAA,eAGR,CAHiCA,CAAA,SAGjC,CAHoDC,CAAAE,KAAA,EAGpD,CAFAH,CAAA,OAEA,CAF0C,CAE1C,GAFkBQ,CAAAC,QAAA,CAmJLC,GAnJK,CAElB,CAAIV,CAAA,OAAJ,CACiC,CAA/B,GAAIQ,CAAAC,QAAA,CA+IUE,QA/IV,CAAJ,CACEX,CAAA,KADF,CACiBY,EADjB,CAEWJ,CAAAK,MAAA,CAAQC,EAAR,CAFX,GAGEd,CAAA,KACA,CADee,CACf,CAAAf,CAAA,cAAA,CACEA,CAAA,SAAAgB,MAAA,CAAuBV,EAAvB,CAAAW,IAAA,EALJ,CADF,CAUIjB,CAAA,KAVJ,CAS+B,CAA7B,GAAIQ,CAAAC,QAAA,CAsIQS,IAtIR,CAAJ,CACiBC,EADjB,CAGiBC,EAvBrB,CA4BA,IADIC,CACJ,CADSrB,CAAA,MACT,CACE,IADM,IACGP,EAAI,CADP,CACUC,EAAI2B,CAAA9B,OADd,CACyB+B,EAAAA,IAAAA,EAA/B,CACG7B,CADH,CACOC,CADP,GACc4B,CADd,CACkBD,CAAA,CAAG5B,CAAH,CADlB,EAC0BA,CAAA,EAD1B,CAEEJ,EAAA,CAASiC,CAAT;AAAYrC,CAAZ,CAGJ,OAAOe,EArCqB,CA8C9BK,QAASA,GAAqB,CAACG,CAAD,CAAI,CAChC,MAAOA,EAAAtB,QAAA,CAAU,uBAAV,CAAmC,QAAQ,CAAA,CAAA,CAAA,CAAA,CAAG,CAC/CqC,CAAAA,CAAO,CAEX,KADEC,CACF,CADW,CACX,CADeD,CAAAhC,OACf,CAAOiC,CAAA,EAAP,CAAA,CACED,CAAA,CAAO,GAAP,CAAaA,CAEf,OAAO,IAAP,CAAcA,CANqC,CAA9C,CADyB;AAkB3BE,QAASA,GAAS,CAACzB,CAAD,CAAO0B,CAAP,CAA2BzC,CAA3B,CAAsC,CAAXA,CAAA,CAAA,IAAA,EAAA,GAAAA,CAAA,CAAO,EAAP,CAAAA,CAElD,KAAI0C,EAAU,EACd,IAAI3B,CAAA,QAAJ,EAAuBA,CAAA,MAAvB,CAAsC,CACpC,IAAIqB,EAAKrB,CAAA,MAAT,CACI,CAAA,IAAAqB,CAAA,CAAAA,CAAA,CAgCFC,CAhCQ,CAAAM,CAgCJ,CAAM,CAAN,CAhCI,CAAA,CAAA,CAAA,EAiCGN,CAjCH,EAiCiBA,CAAA,SAjCjB,EAiCwE,CAjCxE,GAiCmCA,CAAA,SAAAb,QAAA,CAuD/BS,IAvD+B,CAjCnC,CAAV,IAAI,CAAJ,CAA+B,CACpBzB,CAAAA,CAAI,CAAb,KAD6B,IACbC,EAAI2B,CAAA9B,OADS,CACE+B,EAAAA,IAAAA,EAA/B,CACG7B,CADH,CACOC,CADP,GACc4B,CADd,CACkBD,CAAA,CAAG5B,CAAH,CADlB,EAC0BA,CAAA,EAD1B,CAEEkC,CAAA,CAAUF,EAAA,CAAUH,CAAV,CAAaI,CAAb,CAAiCC,CAAjC,CAHiB,CAA/B,IAMYD,EAAA,CAAqB,CAArB,CAAqB,CAAA,QAArB,EACR,CAmCN,CAnCM,CAAA,QAmCN,CADAC,CACA,CADqCA,CAS9BzC,QAAA,CACI2C,EADJ,CACmB,EADnB,CAAA3C,QAAA,CAEI4C,EAFJ,CAEkB,EAFlB,CARP,CAAA,CAAA,CAA6BH,CAkBtBzC,QAAA,CACI6C,EADJ,CACmB,EADnB,CAAA7C,QAAA,CAEI8C,EAFJ,CAEiB,EAFjB,CAtDO,CAGV,EADAL,CACA,CAHUA,CAEAxB,KAAA,EACV,IACEwB,CADF,CACY,IADZ,CACmBA,CADnB,CAC6B,IAD7B,CAXkC,CAiBlCA,CAAJ,GACM3B,CAAA,SAIJ,GAHEf,CAGF,EAHUe,CAAA,SAGV,CAHgD,MAGhD,EADAf,CACA,EADQ0C,CACR,CAAI3B,CAAA,SAAJ,GACEf,CADF,EACU,OADV,CALF,CASA,OAAOA,EA7BsD;AAwE7DgD,IAAAA,GAAYA,CAAZA,CACAC,EAAgBA,CADhBD,CAEAE,GAAYA,CAFZF,CAGAG,GAAYA,GAHZH,CAWAI,GAAUA,mCAXVJ,CAYAK,GAAMA,kBAZNL,CAaAM,GAAYA,mDAbZN,CAcAO,GAAWA,4DAdXP,CAeAQ,GAAYA,yCAfZR,CAgBAS,GAAUA,2CAhBVT,CAiBAU,GAAeA,mBAjBfV,CAkBAW,GAAgBA,M,CEtPX,IAAMC,EAAe,EAAEC,MAAA,SAAF,EAAwBA,MAAA,SAAA,MAAxB,CAArB,CACHC,CAKJC,SAASA,GAAgB,CAACC,CAAD,CAAW,CAEhCF,CAAA,CADEE,CAAJ,EAAgBA,CAAA,kBAAhB,CACwB,CAAA,CADxB,CASwBJ,CATxB,EASwC,EAASK,SAAAC,UAAAtC,MAAA,CAA0B,2BAA1B,CAAT,EACpCuC,CAAAN,MAAAM,IADoC,EACtBC,CAAAD,GAAAC,SADsB,EACN,CAAAD,GAAAC,SAAA,CAAa,YAAb,CAA2B,kBAA3B,CADM,CAVN,CAgB7B,IAAIC,EACPR,OAAAS,SAAJ,EAAoDC,IAAAA,EAApD,GAAuBV,MAAAS,SAAAD,SAAvB,GACEA,EADF,CACaR,MAAAS,SAAAD,SADb,CAIIR,OAAAS,SAAJ,EAAqDC,IAAAA,EAArD,GAAuBV,MAAAS,SAAAE,UAAvB,CACEV,CADF,CACwBD,MAAAS,SAAAE,UADxB,CAEWX,MAAAS,SAAJ,EACLP,EAAA,CAAiBF,MAAAS,SAAjB,CAEA,CAAAT,MAAAS,SAAA,CAAkBC,IAAAA,EAHb,EAKLR,EAAA,CAAiBF,MAAA,cAAjB,EAA4CA,MAAA,cAAA,MAA5C,CAMK;IAAMY,EAA4CX,CAAlD,CAlBIY,EAAAL,E,CCxBJ,IAAMM,EAAa,yHAAnB,CACMC,EAAc,sCADpB,CAEMC,GAAe,2BAFrB,CAGMC,GAAkB,sCAHxB,CAIMC,GAAc,cAJpB,CAMMC,GAAY,Y,CCHzB,IAAMC,GAAe,IAAIC,G,CCSlBC,QAASA,EAAU,CAACxC,CAAD,CAAQyC,CAAR,CAAkB,CAC1C,GAAI,CAACzC,CAAL,CACE,MAAO,EAEY,SAArB,GAAI,MAAOA,EAAX,GACEA,CADF,CACU5C,CAAA,CAAM4C,CAAN,CADV,CAGIyC,EAAJ,EACEC,CAAA,CAAY1C,CAAZ,CAAmByC,CAAnB,CAEF,OAAO5C,GAAA,CAAUG,CAAV,CAAiB8B,CAAjB,CAVmC,CAiBrCa,QAASA,EAAa,CAACC,CAAD,CAAQ,CAC/B,CAACA,CAAA,WAAL,EAA4BA,CAAAC,YAA5B,GACED,CAAA,WADF,CACwBxF,CAAA,CAAMwF,CAAAC,YAAN,CADxB,CAGA,OAAOD,EAAA,WAAP,EAA8B,IAJK,CAc9BE,QAASA,GAAmB,CAACC,CAAD,CAAO,CACxC,MAAO,CAAA,CAAQA,CAAA,OAAf,EACAA,CAAA,OAAA,KADA,GAC2B5D,CAFa,CAWnCuD,QAASA,EAAW,CAACtE,CAAD,CAAO4E,CAAP,CAA0BC,CAA1B,CAAiDC,CAAjD,CAAkE,CAC3F,GAAK9E,CAAL,CAAA,CAGA,IAAI+E,EAAY,CAAA,CAAhB,CACIC,EAAOhF,CAAA,KACX,IAAI8E,CAAJ,EACME,CADN,GACepE,EADf,CACiC,CAC7B,IAAIqE,EAAajF,CAAA,SAAAa,MAAA,CAAuBmD,EAAvB,CACbiB,EAAJ,GAEOnC,MAAAmC,WAAA,CAAkBA,CAAA,CAAW,CAAX,CAAlB,CAAAC,QAFP,GAGIH,CAHJ,CAGgB,CAAA,CAHhB,EAF6B,CAU7BC,CAAJ,GAAa5D,EAAb,CACEwD,CAAA,CAAkB5E,CAAlB,CADF,CAEW6E,CAAJ,EACLG,CADK,GACIjE,CADJ,CAEL8D,CAAA,CAAsB7E,CAAtB,CAFK,CAGIgF,CAHJ,GAGa7D,EAHb,GAIL4D,CAJK,CAIO,CAAA,CAJP,CAOP,KADI1D,CACJ,CADSrB,CAAA,MACT,GAAU,CAAC+E,CAAX,CACE,IAAStF,CAAkB6B,CAAhB,CAAgBA,CAAb5B,CAAa4B,CAAXD,CAAA9B,OAAW+B,CAAAA,CAAAA,CAAAA,IAAAA,EAA3B,CAA+B7B,CAA/B,CAAiCC,CAAjC,GAAwC4B,CAAxC,CAA0CD,CAAA,CAAG5B,CAAH,CAA1C,EAAkDA,CAAA,EAAlD,CACE6E,CAAA,CAAYhD,CAAZ,CAAesD,CAAf,CAAkCC,CAAlC,CAAyDC,CAAzD,CA3BJ,CAD2F;AAyCtFK,QAASA,EAAQ,CAACxD,CAAD,CAAUyD,CAAV,CAAmBC,CAAnB,CAA2BC,CAA3B,CAAwC,CAY9D,IAAId,EAAwCe,QAAAC,cAAA,CAAuB,OAAvB,CAXNJ,EAYtC,EACEZ,CAAAiB,aAAA,CAAmB,OAAnB,CAboCL,CAapC,CAEFZ,EAAAC,YAAA,CAf6B9C,CAC7B+D,GAAA,CAeOlB,CAfP,CAAkBa,CAAlB,CAA0BC,CAA1B,CACA,OAcOd,EAjBuD,CAwBhE,IAAImB,EAAoB,IAOjBC,SAASA,GAAqB,CAACR,CAAD,CAAU,CACzCS,CAAAA,CAAcN,QAAAO,cAAA,CAAuB,wBAAvB,CAChBV,CADgB,CACN,GADM,CAIlB,KAAIW,EAAQR,QAAAS,KACZD,EAAAE,aAAA,CAAmBJ,CAAnB,EAHYF,CAAAO,CACVP,CAAA,YADUO,CACyB,IAErC,GAAyCH,CAAAI,WAAzC,CAEA,OADAR,EACA,CADoBE,CAPyB,CAgBxCH,QAASA,GAAU,CAAClB,CAAD,CAAQa,CAAR,CAAgBC,CAAhB,CAA6B,CACrDD,CAAA,CAASA,CAAT,EAAmBE,QAAAS,KAGnBX,EAAAY,aAAA,CAAoBzB,CAApB,CAFac,CAEb,EAF4BA,CAAAc,YAE5B,EADEf,CAAAc,WACF,CACKR,EAAL,CAIiBnB,CAAA6B,wBAAAC,CAA8BX,CAA9BW,CAJjB,GAKmBC,IAAAC,4BALnB,GAMIb,CANJ,CAMwBnB,CANxB,EACEmB,CADF,CACsBnB,CAN+B;AA+BhDiC,QAASA,EAAiB,CAACxH,CAAD,CAAOyH,CAAP,CAAc,CAE7C,IADA,IAAIC,EAAQ,CAAZ,CACkBjH,EAAET,CAAAM,OAApB,CAAiCE,CAAjC,CAAqCC,CAArC,CAAwCD,CAAA,EAAxC,CACE,GAAgB,GAAhB,GAAIR,CAAA,CAAKQ,CAAL,CAAJ,CACEkH,CAAA,EADF,KAEO,IAAgB,GAAhB,GAAI1H,CAAA,CAAKQ,CAAL,CAAJ,EACW,CADX,GACD,EAAEkH,CADD,CAEH,MAAOlH,EAIb,OAAQ,EAXqC,CAkBxCmH,QAASA,GAA0B,CAACC,CAAD,CAAMxC,CAAN,CAAgB,CAExD,IAAIqC,EAAQG,CAAApG,QAAA,CAAY,MAAZ,CACZ,IAAe,EAAf,GAAIiG,CAAJ,CAEE,MAAOrC,EAAA,CAASwC,CAAT,CAAc,EAAd,CAAkB,EAAlB,CAAsB,EAAtB,CAGT,KAAIC,EAAML,CAAA,CAAkBI,CAAlB,CAAuBH,CAAvB,CAA+B,CAA/B,CAAV,CACIK,EAAQF,CAAA3G,UAAA,CAAcwG,CAAd,CAAsB,CAAtB,CAAyBI,CAAzB,CACRE,EAAAA,CAASH,CAAA3G,UAAA,CAAc,CAAd,CAAiBwG,CAAjB,CAETO,EAAAA,CAASL,EAAA,CAA2BC,CAAA3G,UAAA,CAAc4G,CAAd,CAAoB,CAApB,CAA3B,CAAmDzC,CAAnD,CACT6C,EAAAA,CAAQH,CAAAtG,QAAA,CAAc,GAAd,CAEZ,OAAe,EAAf,GAAIyG,CAAJ,CAES7C,CAAA,CAAS2C,CAAT,CAAiBD,CAAA5G,KAAA,EAAjB,CAA+B,EAA/B,CAAmC8G,CAAnC,CAFT,CAOO5C,CAAA,CAAS2C,CAAT,CAFKD,CAAA7G,UAAA,CAAgB,CAAhB,CAAmBgH,CAAnB,CAAA/G,KAAAgH,EAEL,CADQJ,CAAA7G,UAAA,CAAgBgH,CAAhB,CAAwB,CAAxB,CAAA/G,KAAAiH,EACR,CAAkCH,CAAlC,CAtBiD,CA6BnDI,QAASA,EAAkB,CAACC,CAAD,CAAUH,CAAV,CAAiB,CAE7CtE,CAAJ,CACEyE,CAAA7B,aAAA,CAAqB,OAArB,CAA8B0B,CAA9B,CADF,CAGErE,MAAA,SAAA,cAAA,aAAAyE,KAAA,CAAyDD,CAAzD,CAAkE,OAAlE,CAA2EH,CAA3E,CAL+C;AAS5C,IAAMK,EAAO1E,MAAA,SAAP0E,EAA6B1E,MAAA,SAAA,KAA7B0E,EAA4D,QAAA,CAACxH,CAAD,CAAUA,CAAAA,MAAAA,EAAAA,CAM5EyH,SAASA,EAAY,CAACH,CAAD,CAAU,CACpC,IAAII,EAAYJ,CAAA,UAAhB,CACaK,EAAgB,EAKzBD,EAAJ,CACgC,EADhC,CACMA,CAAAjH,QAAA,CAAkB,GAAlB,CADN,GAIIkH,CACA,CADgBD,CAChB,CAAAE,CAAA,CAAMN,CAAAO,aAAN,EAA8BP,CAAAO,aAAA,CAAqB,IAArB,CAA9B,EAA6D,EALjE,GAQED,CACA,CADsBN,CAADM,GACrB,CAAAD,CAAA,CAAiCL,CAADQ,QATlC,CAWA,OAAO,CAACF,GAAAA,CAAD,CAAKD,EAAAA,CAAL,CAlB6B,CAiD/BI,QAASA,GAAiB,CAACC,CAAD,CAAW,CAG1C,IAFA,IAAMC,EAAQ,EAAd,CACIC,EAAO,EADX,CAESzI,EAAI,CAAb,CAAqB,CAArB,EAAgBA,CAAhB,EAA0BA,CAA1B,CAA8BuI,CAAAzI,OAA9B,CAA+CE,CAAA,EAA/C,CAEE,GAAoB,GAApB,GAAIuI,CAAA,CAASvI,CAAT,CAAJ,CAAyB,CAEvB,IAAMqH,EAAML,CAAA,CAAkBuB,CAAlB,CAA4BvI,CAA5B,CAEZyI,EAAA,EAAQF,CAAAG,MAAA,CAAe1I,CAAf,CAAkBqH,CAAlB,CAAwB,CAAxB,CAERrH,EAAA,CAAIqH,CANmB,CAAzB,IAO2B,GAApB,GAAIkB,CAAA,CAASvI,CAAT,CAAJ,EACLwI,CAAAnI,KAAA,CAAWoI,CAAX,CACA,CAAAA,CAAA,CAAO,EAFF,EAILA,CAJK,EAIGF,CAAA,CAASvI,CAAT,CAIRyI,EAAJ,EACED,CAAAnI,KAAA,CAAWoI,CAAX,CAEF,OAAOD,EAvBmC;AAkCrCG,QAASA,EAAW,CAACd,CAAD,CAAU,CACnC,GAAiB9D,IAAAA,EAAjB,GAAIG,CAAJ,CACE,MAA6BA,EAE/B,IAA2BH,IAAAA,EAA3B,GAAI8D,CAAAe,WAAJ,CAAsC,CAEpC,IAAMC,EAAYhB,CAAAO,aAAA,CAdCU,WAcD,CAClB,IAAID,CAAJ,CACEhB,CAAAe,WAAA,CAAqBC,CADvB,KAEO,CAsC8B,CAAA,CAAA,CACjCE,CAAAA,CAAqC,UAAtB,GAtCoBlB,CAsCpBI,UAAA,CAtCoBJ,CAuCDmB,QAAAtC,WADnB,CAtCoBmB,CAwCrCnB,WACJ,IAAIqC,CAAJ,WAA4BE,QAA5B,GACQC,CACF,CADiBH,CAAA/D,YAAAtE,KAAA,EAAAa,MAAA,CAAsC,GAAtC,CACjB,CA7DeuH,WA6Df,GAAAI,CAAA,CAAa,CAAb,CAFN,EAE0C,CACtC,CAAA,CAAOA,CAAA,CAAa,CAAb,CAAP,OAAA,CADsC,CAI1C,CAAA,CAAO,EAVgC,CApCnC,GAAqB,EAArB,GAAIH,CAAJ,CAAA,CAmEJ,IAAMA,EAAqC,UAAtB,GAjEIlB,CAiEJI,UAAA,CAjEIJ,CAkEemB,QAAAtC,WADnB,CAjEImB,CAmErBnB,WACJqC,EAAAI,WAAAC,YAAA,CAAoCL,CAApC,CAtEI,CAIAlB,CAAAe,WAAA,CAAqBG,CANhB,CAL6B,CActC,MAAOlB,EAAAe,WAAP,EAA6B,EAlBM;AAkE9BS,QAASA,GAAiB,CAACxF,CAAD,CAAgB,CAAfA,CAAA,CAAA,IAAA,EAAA,GAAAA,CAAA,CAAW,EAAX,CAAAA,CAEhC,OAAiB,EAAjB,GAAIA,CAAJ,EAAwBI,CAAxB,CAGOb,CAAA,CAA4B,QAA5B,GAAeS,CAAf,CAAoD,OAApD,GAAuCA,CAH9C,CACS,CAAA,CAHsC,C,CCpWjD,QAAMyF,EAAN,EAAA,EAwBEC,QAAAA,GAAWA,CAAChJ,CAADgJ,CAAOjD,CAAPiD,CAAcA,CAIvBC,CAAAD,CC8DED,CD9DFC,CAAmBhJ,CAAnBgJ,CAHWE,QAAAF,CAAChJ,CAADgJ,CAAUA,CACnB1B,CAAA0B,CAAahJ,CAAbgJ,CAAmBjD,CAAnBiD,EAA4BA,EAA5BA,CADmBA,CAGrBA,CAJuBA,CAWzBC,QAAAA,EAAaA,CAAbA,CAAaA,CAACE,CAADF,CAAYG,CAAZH,CAAyBA,CAChCE,CAAAE,SAAJJ,GAA2B1C,IAAA+C,aAA3BL,EACEG,CAAAH,CAAYE,CAAZF,CAEFA,KAAIM,CACwBN,WAA5BA,GAAIE,CAAAzB,UAAJuB,CAIEM,CAJFN,CAIOO,CAHiDL,CAGhDV,QAADe,EAHiDL,CAG5BM,SAArBD,EAHiDL,CAGjDK,YAJPP,CAMEM,CANFN,CAMmCE,CAADO,SANlCT,EAOME,CAAAK,WAENP,IAAIM,CAAJN,CACEA,IAASxJ,CAATwJ,CAAaA,CAAbA,CAAgBxJ,CAAhBwJ,CAAoBM,CAAAhK,OAApB0J,CAA+BxJ,CAAAwJ,EAA/BA,CACEA,CAAAA,CAAAA,CAAAA,CAAmBM,CAAAN,CAAGxJ,CAAHwJ,CAAnBA,CAA0BG,CAA1BH,CAhBgCA;AA0BtC3B,QAAAA,EAAOA,CAACA,CAADA,CAAUvB,CAAVuB,CAAiBqC,CAAjBrC,CAAoCA,CAIzCA,GAAIvB,CAAJuB,CAEEA,GAAIA,CAAAsC,UAAJtC,CACMqC,CAAJrC,EACEA,CAAAsC,UAAAC,OAAAvC,CAvESwC,aAuETxC,CACAA,CAAAA,CAAAsC,UAAAC,OAAAvC,CAAyBvB,CAAzBuB,CAFFA,GAIEA,CAAAsC,UAAAG,IAAAzC,CA1ESwC,aA0ETxC,CACAA,CAAAA,CAAAsC,UAAAG,IAAAzC,CAAsBvB,CAAtBuB,CALFA,CADFA,KAQOA,IAAIA,CAAAO,aAAJP,CAA0BA,CAC/BA,IAAI0C,EAAI1C,CAAAO,aAAAP,CA+WF2C,OA/WE3C,CACJqC,EAAJrC,CACM0C,CADN1C,GAEQ4C,CACJ5C,CADe0C,CAAA9K,QAAAoI,CAjFRwC,aAiFQxC,CAAsBA,EAAtBA,CAAApI,QAAAoI,CAAkCvB,CAAlCuB,CAAyCA,EAAzCA,CACfA,CAAUA,CAAVA,CAA6BA,CAA7BA,CAAsC4C,CAAtC5C,CAHJA,EAOYA,CAAVA,CAA6BA,CAA7BA,EADgB0C,CAAA1C,CAAI0C,CAAJ1C,CAAQA,GAARA,CAAcA,EAC9BA,EADiDA,cACjDA,CADuDvB,CACvDuB,CAT6BA,CAdMA,CAmC3C6C,QAAAA,GAAeA,CAACnK,CAADmK,CAAOC,CAAPD,CAAiBE,CAAjBF,CAA2BA,CAKxClB,CAAAkB,CEQMpB,CFRNoB,CAAmBnK,CAAnBmK,CAJWjB,QAAAiB,CAACnK,CAADmK,CAAUA,CACnB7C,CAAA6C,CAAanK,CAAbmK,CAAmBC,CAAnBD,CAA6BA,CAAAA,CAA7BA,CACA7C,EAAA6C,CAAanK,CAAbmK,CAAmBE,CAAnBF,CAFmBA,CAIrBA,CALwCA,CAY1CG,QAAAA,GAAcA,CAACtK,CAADsK,CAAOF,CAAPE,CAAiBA,CAI7BrB,CAAAqB,CERIvB,CFQJuB,CAAmBtK,CAAnBsK,CAHWpB,QAAAoB,CAACtK,CAADsK,CAAUA,CACnBhD,CAAAgD,CAAatK,CAAbsK,CAAmBF,CAAnBE,EAA+BA,EAA/BA,CAAmCA,CAAAA,CAAnCA,CADmBA,CAGrBA,CAJ6BA;AAe/BC,QAAAA,EAAaA,CAACjD,CAADiD,CAAUC,CAAVD,CAAsBlG,CAAtBkG,CAAgCjH,CAAhCiH,CAA+C5I,CAA/C4I,CAA6DA,CAA1EA,IAAAA,EGuOSxB,CHvOmDpH,EAAA4I,CAAAA,IAAAA,EAAAA,GAAA5I,CAAA4I,CAAUA,EAAVA,CAAA5I,CAO1C4I,GAAhBA,GAAI5I,CAAJ4I,GACM1H,CAAJ0H,EAAiCA,OAAjCA,IARyCA,IAAAA,EAAAA,GAAAjH,CAAAiH,CAAWA,EAAXA,CAAAjH,CAQzCiH,EACE5I,CADF4I,CACsBA,CAAVA,CAAoBC,CAApBD,CAAgClG,CAAhCkG,CADZA,EAGMA,CACJA,CADoCA,CAAVA,CAAuBjD,CAAvBiD,CAC1BA,CAAA5I,CAAA4I,CAAUE,EAAAF,CAAAA,CAAAA,CAASC,CAATD,CADLA,CAAAA,GACKA,CADDA,CAAAA,EACCA,CAAwClG,CAAxCkG,CAAVA,CAA8DA,MAJhEA,CADFA,CAQAA,OAAO5I,EAAAxB,KAAAoK,EAfiEA,CAsB1EE,QAAAA,GAAGA,CAAHA,CAAGA,CAAC7I,CAAD6I,CAAQ1E,CAAR0E,CAAeC,CAAfD,CAAoBpG,CAApBoG,CAA8BA,CAC/BA,IAAIE,EAAYC,EAAAH,CAAoB1E,CAApB0E,CAA2BC,CAA3BD,CAChB1E,EAAA0E,CAA+B1E,CAc/B8E,CAwRqBC,GAxRrBD,CAd+B9E,CAc/B8E,CAGSA,EAfTJ,OAAiBA,EAAVA,CAAoB7I,CAApB6I,CAA2BA,QAAQA,CAAiB9F,CAAjB8F,CAAuBA,CAC1D9F,CAAAoG,EAALN,GACY9F,CAqCdqG,SApCIP,CADU9F,CAqCKsG,EApCfR,CAqCFS,EAAAF,CAzCSP,CAyCTO,CAtCYrG,CAsCZqG,CAzCSP,CAyBeU,EAgBxBH,CAtCkBjF,CAsClBiF,CAtCyBL,CAsCzBK,CArCEP,CAAA9F,CAAAoG,EAAAN,CAAgBA,CAAAA,CAFlBA,CAIIpG,EAAJoG,EACEpG,CAAAoG,CAAS9F,CAAT8F,CAAe1E,CAAf0E,CAAsBE,CAAtBF,CAN6DA,CAA1DA,CAJwBA,CAuBjCG,QAAAA,GAAcA,CAAC7E,CAAD6E,CAAQF,CAARE,CAAaA,CACzBA,MAAOF,EAAAE,CAAMA,MAANA,CAAa7E,CAAb6E,CAAkBA,GAAlBA,CAAwB7E,CADN6E,CA8B3BM,QAAAA,GAAiBA,CAAjBA,CAAiBA,CAACvG,CAADuG,CAAO9B,CAAP8B,CAAoBnF,CAApBmF,CAA2BP,CAA3BO,CAAsCA,CACrDA,IAAIE,EAAeF,EAAVA,CAA4BvG,CAAAuG,SAA5BA,CAGTA,IAAIA,CAAWA,EAAVA,CAA8BvG,CAA9BuG,CAALA,CAA0CA,CAC/BzL,CAAAA,CAAEyL,CAAXA,KADwCA,IAC1BxL,EAAE0L,CAAA7L,OADwB2L,CACbtL,EAAAA,IAAAA,EAA3BsL,CAA+BzL,CAA/ByL,CAAiCxL,CAAjCwL,GAAwCtL,CAAxCsL,CAA0CE,CAAAF,CAAGzL,CAAHyL,CAA1CA,EAAkDzL,CAAAyL,EAAlDA,CACEE,CAAAF,CAAGzL,CAAHyL,CAAAA,CAAQ9B,CAAA7B,KAAA2D,CAAiBA,CAAjBA,CAAuBtL,CAAvBsL,CAA0BnF,CAA1BmF,CAAiCP,CAAjCO,CAF8BA,CAK1CA,MAAOE,EAAAC,OAAAH,CAAUA,QAAAA,CAAChD,CAADgD,CAAUA,CAAAA,MAAAA,CAAAA,CAAQhD,CAARgD,CAApBA,CAAAI,KAAAJ,CA4NkBK,GA5NlBL,CAT8CA;AAgBvDM,QAAAA,GAAeA,CAACxD,CAADwD,CAAWA,CACxBA,MAAOxD,EAAA9I,QAAAsM,CAAiBC,EAAjBD,CAAsBA,QAAAA,CAACE,CAADF,CAAIxG,CAAJwG,CAAUG,CAAVH,CAAqBA,CACrBA,EAA3BA,CAAIG,CAAAlL,QAAA+K,CAAeA,GAAfA,CAAJA,CACEG,CADFH,CACWG,CAAAzM,QAAAsM,CAAeA,KAAfA,CAAsBA,KAAtBA,CADXA,CAEoCA,EAFpCA,CAEWG,CAAAlL,QAAA+K,CAAeA,KAAfA,CAFXA,GAGEG,CAHFH,CAGWG,CAAAzM,QAAAsM,CAAeA,MAAfA,CAAuBA,GAAvBA,CAHXA,CAKAA,OAAOA,GAAPA,CAAWxG,CAAXwG,CAAeA,GAAfA,CAAmBG,CAAnBH,CAAyBA,GANuBA,CAA3CA,CADiBA,CAmB1BI,QAAAA,GAAsBA,CAAC5D,CAAD4D,CAAWA,CAI/BA,IAFAA,IAAM1G,EAAU0G,EAAhBA,CACI/K,CACJ+K,CAAQ/K,CAAR+K,CAAgB5D,CAAAnH,MAAA+K,CAAeC,EAAfD,CAAhBA,CAAAA,CAA0CA,CACxCA,IAAMlF,EAAQ7F,CAAAiL,MAAdF,CACM9E,EAAgB8E,CAAVA,CAA4B5D,CAA5B4D,CAAsClF,CAAtCkF,CACZA,IAAaA,EAAbA,GAAI9E,CAAJ8E,CACEA,KAAUG,MAAJH,CAAa/K,CAAAmL,MAAbJ,CAAwBA,uBAAxBA,CAANA,CAEI1D,CAAAA,CAAOF,CAAAG,MAAAyD,CAAelF,CAAfkF,CAAsB9E,CAAtB8E,CAA4BA,CAA5BA,CACb5D,EAAA4D,CAAW5D,CAAA9I,QAAA0M,CAAiB1D,CAAjB0D,CA0MWK,QA1MXL,CACX1G,EAAApF,KAAA8L,CAAa1D,CAAb0D,CARwCA,CAU1CA,MAAOA,CAAC5D,EAAAA,CAAD4D,CAAW1G,QAAAA,CAAX0G,CAdwBA,CAyBjCM,QAAAA,GAAqBA,CAAClE,CAADkE,CAAWhH,CAAXgH,CAAoBA,CACvCA,IAAMjE,EAAQD,CAAAhH,MAAAkL,CA2LUD,QA3LVC,CACdA,OAAOhH,EAAAiH,OAAAD,CAAeA,QAAAA,CAACE,CAADF,CAAMG,CAANH,CAAWI,CAAXJ,CAAmBA,CAAAA,MAAAE,EAAAF,CAAMG,CAANH,CAAYjE,CAAAiE,CAAMI,CAANJ,CAAYA,CAAZA,CAAZA,CAAlCA,CAA8DjE,CAAAiE,CAAMA,CAANA,CAA9DA,CAFgCA;AAUzCf,CAAAA,UAAAA,EAAAA,CAAAA,QAAyBA,CAACnD,CAADmD,CAAWpF,CAAXoF,CAAkBR,CAAlBQ,CAA6BA,CACpDA,IAAIoB,EAAOpB,CAAAA,CACXnD,EAAAmD,CAAWnD,CAAA7H,KAAAgL,EAEXA,KAAIqB,EAAQf,EAAAgB,KAAAtB,CAASnD,CAATmD,CACRqB,EAAJrB,GACEnD,CACAmD,CADWnD,CAAA9I,QAAAiM,CAAiBM,EAAjBN,CAAsBA,QAAAA,CAACO,CAADP,CAAInG,CAAJmG,CAAUpE,CAAVoE,CAAoBA,CAAAA,MAAAA,GAAAA,CAAInG,CAAJmG,CAAQA,GAARA,CAAYpE,CAAA7H,QAAAiM,CAAcA,KAAdA,CAAqBA,EAArBA,CAAZA,CAAoCA,GAApCA,CAA1CA,CACXA,CAAAnD,CAAAmD,CAAWK,EAAAL,CAAqBnD,CAArBmD,CAFbA,CAMAA,KAAMuB,EAAYb,EAAAY,KAAAtB,CAAanD,CAAbmD,CAGlBA,IAAIuB,CAAJvB,CAAeA,CACZA,IAAAA,EAAsBS,EAAAT,CAA4BnD,CAA5BmD,CAArBnD,EAAAmD,CAAAA,CAAAA,EAAUjG,EAAAiG,CAAAA,CAAAA,QADCA,CAGfnD,CAAAmD,CAAWnD,CAAA9I,QAAAiM,CAAiBwB,EAAjBxB,CAAgCA,UAAhCA,CACXnD,EAAAmD,CAAWnD,CAAA9I,QAAAiM,CAAiByB,EAAjBzB,CAAsCA,QAAAA,CAACO,CAADP,CAAInB,CAAJmB,CAAO3K,CAAP2K,CAAaA,CACvDoB,CAALpB,GACM0B,CAGJ1B,CAHW2B,EAAA3B,CAAgC3K,CAAhC2K,CAAmCnB,CAAnCmB,CAAsCpF,CAAtCoF,CAA6CR,CAA7CQ,CAGXA,CAFAoB,CAEApB,CAFOoB,CAEPpB,EAFe0B,CAAAN,KAEfpB,CADAnB,CACAmB,CADI0B,CAAAE,EACJ5B,CAAA3K,CAAA2K,CAAI0B,CAAA1F,MAJNgE,CAMAA,OAAOnB,EAAPmB,CAAW3K,CAPiD2K,CAAnDA,CAUPuB,EAAJvB,GACEnD,CADFmD,CACae,EAAAf,CAA2BnD,CAA3BmD,CAAqCjG,CAArCiG,CADbA,CAGIqB,EAAJrB,GACEnD,CADFmD,CACaK,EAAAL,CAAqBnD,CAArBmD,CADbA,CAGAA,OAAOnD,EAlC6CmD,CAqCtD2B;QAAAA,GAA0BA,CAAC9E,CAAD8E,CAAWC,CAAXD,CAAuB/G,CAAvB+G,CAA8BnC,CAA9BmC,CAAyCA,CAEjEA,IAAIE,EAAehF,CAAAvH,QAAAqM,CA6HPG,WA7HOH,CACWA,EAA9BA,EAAI9E,CAAAvH,QAAAqM,CA0HKI,OA1HLJ,CAAJA,CACE9E,CADF8E,CACaK,EAAAL,CAA4B9E,CAA5B8E,CAAsCnC,CAAtCmC,CADbA,CAG4BA,CAH5BA,GAGWE,CAHXF,GAIE9E,CAJF8E,CAIa/G,CAAA+G,CAAQM,EAAAN,CAA8B9E,CAA9B8E,CAAwC/G,CAAxC+G,CAARA,CACT9E,CALJ8E,CASIO,EAAAA,CAAUP,CAAAA,CACMA,EAApBA,EAAIE,CAAJF,GACEC,CACAD,CADaA,EACbA,CAAAO,CAAAP,CAAUA,CAAAA,CAFZA,CAMAA,IAAIO,CAAJP,CAAaA,CACXA,IAAAP,EAAOO,CAAAA,CACHO,EAAJP,GAEE9E,CAFF8E,CAEa9E,CAAA9I,QAAA4N,CAAiBQ,EAAjBR,CAAgCA,QAAAA,CAACpB,CAADoB,CAAIS,CAAJT,CAAcA,CAAAA,MAAAA,KAAAA,CAAMS,CAANT,CAA9CA,CAFbA,CAFWA,CAOb9E,CAAA8E,CAAW9E,CAAA9I,QAAA4N,CAAiBU,EAAjBV,CAA4BA,QAAAA,CAACpB,CAADoB,CAAIW,CAAJX,CAAYY,CAAZZ,CACrCA,CAAAA,MAAAA,QAAAA,CAASY,CAATZ,CAAYA,KAAZA,CAAkBW,CAAlBX,CAAwBA,IAAxBA,CAA6BW,CAA7BX,CAAmCA,QAAnCA,CAA4CY,CAA5CZ,CAA+CA,IAA/CA,CADSA,CAEXA,OAAOA,CAAC3F,MAAOa,CAAR8E,CAAkBC,EAAAA,CAAlBD,CAA8BP,KAAAA,CAA9BO,CA5B0DA,CA+BnEM,QAAAA,GAAwBA,CAACpF,CAADoF,CAAWrH,CAAXqH,CAAkBA,CAClCO,CAAAA,CAAa3F,CAAAhH,MAAAoM,CAAeA,WAAfA,CAGnBA,KADAA,IAAMQ,EAASR,EAAfA,CACS3N,EAAI2N,CAAbA,CAAgB3N,CAAhB2N,CAAoBO,CAAApO,OAApB6N,CAAuC3N,CAAA2N,EAAvCA,CAEEA,GAAgBA,CAAhBA,GAAK3N,CAAL2N,CAASA,CAATA,CACEQ,CAAA9N,KAAAsN,CAAYO,CAAAP,CAAW3N,CAAX2N,CAAZA,CADFA,KAEOA,CACLA,IAAMlF,EAAOyF,CAAAP,CAAW3N,CAAX2N,CAEbA,IAAeA,EAAfA,GAAMlF,CAANkF,EAAqB3N,CAArB2N,GAA2BO,CAAApO,OAA3B6N,CAA+CA,CAA/CA,CACMhC,CAEJgC,CAFSlF,CAAAlH,MAAAoM,CA8FGS,GA9FHT,CAETA,CADAhC,CAAAgC,CAAGA,CAAHA,CACAA,EADSrH,CACTqH,CAAAQ,CAAA9N,KAAAsN,CAAYhC,CAAAE,KAAA8B,CA4FAS,GA5FAT,CAAZA,CANGA,CAWTA,MAAOQ,EAAAtC,KAAA8B,CAAYA,EAAZA,CAnBiCA;AAuB1CD,QAAAA,GAAsBA,CAACnF,CAADmF,CAAWxC,CAAXwC,CAAsBA,CAC1CA,IAAIzB,EAAI1D,CAAAnH,MAAAsM,CAAeW,EAAfX,CAERA,OAAAA,CADII,CACJJ,CADYzB,CACZyB,EADiBzB,CAAAyB,CAAEA,CAAFA,CAAAhN,KAAAgN,EACjBA,EADgCA,EAChCA,EACOI,CAAAJ,CAAMA,CAANA,CAAAtM,MAAAsM,CAAeY,EAAfZ,CAALA,CAcSnF,CAAA9I,QAAAiO,CAAiBW,EAAjBX,CAA6BA,QAAQA,CAACzB,CAADyB,CAAIa,CAAJb,CAAUI,CAAVJ,CAAiBA,CAC3DA,MAAOxC,EAAPwC,CAAmBI,CADwCJ,CAAtDA,CAdTA,CAEqBI,CAAAvM,MAAAmM,CAAYY,EAAZZ,CAAAA,CAAoCA,CAApCA,CAEnBA,GAAqBxC,CAArBwC,CACSI,CADTJ,CA6EkBc,kBAlFtBd,CAyBSnF,CAAA9I,QAAAiO,CA2CAD,OA3CAC,CAAuBxC,CAAvBwC,CA5BiCA,CA6C5Ce,QAAAA,GAAqBA,CAACvJ,CAADuJ,CAAOA,CA2BjBC,OA1BTD,GAAIvJ,CAAAuJ,SAAJA,GACEvJ,CAAAuJ,SADFA,CACqBA,MADrBA,CAD0BA,CAS5BE,CAAAA,UAAAA,EAAAA,CAAAA,QAA0BA,CAACpG,CAADoG,CAAWA,CACnCA,MAAIpG,EAAAnH,MAAAuN,CAgBKlB,OAhBLkB,CAAJA,CAESA,EAFTA,CAGWpG,CAAAnH,MAAAuN,CAeCnB,WAfDmB,CAAJA,CACEA,IAAAjD,EAAAiD,CAA+BpG,CAA/BoG,CAQcC,oBARdD,CADFA,CAGEhB,EAAAgB,CAA8BpG,CAAA7H,KAAAiO,EAA9BA,CAMcC,oBANdD,CAP0BA,CA7ZvCE,GAAA,OAAA,iBAAA,CAAA,CAAA,UAAA,CAAA,CAAA,EACM,CAAA,aAAA,CAAA,CAAA,CAAA,WAAA,CAAA,CAAA,CAAA,IAAaxE,QAAA,EAAA,CACf,MAJeA,aAGA,CAAb,CADN,CAAA,CAyaA;IAAM2B,GAAM,yBAAZ,CAGMmB,GAAsB,wCAH5B,CAIMmB,GAAyB,SAJ/B,CAQMpB,GAAgB,cARtB,CAYMmB,GAAa,0CAZnB,CAcMR,GAAgB,gDAdtB,CAeME,GAAY,2BAflB,CAoBM3B,GAAU,sCApBhB,CAuBA0C,EAAe,IAAIxF,C,CIxbjBjK,QA7BmB0P,EA6BR,CAACC,CAAD,CAAMC,CAAN,CAAmBC,CAAnB,CAAuDhH,CAAvD,CAAsErE,CAAtE,CAAgF,CAEzF,IAAAkH,EAAA,CAAkBiE,CAAlB,EAAyB,IAEzB,KAAAC,EAAA,CAAmBA,CAAnB,EAAkC,IAElC,KAAAC,EAAA,CAA6BA,CAA7B,EAAsD,EAEtD,KAAAC,EAAA,CAA+B,IAI/B,KAAAtL,SAAA,CAAgBA,CAAhB,EAA4B,EAE5B,KAAAqE,EAAA,CAAqBA,CAArB,EAAsC,EAMtC,KAAAkH,EAAA,CAFA,IAAAC,EAEA,CAJA,IAAAC,EAIA,CAJuB,IAhBkE,CAxB3FC,QAAOA,EAAGA,CAAChP,CAADgP,CAAOA,CACfA,MAAIhP,EAAJgP,CACShP,CAAAgP,YADTA,CAGSA,IAJMA,CAYjBC,QAAOA,GAAGA,CAACjP,CAADiP,CAAOC,CAAPD,CAAkBA,CAE1BA,MADAjP,EAAAiP,YACAA,CADgBC,CADUD,CAkC5BE,CAAAA,UAAAA,EAAAA,CAAAA,QAAcA,EAAGA,CACfA,MAAOA,KAAA3E,EADQ2E,CAMnBX,EAAAY,UAAA,eAAA,CAAwCZ,CAAAY,UAAAD,E,CDjDhBE,QAAA,GAAQ,CAACrH,CAAD,CAAW,CACzC,IAAMsH,EAAS,IAAApK,QAAToK,EAAyB,IAAAD,gBAAzBC,EACJ,IAAAC,mBADID,EACuB,IAAAE,kBADvBF,EAEJ,IAAAG,iBAFIH,EAEqB,IAAAI,sBAC3B,OAAOJ,EAAP,EAAiBA,CAAA/H,KAAA,CAAY,IAAZ,CAAkBS,CAAlB,CAJwB,CAO3C,IAAM2H,GAAQzM,SAAAC,UAAAtC,MAAA,CAA0B,SAA1B,CAId,SAAM+O,GAAN,EAAA,EAUEC,QAAAA,GAAcA,CAACjO,CAADiO,CAAQA,CAAAA,IACHC,EAAQD,EADLA,CACSE,EAAYF,EADrBA,CACyBG,EAAYH,CAC/CA,EAAVA,CAAsBjO,CAAtBiO,CAA6BA,QAAQA,CAAClL,CAADkL,CAAOA,CAC1CI,CAAAJ,CAAkBlL,CAAlBkL,CAEAlL,EAAAmH,MAAA+D,CAAaG,CAAAH,EACmBlO,EAAAA,CAAAgD,CAAAuL,EAAAvO,QAwElCwO,KADAA,IAAIzE,CACJyE,CAAQzE,CAARyE,CAAeA,EAAHC,KAAAD,CAAqBxO,CAArBwO,CAAZA,CAAAA,CAA4CA,CAC1CA,IAAIE,EAAO3E,CAAAyE,CAAEA,CAAFA,CAGEA,IAAbA,GAAIzE,CAAAyE,CAAEA,CAAFA,CAAJA,GA5E2DL,CA6EzDK,CAAME,CAANF,CADFA,CACgBA,CAAAA,CADhBA,CAJ0CA,CA5EAN,CAA5CA,CAKGS,QAAwBT,CAAClL,CAADkL,CAAOA,CAChCE,CAAAjQ,KAAA+P,CAAelL,CAAfkL,CADgCA,CALlCA,CASAjO,EAAA2O,EAAAV,CAAmBE,CAEfS,EAAAA,CAAQX,EACZA,KAAKA,IAAIpQ,CAAToQ,GAAcC,EAAdD,CACEW,CAAA1Q,KAAA+P,CAAWpQ,CAAXoQ,CAEFA,OAAOW,EAjBaX;AAqBtBI,QAAAA,EAAYA,CAACtL,CAADsL,CAAOA,CACjBA,GAAIC,CAAAvL,CAAAuL,EAAJD,CAAAA,CADiBA,IAIbpD,EAAOoD,EAJMA,CAIFQ,EAAaR,EACRS,EAAAT,CAAuBtL,CAAvBsL,CAA6BQ,CAA7BR,CACpBA,GACEpD,CAAA4D,EAEAR,CAFkBQ,CAElBR,CAAAtL,CAAAsL,MAAAA,CAAgBA,IAHlBA,CAKApD,EAAAlL,QAAAsO,CAAmCtL,CAkCCgM,cAM7BzR,QAAA0R,CAAmBA,EAAnBA,CAA8BA,EAA9BA,CAAA1R,QAAA0R,CACOA,CADPA,CACmBA,EADnBA,CAvCPjM,EAAAuL,EAAAD,CAAoBpD,CAXpBoD,CADiBA,CAiBnBS,QAAAA,EAAiBA,CAAC/L,CAAD+L,CAAOD,CAAPC,CAAmBA,CAClCA,IAAI7D,EAAOlI,CAAAuL,EACXQ,IAAI7D,CAAJ6D,CACEA,IAAI7D,CAAA4D,EAAJC,CAEEA,MADAG,OAAAC,OAAAJ,CAAcD,CAAdC,CAA0B7D,CAAA4D,EAA1BC,CACOA,CAAAA,CAAAA,CAFTA,CADFA,IAKOA,CAED/O,CAAAA,CAAUgD,CAAA+L,cAGdA,KAFAA,IAAIvJ,CAEJuJ,CAAQhF,CAARgF,CAJeA,CAIHN,KAAAM,CAAQ/O,CAAR+O,CAAZA,CAAAA,CAA+BA,CAE7BvJ,CAAAuJ,CAAQvQ,CAACuL,CAAAgF,CAAEA,CAAFA,CAADvQ,EAASuL,CAAAgF,CAAEA,CAAFA,CAATvQ,MAAAuQ,EAERA,IAAcA,SAAdA,GAAIvJ,CAAJuJ,EAAqCA,OAArCA,GAA2BvJ,CAA3BuJ,CACED,CAAAC,CAAWhF,CAAAgF,CAAEA,CAAFA,CAAAvQ,KAAAuQ,EAAXA,CAAAA,CAA0BvJ,CAE5B4J,EAAAL,CAAMA,CAAAA,CAPuBA,CAS/BA,MAAOK,EAdFL,CAP2BA;AAoEpCM,QAAAA,EAAgBA,CAAhBA,CAAgBA,CAACC,CAADD,CAAWlB,CAAXkB,CAAkBA,CAG5BC,CAAJD,GAEIC,CAFJD,CAC8BA,CAA5BA,EAAIC,CAAAxQ,QAAAuQ,CAAiBA,GAAjBA,CAAJA,CACaE,EAAAF,CAAAA,CAAAA,CAAwBC,CAAxBD,CAAkClB,CAAlCkB,CADbA,CAuBuBA,EAAVA,CAAqCC,CAArCD,CAlBF9H,QAAQ8H,CAAChK,CAADgK,CAAS7J,CAAT6J,CAAgB5J,CAAhB4J,CAA0B/J,CAA1B+J,CAAkCA,CACjDA,GAAIA,CAAC7J,CAAL6J,CACEA,MAAOhK,EAAPgK,CAAgB/J,CAIlB+J,EAFIG,CAEJH,CAFoBA,CAAAA,CALXA,CAKWA,CAAsBlB,CAAAkB,CAAM7J,CAAN6J,CAAtBA,CAAoClB,CAApCkB,CAEpBA,GAAwCA,SAAxCA,GAAsBG,CAAtBH,CAI6BA,oBAJ7BA,GAIWG,CAJXH,GAQEG,CARFH,CAQkBA,SARlBA,EAEEG,CAFFH,CAEkBA,CAAAA,CATTA,CASSA,CAAsBlB,CAAAkB,CAAM5J,CAAN4J,CAAtBA,EAAyC5J,CAAzC4J,CAAmDlB,CAAnDkB,CAFlBA,EAGE5J,CAOF4J,OAAOhK,EAAPgK,EAAiBG,CAAjBH,EAAkCA,EAAlCA,EAAwC/J,CAhBS+J,CAkBxCA,CAxBfA,CA2BAA,OAAOC,EAAPD,EAAmBC,CAAA9Q,KAAA6Q,EAAnBA,EAAsCA,EA9BNA;AAkClCE,QAAAA,GAAkBA,CAAlBA,CAAkBA,CAACD,CAADC,CAAWpB,CAAXoB,CAAkBA,CAC9BjJ,CAAAA,CAAQgJ,CAAAjQ,MAAAkQ,CAAeA,GAAfA,CACZA,KAFkCA,IAEzBzR,EAAEyR,CAFuBA,CAEpBtR,CAFoBsR,CAEjBxF,CAAjBwF,CAAoBzR,CAApByR,CAAsBjJ,CAAA1I,OAAtB2R,CAAoCzR,CAAAyR,EAApCA,CACEA,GAAKtR,CAALsR,CAASjJ,CAAAiJ,CAAMzR,CAANyR,CAATA,CAAoBA,CACfA,CAAHE,UAAAF,CAA2BA,CAE3BA,IADAxF,CACAwF,CADOA,CAAHd,KAAAc,CAAoBtR,CAApBsR,CACJA,CACEtR,CAAAsR,CAAIF,CAAAE,CAAAA,CAAAA,CAAsBpB,CAAAoB,CAAMxF,CAAAwF,CAAEA,CAAFA,CAANA,CAAtBA,CAAmCpB,CAAnCoB,CADNA,KAIEA,IADIG,CACAH,CADQtR,CAAAa,QAAAyQ,CAAUA,GAAVA,CACRA,CAAWA,EAAXA,GAAAG,CAAJH,CAAkBA,CAChBA,IAAII,EAAK1R,CAAAM,UAAAgR,CAAYG,CAAZH,CACTI,EAAAJ,CAAKI,CAAAnR,KAAA+Q,EACLI,EAAAJ,CAAKF,CAAAE,CAAAA,CAAAA,CAAsBI,CAAtBJ,CAA0BpB,CAA1BoB,CAALA,EAAyCI,CACzC1R,EAAAsR,CAAItR,CAAAM,UAAAgR,CAAYA,CAAZA,CAAeG,CAAfH,CAAJA,CAA4BI,CAJZJ,CAOpBjJ,CAAAiJ,CAAMzR,CAANyR,CAAAA,CAAYtR,CAADsR,EAAMtR,CAAAW,YAAA2Q,CAAcA,GAAdA,CAANA,GAA6BtR,CAAAL,OAA7B2R,CAAwCA,CAAxCA,CAETtR,CAAAuI,MAAA+I,CAAQA,CAARA,CAAYA,EAAZA,CAFSA,CAGTtR,CAHSsR,EAGJA,EAjBWA,CAoBtBA,MAAOjJ,EAAAqD,KAAA4F,CAAWA,GAAXA,CAvB2BA;AAoFpCK,QAAAA,GAAsBA,CAAC3P,CAAD2P,CAAQjK,CAARiK,CAAiBA,CACrCA,IAAIzB,EAAQyB,EAAZA,CAEIC,EAAID,EAEEA,EAAVA,CAAsB3P,CAAtB2P,CAA6BA,QAAAA,CAAC5M,CAAD4M,CAAUA,CAGhC5M,CAAAuL,EAALqB,EACEtB,CAAAsB,CAAkB5M,CAAlB4M,CAKFA,KAAIE,EAAkB9M,CAAAsG,EAAlBwG,EAA8C9M,CAAA4M,eAC9CjK,EAAJiK,EAAe5M,CAAAuL,EAAAO,EAAfc,EAA+CE,CAA/CF,EACMlC,EAAA9H,KAAAgK,CAAqBjK,CAArBiK,CAA8BE,CAA9BF,CADNA,GAEIb,CAAAa,CAAuB5M,CAAvB4M,CAA6BzB,CAA7ByB,CA6TR,CA3TqBzF,CA2TrB,CA3TqBnH,CAAAmH,MA2TrB,CAFI0F,CAEJ,CAFQE,QAAA,CAASlS,CAAT,CAAa,EAAb,CAAiB,EAAjB,CAER,CA3TiCgS,CA2TjC,CAAKA,CAAL,CAAA,EA3TiCA,CA2TtB,CAAKA,CAAL,CAAX,EAAsB,CAAtB,EADQ,CACR,EADchS,CACd,CADkB,EA9Td+R,CAVqCA,CAAvCA,CAiBGA,IAjBHA,CAiBSA,CAAAA,CAjBTA,CAkBAA,OAAOA,CAACd,EAAYX,CAAbyB,CAAoBI,IAAKH,CAAzBD,CAvB8BA;AAgCvCK,QAAAA,GAAkBA,CAAC7L,CAAD6L,CAAQjN,CAARiN,CAActO,CAAdsO,CAAwBvN,CAAxBuN,CAAkCA,CAC7CjN,CAAAuL,EAAL0B,EACE3B,CAAA2B,CAAkBjN,CAAlBiN,CAEFA,IAAKjN,CAAAuL,EAAAO,EAALmB,CAAAA,CAGIA,IAAAA,EAAgCA,CAAVA,CAAuB7L,CAAvB6L,CAArBhK,EAAAA,CAAAgK,CAAAA,GAAIjK,EAAAA,CAAAiK,CAAAA,EACLjH,EAAAA,CAAY/C,CAAAgK,CACdhH,EAAAgH,CAAgChK,CAAhCgK,CAAoCjK,CAApCiK,CADcA,CAEdA,MACFA,KAAIC,EAAiBlN,CAAAiN,eAArBA,CACIE,EAA6BF,WAA7BE,GAAUD,CAAVC,EAA+DF,MAA/DE,GAA4CD,CADhDD,CAEIG,EAA6CH,CAA7CG,GAASF,CAAApR,QAAAmR,CAAuBA,OAAvBA,CAATG,EAAkDH,CAACE,CAItCF,QAAjBA,GAAItO,CAAJsO,GAEEE,CAEAF,CAFSC,CAETD,GAF6BjH,CAE7BiH,CAFyCA,OAEzCA,CAFmDjH,CAEnDiH,EAFqGA,EAErGA,GAFiEC,CAAApR,QAAAmR,CAAuBA,MAAvBA,CAEjEA,CAAAG,CAAAH,CAASA,CAACE,CAAVF,EAA0DA,CAA1DA,GAAoBC,CAAApR,QAAAmR,CAAuBjH,CAAvBiH,CAJtBA,CAMAA,IAAKE,CAALF,EAAgBG,CAAhBH,CAGIH,CAeJG,CAfsBjH,CAetBiH,CAdIG,CAcJH,GAZOjN,CAAAsG,EAUL2G,GAREjN,CAAAsG,EAQF2G,CAPE1G,EAAA0G,CAAA7I,CAAA6I,CACEjN,CADFiN,CAEE7I,CAAAoC,EAFFyG,CAGqChK,CH1IzCiD,CAwRqBC,GAxRrBD,CG0IyCjD,CH1IzCiD,CAGSA,EGoIL+G,CAIEjH,CAJFiH,CAOFA,EAAAH,CAAAG,CAAkBjN,CAAAsG,EAAlB2G,EAA8CjH,CAEhDiH,EAAAvN,CAAAuN,CAASA,CACP5J,EAAUyJ,CADHG,CAEPG,EAAQA,CAFDH,CAGPE,EAAQA,CAHDF,CAATA,CArCAA,CAJkDA,CAqDpDI,QAAAA,GAA6BA,CAACjM,CAADiM,CAAQpQ,CAARoQ,CAAe1O,CAAf0O,CAAyBA,CAAAA,IAChDC,EAAYD,EADoCA,CAChCE,EAAYF,EAEtBA,EAAVA,CAAsBpQ,CAAtBoQ,CAA6BA,QAAAA,CAACrN,CAADqN,CAAUA,CAErCJ,EAAAI,CAAwBjM,CAAxBiM,CAA+BrN,CAA/BqN,CAAqC1O,CAArC0O,CAA+CA,QAAAA,CAACnF,CAADmF,CAAUA,CAEnD3C,EAAA9H,KAAAyK,CADUjM,CAAAoM,SACVH,EAD4BjM,CAC5BiM,CAA8BnF,CAAA7E,EAA9BgK,CAAJA,GACMnF,CAAAkF,EAAJC,CACEtB,CAAAsB,CAAuBrN,CAAvBqN,CAA6BC,CAA7BD,CADFA,CAGEtB,CAAAsB,CAAuBrN,CAAvBqN,CAA6BE,CAA7BF,CAJJA,CAFuDA,CAAzDA,CAFqCA,CAAvCA,CAYGA,IAZHA,CAYSA,CAAAA,CAZTA,CAaAA,OAAOA,CAACE,EAAWA,CAAZF,CAAuBC,EAAWA,CAAlCD,CAhB6CA;AAwBtDI,QAAAA,GAAeA,CAAfA,CAAeA,CAAC9K,CAAD8K,CAAU3B,CAAV2B,CAAsBtD,CAAtBsD,CAAqCA,CAE9CA,IAAAA,EAAgCA,CAAVA,CAAuB9K,CAAvB8K,CAAtBA,CACAC,EAAezH,EAAAwH,CADdA,CAAAA,GACcA,CADVA,CAAAA,EACUA,CADfA,CAMAE,EAASF,IAAIG,MAAJH,CNlXUA,eMkXVA,EAHQ9K,CAAAQ,QAAAsK,CACnBA,IADmBA,CACZC,CAAAlK,MAAAiK,CAAmBA,CAAnBA,CAAuBA,EAAvBA,CADYA,CACgBA,KADhBA,CAEnBC,CACWD,ENjXUA,iBMiXVA,CANTA,CAQAA,EAAgCI,CAAAJ,CAAc9K,CAAd8K,CAAnBxQ,EAAAA,CAAZwQ,CAAAA,EAAmB9O,EAAAA,CAAA8O,CAAAA,SACxBA,KAAIK,EACFC,EAAAN,CAAyCxQ,CAAzCwQ,CAAgDtD,CAAhDsD,CACFA,OAAO7H,EAAA6H,CAA+B9K,CAA/B8K,CAAwCxQ,CAAxCwQ,CAA+CA,QAAQA,CAACzN,CAADyN,CAAOA,CAnLrEO,IAAI/E,EAAS+E,EAoLUhO,EAlLlBuL,EAALyC,EACE1C,CAAA0C,CAiLqBhO,CAjLrBgO,CAiLqBhO,EA/KnBuL,EAAAvO,QAAJgR,GACE/E,CADF+E,CACWzB,EAAAyB,CAiKAP,CAjKAO,CA8KYhO,CA9KYuL,EAAAvO,QAAxBgR,CA8KkBlC,CA9KlBkC,CADXA,CA+KuBhO,EA5KvBgO,QAAAA,CAAkB/E,CA6KhBwE,IAAIA,CAACvP,CAALuP,EACIA,CAAWA,EAAVA,CAA8BzN,CAA9ByN,CADLA,EAEIzN,CAAAyN,QAFJA,CAEqBA,CAvKvBQ,IAAIhF,EADA5B,CACA4B,CA0K6BjJ,CA3KrBiO,QAEcA,KAA1BA,EAyKiCjO,CAzK7BkO,EAAJD,GAyKiCjO,CAvK/BkO,EAFFD,CAE0BA,EAAHnG,KAAAmG,CAAwB5G,CAAxB4G,CAFvBA,CAKAA,IAoKiCjO,CApK7BkO,EAAJD,CAIEA,GAAqCA,IAArCA,EAgK+BjO,CAhK3BmO,EAAJF,CAA2CA,CAgKZjO,CA/J7BmO,EAAAF,CAAgCA,EAChCA,KAAKA,IAAIG,CAATH,GA8JmCH,EA9JnCG,CACEI,CAIAJ,CAyJiCH,CA7JrBG,CAAmBG,CAAnBH,CAIZA,CAHAhF,CAGAgF,CAHSI,CAAAJ,CAAU5G,CAAV4G,CAGTA,CAAI5G,CAAJ4G,GAAchF,CAAdgF,GACE5G,CACA4G,CADQhF,CACRgF,CAuJyBjO,CAvJzBmO,EAAAhT,KAAA8S,CAAmCG,CAAnCH,CAFFA,CAPuCA,CAA3CA,IAYOA,CAGLA,IAASnT,CAATmT,CAAaA,CAAbA,CAAgBnT,CAAhBmT,CAiJ6BjO,CAjJTmO,EAAAvT,OAApBqT,CAA0DA,EAAEnT,CAA5DmT,CACEI,CACAJ,CA+IiCH,CAhJrBG,CAgJejO,CAhJImO,EAAAF,CAA8BnT,CAA9BmT,CAAnBA,CACZA,CAAA5G,CAAA4G,CAAQI,CAAAJ,CAAU5G,CAAV4G,CAEVhF,EAAAgF,CAAS5G,CAPJ4G,CAoJwBjO,CA1IjCiO,QAAAA,CAAkBhF,CA2IMjJ,EA4ExBsG,EAAAgI,CA5EwBtO,CA4EGsG,EAA3BgI,EA5EwBtO,CA4E+BsO,SAEnDlN;CAAAA,CAAQkN,GAARlN,CA9EgD+I,CA+EhD7G,EAAAA,CAAkBgL,EAAVA,CA/EYtO,CA6ETsG,EAEHgI,CACHxT,EAAAA,CAAEwT,CAAXA,KALkDA,IAKpCvT,GAAEuI,CAAA1I,OALkC0T,CAKpBrT,EAAAA,IAAAA,EAA9BqT,CAAkCxT,CAAlCwT,CAAoCvT,EAApCuT,GAA2CrT,CAA3CqT,CAA6ChL,CAAAgL,CAAMxT,CAANwT,CAA7CA,EAAwDxT,CAAAwT,EAAxDA,CACEhL,CAAAgL,CAAMxT,CAANwT,CAAAA,CAAWrT,CAAAiB,MAAAoS,CAjFiBX,CAiFjBW,CAAAA,CACTrT,CAAAV,QAAA+T,CAlFkCZ,CAkFlCY,CAAwBlN,CAAxBkN,CADSA,CAETlN,CAFSkN,CAEDA,GAFCA,CAEKrT,CAnFM+E,EAqFxBsO,SAAAA,CAAmBhL,CAAAqD,KAAA2H,CAAWA,GAAXA,CAzFIb,CAJ8CA,CAA9DA,CAUJ9O,CAVI8O,CAb2CA,CAgCpDM,QAAAA,GAA0BA,CAAU9Q,CAAV8Q,CAAiB5D,CAAjB4D,CAAgCA,CACpDQ,CAAAA,CAAiBtR,CAAA2O,EACrBmC,KAAID,EAAqBC,EACzBA,IAAIA,CAAC7P,CAAL6P,EAAqBQ,CAArBR,CAIEA,IAJmCA,IAI1BjT,EAAIiT,CAJsBA,CAInB/P,EAAgBuQ,CAAAR,CAAejT,CAAfiT,CAAhCA,CACKjT,CADLiT,CACSQ,CAAA3T,OADTmT,CAEK/P,CAFL+P,CAEqBQ,CAAAR,CAAeA,EAAEjT,CAAjBiT,CAFrBA,CAE0CA,CACnB/P,IAAAA,EAAAA,CAAAA,CAAemM,EAAAA,CAiCxCnK,EAAAwO,EAAAC,CAAuBA,IAAIb,MAAJa,CAAWA,KAAXA,CAAiBzO,CAAAyO,cAAjBA,CAAsCA,WAAtCA,CAAmDA,GAAnDA,CACvBzO,EAAA0O,EAAAD,CAAgCzO,CAAAyO,cAAhCA,CAAwDA,GAAxDA,CAA8DE,CAC9D3O,EAAAsG,EAAAmI,CAA2BzO,CAAAsG,EAA3BmI,EAAuDzO,CAAAyO,SACvDzO,EAAAyO,SAAAA,CAAmBzO,CAAAsG,EAAA/L,QAAAkU,CACfzO,CAAAyO,cADeA,CACQzO,CAAA0O,EADRD,CAnCfX,EAAAC,CAAmB/P,CAAA+P,cAAnBA,CAAAA,CACIa,EAAAb,CAA+B/P,CAA/B+P,CAHoCA,CAM5CA,MAAOD,EAfiDC,CAwB1Da,QAAAA,GAAyBA,CAAC5Q,CAAD4Q,CAAgBA,CACvCA,MAAOA,SAAQA,CAAC5R,CAAD4R,CAAUA,CACvBA,MAAO5R,EAAAzC,QAAAqU,CACH5Q,CAAAwQ,EADGI,CAEH5Q,CAAA0Q,EAFGE,CADgBA,CADcA;AA4IzCC,QAAAA,GAAgBA,CAAChP,CAADgP,CAAQ/C,CAAR+C,CAAoBA,CAApCA,IAAAA,EFzJM5D,CEyJN4D,CACM5R,EAAkB4R,CAAVA,CAAwDhP,CAAxDgP,CAEZhP,EAAAC,YAAA+O,CAA8BA,CAAVA,CAAoB5R,CAApB4R,CAA2BA,QAAQA,CAAiB7O,CAAjB6O,CAAuBA,CAC5EA,IAAI/I,EAAM9F,CAAA6O,QAAN/I,CAAwB9F,CAAA6O,cACxB7O,EAAAuL,EAAJsD,EAAyB7O,CAAAuL,EAAAvO,QAAzB6R,GASE/I,CAEA+I,CAFuD/I,CT9WtDvL,QAAA,CACI2C,EADJ,CACmB,EADnB,CAAA3C,QAAAsU,CAEI1R,EAFJ0R,CAEkB,EAFlBA,CSgXDA,CAAA7O,CAAA6O,QAAAA,CAAkBtC,EAAAsC,CAdXA,CAcWA,CAAwB/I,CAAxB+I,CAA6B/C,CAA7B+C,CAXpBA,CAF4EA,CAA1DA,CAHcA,CA3hBtClF,EAAA,OAAA,iBAAA,CAAA,EAAA,UAAA,CAAA,CAAA,EACM,CAAA,aAAA,CAAA,CAAA,CAAA,WAAA,CAAA,CAAA,CAAA,IAAcmF,QAAA,EAAA,CAChB,MAJgBA,SAGA,CAAd,CADN,CAAA,CA2jBA,KAAAlF,EAAe,IAAIqB,E,CE/kBnB,IAAM8D,EAAiB,EAAvB,CAsBMC,EAAK7Q,MAAA,eACX,IAAI6Q,CAAJ,EAAU,CAAC9Q,CAAX,CAAyB,CAIvB,IAAM+Q,GAAaD,CAAA,OAUnBA,EAAA,OAAA,CAJsBE,QAAA,CAACxD,CAAD,CAAOyD,CAAP,CAAcC,CAAd,CAA0B,CAnB3CL,CAAA,CAoBoBrD,CApBpB,CAAL,GACEqD,CAAA,CAmBuBrD,CAnBvB,CADF,CACgCzK,EAAA,CAmBPyK,CAnBO,CADhC,CAqBEuD,GAAArM,KAAA,CAAsDoM,CAAtD,CAA2DtD,CAA3D,CAAiEyD,CAAjE,CAAwEC,CAAxE,CAF8C,CAVzB,C,CC3BvBjV,QADmBkV,GACR,EAAgB,CAEzB,IAAAC,MAAA,CAAa,EAFY,CAiB3BC,EAAAA,UAAAA,MAAAA,CAAAA,QAAKA,CAACC,CAADD,CAAUzD,CAAVyD,CAAsBE,CAAtBF,CAAoCpF,CAApCoF,CAAmDA,CACtDA,IAAIG,EAAOH,IAAAD,MAAAC,CAAWC,CAAXD,CAAPG,EAA8BH,EAClCG,EAAAvU,KAAAoU,CAAUA,CAACzD,EAAAA,CAADyD,CAAaE,aAAAA,CAAbF,CAA2BpF,EAAAA,CAA3BoF,CAAVA,CAnBoBI,IAoBpBJ,CAAIG,CAAA9U,OAAJ2U,EACEG,CAAAE,MAAAL,EAEFA,KAAAD,MAAAC,CAAWC,CAAXD,CAAAA,CAAsBG,CANgCH,C,CJbvCM,QAAA,GAAQ,EAAG,EAgB9B,IAAMC,GAAc,IAAIlC,MAAJ,CAAcxJ,CAAAe,EAAd,CAAyC,eAAzC,CAMb4K,SAASA,GAAe,CAACpN,CAAD,CAAU,CAEvC,MAAA,CADMzG,CACN,CADcA,CAAWyG,CAhBrBsC,UAAJ,EAgByBtC,CAhBAsC,UAAAzC,MAAzB,CAgByBG,CAfhBsC,UAAAzC,MADT,CAgByBG,CAXhBO,aAAA,CAAqB,OAArB,CALT,EAK0C,EAW5BhH,OAAA,CAA0B4T,EAA1B,CACd,EACS5T,CAAA,CAAM,CAAN,CADT,CAGS,EAL8B,CAYlC8T,QAASA,GAAa,CAAC3U,CAAD,CAAO,CAClC,IAAM4U,EAAYpN,CAAA,CAAKxH,CAAL,CAAA6U,YAAA,EAClB,OAAID,EAAJ,GAAkB5U,CAAlB,EAA0B4U,CAA1B,GAAwC5U,CAAA8U,cAAxC,CACS,EADT,CAIA,CADM9G,CACN,CADwC4G,CAAD5G,KACvC,EAIOvG,CAAA,CAAauG,CAAb,CAAApG,GAJP,CAES,EARyB;AAyEpCmN,QAASA,GAAO,CAACC,CAAD,CAAO,CACrB,IAAK,IAAIC,EAAE,CAAX,CAAcA,CAAd,CAAkBD,CAAAzV,OAAlB,CAA+B0V,CAAA,EAA/B,CAAoC,CAClC,IAAIC,EAAMF,CAAA,CAAKC,CAAL,CACV,IAAIC,CAAA7P,OAAJ,GAAmBE,QAAA4P,gBAAnB,EACED,CAAA7P,OADF,GACiBE,QAAAS,KADjB,CAIA,IAAK,IAAIvG,EAAE,CAAX,CAAcA,CAAd,CAAkByV,CAAAE,WAAA7V,OAAlB,CAAyCE,CAAA,EAAzC,CAA8C,CAC5C,IAAID,EAAI0V,CAAAE,WAAA,CAAe3V,CAAf,CACR,IAAID,CAAA6J,SAAJ,GAAmB9C,IAAA+C,aAAnB,CAAA,CAIA,IAAIhK,EAAOE,CAAAqV,YAAA,EAAX,CACIQ,EAAeX,EAAA,CAAgBlV,CAAhB,CAInB,IAAI6V,CAAJ,EAAoB/V,CAApB,GAA6BE,CAAAsV,cAA7B,GA3BiB,OA2BjB,GAAuEtV,CA3BvEkI,UA2BA,EA3B6C,UA2B7C,GAAuElI,CA3B3CkI,UA2B5B,EHgO4B,EGhO5B,GHgOGU,CAAA,CGhOoE5I,CHgOpE,CGhOH,EACE8K,EAAA,CAAgC9K,CAAhC,CAAmC6V,CAAnC,CADF,KAEO,IAAI/V,CAAJ,WAAoBgW,WAApB,CAnDX,IAoDYjL,CApDHkL,CAoDcZ,EAAA,CAAcnV,CAAd,CApDd+V,CAsDClL,CAtDDkL,GAsDcF,CAtDdE,EAuDDpL,EAAA,CAAiC3K,CAAjC,CAAoC6V,CAApC,CAAkDhL,CAAlD,CAvDCkL,CAHHC,CAGGD,CAHazS,MAAA,SAAA,cAAA,iBAAAyE,KAAA,CA6DY/H,CA7DZ,CACX,QADW,CACFuJ,CAAAe,EADE,CACyB,GADzB,CAGbyL,CAAAA,CAAAA,CAAI,CAAb,CAAgBA,CAAhB,CAAoBC,CAAAjW,OAApB,CAA0CgW,CAAA,EAA1C,CAA+C,CASvCE,CAAAA,CAAeD,CAAA,CAAcD,CAAd,CACrB;IAAMG,EAAiCf,EAAA,CAAcc,CAAd,CACnCC,EAAJ,EACEpO,CAAA,CAAyBmO,CAAzB,CAAuCC,CAAvC,CAZ2C,CAwC3C,CAF4C,CANZ,CADf;AAkCvB,GAAI,EAAC7S,CAAD,EAAmBC,MAAA,SAAnB,EAAyCA,MAAA,SAAA,sBAAzC,CAAJ,CAA2F,CACzF,IAAI6S,GAAW,IAAIC,gBAAJ,CAAqBb,EAArB,CAAf,CACIrO,GAAQA,QAAA,CAAC1G,CAAD,CAAU,CACpB2V,EAAAE,QAAA,CAAiB7V,CAAjB,CAAuB,CAAC8V,UAAW,CAAA,CAAZ,CAAkBC,QAAS,CAAA,CAA3B,CAAvB,CADoB,CAStB,IAN4BjT,MAAA,eAM5B,EALE,CAACA,MAAA,eAAA,0BAKH,CACE4D,EAAA,CAAMnB,QAAN,CADF,KAEO,CACL,IAAIyQ,GAAeA,QAAA,EAAM,CACvBtP,EAAA,CAAMnB,QAAA0Q,KAAN,CADuB,CAIrBnT,OAAA,YAAJ,CACEA,MAAA,YAAA,UAAA,CAAmCkT,EAAnC,CADF,CAKEE,qBAAA,CAAsB,QAAQ,EAAG,CAC/B,GAA4B,SAA5B,GAAI3Q,QAAA4Q,WAAJ,CAAuC,CACrC,IAAIC,EAAWA,QAAQ,EAAG,CACxBJ,EAAA,EACAzQ,SAAA8Q,oBAAA,CAA6B,kBAA7B,CAAiDD,CAAjD,CAFwB,CAI1B7Q,SAAA+Q,iBAAA,CAA0B,kBAA1B;AAA8CF,CAA9C,CALqC,CAAvC,IAOEJ,GAAA,EAR6B,CAAjC,CAVG,CAwBPxB,EAAA,CAAQA,QAAQ,EAAG,CACjBO,EAAA,CAAQY,EAAAY,YAAA,EAAR,CADiB,CArCsE,CA7IhF/B,IAAAgC,GAAAhC,E,CKDX,IAAMiC,EAAc,E,CCkBpB,IAAMC,GAAUC,OAAAC,QAAA,EAKTC,SAASA,GAAU,CAACC,CAAD,CAAa,CAErC,GADIC,CACJ,CDxBaN,CCuBE,CAAYK,CAAZ,CACf,CACqBC,CAerB,yBAIA,CAnBqBA,CAeO,yBAI5B,EAJyD,CAIzD,CAnBqBA,CAiBrB,4BAEA,CAnBqBA,CAiBU,4BAE/B,EAF+D,CAE/D,CAnBqBA,CAmBrB,sBAAA,EAnBqBA,CAmBK,sBAA1B,EAAoD,CAApD,EAAyD,CAtBpB,CAyChCC,QAASA,GAAe,CAACD,CAAD,CAAW,CACxC,MAAOA,EAAA,yBAAP,GAAqCA,CAAA,sBADG,CA4CnCE,QAASA,GAAuB,CAACF,CAAD,CAAW,CAEhDA,CAAA,4BAAA,CAA+BA,CAAA,sBAE1BA,EAAAG,YAAL,GACEH,CAAAG,YACA,CADuB,CAAA,CACvB,CAAAR,EAAAS,KAAA,CAAa,QAAQ,EAAG,CAEtBJ,CAAA,yBAAA,CAA4BA,CAAA,sBAC5BA,EAAAG,YAAA,CAAuB,CAAA,CAHD,CAAxB,CAFF,CAJgD,C,CP9FlD,IAAME,GAAa,IAAIpD,EAGrBlV,SADmBuY,EACR,EAAG,CACZ,IAAAC,EAAA,CAAqB,EACrB,KAAAC,EAAA,CAAkDhS,QAAA4P,gBAClD,KAAI1G,EAAM,IAAI1P,CACd0P,EAAA,MAAA,CAAe,EACf,KAAA+I,EAAA,CAA+BC,EAAA,CAAc,IAAAF,EAAd,CAAmC,IAAI/I,CAAJ,CAAcC,CAAd,CAAnC,CAC/B,KAAAiJ,EAAA,CAA4B,CAAA,CAI5B,KAAAC,EAAA,CAFA,IAAAC,EAEA,CAFkB,IARN,CAYdpD,CAAAA,CAAAA,CAAAA,UAAAqD,EAAArD,MAAAA,CAAAA,QAAKA,EAAGA,CACNgC,EAAAhC,EADMA,CAORsD,EAAAC,EAAAA,CAAAA,QAAWA,CAACvT,CAADuT,CAAQA,CACjBA,MAAiBA,EAAVA,CAAwBvT,CAAxBuT,CADUA,CAGnBC,EAAAC,EAAAA,CAAAA,QAAgBA,CAACxJ,CAADwJ,CAAMA,CACpBA,MAAiBA,EAAVA,CAAoBxJ,CAApBwJ,CADaA,CAatBC,EAAAC,gBAAAA,CAAAA,QAAeA,CAACpB,CAADoB,CAAWrB,CAAXqB,CAAwBxQ,CAAxBwQ,CAAuCA,CACpDA,IAAAC,mBAAAD,CAAwBpB,CAAxBoB,CAAkCrB,CAAlCqB,CACAA,KAAAE,sBAAAF,CAA2BpB,CAA3BoB,CAAqCrB,CAArCqB,CAAkDxQ,CAAlDwQ,CAFoDA,CAUtDG;CAAAD,sBAAAA,CAAAA,QAAqBA,CAACtB,CAADsB,CAAWvB,CAAXuB,CAAwB1Q,CAAxB0Q,CAAuCA,CAC1DA,GAAIE,CAAAxB,CAAAwB,UAAJF,CAAAA,CAIKxV,CAALwV,EIpDG3E,CAAA,CJqDsBoD,CIrDtB,CJoDHuB,GInDA3E,CAAA,CJoDyBoD,CIpDzB,CJmDAuB,CInD8BzS,EAAA,CJoDLkR,CIpDK,CJmD9BuB,CAGAtB,EAAAwB,UAAAF,CAAqBA,CAAAA,CACrBtB,EAAA1G,KAAAgI,CAAgBvB,CAChBC,EAAAjP,QAAAuQ,CAAmB1Q,CMvER8O,ENwEX4B,CAAYvB,CAAZuB,CAAAA,CAA2BtB,CAC3BsB,KAAI/U,EAAqB+U,CAAVA,CAAsBtB,CAAtBsB,CAAfA,CACMG,EAAyBH,EAAVA,CAA4B/U,CAA5B+U,CACjBxL,EAAAA,CAAOwL,CACTzQ,GAAIkP,CADKuB,CAETvQ,QAASH,CAFA0Q,CFqLPI,KAAAA,EAAiB,EAEvB,KADA,IAAMC,EElL6B3B,CArCAtO,QFuNwBkQ,iBAAA,CAAyB,OAAzB,CAA3D,CACSlZ,EAAI,CAAb,CAAgBA,CAAhB,CAAoBiZ,CAAAnZ,OAApB,CAAmCE,CAAA,EAAnC,CAAwC,CACtC,IAAM+E,EAAQkU,CAAA,CAAOjZ,CAAP,CACd,IAAoB+E,CD9OfoU,aAAA,CAvBuBC,gBAuBvB,CC8OL,CACE,IAAI,CAAChW,CAAL,CAAmB,CD7PvB,IAAM5D,EC8PqBuF,CD9PdC,YACRP,GAAA4U,IAAA,CAAiB7Z,CAAjB,CAAL,GACEiF,EAAA6F,IAAA,CAAiB9K,CAAjB,CAEA,CADM8Z,CACN,CC0PyBvU,CD3PRwU,UAAA,CAAgB,CAAA,CAAhB,CACjB,CAAAzT,QAAAS,KAAAiT,YAAA,CAA0BF,CAA1B,CAHF,CC8PMvU,EAAAoE,WAAAC,YAAA,CAA6BrE,CAA7B,CAFiB,CAAnB,CADF,IAMEiU,EAAA3Y,KAAA,CAAoB0E,CAAAC,YAApB,CACA,CAAAD,CAAAoE,WAAAC,YAAA,CAA6BrE,CAA7B,CAToC,CAYxC,CAAA,CAAOiU,CAAAnN,KAAA,CAAoB,EAApB,CAAAnL,KAAA,EE7LL+Y,EAAAb,CAAAA,IAAAA,CACAA,IAAIA,CAACG,CAALH,CAAmBA,CACDA,GAAAA,CAAAA;AAAAA,CAAAA,CAAAA,CQ9CdS,CAGN,CAHYjV,CAAA4I,KAAA,CR8CqB4L,CQ9CrB,CAGZ,EAHyCzU,CAAA6I,KAAA,CR8CR4L,CQ9CQ,CAGzC,CADAxU,CAAAuN,UACA,CADwB,CACxB,CAAAxN,CAAAwN,UAAA,CAAuB,CR4Cf3C,EAAAA,CAAMzP,CAAAqZ,CAAM1W,CAAN0W,CAENc,EAAJd,EAAiB3U,CAAjB2U,EAAuCA,IAAAT,EAAvCS,EACEA,IAAAT,EAAAS,eAAAA,CAAkC5J,CAAlC4J,CAAuCvB,CAAvCuB,CAEFtB,EAAAsB,UAAAA,CAAwB5J,CAPP4J,CASfe,CAAAA,CAAmBf,EAClB3U,EAAL2U,GACEe,CADFf,CACqBxI,EAAAwI,CAA+BtB,CAAAsB,UAA/BA,CADrBA,CAGAA,IAAIA,CAACe,CAAA7Z,OAAL8Y,EAAgC3U,CAAhC2U,CACaA,CAGXA,CAHWxV,CAAAwV,CAAetB,CAAAtO,QAAf4P,CAAkCA,IAG7CA,CI5FGA,CJ4FHA,CI5FG3E,CAAA2E,CJ0FmCvB,CI1FnCuB,CJ4FHA,EI5FkCA,IJ4FlCA,CA0BF1W,CA1BE0W,CA0BQ9N,CAAA8O,CA3B8BxM,CA2B9BwM,CA3BoCtC,CAAAsB,UA2BpCgB,CAA4CA,IAA5CA,CA3B8E/V,CA2B9E+V,CA3BwFb,CAAAH,CAAe1W,CAAf0W,CAAyBA,EA2BjHgB,CA1BRhB,CA2BFgB,CA3BEhB,CA2BE1W,CAAApC,OAAJ8Z,CACmBA,CAAVA,CAAmB1X,CAAnB0X,CA7B+BxM,CA6BHjF,GAA5ByR,CAAqCC,CAArCD,CAAiD3K,CAAjD2K,CADTA,CAGOA,IA9BLhB,CAAAtB,CAAAwC,OAAAlB,CAAkB7T,CAEpBuS,EAAAyC,EAAAnB,CAA6Be,CAvC7Bf,CAD0DA,CA+C5DoB,EAAArB,mBAAAA,CAAAA,QAAkBA,CAACrB,CAADqB,CAAWtB,CAAXsB,CAAwBA,CACxCA,IAAM9U,EAAqB8U,CAAVA,CAAsBrB,CAAtBqB,CACZvV,EAALuV,EAAkCA,OAAlCA,GAAqB9U,CAArB8U,EAA8CrB,CAAA2C,aAA9CtB,GACErB,CAAA2C,aACAtB,CADwBA,CAAAA,CACxBA,CAAApP,EAAAoP,CAA6BrB,CAAAtO,QAA7B2P,CAA+CtB,CAA/CsB,CAFFA,CAFwCA,CAuB1CuB;QAAAA,GAAYA,CAAC3L,CAAD2L,CAAOA,CACXA,IAAAA,EAAgCA,CAAVA,CAAuB3L,CAAvB2L,CAAtBA,CAAC/R,EAAA+R,CAAAA,GAAIhS,EAAAA,CAAAgS,CAAAA,EACXA,KAAMjL,EI9HDgF,CAAA,CJ8HmC9L,CI9HnC,CJ8HC8G,EI9H8B,IJ8HpCiL,CACM5C,EMtIKN,CNsIMkD,CAAY/R,CAAZ+R,CACjBA,IAAK5C,CAAL4C,CAAAA,CAGMlL,CAAAA,CAAMsI,CAAA4C,UACZA,KAAMhL,EAAwBoI,CAAAyC,EACxBlW,EAAAA,CAAqBqW,CAAVA,CAAsB5C,CAAtB4C,CACXzK,EAAAA,CAAYyK,IAAInL,CAAJmL,CAChBlL,CADgBkL,CAEhBjL,CAFgBiL,CAGhBhL,CAHgBgL,CAKhBhS,CALgBgS,CAMhBrW,CANgBqW,CAQlBlC,GAAAkC,CAAc3L,CAAd2L,CAAoBzK,CAApByK,CACAA,OAAOzK,EAfPyK,CAJiBA,CA6BnBC,QAAAA,GAA2BA,CAA3BA,CAA2BA,CAAGA,CACxBjC,CAAAiC,CAAAjC,EAAJiC,EAEW9W,MAAAS,SAFXqW,EAE8B9W,MAAAS,SAAAsW,qBAF9BD,GAGEA,CAAAjC,EAGAiC,CAH2E9W,MAAAS,SAAAsW,qBAG3ED,CADAA,CAAAjC,EAAAiC,kBACAA,CADkDA,QAAAA,CAACpV,CAADoV,CAAWA,CANnCA,CAMoCE,EAAAF,CAAqCpV,CAArCoV,CAADA,CAC7DA,CAAAA,CAAAjC,EAAAiC,iBAAAA,CAAiDA,QAAAA,EAAMA,CACrD1D,qBAAA0D,CAAsBA,QAAAA,EAAMA,CAC1BA,CATsBA,CASlBjC,EAAAiC,SAAJA,EATsBA,CASwBlC,EAA9CkC,GATsBA,CAUpBG,kBAAAH,EAFwBA,CAA5BA,CADqDA,CANzDA,CAD4BA;AAgB9BV,QAAAA,EAAOA,CAAPA,CAAOA,CAAGA,CAvBJtB,CAwBJsB,CAxBItB,EAAJoC,EAEWlX,MAAAS,SAFXyW,EAE8BlX,MAAAS,SAAA0W,UAF9BD,GAwBAd,CArBEtB,EACAoC,CAD0ClX,MAAAS,SAAA0W,UAC1CD,CAoBFd,CApBEtB,EAAAoC,gBAAAA,CAAoDA,EAJtDA,CAyBAJ,GAAAV,CAAAA,CAAAA,CAFQA;AAOVgB,CAAAH,kBAAAA,CAAAA,QAAiBA,EAAGA,CAClBb,CAAAa,CAAAA,IAAAA,CACAA,IAAKA,IAAApC,EAALoC,CAAAA,CAGAA,IAAII,EAAeJ,IAAApC,EAAAoC,cAAAA,EAEnBA,IAAKA,IAAApC,EAAAoC,SAALA,EAIIA,CAAUA,EAAVA,CAA4BA,IAAAvC,EAAAlU,SAA5ByW,CAJJA,CAIAA,CAGAA,GAAKrW,CAALqW,CAOOA,IAAKzW,CAAAyW,IAAAvC,EAAAlU,SAALyW,CA6LPK,IAAKA,IAAI3a,EAAI2a,CAAbA,CAAgB3a,CAAhB2a,CA5LuCD,CA4LnB5a,OAApB6a,CAAyC3a,CAAA2a,EAAzCA,CAA8CA,CAE5CA,IAAI5Z,EA9LJuZ,IA8LQpC,EAAAyC,uBAAAA,CA9L6BD,CA6L7BC,CAAa3a,CAAb2a,CACAA,CACRA,IAAI5Z,CAAJ4Z,EAyCE1W,CAzCF0W,EA/LAL,IAwOwBnC,EAzCxBwC,CAyCyCC,CACzCA,IAAI5L,EAAgB4L,CAAVA,CAAwB7V,CAAxB6V,CACVnB,EAAAmB,CA1OAN,IA0OAM,CA1OAN,KA2OAnC,EAAAyC,eAAAA,CAAkC5L,CAAlC4L,CACA7V,EAAAC,YAAA4V,CAA8BA,CAAVA,CAAoB5L,CAApB4L,CAJqBA,CA5CGD,CA7LvCL,CAPPA,IAAyBA,CACvBO,EAAAP,CAAAA,IAAAA,CAAuBA,IAAAxC,EAAvBwC,CAA4CA,IAAAvC,EAA5CuC,CA4MFQ,KAAS9a,CAAT8a,CAAaA,CAAbA,CAAgB9a,CAAhB8a,CA3M0BJ,CA2MN5a,OAApBgb,CAAyC9a,CAAA8a,EAAzCA,CAGEA,CADI/Z,CACJ+Z,CA9MAR,IA6MQpC,EAAA4C,uBAAAA,CA7MgBJ,CA4MhBI,CAAa9a,CAAb8a,CACAA,CACRA,GACE/G,EAAA+G,CAAiC/Z,CAAjC+Z,CA/MFR,IA+MsCvC,EAAAzI,EAApCwL,CA9MER,KAAArC,EAAJqC,EAEEA,IAAAS,cAAAT,EALqBA,CAUzBA,IAAApC,EAAAoC,SAAAA,CAAyCA,CAAAA,CAbzCA,CATAA,CAFkBA,CAgCpBU;CAAArG,aAAAA,CAAAA,QAAYA,CAACpG,CAADoG,CAAOsG,CAAPtG,CAAsBA,CAChCA,IAAMlF,EAAYsD,CAAA4B,CAAcpG,CAAdoG,CAAZlF,EAAmCyK,EAAAvF,CAAkBpG,CAAlBoG,CAEzCA,IAAKlF,CAALkF,CAYAA,GARuBpG,CAQlBtK,GARA0Q,IA6EYmD,EArEZ7T,GAPH0Q,IAAAsD,EAOGhU,CAPyB0Q,CAAAA,CAOzB1Q,EALDgX,CAKChX,GAJHwL,CAAAN,EAEAwF,CADElF,CAAAN,EACFwF,EADuCA,EACvCA,CAAAvD,MAAAC,OAAAsD,CAAclF,CAAAN,EAAdwF,CAAiDsG,CAAjDtG,CAEG1Q,EAAAA,CAAL0Q,CAAAA,CAsBQxM,CAAAA,CAAiB+S,CAAVA,CAnBoB3M,CAmBpB2M,CAAPA,GACRA,IApByCzL,CAoBrCN,EAAJ+L,CAAAA,CAC+B/L,IAAAA,EArBUM,CAqBVN,EAAAA,CQ1PxBhP,CAAT,KAASA,CAAT,GAAc6Q,EAAd,CAEY,IAAV,GAAI7Q,CAAJ,CRmOmCoO,CQlOjCxJ,MAAAoW,eAAA,CAA6Bhb,CAA7B,CADF,CRmOmCoO,CQhOjCxJ,MAAAqW,YAAA,CAA0Bjb,CAA1B,CAA6B6Q,CAAA,CAAW7Q,CAAX,CAA7B,CRoPF+a,CAKAA,GAAIA,EAFE5D,EAAAA,CAAAA,CMhQKN,CNgQMkE,CAAY/S,CAAZ+S,CAAX5D,CAEF4D,EAzB+B3M,CAyB/B2M,GAzBFvG,IAkEemD,EAzCboD,EAIA5D,CAJA4D,EF2F0B,EE3F1BA,GF2FCvS,CAAA,CEvFwC2O,CFuFxC,CE3FD4D,CAAJA,EAOI5D,CAPJ4D,EAOgB5D,CAAAwC,OAPhBoB,EAOmCA,CAAgBA,EAAfA,CAA+B5D,CAA/B4D,CAPpCA,CAO8EA,CAE5EA,GOrLI3D,EAAA,CPqLqCD,COrLrC,CPqLJ4D,EAAyC5D,COrLR,4BPqLjC4D,GAAyC5D,COrLyB,sBPqLlE4D,CACEzB,CAAAyB,CAnCFvG,IAmCEuG,CAGAA,CAtCFvG,IAoCEwD,EAEA+C,EAtCFvG,IAoCqBwD,EAAA+C,eAAAA,CAAkC5D,CAAA4D,UAAlCA,CAAyD/S,CAAzD+S,CAEnBA,CADA5D,CAAAwC,OAAA9U,YACAkW,CAD8BpQ,CAAAoQ,CArCC3M,CAqCD2M,CArCOzL,CAqC8B1E,EAArCmQ,CAC9BA,CAAeA,EAAfA,CAAuC5D,CAAvC4D,CAGE9X,EAAJ8X,GACMrb,CADNqb,CAzCiC3M,CA0CpB8M,WADbH,IAGQnW,CAHRmW,CAGgBrb,CAAAyb,cAAAJ,CAAmBA,OAAnBA,CAHhBA,IAKMnW,CAAAC,YALNkW;AAK0BpQ,CAAAoQ,CA9CO3M,CA8CP2M,CA9CazL,CA8CwB1E,EAArCmQ,CAL1BA,CAzCuCzL,EAkDvC1E,EAAAmQ,CAAuB5D,CAAA4D,UAlBqDA,CAnC9EvG,CAAAA,IAaA4G,IAZE5G,IAUFI,MAAAwG,EAEIA,CADJV,EAAAU,CAXE5G,IAWF4G,CAXiChN,CAWjCgN,CAXuC9L,CAWvC8L,CACIA,CAZmC9L,CAYnCP,EAAAqM,EAZmC9L,CAYAP,EAAApP,OAAvCyb,CAAAA,CA2DIpT,CAAAA,CAAeqT,CAAVA,CAvEwBjN,CAuExBiN,CAAArT,GKxRkCsT,EAAAA,CAAAA,CAE3CA,GADI7G,CACJ6G,CLuRiB9D,EKxRNnD,MAAAiH,CLwRuBtT,CKxRvBsT,CACXA,CAIAA,IAAS5O,CAAT4O,CAAe7G,CAAA9U,OAAf2b,CAA6BA,CAA7BA,CAAuCA,CAAvCA,EAAgC5O,CAAhC4O,CAA0C5O,CAAA4O,EAA1CA,CAAiDA,CAC/CA,IAAIC,EAAQ9G,CAAA6G,CAAK5O,CAAL4O,CA1BoCE,EAAAA,CAAAA,CL4SezM,IAAAA,EAxE1BO,CAwE0BP,EK3SjEyM,KAAKA,IAAI9O,EAAM8O,CAAfA,CAAkB9O,CAAlB8O,CAAwBhC,CAAA7Z,OAAxB6b,CAAiD9O,CAAA8O,EAAjDA,CAAwDA,CACtDA,IAAIC,EAAKjC,CAAAgC,CAAiB9O,CAAjB8O,CACTA,IAwBmBD,CAxBf1K,EAAA2K,CAAsBC,CAAtBD,CAAJA,GLiOqClM,CAwEDH,EKzSFqM,CAAWC,CAAXD,CAAlCA,CAAkDA,CAChDA,CAAAA,CAAOA,CAAAA,CAAPA,OAAAA,CADgDA,CAFIA,CAMxDA,CAAAA,CAAOA,CAAAA,CAP2CA,CA2BhDF,GAAIA,CAAJA,CAAyDA,CACvDA,CAAAA,CAAOC,CAAPD,OAAAA,CADuDA,CAFVA,CANNA,CAAAA,CAAAA,IAAAA,EAAAA,CL2RvCI,CAAAA,CAAcC,CAAAN,CAAaM,CAAAnH,aAAb6G,CAAuCA,IACrDO,EAAAA,CA3EmCtM,CA2EhBJ,EAEGmM,EAJAA,CAIAA,CAJAA,CAIAA,EAJAA,CAAAA,EAIAA,IApRtBQ,CACJC,CAsMEtH,IAvMOkD,EAAAoE,CAoRwCT,CApRxCS,CACTA,EAsMEtH,IAvMmCkD,EAAAoE,CAoRYT,CApRZS,CACrCA,EADiEA,CACjEA,EADsEA,CACtEA,CAAAA,CAAAA,CAmRiDT,CAnRjDS,CAAcA,GAAdA,CAAkBD,CAmRQR,CA7Ea/L,EA6EvCJ,EAAAmM,CAA0BA,CACqDnM,EAAAA,CA9ExCI,CA8EwCJ,EE4LjF6M,EAAAA,CF5Lc/L,CE8LRjO,EAAAA,CAAU6C,CAAAmX,CAAQnX,CAAAC,YAARkX,EAA6BA,EAA7BA,CACZvJ,EAAAuJ,CAAAA,CAAAA,CF7Q+B3N,CE6Q/B2N,CF7QqCzM,CA8EaH,EE+LlD4M,CAA0C3T,CAA1C2T,CAEEzM,EAAAA,CAAYsD,CAAAmJ,CF/QiB3N,CE+QjB2N,CAChBA,KAAInb,EAAI0O,CAAAL,EACJrO,EAAJmb,EAASA,CAAC9Y,CAAV8Y,EAA2Bnb,CAA3Bmb,GAAiCnX,CAAjCmX,GACEnb,CAAAmb,UAAAA,EACAA,CAAsBA,CAAtBA,EAAInb,CAAAmb,UAAJA,EAA2Bnb,CAAAoI,WAA3B+S,EACEnb,CAAAoI,WAAAC,YAAA8S,CAAyBnb,CAAzBmb,CAHJA,CAQI9Y;CAAJ8Y,CAEMzM,CAAAL,EAAJ8M,EACEzM,CAAAL,EAAApK,YACAkX,CADoCha,CACpCga,CAAAnX,CAAAmX,CAAQzM,CAAAL,EAFV8M,EAIWha,CAJXga,GAOEnX,CAPFmX,CAOoBA,CAAVA,CAAmBha,CAAnBga,CAA4B3T,CAA5B2T,CFlSqB3N,CEkSiB8M,WAAtCa,CACNzM,CAAAR,EADMiN,CAPVA,CAFFA,CAcOnX,CAALmX,CAQYnX,CAAAoE,WARZ+S,GASMhM,EAKJgM,EAL0CA,EAK1CA,CALaha,CAAAlB,QAAAkb,CAAgBA,QAAhBA,CAKbA,GAFEnX,CAAAC,YAEFkX,CAFsBha,CAEtBga,EAAUA,EAAVA,CAAqBnX,CAArBmX,CAA4BA,IAA5BA,CAAkCzM,CAAAR,EAAlCiN,CAdFA,EAGMha,CAHNga,GAIInX,CAJJmX,CAIsBA,CAAVA,CAAmBha,CAAnBga,CAA4B3T,CAA5B2T,CAAsCA,IAAtCA,CACNzM,CAAAR,EADMiN,CAJZA,CAkBEnX,EAAJmX,GACEnX,CAAAmX,UAKAA,CALqBnX,CAAAmX,UAKrBA,EAL2CA,CAK3CA,CAHIzM,CAAAL,EAGJ8M,EAH6BnX,CAG7BmX,EAFEnX,CAAAmX,UAAAA,EAEFA,CAAAzM,CAAAL,EAAA8M,CAAwBnX,CAN1BmX,CAQAA,EAAAA,CAAOnX,CFlPF3B,EAALoY,GACkDnM,CE8KlD8M,CF9PuC1M,CAgFWJ,EE8KlD8M,CANIC,CAMJD,CAPI5R,CAOJ4R,CF9PiC5N,CEuPzBnG,aAAA+T,CAAqBA,OAArBA,CAORA,EAPyCA,EAOzCA,CF9K2EJ,CE8K3EI,GAJEC,CAIFD,CAJM5R,CAAA9K,QAAA0c,CACFA,IAAIrJ,MAAJqJ,CAAWA,iBAAXA,CF3KuEJ,CE2KvEI,CAAiDA,MAAjDA,CAAyDA,GAAzDA,CADEA,CAC6DA,GAD7DA,CAINA,EADAC,CACAD,GADMC,CAAAD,CAAIA,GAAJA,CAAUA,EAChBA,EADoCA,UACpCA,CAD0C5T,CAC1C4T,CAAI5R,CAAJ4R,GAAUC,CAAVD,EACYA,CAAVA,CF/P+B5N,CE+P/B4N,CAAsCC,CAAtCD,CFhLFX,CAGKM,EAALN,EACE7D,EAAAlD,MAAA+G,CAAiBrT,CAAjBqT,CAnFqC/L,CAmFhBH,EAArBkM,CAAgDzW,CAAhDyW,CAnFqC/L,CAmFkBJ,EAAvDmM,CAvEFD,CA5BgC5G,CAuElC0H,SAAAA,GAAkBA,CAAlBA,CAAkBA,CAAC9b,CAAD8b,CAAOA,CAGvBA,MAAAA,CADI9N,CACJ8N,CAFqBA,CAAVA,CAAe9b,CAAf8b,CAAAjH,YAAAiH,EACA9N,KACX8N,EACMtJ,CAAAsJ,CAAc9N,CAAd8N,CAAJA,EAA2BnC,EAAAmC,CAAkB9N,CAAlB8N,CAA3BA,CACS9N,CADT8N,CAGSA,EAAAA,CAAAA,CAAAA,CAAwB9N,CAAxB8N,CAJXA,CAOOA,CAAAvE,EAVgBuE;AAgCzBxB,QAAAA,GAAiBA,CAAjBA,CAAiBA,CAACtM,CAADsM,CAAOpL,CAAPoL,CAAkBA,CACjCA,IAAIyB,EAAQD,EAAAxB,CAAAA,CAAAA,CAAwBtM,CAAxBsM,CAAZA,CACI0B,EAAiBxJ,CAAA8H,CAAcyB,CAAdzB,CADrBA,CAEI2B,EAAkBD,CAAAjN,EAIlBgN,EAAJzB,GAAcA,CAAA/C,EAAd+C,EAAsC2B,CAAtC3B,GACEA,EAAAA,CAAAA,CAAAA,CAAuByB,CAAvBzB,CAA8B0B,CAA9B1B,CACAA,CAAA2B,CAAA3B,CAAkB0B,CAAAjN,EAFpBuL,CAIIxK,EAAAA,CAAQe,MAAAqL,OAAA5B,CAAc2B,CAAd3B,EAAiCA,IAAjCA,CACR6B,EAAAA,CAAmBnK,EAAAsI,CAA8CtM,CAA9CsM,CAAoDpL,CAAA1E,EAApD8P,CAA0EpL,CAAA5L,SAA1EgX,CAEnB8B,EAAAA,CADe7K,EAAA+I,CAAuC0B,CAAAxR,EAAvC8P,CAAkEtM,CAAlEsM,CACU7J,EAC7BI,OAAAC,OAAAwJ,CACExK,CADFwK,CAEE6B,CAAAlK,EAFFqI,CAGE8B,CAHF9B,CAIE6B,CAAAjK,EAJFoI,CAMiC1L,EAAAA,CAAAM,CAAAN,EAKjCyN,KAAKA,IAAIzc,CAATyc,GAAcC,EAAdD,CAIEA,IAHIR,CAGJQ,CAHQC,CAAAD,CAAUzc,CAAVyc,CAGRA,GAAeA,CAAfA,GAASR,CAATQ,CATwBvM,CAUtBuM,CAAMzc,CAANyc,CAAAA,CAAWR,CEtOjBU,EAAAA,CF6NE3M,CE1NIY,EAAAA,CAAQK,MAAA2L,oBAAAD,CF0NUzM,CE1NVyM,CACZA,KAAS9c,CAAT8c,CAAWA,CAAXA,CAAiB9c,CAAjB8c,CAAqB/L,CAAAjR,OAArBgd,CAAmC9c,CAAA8c,EAAnCA,CACE/c,CACA+c,CADI/L,CAAA+L,CAAM9c,CAAN8c,CACJA,CFuNoBzM,CEvNpByM,CAAM/c,CAAN+c,CAAAA,CAAWvL,CAAAuL,CAAAA,CAAAA,CFuNSzM,CEvNayM,CAAM/c,CAAN+c,CAAtBA,CFuNSzM,CEvNTyM,CFwNbrN,EAAAH,EAAAuL,CAA4BxK,CAvBKwK,CAwCnCmC,CAAAjC,cAAAA,CAAAA,QAAaA,CAAC/J,CAAD+J,CAAaA,CACxBA,IAAAkC,aAAAlC,CAAkBA,IAAAjD,EAAlBiD,CAAuC/J,CAAvC+J,CADwBA,CAS1BmC;CAAAD,aAAAA,CAAAA,QAAYA,CAAC1O,CAAD0O,CAAOjM,CAAPiM,CAAmBA,CAC7BA,IAAIpd,EAAO0O,CAAA8M,WACX4B,EAAIpd,CAAJod,EAA8B1O,CAA9B0O,GAAYA,IAtEKnF,EAsEjBmF,GACEA,IAAAtI,aAAAsI,CAAkB1O,CAAlB0O,CAAwBjM,CAAxBiM,CAKFA,IAFIE,CAEJF,CADIpd,CACJod,GADyCpd,CAADoK,SACxCgT,EAD2Dpd,CAAAkK,WAC3DkT,EACEA,IAASjd,CAATid,CAAaA,CAAbA,CAAgBjd,CAAhBid,CAAoBE,CAAArd,OAApBmd,CAA2Cjd,CAAAid,EAA3CA,CAEEA,IAAAA,aAAAA,CADoCE,CAAAF,CAAejd,CAAfid,CACpCA,CAHJA,KAQEA,IADIhT,CACJgT,CADe1O,CAAAtE,SACfgT,EADgC1O,CAAAxE,WAChCkT,CACEA,IAASjd,CAATid,CAAaA,CAAbA,CAAgBjd,CAAhBid,CAAoBhT,CAAAnK,OAApBmd,CAAqCjd,CAAAid,EAArCA,CAEEA,IAAAA,aAAAA,CADoChT,CAAAgT,CAASjd,CAATid,CACpCA,CAnBuBA,CA2C/BG;CAAA/C,EAAAA,CAAAA,QAA+BA,CAACtV,CAADsV,CAAQA,CAAAA,IAAAA,EAAAA,IAAAA,CAC/BxW,EAAqBwW,CAAVA,CAAsBtV,CAAtBsV,CACbxW,EAAJwW,GAAiBA,IAAAtC,EAAAlU,SAAjBwW,GACEA,IAAAtC,EAAAlU,SADFwW,CAC0CxW,CAD1CwW,CAGAA,IAAIA,CAAUA,EAAVA,CAA4BxW,CAA5BwW,CAAJA,CAAAA,CAGAA,IAAIrL,EAAgBqL,CAAVA,CAAwBtV,CAAxBsV,CACAA,EAAVA,CAAsBrL,CAAtBqL,CAA2BA,QAAAA,CAACnV,CAADmV,CAAUA,CACnCA,GAAIjX,CAAJiX,CACE5L,EAAA4L,CAAuCnV,CAAvCmV,CADFA,KAAAA,CDJJgD,IAAAA,ECOM/T,CAA8BpE,EDLlCmY,SAAAA,CCKkCnY,CDLfmY,eACnB5O,GAAA4O,CCIkCnY,CDJlCmY,CCIkCnY,EDrNlCqG,SAAAA,CCqNkCrG,CDrNfsG,EAAnBD,CACEE,EAAAF,CAiNF8R,CAjNE9R,CCoNgCrG,CDpNhCqG,CAiNwB8R,CAAA1O,EAjNxBpD,CAiNF8R,IAAAA,EAjNE9R,CAiNF8R,IAAAA,EAjNE9R,CCiNA8O,CAKIpW,CAAJoW,EAAuCA,EAAvCA,GAA0BxW,CAA1BwW,GACEZ,CAAAY,CAAAA,CAAAA,CACAA,CAAAA,CAAAlC,EAAAkC,EAAmBA,CAAAlC,EAAAkC,cAAAA,CAAiCnV,CAAjCmV,CAFrBA,CANmCA,CAArCA,CAWIpW,EAAJoW,CACEtV,CAAAC,YADFqV,CACgCA,CAAVA,CAAoBrL,CAApBqL,CADtBA,CAGEA,IAAAtC,EAAAhN,EAAAsP,MAAAha,KAAAga,CAAsDrL,CAAtDqL,CAlBFA,CALqCA,CAkCvCiD,EAAAC,sBAAAA,CAAAA,QAAqBA,CAAC1V,CAAD0V,CAAU/L,CAAV+L,CAAoBA,CACvCA,IAAI7V,CACCzD,EAALsZ,GAGE7V,CAHF6V,CAGUjO,CADQyD,CAAAwK,CAAc1V,CAAd0V,CACRjO,EADkCyD,CAAAwK,CAAclB,EAAAkB,CAAAA,IAAAA,CAAwB1V,CAAxB0V,CAAdA,CAClCjO,GAAAiO,CAA0B/L,CAA1B+L,CAHVA,CASAA,OAAOA,CAHP7V,CAGO6V,CAHC7V,CAGD6V,EAHUla,MAAAma,iBAAAD,CAAwB1V,CAAxB0V,CAAAE,iBAAAF,CAAkD/L,CAAlD+L,CAGVA,EAAQ7V,CAAAhH,KAAA6c,EAARA,CAAuBA,EAXSA,CAgBzCG;CAAAC,EAAAA,CAAAA,QAAeA,CAAC9V,CAAD8V,CAAUC,CAAVD,CAAuBA,CACpCA,IAAI9d,EAAiB8d,CAAVA,CAAe9V,CAAf8V,CAAAvI,YAAAuI,EACPE,EAAAA,CAAUD,CAAAD,CAAcC,CAAArc,MAAAoc,CAAkBA,IAAlBA,CAAdA,CAAwCA,EAClDG,EAAAA,CAAYje,CAAA0O,KAAZuP,EAAyBje,CAAA0O,KAAAtG,UAI7B0V,IAAIA,CAACG,CAALH,CAAgBA,CACdA,IAAII,EAAYlW,CAAAO,aAAAuV,CAAqBA,OAArBA,CAChBA,IAAII,CAAJJ,CAAeA,CACTK,CAAAA,CAAKD,CAAAxc,MAAAoc,CAAgBA,IAAhBA,CACTA,KAAKA,IAAI3d,EAAE2d,CAAXA,CAAc3d,CAAd2d,CAAkBK,CAAAle,OAAlB6d,CAA6B3d,CAAA2d,EAA7BA,CACEA,GAAIK,CAAAL,CAAG3d,CAAH2d,CAAJA,GAAcrU,CAAAe,EAAdsT,CAA2CA,CACzCG,CAAAH,CAAYK,CAAAL,CAAG3d,CAAH2d,CAAKA,CAALA,CACZA,MAFyCA,CAHhCA,CAFDA,CAYZG,CAAJH,EACEE,CAAAxd,KAAAsd,CAAarU,CAAAe,EAAbsT,CAA0CG,CAA1CH,CAEG1Z,EAAL0Z,GACMlO,CADNkO,CACkB5K,CAAA4K,CAAc9V,CAAd8V,CADlBA,GAEmBlO,CAAAJ,EAFnBsO,EAGIE,CAAAxd,KAAAsd,CAAaxN,CAAA6D,EAAb2J,CAA0ClO,CAAAJ,EAA1CsO,CAGMA,EAAVA,CAA6B9V,CAA7B8V,CAAsCE,CAAAhS,KAAA8R,CAAaA,GAAbA,CAAtCA,CA5BoCA,CA8BtCM,EAAAC,EAAAA,CAAAA,QAAiBA,CAAC3d,CAAD2d,CAAOA,CACtBA,MAAOnL,EAAAmL,CAAc3d,CAAd2d,CADeA,CAOxBC,EAAAC,EAAAA,CAAAA,QAASA,CAAC7d,CAAD6d,CAAO9X,CAAP8X,CAAcA,CACrBvW,CAAAuW,CAAyB7d,CAAzB6d,CAA+B9X,CAA/B8X,CADqBA,CAOvBC,EAAAC,EAAAA,CAAAA,QAAWA,CAAC/d,CAAD+d,CAAOhY,CAAPgY,CAAcA,CACvBzW,CAAAyW,CAAyB/d,CAAzB+d,CAA+BhY,CAA/BgY,CAAsCA,CAAAA,CAAtCA,CADuBA,CAOzBC,EAAAC,EAAAA,CAAAA,QAAYA,CAACje,CAADie,CAAOA,CACjBA,MAAOtJ,GAAAsJ,CAAcje,CAAdie,CADUA,CAOnBC,EAAAC,EAAAA,CAAAA,QAAmBA,CAACne,CAADme,CAAOA,CACxBA,MAAOzJ,GAAAyJ,CAAgBne,CAAhBme,CADiBA,CAO5B9G,EAAAjI,UAAA,MAAA,CAAiCiI,CAAAjI,UAAAoF,MACjC6C,EAAAjI,UAAA,gBAAA,CAA2CiI,CAAAjI,UAAA+I,gBAC3Cd;CAAAjI,UAAA,aAAA,CAAwCiI,CAAAjI,UAAAgF,aACxCiD,EAAAjI,UAAA,cAAA,CAAyCiI,CAAAjI,UAAAoL,cACzCnD,EAAAjI,UAAA,aAAA,CAAwCiI,CAAAjI,UAAAsN,aACxCrF,EAAAjI,UAAA,sBAAA,CAAiDiI,CAAAjI,UAAA4N,sBACjD3F,EAAAjI,UAAA,gBAAA,CAA2CiI,CAAAjI,UAAAgO,EAC3C/F,EAAAjI,UAAA,kBAAA,CAA6CiI,CAAAjI,UAAAuO,EAC7CtG,EAAAjI,UAAA,gCAAA,CAA2DiI,CAAAjI,UAAA0K,EAC3DzC,EAAAjI,UAAA,YAAA,CAAuCiI,CAAAjI,UAAA2I,EACvCV,EAAAjI,UAAA,iBAAA,CAA4CiI,CAAAjI,UAAA6I,EAC5CZ,EAAAjI,UAAA,kBAAA,CAA6CiI,CAAAjI,UAAA2K,kBAC7C1C;CAAAjI,UAAA,UAAA,CAAqCiI,CAAAjI,UAAAyO,EACrCxG,EAAAjI,UAAA,YAAA,CAAuCiI,CAAAjI,UAAA2O,EACvC1G,EAAAjI,UAAA,aAAA,CAAwCiI,CAAAjI,UAAA6O,EACxC5G,EAAAjI,UAAA,oBAAA,CAA+CiI,CAAAjI,UAAA+O,EAE/CtN,OAAAuN,iBAAA,CAAwB/G,CAAAjI,UAAxB,CAA+C,CAC7C,aAAgB,CACd,IAAAJ,QAAG,EAAG,CACJ,MAAOnM,EADH,CADQ,CAD6B,CAM7C,UAAa,CACX,IAAAmM,QAAG,EAAG,CACJ,MAAOtL,EADH,CADK,CANgC,CAA/C,C,CS9hBA,IAAM2a,EAAc,IAAIhH,CAAxB,CAEI4C,EAFJ,CAEeJ,EAEX/W,OAAA,SAAJ,GACEmX,EACA,CADYnX,MAAA,SAAA,UACZ,CAAA+W,EAAA,CAAuB/W,MAAA,SAAA,qBAFzB,CAKAA;MAAAS,SAAA,CAAkB,CAChB8T,YAAagH,CADG,CAOhB,gBAAAlG,QAAe,CAACpB,CAAD,CAAWD,CAAX,CAAwBwH,CAAxB,CAAwC,CACrDD,CAAAtE,kBAAA,EACAsE,EAAAlG,gBAAA,CAA4BpB,CAA5B,CAAsCD,CAAtC,CAAmDwH,CAAnD,CAFqD,CAPvC,CAgBhB,mBAAAlG,QAAkB,CAACrB,CAAD,CAAWD,CAAX,CAAwB,CACxCuH,CAAAjG,mBAAA,CAA+BrB,CAA/B,CAAyCD,CAAzC,CADwC,CAhB1B,CAyBhB,sBAAAuB,QAAqB,CAACtB,CAAD,CAAWD,CAAX,CAAwBwH,CAAxB,CAAwC,CAC3DD,CAAAtE,kBAAA,EACAsE,EAAAhG,sBAAA,CAAkCtB,CAAlC,CAA4CD,CAA5C,CAAyDwH,CAAzD,CAF2D,CAzB7C,CAiChB,aAAA5B,QAAY,CAACpV,CAAD,CAAUmJ,CAAV,CAAsB,CAChC4N,CAAAtE,kBAAA,EACAsE,EAAA3B,aAAA,CAAyBpV,CAAzB,CAAkCmJ,CAAlC,CAFgC,CAjClB,CAyChB,aAAA2D,QAAY,CAAC9M,CAAD,CAAU,CACpB+W,CAAAtE,kBAAA,EACAsE,EAAAjK,aAAA,CAAyB9M,CAAzB,CAFoB,CAzCN,CAiDhB,cAAAkT,QAAa,CAAC/J,CAAD,CAAa,CACxB4N,CAAAtE,kBAAA,EACAsE,EAAA7D,cAAA,CAA0B/J,CAA1B,CAFwB,CAjDV,CAsDhB,kBAAAsJ,QAAiB,EAAG,CAClBsE,CAAAtE,kBAAA,EADkB,CAtDJ;AA+DhB,sBAAAiD,QAAqB,CAAC1V,CAAD,CAAU2J,CAAV,CAAoB,CACvC,MAAOoN,EAAArB,sBAAA,CAAkC1V,CAAlC,CAA2C2J,CAA3C,CADgC,CA/DzB,CAmEhBxN,UAAWC,CAnEK,CAqEhBb,aAAcA,CArEE,CAuEhBS,SAAUK,CAvEM,CA0EdsW,GAAJ,GACEnX,MAAAS,SAAA0W,UADF,CAC8BA,EAD9B,CAIIJ,GAAJ,GACE/W,MAAAS,SAAAsW,qBADF,CACyCA,EADzC","file":"scoping-shim.min.js","sourcesContent":["/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n/*\nExtremely simple css parser. Intended to be not more than what we need\nand definitely not necessarily correct =).\n*/\n\n'use strict';\n\n/** @unrestricted */\nclass StyleNode {\n constructor() {\n /** @type {number} */\n this['start'] = 0;\n /** @type {number} */\n this['end'] = 0;\n /** @type {StyleNode} */\n this['previous'] = null;\n /** @type {StyleNode} */\n this['parent'] = null;\n /** @type {Array<StyleNode>} */\n this['rules'] = null;\n /** @type {string} */\n this['parsedCssText'] = '';\n /** @type {string} */\n this['cssText'] = '';\n /** @type {boolean} */\n this['atRule'] = false;\n /** @type {number} */\n this['type'] = 0;\n /** @type {string} */\n this['keyframesName'] = '';\n /** @type {string} */\n this['selector'] = '';\n /** @type {string} */\n this['parsedSelector'] = '';\n }\n}\n\nexport {StyleNode}\n\n// given a string of css, return a simple rule tree\n/**\n * @param {string} text\n * @return {StyleNode}\n */\nexport function parse(text) {\n text = clean(text);\n return parseCss(lex(text), text);\n}\n\n// remove stuff we don't care about that may hinder parsing\n/**\n * @param {string} cssText\n * @return {string}\n */\nfunction clean(cssText) {\n return cssText.replace(RX.comments, '').replace(RX.port, '');\n}\n\n// super simple {...} lexer that returns a node tree\n/**\n * @param {string} text\n * @return {StyleNode}\n */\nfunction lex(text) {\n let root = new StyleNode();\n root['start'] = 0;\n root['end'] = text.length\n let n = root;\n for (let i = 0, l = text.length; i < l; i++) {\n if (text[i] === OPEN_BRACE) {\n if (!n['rules']) {\n n['rules'] = [];\n }\n let p = n;\n let previous = p['rules'][p['rules'].length - 1] || null;\n n = new StyleNode();\n n['start'] = i + 1;\n n['parent'] = p;\n n['previous'] = previous;\n p['rules'].push(n);\n } else if (text[i] === CLOSE_BRACE) {\n n['end'] = i + 1;\n n = n['parent'] || root;\n }\n }\n return root;\n}\n\n// add selectors/cssText to node tree\n/**\n * @param {StyleNode} node\n * @param {string} text\n * @return {StyleNode}\n */\nfunction parseCss(node, text) {\n let t = text.substring(node['start'], node['end'] - 1);\n node['parsedCssText'] = node['cssText'] = t.trim();\n if (node['parent']) {\n let ss = node['previous'] ? node['previous']['end'] : node['parent']['start'];\n t = text.substring(ss, node['start'] - 1);\n t = _expandUnicodeEscapes(t);\n t = t.replace(RX.multipleSpaces, ' ');\n // TODO(sorvell): ad hoc; make selector include only after last ;\n // helps with mixin syntax\n t = t.substring(t.lastIndexOf(';') + 1);\n let s = node['parsedSelector'] = node['selector'] = t.trim();\n node['atRule'] = (s.indexOf(AT_START) === 0);\n // note, support a subset of rule types...\n if (node['atRule']) {\n if (s.indexOf(MEDIA_START) === 0) {\n node['type'] = types.MEDIA_RULE;\n } else if (s.match(RX.keyframesRule)) {\n node['type'] = types.KEYFRAMES_RULE;\n node['keyframesName'] =\n node['selector'].split(RX.multipleSpaces).pop();\n }\n } else {\n if (s.indexOf(VAR_START) === 0) {\n node['type'] = types.MIXIN_RULE;\n } else {\n node['type'] = types.STYLE_RULE;\n }\n }\n }\n let r$ = node['rules'];\n if (r$) {\n for (let i = 0, l = r$.length, r;\n (i < l) && (r = r$[i]); i++) {\n parseCss(r, text);\n }\n }\n return node;\n}\n\n/**\n * conversion of sort unicode escapes with spaces like `\\33 ` (and longer) into\n * expanded form that doesn't require trailing space `\\000033`\n * @param {string} s\n * @return {string}\n */\nfunction _expandUnicodeEscapes(s) {\n return s.replace(/\\\\([0-9a-f]{1,6})\\s/gi, function() {\n let code = arguments[1],\n repeat = 6 - code.length;\n while (repeat--) {\n code = '0' + code;\n }\n return '\\\\' + code;\n });\n}\n\n/**\n * stringify parsed css.\n * @param {StyleNode} node\n * @param {boolean=} preserveProperties\n * @param {string=} text\n * @return {string}\n */\nexport function stringify(node, preserveProperties, text = '') {\n // calc rule cssText\n let cssText = '';\n if (node['cssText'] || node['rules']) {\n let r$ = node['rules'];\n if (r$ && !_hasMixinRules(r$)) {\n for (let i = 0, l = r$.length, r;\n (i < l) && (r = r$[i]); i++) {\n cssText = stringify(r, preserveProperties, cssText);\n }\n } else {\n cssText = preserveProperties ? node['cssText'] :\n removeCustomProps(node['cssText']);\n cssText = cssText.trim();\n if (cssText) {\n cssText = ' ' + cssText + '\\n';\n }\n }\n }\n // emit rule if there is cssText\n if (cssText) {\n if (node['selector']) {\n text += node['selector'] + ' ' + OPEN_BRACE + '\\n';\n }\n text += cssText;\n if (node['selector']) {\n text += CLOSE_BRACE + '\\n\\n';\n }\n }\n return text;\n}\n\n/**\n * @param {Array<StyleNode>} rules\n * @return {boolean}\n */\nfunction _hasMixinRules(rules) {\n let r = rules[0];\n return Boolean(r) && Boolean(r['selector']) && r['selector'].indexOf(VAR_START) === 0;\n}\n\n/**\n * @param {string} cssText\n * @return {string}\n */\nfunction removeCustomProps(cssText) {\n cssText = removeCustomPropAssignment(cssText);\n return removeCustomPropApply(cssText);\n}\n\n/**\n * @param {string} cssText\n * @return {string}\n */\nexport function removeCustomPropAssignment(cssText) {\n return cssText\n .replace(RX.customProp, '')\n .replace(RX.mixinProp, '');\n}\n\n/**\n * @param {string} cssText\n * @return {string}\n */\nfunction removeCustomPropApply(cssText) {\n return cssText\n .replace(RX.mixinApply, '')\n .replace(RX.varApply, '');\n}\n\n/** @enum {number} */\nexport const types = {\n STYLE_RULE: 1,\n KEYFRAMES_RULE: 7,\n MEDIA_RULE: 4,\n MIXIN_RULE: 1000\n}\n\nconst OPEN_BRACE = '{';\nconst CLOSE_BRACE = '}';\n\n// helper regexp's\nconst RX = {\n comments: /\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//gim,\n port: /@import[^;]*;/gim,\n customProp: /(?:^[^;\\-\\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\\n]|$)/gim,\n mixinProp: /(?:^[^;\\-\\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\\n]|$)?/gim,\n mixinApply: /@apply\\s*\\(?[^);]*\\)?\\s*(?:[;\\n]|$)?/gim,\n varApply: /[^;:]*?:[^;]*?var\\([^;]*\\)(?:[;\\n]|$)?/gim,\n keyframesRule: /^@[^\\s]*keyframes/,\n multipleSpaces: /\\s+/g\n}\n\nconst VAR_START = '--';\nconst MEDIA_START = '@media';\nconst AT_START = '@';\n",null,"/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nexport const nativeShadow = !(window['ShadyDOM'] && window['ShadyDOM']['inUse']);\nlet nativeCssVariables_;\n\n/**\n * @param {(ShadyCSSOptions | ShadyCSSInterface)=} settings\n */\nfunction calcCssVariables(settings) {\n if (settings && settings['shimcssproperties']) {\n nativeCssVariables_ = false;\n } else {\n // chrome 49 has semi-working css vars, check if box-shadow works\n // safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782\n // However, shim css custom properties are only supported with ShadyDOM enabled,\n // so fall back on native if we do not detect ShadyDOM\n // Edge 15: custom properties used in ::before and ::after will also be used in the parent element\n // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12414257/\n nativeCssVariables_ = nativeShadow || Boolean(!navigator.userAgent.match(/AppleWebKit\\/601|Edge\\/15/) &&\n window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)'));\n }\n}\n\n/** @type {string | undefined} */\nexport let cssBuild;\nif (window.ShadyCSS && window.ShadyCSS.cssBuild !== undefined) {\n cssBuild = window.ShadyCSS.cssBuild;\n}\n\nif (window.ShadyCSS && window.ShadyCSS.nativeCss !== undefined) {\n nativeCssVariables_ = window.ShadyCSS.nativeCss;\n} else if (window.ShadyCSS) {\n calcCssVariables(window.ShadyCSS);\n // reset window variable to let ShadyCSS API take its place\n window.ShadyCSS = undefined;\n} else {\n calcCssVariables(window['WebComponents'] && window['WebComponents']['flags']);\n}\n\n// Hack for type error under new type inference which doesn't like that\n// nativeCssVariables is updated in a function and assigns the type\n// `function(): ?` instead of `boolean`.\nexport const nativeCssVariables = /** @type {boolean} */(nativeCssVariables_);","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\nexport const VAR_ASSIGN = /(?:^|[;\\s{]\\s*)(--[\\w-]*?)\\s*:\\s*(?:((?:'(?:\\\\'|.)*?'|\"(?:\\\\\"|.)*?\"|\\([^)]*?\\)|[^};{])+)|\\{([^}]*)\\}(?:(?=[;\\s}])|$))/gi;\nexport const MIXIN_MATCH = /(?:^|\\W+)@apply\\s*\\(?([^);\\n]*)\\)?/gi;\nexport const VAR_CONSUMED = /(--[\\w-]+)\\s*([:,;)]|$)/gi;\nexport const ANIMATION_MATCH = /(animation\\s*:)|(animation-name\\s*:)/;\nexport const MEDIA_MATCH = /@media\\s(.*)/;\nexport const IS_VAR = /^--/;\nexport const BRACKETED = /\\{[^}]*\\}/g;\nexport const HOST_PREFIX = '(?:^|[^.#[:])';\nexport const HOST_SUFFIX = '($|[.:[\\\\s>+~])';\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\n/** @type {!Set<string>} */\nconst styleTextSet = new Set();\n\nexport const scopingAttribute = 'shady-unscoped';\n\n/**\n * Add a specifically-marked style to the document directly, and only one copy of that style.\n *\n * @param {!HTMLStyleElement} style\n * @return {undefined}\n */\nexport function processUnscopedStyle(style) {\n const text = style.textContent;\n if (!styleTextSet.has(text)) {\n styleTextSet.add(text);\n const newStyle = style.cloneNode(true);\n document.head.appendChild(newStyle);\n }\n}\n\n/**\n * Check if a style is supposed to be unscoped\n * @param {!HTMLStyleElement} style\n * @return {boolean} true if the style has the unscoping attribute\n */\nexport function isUnscopedStyle(style) {\n return style.hasAttribute(scopingAttribute);\n}","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {nativeShadow, nativeCssVariables, cssBuild} from './style-settings.js';\nimport {parse, stringify, types, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\nimport {MEDIA_MATCH} from './common-regex.js';\nimport {processUnscopedStyle, isUnscopedStyle} from './unscoped-style-handler.js';\n\n/**\n * @param {string|StyleNode} rules\n * @param {function(StyleNode)=} callback\n * @return {string}\n */\nexport function toCssText (rules, callback) {\n if (!rules) {\n return '';\n }\n if (typeof rules === 'string') {\n rules = parse(rules);\n }\n if (callback) {\n forEachRule(rules, callback);\n }\n return stringify(rules, nativeCssVariables);\n}\n\n/**\n * @param {HTMLStyleElement} style\n * @return {StyleNode}\n */\nexport function rulesForStyle(style) {\n if (!style['__cssRules'] && style.textContent) {\n style['__cssRules'] = parse(style.textContent);\n }\n return style['__cssRules'] || null;\n}\n\n// Tests if a rule is a keyframes selector, which looks almost exactly\n// like a normal selector but is not (it has nothing to do with scoping\n// for example).\n/**\n * @param {StyleNode} rule\n * @return {boolean}\n */\nexport function isKeyframesSelector(rule) {\n return Boolean(rule['parent']) &&\n rule['parent']['type'] === types.KEYFRAMES_RULE;\n}\n\n/**\n * @param {StyleNode} node\n * @param {Function=} styleRuleCallback\n * @param {Function=} keyframesRuleCallback\n * @param {boolean=} onlyActiveRules\n */\nexport function forEachRule(node, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) {\n if (!node) {\n return;\n }\n let skipRules = false;\n let type = node['type'];\n if (onlyActiveRules) {\n if (type === types.MEDIA_RULE) {\n let matchMedia = node['selector'].match(MEDIA_MATCH);\n if (matchMedia) {\n // if rule is a non matching @media rule, skip subrules\n if (!window.matchMedia(matchMedia[1]).matches) {\n skipRules = true;\n }\n }\n }\n }\n if (type === types.STYLE_RULE) {\n styleRuleCallback(node);\n } else if (keyframesRuleCallback &&\n type === types.KEYFRAMES_RULE) {\n keyframesRuleCallback(node);\n } else if (type === types.MIXIN_RULE) {\n skipRules = true;\n }\n let r$ = node['rules'];\n if (r$ && !skipRules) {\n for (let i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {\n forEachRule(r, styleRuleCallback, keyframesRuleCallback, onlyActiveRules);\n }\n }\n}\n\n// add a string of cssText to the document.\n/**\n * @param {string} cssText\n * @param {string} moniker\n * @param {Node} target\n * @param {Node} contextNode\n * @return {HTMLStyleElement}\n */\nexport function applyCss(cssText, moniker, target, contextNode) {\n let style = createScopeStyle(cssText, moniker);\n applyStyle(style, target, contextNode);\n return style;\n}\n\n/**\n * @param {string} cssText\n * @param {string} moniker\n * @return {HTMLStyleElement}\n */\nexport function createScopeStyle(cssText, moniker) {\n let style = /** @type {HTMLStyleElement} */(document.createElement('style'));\n if (moniker) {\n style.setAttribute('scope', moniker);\n }\n style.textContent = cssText;\n return style;\n}\n\n/**\n * Track the position of the last added style for placing placeholders\n * @type {Node}\n */\nlet lastHeadApplyNode = null;\n\n// insert a comment node as a styling position placeholder.\n/**\n * @param {string} moniker\n * @return {!Comment}\n */\nexport function applyStylePlaceHolder(moniker) {\n let placeHolder = document.createComment(' Shady DOM styles for ' +\n moniker + ' ');\n let after = lastHeadApplyNode ?\n lastHeadApplyNode['nextSibling'] : null;\n let scope = document.head;\n scope.insertBefore(placeHolder, after || scope.firstChild);\n lastHeadApplyNode = placeHolder;\n return placeHolder;\n}\n\n/**\n * @param {HTMLStyleElement} style\n * @param {?Node} target\n * @param {?Node} contextNode\n */\nexport function applyStyle(style, target, contextNode) {\n target = target || document.head;\n let after = (contextNode && contextNode.nextSibling) ||\n target.firstChild;\n target.insertBefore(style, after);\n if (!lastHeadApplyNode) {\n lastHeadApplyNode = style;\n } else {\n // only update lastHeadApplyNode if the new style is inserted after the old lastHeadApplyNode\n let position = style.compareDocumentPosition(lastHeadApplyNode);\n if (position === Node.DOCUMENT_POSITION_PRECEDING) {\n lastHeadApplyNode = style;\n }\n }\n}\n\n/**\n * @param {string} buildType\n * @return {boolean}\n */\nexport function isTargetedBuild(buildType) {\n return nativeShadow ? buildType === 'shadow' : buildType === 'shady';\n}\n\n/**\n * Walk from text[start] matching parens and\n * returns position of the outer end paren\n * @param {string} text\n * @param {number} start\n * @return {number}\n */\nexport function findMatchingParen(text, start) {\n let level = 0;\n for (let i=start, l=text.length; i < l; i++) {\n if (text[i] === '(') {\n level++;\n } else if (text[i] === ')') {\n if (--level === 0) {\n return i;\n }\n }\n }\n return -1;\n}\n\n/**\n * @param {string} str\n * @param {function(string, string, string, string)} callback\n */\nexport function processVariableAndFallback(str, callback) {\n // find 'var('\n let start = str.indexOf('var(');\n if (start === -1) {\n // no var?, everything is prefix\n return callback(str, '', '', '');\n }\n //${prefix}var(${inner})${suffix}\n let end = findMatchingParen(str, start + 3);\n let inner = str.substring(start + 4, end);\n let prefix = str.substring(0, start);\n // suffix may have other variables\n let suffix = processVariableAndFallback(str.substring(end + 1), callback);\n let comma = inner.indexOf(',');\n // value and fallback args should be trimmed to match in property lookup\n if (comma === -1) {\n // variable, no fallback\n return callback(prefix, inner.trim(), '', suffix);\n }\n // var(${value},${fallback})\n let value = inner.substring(0, comma).trim();\n let fallback = inner.substring(comma + 1).trim();\n return callback(prefix, value, fallback, suffix);\n}\n\n/**\n * @param {Element} element\n * @param {string} value\n */\nexport function setElementClassRaw(element, value) {\n // use native setAttribute provided by ShadyDOM when setAttribute is patched\n if (nativeShadow) {\n element.setAttribute('class', value);\n } else {\n window['ShadyDOM']['nativeMethods']['setAttribute'].call(element, 'class', value);\n }\n}\n\nexport const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] || ((node) => node);\n\n/**\n * @param {Element | {is: string, extends: string}} element\n * @return {{is: string, typeExtension: string}}\n */\nexport function getIsExtends(element) {\n let localName = element['localName'];\n let is = '', typeExtension = '';\n /*\n NOTE: technically, this can be wrong for certain svg elements\n with `-` in the name like `<font-face>`\n */\n if (localName) {\n if (localName.indexOf('-') > -1) {\n is = localName;\n } else {\n typeExtension = localName;\n is = (element.getAttribute && element.getAttribute('is')) || '';\n }\n } else {\n is = /** @type {?} */(element).is;\n typeExtension = /** @type {?} */(element).extends;\n }\n return {is, typeExtension};\n}\n\n/**\n * @param {Element|DocumentFragment} element\n * @return {string}\n */\nexport function gatherStyleText(element) {\n /** @type {!Array<string>} */\n const styleTextParts = [];\n const styles = /** @type {!NodeList<!HTMLStyleElement>} */(element.querySelectorAll('style'));\n for (let i = 0; i < styles.length; i++) {\n const style = styles[i];\n if (isUnscopedStyle(style)) {\n if (!nativeShadow) {\n processUnscopedStyle(style);\n style.parentNode.removeChild(style);\n }\n } else {\n styleTextParts.push(style.textContent);\n style.parentNode.removeChild(style);\n }\n }\n return styleTextParts.join('').trim();\n}\n\n/**\n * Split a selector separated by commas into an array in a smart way\n * @param {string} selector\n * @return {!Array<string>}\n */\nexport function splitSelectorList(selector) {\n const parts = [];\n let part = '';\n for (let i = 0; i >= 0 && i < selector.length; i++) {\n // A selector with parentheses will be one complete part\n if (selector[i] === '(') {\n // find the matching paren\n const end = findMatchingParen(selector, i);\n // push the paren block into the part\n part += selector.slice(i, end + 1);\n // move the index to after the paren block\n i = end;\n } else if (selector[i] === ',') {\n parts.push(part);\n part = '';\n } else {\n part += selector[i];\n }\n }\n // catch any pieces after the last comma\n if (part) {\n parts.push(part);\n }\n return parts;\n}\n\nconst CSS_BUILD_ATTR = 'css-build';\n\n/**\n * Return the polymer-css-build \"build type\" applied to this element\n *\n * @param {!HTMLElement} element\n * @return {string} Can be \"\", \"shady\", or \"shadow\"\n */\nexport function getCssBuild(element) {\n if (cssBuild !== undefined) {\n return /** @type {string} */(cssBuild);\n }\n if (element.__cssBuild === undefined) {\n // try attribute first, as it is the common case\n const attrValue = element.getAttribute(CSS_BUILD_ATTR);\n if (attrValue) {\n element.__cssBuild = attrValue;\n } else {\n const buildComment = getBuildComment(element);\n if (buildComment !== '') {\n // remove build comment so it is not needlessly copied into every element instance\n removeBuildComment(element);\n }\n element.__cssBuild = buildComment;\n }\n }\n return element.__cssBuild || '';\n}\n\n/**\n * Check if the given element, either a <template> or <style>, has been processed\n * by polymer-css-build.\n *\n * If so, then we can make a number of optimizations:\n * - polymer-css-build will decompose mixins into individual CSS Custom Properties,\n * so the ApplyShim can be skipped entirely.\n * - Under native ShadowDOM, the style text can just be copied into each instance\n * without modification\n * - If the build is \"shady\" and ShadyDOM is in use, the styling does not need\n * scoping beyond the shimming of CSS Custom Properties\n *\n * @param {!HTMLElement} element\n * @return {boolean}\n */\nexport function elementHasBuiltCss(element) {\n return getCssBuild(element) !== '';\n}\n\n/**\n * For templates made with tagged template literals, polymer-css-build will\n * insert a comment of the form `<!--css-build:shadow-->`\n *\n * @param {!HTMLElement} element\n * @return {string}\n */\nexport function getBuildComment(element) {\n const buildComment = element.localName === 'template' ?\n /** @type {!HTMLTemplateElement} */ (element).content.firstChild :\n element.firstChild;\n if (buildComment instanceof Comment) {\n const commentParts = buildComment.textContent.trim().split(':');\n if (commentParts[0] === CSS_BUILD_ATTR) {\n return commentParts[1];\n }\n }\n return '';\n}\n\n/**\n * Check if the css build status is optimal, and do no unneeded work.\n *\n * @param {string=} cssBuild CSS build status\n * @return {boolean} css build is optimal or not\n */\nexport function isOptimalCssBuild(cssBuild = '') {\n // CSS custom property shim always requires work\n if (cssBuild === '' || !nativeCssVariables) {\n return false;\n }\n return nativeShadow ? cssBuild === 'shadow' : cssBuild === 'shady';\n}\n\n/**\n * @param {!HTMLElement} element\n */\nfunction removeBuildComment(element) {\n const buildComment = element.localName === 'template' ?\n /** @type {!HTMLTemplateElement} */ (element).content.firstChild :\n element.firstChild;\n buildComment.parentNode.removeChild(buildComment);\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\nimport * as StyleUtil from './style-util.js';\nimport {nativeShadow} from './style-settings.js';\n\n/* Transforms ShadowDOM styling into ShadyDOM styling\n\n* scoping:\n\n * elements in scope get scoping selector class=\"x-foo-scope\"\n * selectors re-written as follows:\n\n div button -> div.x-foo-scope button.x-foo-scope\n\n* :host -> scopeName\n\n* :host(...) -> scopeName...\n\n* ::slotted(...) -> scopeName > ...\n\n* ...:dir(ltr|rtl) -> [dir=\"ltr|rtl\"] ..., ...[dir=\"ltr|rtl\"]\n\n* :host(:dir[rtl]) -> scopeName:dir(rtl) -> [dir=\"rtl\"] scopeName, scopeName[dir=\"rtl\"]\n\n*/\nconst SCOPE_NAME = 'style-scope';\n\nclass StyleTransformer {\n get SCOPE_NAME() {\n return SCOPE_NAME;\n }\n /**\n * Given a node and scope name, add a scoping class to each node\n * in the tree. This facilitates transforming css into scoped rules.\n * @param {!Node} node\n * @param {string} scope\n * @param {boolean=} shouldRemoveScope\n * @deprecated\n */\n dom(node, scope, shouldRemoveScope) {\n const fn = (node) => {\n this.element(node, scope || '', shouldRemoveScope);\n };\n this._transformDom(node, fn);\n }\n\n /**\n * Given a node and scope name, add a scoping class to each node in the tree.\n * @param {!Node} node\n * @param {string} scope\n */\n domAddScope(node, scope) {\n const fn = (node) => {\n this.element(node, scope || '');\n };\n this._transformDom(node, fn);\n }\n\n /**\n * @param {!Node} startNode\n * @param {!function(!Node)} transformer\n */\n _transformDom(startNode, transformer) {\n if (startNode.nodeType === Node.ELEMENT_NODE) {\n transformer(startNode)\n }\n let c$;\n if (startNode.localName === 'template') {\n const template = /** @type {!HTMLTemplateElement} */ (startNode);\n // In case the template is in svg context, fall back to the node\n // since it won't be an HTMLTemplateElement with a .content property\n c$ = (template.content || template._content || template).childNodes;\n } else {\n c$ = /** @type {!ParentNode} */ (startNode).children ||\n startNode.childNodes;\n }\n if (c$) {\n for (let i = 0; i < c$.length; i++) {\n this._transformDom(c$[i], transformer);\n }\n }\n }\n\n /**\n * @param {?} element\n * @param {?} scope\n * @param {?=} shouldRemoveScope\n */\n element(element, scope, shouldRemoveScope) {\n // note: if using classes, we add both the general 'style-scope' class\n // as well as the specific scope. This enables easy filtering of all\n // `style-scope` elements\n if (scope) {\n // note: svg on IE does not have classList so fallback to class\n if (element.classList) {\n if (shouldRemoveScope) {\n element.classList.remove(SCOPE_NAME);\n element.classList.remove(scope);\n } else {\n element.classList.add(SCOPE_NAME);\n element.classList.add(scope);\n }\n } else if (element.getAttribute) {\n let c = element.getAttribute(CLASS);\n if (shouldRemoveScope) {\n if (c) {\n let newValue = c.replace(SCOPE_NAME, '').replace(scope, '');\n StyleUtil.setElementClassRaw(element, newValue);\n }\n } else {\n let newValue = (c ? c + ' ' : '') + SCOPE_NAME + ' ' + scope;\n StyleUtil.setElementClassRaw(element, newValue);\n }\n }\n }\n }\n\n /**\n * Given a node, replace the scoping class to each subnode in the tree.\n * @param {!Node} node\n * @param {string} oldScope\n * @param {string} newScope\n */\n domReplaceScope(node, oldScope, newScope) {\n const fn = (node) => {\n this.element(node, oldScope, true);\n this.element(node, newScope);\n };\n this._transformDom(node, fn);\n }\n /**\n * Given a node, remove the scoping class to each subnode in the tree.\n * @param {!Node} node\n * @param {string} oldScope\n */\n domRemoveScope(node, oldScope) {\n const fn = (node) => {\n this.element(node, oldScope || '', true);\n };\n this._transformDom(node, fn);\n }\n\n /**\n * @param {?} element\n * @param {?} styleRules\n * @param {?=} callback\n * @param {string=} cssBuild\n * @param {string=} cssText\n * @return {string}\n */\n elementStyles(element, styleRules, callback, cssBuild = '', cssText = '') {\n // no need to shim selectors if settings.useNativeShadow, also\n // a shady css build will already have transformed selectors\n // NOTE: This method may be called as part of static or property shimming.\n // When there is a targeted build it will not be called for static shimming,\n // but when the property shim is used it is called and should opt out of\n // static shimming work when a proper build exists.\n if (cssText === '') {\n if (nativeShadow || cssBuild === 'shady') {\n cssText = StyleUtil.toCssText(styleRules, callback);\n } else {\n let {is, typeExtension} = StyleUtil.getIsExtends(element);\n cssText = this.css(styleRules, is, typeExtension, callback) + '\\n\\n';\n }\n }\n return cssText.trim();\n }\n\n // Given a string of cssText and a scoping string (scope), returns\n // a string of scoped css where each selector is transformed to include\n // a class created from the scope. ShadowDOM selectors are also transformed\n // (e.g. :host) to use the scoping selector.\n css(rules, scope, ext, callback) {\n let hostScope = this._calcHostScope(scope, ext);\n scope = this._calcElementScope(scope);\n let self = this;\n return StyleUtil.toCssText(rules, function(/** StyleNode */rule) {\n if (!rule.isScoped) {\n self.rule(rule, scope, hostScope);\n rule.isScoped = true;\n }\n if (callback) {\n callback(rule, scope, hostScope);\n }\n });\n }\n\n _calcElementScope(scope) {\n if (scope) {\n return CSS_CLASS_PREFIX + scope;\n } else {\n return '';\n }\n }\n\n _calcHostScope(scope, ext) {\n return ext ? `[is=${scope}]` : scope;\n }\n\n rule(rule, scope, hostScope) {\n this._transformRule(rule, this._transformComplexSelector,\n scope, hostScope);\n }\n\n /**\n * transforms a css rule to a scoped rule.\n *\n * @param {StyleNode} rule\n * @param {Function} transformer\n * @param {string=} scope\n * @param {string=} hostScope\n */\n _transformRule(rule, transformer, scope, hostScope) {\n // NOTE: save transformedSelector for subsequent matching of elements\n // against selectors (e.g. when calculating style properties)\n rule['selector'] = rule.transformedSelector =\n this._transformRuleCss(rule, transformer, scope, hostScope);\n }\n\n /**\n * @param {StyleNode} rule\n * @param {Function} transformer\n * @param {string=} scope\n * @param {string=} hostScope\n */\n _transformRuleCss(rule, transformer, scope, hostScope) {\n let p$ = StyleUtil.splitSelectorList(rule['selector']);\n // we want to skip transformation of rules that appear in keyframes,\n // because they are keyframe selectors, not element selectors.\n if (!StyleUtil.isKeyframesSelector(rule)) {\n for (let i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {\n p$[i] = transformer.call(this, p, scope, hostScope);\n }\n }\n return p$.filter((part) => Boolean(part)).join(COMPLEX_SELECTOR_SEP);\n }\n\n /**\n * @param {string} selector\n * @return {string}\n */\n _twiddleNthPlus(selector) {\n return selector.replace(NTH, (m, type, inside) => {\n if (inside.indexOf('+') > -1) {\n inside = inside.replace(/\\+/g, '___');\n } else if (inside.indexOf('___') > -1) {\n inside = inside.replace(/___/g, '+');\n }\n return `:${type}(${inside})`;\n });\n }\n\n /**\n * Preserve `:matches()` selectors by replacing them with MATCHES_REPLACMENT\n * and returning an array of `:matches()` selectors.\n * Use `_replacesMatchesPseudo` to replace the `:matches()` parts\n *\n * @param {string} selector\n * @return {{selector: string, matches: !Array<string>}}\n */\n _preserveMatchesPseudo(selector) {\n /** @type {!Array<string>} */\n const matches = [];\n let match;\n while ((match = selector.match(MATCHES))) {\n const start = match.index;\n const end = StyleUtil.findMatchingParen(selector, start);\n if (end === -1) {\n throw new Error(`${match.input} selector missing ')'`)\n }\n const part = selector.slice(start, end + 1);\n selector = selector.replace(part, MATCHES_REPLACEMENT);\n matches.push(part);\n }\n return {selector, matches};\n }\n\n /**\n * Replace MATCHES_REPLACMENT character with the given set of `:matches()`\n * selectors.\n *\n * @param {string} selector\n * @param {!Array<string>} matches\n * @return {string}\n */\n _replaceMatchesPseudo(selector, matches) {\n const parts = selector.split(MATCHES_REPLACEMENT);\n return matches.reduce((acc, cur, idx) => acc + cur + parts[idx + 1], parts[0]);\n }\n\n/**\n * @param {string} selector\n * @param {string} scope\n * @param {string=} hostScope\n */\n _transformComplexSelector(selector, scope, hostScope) {\n let stop = false;\n selector = selector.trim();\n // Remove spaces inside of selectors like `:nth-of-type` because it confuses SIMPLE_SELECTOR_SEP\n let isNth = NTH.test(selector);\n if (isNth) {\n selector = selector.replace(NTH, (m, type, inner) => `:${type}(${inner.replace(/\\s/g, '')})`)\n selector = this._twiddleNthPlus(selector);\n }\n // Preserve selectors like `:-webkit-any` so that SIMPLE_SELECTOR_SEP does\n // not get confused by spaces inside the pseudo selector\n const isMatches = MATCHES.test(selector);\n /** @type {!Array<string>} */\n let matches;\n if (isMatches) {\n ({selector, matches} = this._preserveMatchesPseudo(selector));\n }\n selector = selector.replace(SLOTTED_START, `${HOST} $1`);\n selector = selector.replace(SIMPLE_SELECTOR_SEP, (m, c, s) => {\n if (!stop) {\n let info = this._transformCompoundSelector(s, c, scope, hostScope);\n stop = stop || info.stop;\n c = info.combinator;\n s = info.value;\n }\n return c + s;\n });\n // replace `:matches()` selectors\n if (isMatches) {\n selector = this._replaceMatchesPseudo(selector, matches);\n }\n if (isNth) {\n selector = this._twiddleNthPlus(selector);\n }\n return selector;\n }\n\n _transformCompoundSelector(selector, combinator, scope, hostScope) {\n // replace :host with host scoping class\n let slottedIndex = selector.indexOf(SLOTTED);\n if (selector.indexOf(HOST) >= 0) {\n selector = this._transformHostSelector(selector, hostScope);\n // replace other selectors with scoping class\n } else if (slottedIndex !== 0) {\n selector = scope ? this._transformSimpleSelector(selector, scope) :\n selector;\n }\n // mark ::slotted() scope jump to replace with descendant selector + arg\n // also ignore left-side combinator\n let slotted = false;\n if (slottedIndex >= 0) {\n combinator = '';\n slotted = true;\n }\n // process scope jumping selectors up to the scope jump and then stop\n let stop;\n if (slotted) {\n stop = true;\n if (slotted) {\n // .zonk ::slotted(.foo) -> .zonk.scope > .foo\n selector = selector.replace(SLOTTED_PAREN, (m, paren) => ` > ${paren}`);\n }\n }\n selector = selector.replace(DIR_PAREN, (m, before, dir) =>\n `[dir=\"${dir}\"] ${before}, ${before}[dir=\"${dir}\"]`);\n return {value: selector, combinator, stop};\n }\n\n _transformSimpleSelector(selector, scope) {\n const attributes = selector.split(/(\\[.+?\\])/);\n\n const output = [];\n for (let i = 0; i < attributes.length; i++) {\n // Do not attempt to transform any attribute selector content\n if ((i % 2) === 1) {\n output.push(attributes[i]);\n } else {\n const part = attributes[i];\n\n if (!(part === '' && i === attributes.length - 1)) {\n let p$ = part.split(PSEUDO_PREFIX);\n p$[0] += scope;\n output.push(p$.join(PSEUDO_PREFIX));\n }\n }\n }\n\n return output.join('');\n }\n\n // :host(...) -> scopeName...\n _transformHostSelector(selector, hostScope) {\n let m = selector.match(HOST_PAREN);\n let paren = m && m[2].trim() || '';\n if (paren) {\n if (!paren[0].match(SIMPLE_SELECTOR_PREFIX)) {\n // paren starts with a type selector\n let typeSelector = paren.split(SIMPLE_SELECTOR_PREFIX)[0];\n // if the type selector is our hostScope then avoid pre-pending it\n if (typeSelector === hostScope) {\n return paren;\n // otherwise, this selector should not match in this scope so\n // output a bogus selector.\n } else {\n return SELECTOR_NO_MATCH;\n }\n } else {\n // make sure to do a replace here to catch selectors like:\n // `:host(.foo)::before`\n return selector.replace(HOST_PAREN, function(m, host, paren) {\n return hostScope + paren;\n });\n }\n // if no paren, do a straight :host replacement.\n // TODO(sorvell): this should not strictly be necessary but\n // it's needed to maintain support for `:host[foo]` type selectors\n // which have been improperly used under Shady DOM. This should be\n // deprecated.\n } else {\n return selector.replace(HOST, hostScope);\n }\n }\n\n /**\n * @param {StyleNode} rule\n */\n documentRule(rule) {\n // reset selector in case this is redone.\n rule['selector'] = rule['parsedSelector'];\n this.normalizeRootSelector(rule);\n this._transformRule(rule, this._transformDocumentSelector);\n }\n\n /**\n * @param {StyleNode} rule\n */\n normalizeRootSelector(rule) {\n if (rule['selector'] === ROOT) {\n rule['selector'] = 'html';\n }\n }\n\n/**\n * @param {string} selector\n */\n _transformDocumentSelector(selector) {\n if (selector.match(HOST)) {\n // remove ':host' type selectors in document rules\n return '';\n } else if (selector.match(SLOTTED)) {\n return this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR)\n } else {\n return this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR);\n }\n }\n}\n\nconst NTH = /:(nth[-\\w]+)\\(([^)]+)\\)/;\nconst SCOPE_DOC_SELECTOR = `:not(.${SCOPE_NAME})`;\nconst COMPLEX_SELECTOR_SEP = ',';\nconst SIMPLE_SELECTOR_SEP = /(^|[\\s>+~]+)((?:\\[.+?\\]|[^\\s>+~=[])+)/g;\nconst SIMPLE_SELECTOR_PREFIX = /[[.:#*]/;\nconst HOST = ':host';\nconst ROOT = ':root';\nconst SLOTTED = '::slotted';\nconst SLOTTED_START = new RegExp(`^(${SLOTTED})`);\n// NOTE: this supports 1 nested () pair for things like\n// :host(:not([selected]), more general support requires\n// parsing which seems like overkill\nconst HOST_PAREN = /(:host)(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))/;\n// similar to HOST_PAREN\nconst SLOTTED_PAREN = /(?:::slotted)(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))/;\nconst DIR_PAREN = /(.*):dir\\((?:(ltr|rtl))\\)/;\nconst CSS_CLASS_PREFIX = '.';\nconst PSEUDO_PREFIX = ':';\nconst CLASS = 'class';\nconst SELECTOR_NO_MATCH = 'should_not_match';\nconst MATCHES = /:(?:matches|any|-(?:webkit|moz)-any)/;\nconst MATCHES_REPLACEMENT = '\\u{e000}';\n\nexport default new StyleTransformer()\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {parse, StyleNode} from './css-parse.js';\nimport {nativeShadow, nativeCssVariables} from './style-settings.js';\nimport StyleTransformer from './style-transformer.js';\nimport * as StyleUtil from './style-util.js';\nimport StyleProperties from './style-properties.js';\nimport {ensureStylePlaceholder, getStylePlaceholder} from './style-placeholder.js';\nimport StyleInfo from './style-info.js';\nimport StyleCache from './style-cache.js';\nimport {flush as watcherFlush, getOwnerScope, getCurrentScope} from './document-watcher.js';\nimport templateMap from './template-map.js';\nimport * as ApplyShimUtils from './apply-shim-utils.js';\nimport {updateNativeProperties, detectMixin} from './common-utils.js';\nimport {CustomStyleInterfaceInterface} from './custom-style-interface.js'; // eslint-disable-line no-unused-vars\n\n/**\n * @const {StyleCache}\n */\nconst styleCache = new StyleCache();\n\nexport default class ScopingShim {\n constructor() {\n this._scopeCounter = {};\n this._documentOwner = /** @type {!HTMLElement} */(document.documentElement);\n let ast = new StyleNode();\n ast['rules'] = [];\n this._documentOwnerStyleInfo = StyleInfo.set(this._documentOwner, new StyleInfo(ast));\n this._elementsHaveApplied = false;\n /** @type {?Object} */\n this._applyShim = null;\n /** @type {?CustomStyleInterfaceInterface} */\n this._customStyleInterface = null;\n }\n flush() {\n watcherFlush();\n }\n _generateScopeSelector(name) {\n let id = this._scopeCounter[name] = (this._scopeCounter[name] || 0) + 1;\n return `${name}-${id}`;\n }\n getStyleAst(style) {\n return StyleUtil.rulesForStyle(style);\n }\n styleAstToString(ast) {\n return StyleUtil.toCssText(ast);\n }\n _gatherStyles(template) {\n return StyleUtil.gatherStyleText(template.content);\n }\n /**\n * Prepare the styling and template for the given element type\n *\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} typeExtension\n */\n prepareTemplate(template, elementName, typeExtension) {\n this.prepareTemplateDom(template, elementName);\n this.prepareTemplateStyles(template, elementName, typeExtension);\n }\n /**\n * Prepare styling for the given element type\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} typeExtension\n */\n prepareTemplateStyles(template, elementName, typeExtension) {\n if (template._prepared) {\n return;\n }\n // style placeholders are only used when ShadyDOM is active\n if (!nativeShadow) {\n ensureStylePlaceholder(elementName);\n }\n template._prepared = true;\n template.name = elementName;\n template.extends = typeExtension;\n templateMap[elementName] = template;\n let cssBuild = StyleUtil.getCssBuild(template);\n const optimalBuild = StyleUtil.isOptimalCssBuild(cssBuild);\n let info = {\n is: elementName,\n extends: typeExtension,\n };\n let cssText = this._gatherStyles(template);\n // check if the styling has mixin definitions or uses\n this._ensure();\n if (!optimalBuild) {\n let hasMixins = !cssBuild && detectMixin(cssText);\n let ast = parse(cssText);\n // only run the applyshim transforms if there is a mixin involved\n if (hasMixins && nativeCssVariables && this._applyShim) {\n this._applyShim['transformRules'](ast, elementName);\n }\n template['_styleAst'] = ast;\n }\n let ownPropertyNames = [];\n if (!nativeCssVariables) {\n ownPropertyNames = StyleProperties.decorateStyles(template['_styleAst']);\n }\n if (!ownPropertyNames.length || nativeCssVariables) {\n let root = nativeShadow ? template.content : null;\n let placeholder = getStylePlaceholder(elementName);\n let style = this._generateStaticStyle(info, template['_styleAst'], root, placeholder, cssBuild, optimalBuild ? cssText : '');\n template._style = style;\n }\n template._ownPropertyNames = ownPropertyNames;\n }\n /**\n * Prepare template for the given element type\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n */\n prepareTemplateDom(template, elementName) {\n const cssBuild = StyleUtil.getCssBuild(template);\n if (!nativeShadow && cssBuild !== 'shady' && !template._domPrepared) {\n template._domPrepared = true;\n StyleTransformer.domAddScope(template.content, elementName);\n }\n }\n /**\n * @param {!{is: string, extends: (string|undefined)}} info\n * @param {!StyleNode} rules\n * @param {DocumentFragment} shadowroot\n * @param {Node} placeholder\n * @param {string} cssBuild\n * @param {string=} cssText\n * @return {?HTMLStyleElement}\n */\n _generateStaticStyle(info, rules, shadowroot, placeholder, cssBuild, cssText) {\n cssText = StyleTransformer.elementStyles(info, rules, null, cssBuild, cssText);\n if (cssText.length) {\n return StyleUtil.applyCss(cssText, info.is, shadowroot, placeholder);\n }\n return null;\n }\n _prepareHost(host) {\n const {is, typeExtension} = StyleUtil.getIsExtends(host);\n const placeholder = getStylePlaceholder(is);\n const template = templateMap[is];\n if (!template) {\n return;\n }\n const ast = template['_styleAst'];\n const ownStylePropertyNames = template._ownPropertyNames;\n const cssBuild = StyleUtil.getCssBuild(template);\n const styleInfo = new StyleInfo(\n ast,\n placeholder,\n ownStylePropertyNames,\n is,\n typeExtension,\n cssBuild\n );\n StyleInfo.set(host, styleInfo);\n return styleInfo;\n }\n _ensureApplyShim() {\n if (this._applyShim) {\n return;\n } else if (window.ShadyCSS && window.ShadyCSS.ApplyShim) {\n this._applyShim = /** @type {!Object} */ (window.ShadyCSS.ApplyShim);\n this._applyShim['invalidCallback'] = ApplyShimUtils.invalidate;\n }\n }\n _ensureCustomStyleInterface() {\n if (this._customStyleInterface) {\n return;\n } else if (window.ShadyCSS && window.ShadyCSS.CustomStyleInterface) {\n this._customStyleInterface = /** @type {!CustomStyleInterfaceInterface} */(window.ShadyCSS.CustomStyleInterface);\n /** @type {function(!HTMLStyleElement)} */\n this._customStyleInterface['transformCallback'] = (style) => {this.transformCustomStyleForDocument(style)};\n this._customStyleInterface['validateCallback'] = () => {\n requestAnimationFrame(() => {\n if (this._customStyleInterface['enqueued'] || this._elementsHaveApplied) {\n this.flushCustomStyles();\n }\n })\n };\n }\n }\n _ensure() {\n this._ensureApplyShim();\n this._ensureCustomStyleInterface();\n }\n /**\n * Flush and apply custom styles to document\n */\n flushCustomStyles() {\n this._ensure();\n if (!this._customStyleInterface) {\n return;\n }\n let customStyles = this._customStyleInterface['processStyles']();\n // early return if custom-styles don't need validation\n if (!this._customStyleInterface['enqueued']) {\n return;\n }\n // bail if custom styles are built optimally\n if (StyleUtil.isOptimalCssBuild(this._documentOwnerStyleInfo.cssBuild)) {\n return;\n }\n if (!nativeCssVariables) {\n this._updateProperties(this._documentOwner, this._documentOwnerStyleInfo);\n this._applyCustomStyles(customStyles);\n if (this._elementsHaveApplied) {\n // if custom elements have upgraded and there are no native css variables, we must recalculate the whole tree\n this.styleDocument();\n }\n } else if (!this._documentOwnerStyleInfo.cssBuild) {\n this._revalidateCustomStyleApplyShim(customStyles);\n }\n this._customStyleInterface['enqueued'] = false;\n }\n /**\n * Apply styles for the given element\n *\n * @param {!HTMLElement} host\n * @param {Object=} overrideProps\n */\n styleElement(host, overrideProps) {\n const styleInfo = StyleInfo.get(host) || this._prepareHost(host);\n // if there is no style info at this point, bail\n if (!styleInfo) {\n return;\n }\n // Only trip the `elementsHaveApplied` flag if a node other that the root document has `applyStyle` called\n if (!this._isRootOwner(host)) {\n this._elementsHaveApplied = true;\n }\n if (overrideProps) {\n styleInfo.overrideStyleProperties =\n styleInfo.overrideStyleProperties || {};\n Object.assign(styleInfo.overrideStyleProperties, overrideProps);\n }\n if (!nativeCssVariables) {\n this.styleElementShimVariables(host, styleInfo);\n } else {\n this.styleElementNativeVariables(host, styleInfo);\n }\n }\n /**\n * @param {!HTMLElement} host\n * @param {!StyleInfo} styleInfo\n */\n styleElementShimVariables(host, styleInfo) {\n this.flush();\n this._updateProperties(host, styleInfo);\n if (styleInfo.ownStylePropertyNames && styleInfo.ownStylePropertyNames.length) {\n this._applyStyleProperties(host, styleInfo);\n }\n }\n /**\n * @param {!HTMLElement} host\n * @param {!StyleInfo} styleInfo\n */\n styleElementNativeVariables(host, styleInfo) {\n const { is } = StyleUtil.getIsExtends(host);\n if (styleInfo.overrideStyleProperties) {\n updateNativeProperties(host, styleInfo.overrideStyleProperties);\n }\n const template = templateMap[is];\n // bail early if there is no shadowroot for this element\n if (!template && !this._isRootOwner(host)) {\n return;\n }\n // bail early if the template was built with polymer-css-build\n if (template && StyleUtil.elementHasBuiltCss(template)) {\n return;\n }\n if (template && template._style && !ApplyShimUtils.templateIsValid(template)) {\n // update template\n if (!ApplyShimUtils.templateIsValidating(template)) {\n this._ensure();\n this._applyShim && this._applyShim['transformRules'](template['_styleAst'], is);\n template._style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules);\n ApplyShimUtils.startValidatingTemplate(template);\n }\n // update instance if native shadowdom\n if (nativeShadow) {\n let root = host.shadowRoot;\n if (root) {\n let style = root.querySelector('style');\n if (style) {\n style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules);\n }\n }\n }\n styleInfo.styleRules = template['_styleAst'];\n }\n }\n _styleOwnerForNode(node) {\n let root = StyleUtil.wrap(node).getRootNode();\n let host = root.host;\n if (host) {\n if (StyleInfo.get(host) || this._prepareHost(host)) {\n return host;\n } else {\n return this._styleOwnerForNode(host);\n }\n }\n return this._documentOwner;\n }\n _isRootOwner(node) {\n return (node === this._documentOwner);\n }\n _applyStyleProperties(host, styleInfo) {\n let is = StyleUtil.getIsExtends(host).is;\n let cacheEntry = styleCache.fetch(is, styleInfo.styleProperties, styleInfo.ownStylePropertyNames);\n let cachedScopeSelector = cacheEntry && cacheEntry.scopeSelector;\n let cachedStyle = cacheEntry ? cacheEntry.styleElement : null;\n let oldScopeSelector = styleInfo.scopeSelector;\n // only generate new scope if cached style is not found\n styleInfo.scopeSelector = cachedScopeSelector || this._generateScopeSelector(is);\n let style = StyleProperties.applyElementStyle(host, styleInfo.styleProperties, styleInfo.scopeSelector, cachedStyle);\n if (!nativeShadow) {\n StyleProperties.applyElementScopeSelector(host, styleInfo.scopeSelector, oldScopeSelector);\n }\n if (!cacheEntry) {\n styleCache.store(is, styleInfo.styleProperties, style, styleInfo.scopeSelector);\n }\n return style;\n }\n _updateProperties(host, styleInfo) {\n let owner = this._styleOwnerForNode(host);\n let ownerStyleInfo = StyleInfo.get(owner);\n let ownerProperties = ownerStyleInfo.styleProperties;\n // style owner has not updated properties yet\n // go up the chain and force property update,\n // except if the owner is the document\n if (owner !== this._documentOwner && !ownerProperties) {\n this._updateProperties(owner, ownerStyleInfo);\n ownerProperties = ownerStyleInfo.styleProperties;\n }\n let props = Object.create(ownerProperties || null);\n let hostAndRootProps = StyleProperties.hostAndRootPropertiesForScope(host, styleInfo.styleRules, styleInfo.cssBuild);\n let propertyData = StyleProperties.propertyDataFromStyles(ownerStyleInfo.styleRules, host);\n let propertiesMatchingHost = propertyData.properties\n Object.assign(\n props,\n hostAndRootProps.hostProps,\n propertiesMatchingHost,\n hostAndRootProps.rootProps\n );\n this._mixinOverrideStyles(props, styleInfo.overrideStyleProperties);\n StyleProperties.reify(props);\n styleInfo.styleProperties = props;\n }\n _mixinOverrideStyles(props, overrides) {\n for (let p in overrides) {\n let v = overrides[p];\n // skip override props if they are not truthy or 0\n // in order to fall back to inherited values\n if (v || v === 0) {\n props[p] = v;\n }\n }\n }\n /**\n * Update styles of the whole document\n *\n * @param {Object=} properties\n */\n styleDocument(properties) {\n this.styleSubtree(this._documentOwner, properties);\n }\n /**\n * Update styles of a subtree\n *\n * @param {!HTMLElement} host\n * @param {Object=} properties\n */\n styleSubtree(host, properties) {\n let root = host.shadowRoot;\n if (root || this._isRootOwner(host)) {\n this.styleElement(host, properties);\n }\n // process the shadowdom children of `host`\n let shadowChildren =\n root && (/** @type {!ParentNode} */ (root).children || root.childNodes);\n if (shadowChildren) {\n for (let i = 0; i < shadowChildren.length; i++) {\n let c = /** @type {!HTMLElement} */(shadowChildren[i]);\n this.styleSubtree(c);\n }\n } else {\n // process the lightdom children of `host`\n let children = host.children || host.childNodes;\n if (children) {\n for (let i = 0; i < children.length; i++) {\n let c = /** @type {!HTMLElement} */(children[i]);\n this.styleSubtree(c);\n }\n }\n }\n }\n /* Custom Style operations */\n _revalidateCustomStyleApplyShim(customStyles) {\n for (let i = 0; i < customStyles.length; i++) {\n let c = customStyles[i];\n let s = this._customStyleInterface['getStyleForCustomStyle'](c);\n if (s) {\n this._revalidateApplyShim(s);\n }\n }\n }\n _applyCustomStyles(customStyles) {\n for (let i = 0; i < customStyles.length; i++) {\n let c = customStyles[i];\n let s = this._customStyleInterface['getStyleForCustomStyle'](c);\n if (s) {\n StyleProperties.applyCustomStyle(s, this._documentOwnerStyleInfo.styleProperties);\n }\n }\n }\n transformCustomStyleForDocument(style) {\n const cssBuild = StyleUtil.getCssBuild(style);\n if (cssBuild !== this._documentOwnerStyleInfo.cssBuild) {\n this._documentOwnerStyleInfo.cssBuild = cssBuild;\n }\n if (StyleUtil.isOptimalCssBuild(cssBuild)) {\n return;\n }\n let ast = StyleUtil.rulesForStyle(style);\n StyleUtil.forEachRule(ast, (rule) => {\n if (nativeShadow) {\n StyleTransformer.normalizeRootSelector(rule);\n } else {\n StyleTransformer.documentRule(rule);\n }\n if (nativeCssVariables && cssBuild === '') {\n this._ensure();\n this._applyShim && this._applyShim['transformRule'](rule);\n }\n });\n if (nativeCssVariables) {\n style.textContent = StyleUtil.toCssText(ast);\n } else {\n this._documentOwnerStyleInfo.styleRules['rules'].push(ast);\n }\n }\n _revalidateApplyShim(style) {\n if (nativeCssVariables && this._applyShim) {\n let ast = StyleUtil.rulesForStyle(style);\n this._ensure();\n this._applyShim['transformRules'](ast);\n style.textContent = StyleUtil.toCssText(ast);\n }\n }\n getComputedStyleValue(element, property) {\n let value;\n if (!nativeCssVariables) {\n // element is either a style host, or an ancestor of a style host\n let styleInfo = StyleInfo.get(element) || StyleInfo.get(this._styleOwnerForNode(element));\n value = styleInfo.styleProperties[property];\n }\n // fall back to the property value from the computed styling\n value = value || window.getComputedStyle(element).getPropertyValue(property);\n // trim whitespace that can come after the `:` in css\n // example: padding: 2px -> \" 2px\"\n return value ? value.trim() : '';\n }\n // given an element and a classString, replaces\n // the element's class with the provided classString and adds\n // any necessary ShadyCSS static and property based scoping selectors\n setElementClass(element, classString) {\n let root = StyleUtil.wrap(element).getRootNode();\n let classes = classString ? classString.split(/\\s/) : [];\n let scopeName = root.host && root.host.localName;\n // If no scope, try to discover scope name from existing class.\n // This can occur if, for example, a template stamped element that\n // has been scoped is manipulated when not in a root.\n if (!scopeName) {\n var classAttr = element.getAttribute('class');\n if (classAttr) {\n let k$ = classAttr.split(/\\s/);\n for (let i=0; i < k$.length; i++) {\n if (k$[i] === StyleTransformer.SCOPE_NAME) {\n scopeName = k$[i+1];\n break;\n }\n }\n }\n }\n if (scopeName) {\n classes.push(StyleTransformer.SCOPE_NAME, scopeName);\n }\n if (!nativeCssVariables) {\n let styleInfo = StyleInfo.get(element);\n if (styleInfo && styleInfo.scopeSelector) {\n classes.push(StyleProperties.XSCOPE_NAME, styleInfo.scopeSelector);\n }\n }\n StyleUtil.setElementClassRaw(element, classes.join(' '));\n }\n _styleInfoForNode(node) {\n return StyleInfo.get(node);\n }\n /**\n * @param {!Element} node\n * @param {string} scope\n */\n scopeNode(node, scope) {\n StyleTransformer.element(node, scope);\n }\n /**\n * @param {!Element} node\n * @param {string} scope\n */\n unscopeNode(node, scope) {\n StyleTransformer.element(node, scope, true);\n }\n /**\n * @param {!Node} node\n * @return {string}\n */\n scopeForNode(node) {\n return getOwnerScope(node);\n }\n /**\n * @param {!Element} node\n * @return {string}\n */\n currentScopeForNode(node) {\n return getCurrentScope(node);\n }\n}\n\n/* exports */\n/* eslint-disable no-self-assign */\nScopingShim.prototype['flush'] = ScopingShim.prototype.flush;\nScopingShim.prototype['prepareTemplate'] = ScopingShim.prototype.prepareTemplate;\nScopingShim.prototype['styleElement'] = ScopingShim.prototype.styleElement;\nScopingShim.prototype['styleDocument'] = ScopingShim.prototype.styleDocument;\nScopingShim.prototype['styleSubtree'] = ScopingShim.prototype.styleSubtree;\nScopingShim.prototype['getComputedStyleValue'] = ScopingShim.prototype.getComputedStyleValue;\nScopingShim.prototype['setElementClass'] = ScopingShim.prototype.setElementClass;\nScopingShim.prototype['_styleInfoForNode'] = ScopingShim.prototype._styleInfoForNode;\nScopingShim.prototype['transformCustomStyleForDocument'] = ScopingShim.prototype.transformCustomStyleForDocument;\nScopingShim.prototype['getStyleAst'] = ScopingShim.prototype.getStyleAst;\nScopingShim.prototype['styleAstToString'] = ScopingShim.prototype.styleAstToString;\nScopingShim.prototype['flushCustomStyles'] = ScopingShim.prototype.flushCustomStyles;\nScopingShim.prototype['scopeNode'] = ScopingShim.prototype.scopeNode;\nScopingShim.prototype['unscopeNode'] = ScopingShim.prototype.unscopeNode;\nScopingShim.prototype['scopeForNode'] = ScopingShim.prototype.scopeForNode;\nScopingShim.prototype['currentScopeForNode'] = ScopingShim.prototype.currentScopeForNode;\n/* eslint-enable no-self-assign */\nObject.defineProperties(ScopingShim.prototype, {\n 'nativeShadow': {\n get() {\n return nativeShadow;\n }\n },\n 'nativeCss': {\n get() {\n return nativeCssVariables;\n }\n }\n});\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {nativeShadow} from './style-settings.js';\nimport StyleTransformer from './style-transformer.js';\nimport {getIsExtends, elementHasBuiltCss, wrap} from './style-util.js';\n\nexport let flush = function() {};\n\n/**\n * @param {!Element} element\n * @return {string}\n */\nfunction getClasses(element) {\n if (element.classList && element.classList.value) {\n return element.classList.value;\n } else {\n // NOTE: className is patched to remove scoping classes in ShadyDOM\n // use getAttribute('class') instead, which is unpatched\n return element.getAttribute('class') || '';\n }\n}\n\nconst scopeRegExp = new RegExp(`${StyleTransformer.SCOPE_NAME}\\\\s*([^\\\\s]*)`);\n\n/**\n * @param {!Element} element\n * @return {string}\n */\nexport function getCurrentScope(element) {\n const match = getClasses(element).match(scopeRegExp);\n if (match) {\n return match[1];\n } else {\n return '';\n }\n}\n\n/**\n * @param {!Node} node\n */\nexport function getOwnerScope(node) {\n const ownerRoot = wrap(node).getRootNode();\n if (ownerRoot === node || ownerRoot === node.ownerDocument) {\n return '';\n }\n const host = /** @type {!ShadowRoot} */(ownerRoot).host;\n if (!host) {\n // this may actually be a document fragment\n return '';\n }\n return getIsExtends(host).is;\n}\n\n/**\n * @param {!Element} element\n */\nexport function ensureCorrectScope(element) {\n const currentScope = getCurrentScope(element);\n const ownerRoot = wrap(element).getRootNode();\n if (ownerRoot === element) {\n return;\n }\n if (currentScope && ownerRoot === element.ownerDocument) {\n // node was scoped, but now is in document\n StyleTransformer.domRemoveScope(element, currentScope);\n } else if (ownerRoot instanceof ShadowRoot) {\n const ownerScope = getOwnerScope(element);\n if (ownerScope !== currentScope) {\n // node was scoped, but not by its current owner\n StyleTransformer.domReplaceScope(element, currentScope, ownerScope);\n }\n }\n}\n\n/**\n * @param {!HTMLElement|!HTMLDocument} element\n */\nexport function ensureCorrectSubtreeScoping(element) {\n // find unscoped subtree nodes\n const unscopedNodes = window['ShadyDOM']['nativeMethods']['querySelectorAll'].call(\n element, `:not(.${StyleTransformer.SCOPE_NAME})`);\n\n for (let j = 0; j < unscopedNodes.length; j++) {\n // it's possible, during large batch inserts, that nodes that aren't\n // scoped within the current scope were added.\n // To make sure that any unscoped nodes that were inserted in the current batch are correctly styled,\n // query all unscoped nodes and force their style-scope to be applied.\n // This could happen if a sub-element appended an unscoped node in its shadowroot and this function\n // runs on a parent element of the host of that unscoped node:\n // parent-element -> element -> unscoped node\n // Here unscoped node should have the style-scope element, not parent-element.\n const unscopedNode = unscopedNodes[j];\n const scopeForPreviouslyUnscopedNode = getOwnerScope(unscopedNode);\n if (scopeForPreviouslyUnscopedNode) {\n StyleTransformer.element(unscopedNode, scopeForPreviouslyUnscopedNode);\n }\n }\n}\n\n/**\n * @param {HTMLElement} el\n * @return {boolean}\n */\nfunction isElementWithBuiltCss(el) {\n if (el.localName === 'style' || el.localName === 'template') {\n return elementHasBuiltCss(el);\n }\n return false;\n}\n\n/**\n * @param {Array<MutationRecord|null>|null} mxns\n */\nfunction handler(mxns) {\n for (let x=0; x < mxns.length; x++) {\n let mxn = mxns[x];\n if (mxn.target === document.documentElement ||\n mxn.target === document.head) {\n continue;\n }\n for (let i=0; i < mxn.addedNodes.length; i++) {\n let n = mxn.addedNodes[i];\n if (n.nodeType !== Node.ELEMENT_NODE) {\n continue;\n }\n n = /** @type {HTMLElement} */(n); // eslint-disable-line no-self-assign\n let root = n.getRootNode();\n let currentScope = getCurrentScope(n);\n // node was scoped, but now is in document\n // If this element has built css, we must not remove scoping as this node\n // will be used as a template or style without re - applying scoping as an optimization\n if (currentScope && root === n.ownerDocument && !isElementWithBuiltCss(n)) {\n StyleTransformer.domRemoveScope(n, currentScope);\n } else if (root instanceof ShadowRoot) {\n const newScope = getOwnerScope(n);\n // rescope current node and subtree if necessary\n if (newScope !== currentScope) {\n StyleTransformer.domReplaceScope(n, currentScope, newScope);\n }\n // make sure all the subtree elements are scoped correctly\n ensureCorrectSubtreeScoping(n);\n }\n }\n }\n}\n\n// if native Shadow DOM is being used, or ShadyDOM handles dynamic scoiping, do not activate the MutationObserver\nif (!nativeShadow && !(window['ShadyDOM'] && window['ShadyDOM']['handlesDynamicScoping'])) {\n let observer = new MutationObserver(handler);\n let start = (node) => {\n observer.observe(node, {childList: true, subtree: true});\n }\n let nativeCustomElements = (window['customElements'] &&\n !window['customElements']['polyfillWrapFlushCallback']);\n // need to start immediately with native custom elements\n // TODO(dfreedm): with polyfilled HTMLImports and native custom elements\n // excessive mutations may be observed; this can be optimized via cooperation\n // with the HTMLImports polyfill.\n if (nativeCustomElements) {\n start(document);\n } else {\n let delayedStart = () => {\n start(document.body);\n }\n // use polyfill timing if it's available\n if (window['HTMLImports']) {\n window['HTMLImports']['whenReady'](delayedStart);\n // otherwise push beyond native imports being ready\n // which requires RAF + readystate interactive.\n } else {\n requestAnimationFrame(function() {\n if (document.readyState === 'loading') {\n let listener = function() {\n delayedStart();\n document.removeEventListener('readystatechange', listener);\n }\n document.addEventListener('readystatechange', listener);\n } else {\n delayedStart();\n }\n });\n }\n }\n\n flush = function() {\n handler(observer.takeRecords());\n }\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {removeCustomPropAssignment, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\nimport {nativeShadow} from './style-settings.js';\nimport StyleTransformer from './style-transformer.js';\nimport * as StyleUtil from './style-util.js';\nimport * as RX from './common-regex.js';\nimport StyleInfo from './style-info.js';\n\n// TODO: dedupe with shady\n/**\n * @param {string} selector\n * @return {boolean}\n * @this {Element}\n */\nconst matchesSelector = function(selector) {\n const method = this.matches || this.matchesSelector ||\n this.mozMatchesSelector || this.msMatchesSelector ||\n this.oMatchesSelector || this.webkitMatchesSelector;\n return method && method.call(this, selector);\n};\n\nconst IS_IE = navigator.userAgent.match('Trident');\n\nconst XSCOPE_NAME = 'x-scope';\n\nclass StyleProperties {\n get XSCOPE_NAME() {\n return XSCOPE_NAME;\n }\n/**\n * decorates styles with rule info and returns an array of used style property names\n *\n * @param {StyleNode} rules\n * @return {Array<string>}\n */\n decorateStyles(rules) {\n let self = this, props = {}, keyframes = [], ruleIndex = 0;\n StyleUtil.forEachRule(rules, function(rule) {\n self.decorateRule(rule);\n // mark in-order position of ast rule in styles block, used for cache key\n rule.index = ruleIndex++;\n self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);\n }, function onKeyframesRule(rule) {\n keyframes.push(rule);\n });\n // Cache all found keyframes rules for later reference:\n rules._keyframes = keyframes;\n // return this list of property names *consumes* in these styles.\n let names = [];\n for (let i in props) {\n names.push(i);\n }\n return names;\n }\n\n // decorate a single rule with property info\n decorateRule(rule) {\n if (rule.propertyInfo) {\n return rule.propertyInfo;\n }\n let info = {}, properties = {};\n let hasProperties = this.collectProperties(rule, properties);\n if (hasProperties) {\n info.properties = properties;\n // TODO(sorvell): workaround parser seeing mixins as additional rules\n rule['rules'] = null;\n }\n info.cssText = this.collectCssText(rule);\n rule.propertyInfo = info;\n return info;\n }\n\n // collects the custom properties from a rule's cssText\n collectProperties(rule, properties) {\n let info = rule.propertyInfo;\n if (info) {\n if (info.properties) {\n Object.assign(properties, info.properties);\n return true;\n }\n } else {\n let m, rx = RX.VAR_ASSIGN;\n let cssText = rule['parsedCssText'];\n let value;\n let any;\n while ((m = rx.exec(cssText))) {\n // note: group 2 is var, 3 is mixin\n value = (m[2] || m[3]).trim();\n // value of 'inherit' or 'unset' is equivalent to not setting the property here\n if (value !== 'inherit' || value !== 'unset') {\n properties[m[1].trim()] = value;\n }\n any = true;\n }\n return any;\n }\n\n }\n\n // returns cssText of properties that consume variables/mixins\n collectCssText(rule) {\n return this.collectConsumingCssText(rule['parsedCssText']);\n }\n\n // NOTE: we support consumption inside mixin assignment\n // but not production, so strip out {...}\n collectConsumingCssText(cssText) {\n return cssText.replace(RX.BRACKETED, '')\n .replace(RX.VAR_ASSIGN, '');\n }\n\n collectPropertiesInCssText(cssText, props) {\n let m;\n while ((m = RX.VAR_CONSUMED.exec(cssText))) {\n let name = m[1];\n // This regex catches all variable names, and following non-whitespace char\n // If next char is not ':', then variable is a consumer\n if (m[2] !== ':') {\n props[name] = true;\n }\n }\n }\n\n // turns custom properties into realized values.\n reify(props) {\n // big perf optimization here: reify only *own* properties\n // since this object has __proto__ of the element's scope properties\n let names = Object.getOwnPropertyNames(props);\n for (let i=0, n; i < names.length; i++) {\n n = names[i];\n props[n] = this.valueForProperty(props[n], props);\n }\n }\n\n // given a property value, returns the reified value\n // a property value may be:\n // (1) a literal value like: red or 5px;\n // (2) a variable value like: var(--a), var(--a, red), or var(--a, --b) or\n // var(--a, var(--b));\n // (3) a literal mixin value like { properties }. Each of these properties\n // can have values that are: (a) literal, (b) variables, (c) @apply mixins.\n valueForProperty(property, props) {\n // case (1) default\n // case (3) defines a mixin and we have to reify the internals\n if (property) {\n if (property.indexOf(';') >=0) {\n property = this.valueForProperties(property, props);\n } else {\n // case (2) variable\n let self = this;\n let fn = function(prefix, value, fallback, suffix) {\n if (!value) {\n return prefix + suffix;\n }\n let propertyValue = self.valueForProperty(props[value], props);\n // if value is \"initial\", then the variable should be treated as unset\n if (!propertyValue || propertyValue === 'initial') {\n // fallback may be --a or var(--a) or literal\n propertyValue = self.valueForProperty(props[fallback] || fallback, props) ||\n fallback;\n } else if (propertyValue === 'apply-shim-inherit') {\n // CSS build will replace `inherit` with `apply-shim-inherit`\n // for use with native css variables.\n // Since we have full control, we can use `inherit` directly.\n propertyValue = 'inherit';\n }\n return prefix + (propertyValue || '') + suffix;\n };\n property = StyleUtil.processVariableAndFallback(property, fn);\n }\n }\n return property && property.trim() || '';\n }\n\n // note: we do not yet support mixin within mixin\n valueForProperties(property, props) {\n let parts = property.split(';');\n for (let i=0, p, m; i<parts.length; i++) {\n if ((p = parts[i])) {\n RX.MIXIN_MATCH.lastIndex = 0;\n m = RX.MIXIN_MATCH.exec(p);\n if (m) {\n p = this.valueForProperty(props[m[1]], props);\n } else {\n let colon = p.indexOf(':');\n if (colon !== -1) {\n let pp = p.substring(colon);\n pp = pp.trim();\n pp = this.valueForProperty(pp, props) || pp;\n p = p.substring(0, colon) + pp;\n }\n }\n parts[i] = (p && p.lastIndexOf(';') === p.length - 1) ?\n // strip trailing ;\n p.slice(0, -1) :\n p || '';\n }\n }\n return parts.join(';');\n }\n\n applyProperties(rule, props) {\n let output = '';\n // dynamically added sheets may not be decorated so ensure they are.\n if (!rule.propertyInfo) {\n this.decorateRule(rule);\n }\n if (rule.propertyInfo.cssText) {\n output = this.valueForProperties(rule.propertyInfo.cssText, props);\n }\n rule['cssText'] = output;\n }\n\n // Apply keyframe transformations to the cssText of a given rule. The\n // keyframeTransforms object is a map of keyframe names to transformer\n // functions which take in cssText and spit out transformed cssText.\n applyKeyframeTransforms(rule, keyframeTransforms) {\n let input = rule['cssText'];\n let output = rule['cssText'];\n if (rule.hasAnimations == null) {\n // Cache whether or not the rule has any animations to begin with:\n rule.hasAnimations = RX.ANIMATION_MATCH.test(input);\n }\n // If there are no animations referenced, we can skip transforms:\n if (rule.hasAnimations) {\n let transform;\n // If we haven't transformed this rule before, we iterate over all\n // transforms:\n if (rule.keyframeNamesToTransform == null) {\n rule.keyframeNamesToTransform = [];\n for (let keyframe in keyframeTransforms) {\n transform = keyframeTransforms[keyframe];\n output = transform(input);\n // If the transform actually changed the CSS text, we cache the\n // transform name for future use:\n if (input !== output) {\n input = output;\n rule.keyframeNamesToTransform.push(keyframe);\n }\n }\n } else {\n // If we already have a list of keyframe names that apply to this\n // rule, we apply only those keyframe name transforms:\n for (let i = 0; i < rule.keyframeNamesToTransform.length; ++i) {\n transform = keyframeTransforms[rule.keyframeNamesToTransform[i]];\n input = transform(input);\n }\n output = input;\n }\n }\n rule['cssText'] = output;\n }\n\n // Test if the rules in these styles matches the given `element` and if so,\n // collect any custom properties into `props`.\n /**\n * @param {StyleNode} rules\n * @param {Element} element\n */\n propertyDataFromStyles(rules, element) {\n let props = {};\n // generates a unique key for these matches\n let o = [];\n // note: active rules excludes non-matching @media rules\n StyleUtil.forEachRule(rules, (rule) => {\n // TODO(sorvell): we could trim the set of rules at declaration\n // time to only include ones that have properties\n if (!rule.propertyInfo) {\n this.decorateRule(rule);\n }\n // match element against transformedSelector: selector may contain\n // unwanted uniquification and parsedSelector does not directly match\n // for :host selectors.\n let selectorToMatch = rule.transformedSelector || rule['parsedSelector'];\n if (element && rule.propertyInfo.properties && selectorToMatch) {\n if (matchesSelector.call(element, selectorToMatch)) {\n this.collectProperties(rule, props);\n // produce numeric key for these matches for lookup\n addToBitMask(rule.index, o);\n }\n }\n }, null, true);\n return {properties: props, key: o};\n }\n\n /**\n * @param {Element} scope\n * @param {StyleNode} rule\n * @param {string} cssBuild\n * @param {function(Object)} callback\n */\n whenHostOrRootRule(scope, rule, cssBuild, callback) {\n if (!rule.propertyInfo) {\n this.decorateRule(rule);\n }\n if (!rule.propertyInfo.properties) {\n return;\n }\n let {is, typeExtension} = StyleUtil.getIsExtends(scope);\n let hostScope = is ?\n StyleTransformer._calcHostScope(is, typeExtension) :\n 'html';\n let parsedSelector = rule['parsedSelector'];\n let isRoot = (parsedSelector === ':host > *' || parsedSelector === 'html');\n let isHost = parsedSelector.indexOf(':host') === 0 && !isRoot;\n // build info is either in scope (when scope is an element) or in the style\n // when scope is the default scope; note: this allows default scope to have\n // mixed mode built and unbuilt styles.\n if (cssBuild === 'shady') {\n // :root -> x-foo > *.x-foo for elements and html for custom-style\n isRoot = parsedSelector === (hostScope + ' > *.' + hostScope) || parsedSelector.indexOf('html') !== -1;\n // :host -> x-foo for elements, but sub-rules have .x-foo in them\n isHost = !isRoot && parsedSelector.indexOf(hostScope) === 0;\n }\n if (!isRoot && !isHost) {\n return;\n }\n let selectorToMatch = hostScope;\n if (isHost) {\n // need to transform :host because `:host` does not work with `matches`\n if (!rule.transformedSelector) {\n // transform :host into a matchable selector\n rule.transformedSelector =\n StyleTransformer._transformRuleCss(\n rule,\n StyleTransformer._transformComplexSelector,\n StyleTransformer._calcElementScope(is),\n hostScope\n );\n }\n selectorToMatch = rule.transformedSelector || hostScope;\n }\n callback({\n selector: selectorToMatch,\n isHost: isHost,\n isRoot: isRoot\n });\n }\n/**\n * @param {Element} scope\n * @param {StyleNode} rules\n * @param {string} cssBuild\n * @return {Object}\n */\n hostAndRootPropertiesForScope(scope, rules, cssBuild) {\n let hostProps = {}, rootProps = {};\n // note: active rules excludes non-matching @media rules\n StyleUtil.forEachRule(rules, (rule) => {\n // if scope is StyleDefaults, use _element for matchesSelector\n this.whenHostOrRootRule(scope, rule, cssBuild, (info) => {\n let element = scope._element || scope;\n if (matchesSelector.call(element, info.selector)) {\n if (info.isHost) {\n this.collectProperties(rule, hostProps);\n } else {\n this.collectProperties(rule, rootProps);\n }\n }\n });\n }, null, true);\n return {rootProps: rootProps, hostProps: hostProps};\n }\n\n /**\n * @param {Element} element\n * @param {Object} properties\n * @param {string} scopeSelector\n */\n transformStyles(element, properties, scopeSelector) {\n let self = this;\n let {is, typeExtension} = StyleUtil.getIsExtends(element);\n let hostSelector = StyleTransformer\n ._calcHostScope(is, typeExtension);\n let rxHostSelector = element.extends ?\n '\\\\' + hostSelector.slice(0, -1) + '\\\\]' :\n hostSelector;\n let hostRx = new RegExp(RX.HOST_PREFIX + rxHostSelector +\n RX.HOST_SUFFIX);\n let {styleRules: rules, cssBuild} = StyleInfo.get(element);\n let keyframeTransforms =\n this._elementKeyframeTransforms(element, rules, scopeSelector);\n return StyleTransformer.elementStyles(element, rules, function(rule) {\n self.applyProperties(rule, properties);\n if (!nativeShadow &&\n !StyleUtil.isKeyframesSelector(rule) &&\n rule['cssText']) {\n // NOTE: keyframe transforms only scope munge animation names, so it\n // is not necessary to apply them in ShadowDOM.\n self.applyKeyframeTransforms(rule, keyframeTransforms);\n self._scopeSelector(rule, hostRx, hostSelector, scopeSelector);\n }\n }, cssBuild);\n }\n\n /**\n * @param {Element} element\n * @param {StyleNode} rules\n * @param {string} scopeSelector\n * @return {Object}\n */\n _elementKeyframeTransforms(element, rules, scopeSelector) {\n let keyframesRules = rules._keyframes;\n let keyframeTransforms = {};\n if (!nativeShadow && keyframesRules) {\n // For non-ShadowDOM, we transform all known keyframes rules in\n // advance for the current scope. This allows us to catch keyframes\n // rules that appear anywhere in the stylesheet:\n for (let i = 0, keyframesRule = keyframesRules[i];\n i < keyframesRules.length;\n keyframesRule = keyframesRules[++i]) {\n this._scopeKeyframes(keyframesRule, scopeSelector);\n keyframeTransforms[keyframesRule['keyframesName']] =\n this._keyframesRuleTransformer(keyframesRule);\n }\n }\n return keyframeTransforms;\n }\n\n // Generate a factory for transforming a chunk of CSS text to handle a\n // particular scoped keyframes rule.\n /**\n * @param {StyleNode} keyframesRule\n * @return {function(string):string}\n */\n _keyframesRuleTransformer(keyframesRule) {\n return function(cssText) {\n return cssText.replace(\n keyframesRule.keyframesNameRx,\n keyframesRule.transformedKeyframesName);\n };\n }\n\n/**\n * Transforms `@keyframes` names to be unique for the current host.\n * Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0\n *\n * @param {StyleNode} rule\n * @param {string} scopeId\n */\n _scopeKeyframes(rule, scopeId) {\n // Animation names are of the form [\\w-], so ensure that the name regex does not partially apply\n // to similarly named keyframe names by checking for a word boundary at the beginning and\n // a non-word boundary or `-` at the end.\n rule.keyframesNameRx = new RegExp(`\\\\b${rule['keyframesName']}(?!\\\\B|-)`, 'g');\n rule.transformedKeyframesName = rule['keyframesName'] + '-' + scopeId;\n rule.transformedSelector = rule.transformedSelector || rule['selector'];\n rule['selector'] = rule.transformedSelector.replace(\n rule['keyframesName'], rule.transformedKeyframesName);\n }\n\n // Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes):\n // non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo\n // host selector: x-foo.wide -> .x-foo-42.wide\n // note: we use only the scope class (.x-foo-42) and not the hostSelector\n // (x-foo) to scope :host rules; this helps make property host rules\n // have low specificity. They are overrideable by class selectors but,\n // unfortunately, not by type selectors (e.g. overriding via\n // `.special` is ok, but not by `x-foo`).\n /**\n * @param {StyleNode} rule\n * @param {RegExp} hostRx\n * @param {string} hostSelector\n * @param {string} scopeId\n */\n _scopeSelector(rule, hostRx, hostSelector, scopeId) {\n rule.transformedSelector = rule.transformedSelector || rule['selector'];\n let selector = rule.transformedSelector;\n let scope = '.' + scopeId;\n let parts = StyleUtil.splitSelectorList(selector);\n for (let i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {\n parts[i] = p.match(hostRx) ?\n p.replace(hostSelector, scope) :\n scope + ' ' + p;\n }\n rule['selector'] = parts.join(',');\n }\n\n /**\n * @param {Element} element\n * @param {string} selector\n * @param {string} old\n */\n applyElementScopeSelector(element, selector, old) {\n let c = element.getAttribute('class') || '';\n let v = c;\n if (old) {\n v = c.replace(\n new RegExp('\\\\s*' + XSCOPE_NAME + '\\\\s*' + old + '\\\\s*', 'g'), ' ');\n }\n v += (v ? ' ' : '') + XSCOPE_NAME + ' ' + selector;\n if (c !== v) {\n StyleUtil.setElementClassRaw(element, v);\n }\n }\n\n /**\n * @param {HTMLElement} element\n * @param {Object} properties\n * @param {string} selector\n * @param {HTMLStyleElement} style\n * @return {HTMLStyleElement}\n */\n applyElementStyle(element, properties, selector, style) {\n // calculate cssText to apply\n let cssText = style ? style.textContent || '' :\n this.transformStyles(element, properties, selector);\n // if shady and we have a cached style that is not style, decrement\n let styleInfo = StyleInfo.get(element);\n let s = styleInfo.customStyle;\n if (s && !nativeShadow && (s !== style)) {\n s['_useCount']--;\n if (s['_useCount'] <= 0 && s.parentNode) {\n s.parentNode.removeChild(s);\n }\n }\n // apply styling always under native or if we generated style\n // or the cached style is not in document(!)\n if (nativeShadow) {\n // update existing style only under native\n if (styleInfo.customStyle) {\n styleInfo.customStyle.textContent = cssText;\n style = styleInfo.customStyle;\n // otherwise, if we have css to apply, do so\n } else if (cssText) {\n // apply css after the scope style of the element to help with\n // style precedence rules.\n style = StyleUtil.applyCss(cssText, selector, element.shadowRoot,\n styleInfo.placeholder);\n }\n } else {\n // shady and no cache hit\n if (!style) {\n // apply css after the scope style of the element to help with\n // style precedence rules.\n if (cssText) {\n style = StyleUtil.applyCss(cssText, selector, null,\n styleInfo.placeholder);\n }\n // shady and cache hit but not in document\n } else if (!style.parentNode) {\n if (IS_IE && cssText.indexOf('@media') > -1) {\n // @media rules may be stale in IE 10 and 11\n // refresh the text content of the style to revalidate them.\n style.textContent = cssText;\n }\n StyleUtil.applyStyle(style, null, styleInfo.placeholder);\n }\n }\n // ensure this style is our custom style and increment its use count.\n if (style) {\n style['_useCount'] = style['_useCount'] || 0;\n // increment use count if we changed styles\n if (styleInfo.customStyle != style) {\n style['_useCount']++;\n }\n styleInfo.customStyle = style;\n }\n return style;\n }\n\n /**\n * @param {Element} style\n * @param {Object} properties\n */\n applyCustomStyle(style, properties) {\n let rules = StyleUtil.rulesForStyle(/** @type {HTMLStyleElement} */(style));\n let self = this;\n style.textContent = StyleUtil.toCssText(rules, function(/** StyleNode */rule) {\n let css = rule['cssText'] = rule['parsedCssText'];\n if (rule.propertyInfo && rule.propertyInfo.cssText) {\n // remove property assignments\n // so next function isn't confused\n // NOTE: we have 3 categories of css:\n // (1) normal properties,\n // (2) custom property assignments (--foo: red;),\n // (3) custom property usage: border: var(--foo); @apply(--foo);\n // In elements, 1 and 3 are separated for efficiency; here they\n // are not and this makes this case unique.\n css = removeCustomPropAssignment(/** @type {string} */(css));\n // replace with reified properties, scenario is same as mixin\n rule['cssText'] = self.valueForProperties(css, properties);\n }\n });\n }\n}\n\n/**\n * @param {number} n\n * @param {Array<number>} bits\n */\nfunction addToBitMask(n, bits) {\n let o = parseInt(n / 32, 10);\n let v = 1 << (n % 32);\n bits[o] = (bits[o] || 0) | v;\n}\n\nexport default new StyleProperties();","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\n\n/** @const {string} */\nconst infoKey = '__styleInfo';\n\nexport default class StyleInfo {\n /**\n * @param {Element} node\n * @return {StyleInfo}\n */\n static get(node) {\n if (node) {\n return node[infoKey];\n } else {\n return null;\n }\n }\n /**\n * @param {!Element} node\n * @param {StyleInfo} styleInfo\n * @return {StyleInfo}\n */\n static set(node, styleInfo) {\n node[infoKey] = styleInfo;\n return styleInfo;\n }\n /**\n * @param {StyleNode} ast\n * @param {Node=} placeholder\n * @param {Array<string>=} ownStylePropertyNames\n * @param {string=} elementName\n * @param {string=} typeExtension\n * @param {string=} cssBuild\n */\n constructor(ast, placeholder, ownStylePropertyNames, elementName, typeExtension, cssBuild) {\n /** @type {StyleNode} */\n this.styleRules = ast || null;\n /** @type {Node} */\n this.placeholder = placeholder || null;\n /** @type {!Array<string>} */\n this.ownStylePropertyNames = ownStylePropertyNames || [];\n /** @type {Array<Object>} */\n this.overrideStyleProperties = null;\n /** @type {string} */\n this.elementName = elementName || '';\n /** @type {string} */\n this.cssBuild = cssBuild || '';\n /** @type {string} */\n this.typeExtension = typeExtension || '';\n /** @type {Object<string, string>} */\n this.styleProperties = null;\n /** @type {?string} */\n this.scopeSelector = null;\n /** @type {HTMLStyleElement} */\n this.customStyle = null;\n }\n _getStyleRules() {\n return this.styleRules;\n }\n}\n\n/* eslint-disable-next-line no-self-assign */\nStyleInfo.prototype['_getStyleRules'] = StyleInfo.prototype._getStyleRules;\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport {applyStylePlaceHolder} from './style-util.js';\nimport {nativeShadow} from './style-settings.js';\n\n/** @type {!Object<string, !Node>} */\nconst placeholderMap = {};\n\n/**\n * @param {string} elementName\n * @return {Node}\n */\nexport function getStylePlaceholder(elementName) {\n return placeholderMap[elementName] || null;\n}\n\n/**\n * @param {string} elementName\n */\nexport function ensureStylePlaceholder(elementName) {\n if (!placeholderMap[elementName]) {\n placeholderMap[elementName] = applyStylePlaceHolder(elementName);\n }\n}\n\n/**\n * @const {CustomElementRegistry}\n */\nconst ce = window['customElements'];\nif (ce && !nativeShadow) {\n /**\n * @const {function(this:CustomElementRegistry, string,function(new:HTMLElement),{extends: string}=)}\n */\n const origDefine = ce['define'];\n /**\n * @param {string} name\n * @param {function(new:HTMLElement)} clazz\n * @param {{extends: string}=} options\n */\n const wrappedDefine = (name, clazz, options) => {\n ensureStylePlaceholder(name);\n origDefine.call(/** @type {!CustomElementRegistry} */(ce), name, clazz, options);\n };\n ce['define'] = wrappedDefine;\n}","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n'use strict';\n\nexport default class StyleCache {\n constructor(typeMax = 100) {\n // map element name -> [{properties, styleElement, scopeSelector}]\n this.cache = {};\n /** @type {number} */\n this.typeMax = typeMax;\n }\n\n _validate(cacheEntry, properties, ownPropertyNames) {\n for (let idx = 0; idx < ownPropertyNames.length; idx++) {\n let pn = ownPropertyNames[idx];\n if (cacheEntry.properties[pn] !== properties[pn]) {\n return false;\n }\n }\n return true;\n }\n\n store(tagname, properties, styleElement, scopeSelector) {\n let list = this.cache[tagname] || [];\n list.push({properties, styleElement, scopeSelector});\n if (list.length > this.typeMax) {\n list.shift();\n }\n this.cache[tagname] = list;\n }\n\n fetch(tagname, properties, ownPropertyNames) {\n let list = this.cache[tagname];\n if (!list) {\n return;\n }\n // reverse list for most-recent lookups\n for (let idx = list.length - 1; idx >= 0; idx--) {\n let entry = list[idx];\n if (this._validate(entry, properties, ownPropertyNames)) {\n return entry;\n }\n }\n }\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\n/**\n * @const {!Object<string, !HTMLTemplateElement>}\n */\nconst templateMap = {};\nexport default templateMap;\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\nimport templateMap from './template-map.js';\nimport {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars\n\n/*\n * Utilities for handling invalidating apply-shim mixins for a given template.\n *\n * The invalidation strategy involves keeping track of the \"current\" version of a template's mixins, and updating that count when a mixin is invalidated.\n * The template\n */\n\n/** @const {string} */\nconst CURRENT_VERSION = '_applyShimCurrentVersion';\n\n/** @const {string} */\nconst NEXT_VERSION = '_applyShimNextVersion';\n\n/** @const {string} */\nconst VALIDATING_VERSION = '_applyShimValidatingVersion';\n\n/**\n * @const {Promise<void>}\n */\nconst promise = Promise.resolve();\n\n/**\n * @param {string} elementName\n */\nexport function invalidate(elementName){\n let template = templateMap[elementName];\n if (template) {\n invalidateTemplate(template);\n }\n}\n\n/**\n * This function can be called multiple times to mark a template invalid\n * and signal that the style inside must be regenerated.\n *\n * Use `startValidatingTemplate` to begin an asynchronous validation cycle.\n * During that cycle, call `templateIsValidating` to see if the template must\n * be revalidated\n * @param {HTMLTemplateElement} template\n */\nexport function invalidateTemplate(template) {\n // default the current version to 0\n template[CURRENT_VERSION] = template[CURRENT_VERSION] || 0;\n // ensure the \"validating for\" flag exists\n template[VALIDATING_VERSION] = template[VALIDATING_VERSION] || 0;\n // increment the next version\n template[NEXT_VERSION] = (template[NEXT_VERSION] || 0) + 1;\n}\n\n/**\n * @param {string} elementName\n * @return {boolean}\n */\nexport function isValid(elementName) {\n let template = templateMap[elementName];\n if (template) {\n return templateIsValid(template);\n }\n return true;\n}\n\n/**\n * @param {HTMLTemplateElement} template\n * @return {boolean}\n */\nexport function templateIsValid(template) {\n return template[CURRENT_VERSION] === template[NEXT_VERSION];\n}\n\n/**\n * @param {string} elementName\n * @return {boolean}\n */\nexport function isValidating(elementName) {\n let template = templateMap[elementName];\n if (template) {\n return templateIsValidating(template);\n }\n return false;\n}\n\n/**\n * Returns true if the template is currently invalid and `startValidating` has been called since the last invalidation.\n * If false, the template must be validated.\n * @param {HTMLTemplateElement} template\n * @return {boolean}\n */\nexport function templateIsValidating(template) {\n return !templateIsValid(template) && template[VALIDATING_VERSION] === template[NEXT_VERSION];\n}\n\n/**\n * the template is marked as `validating` for one microtask so that all instances\n * found in the tree crawl of `applyStyle` will update themselves,\n * but the template will only be updated once.\n * @param {string} elementName\n*/\nexport function startValidating(elementName) {\n let template = templateMap[elementName];\n startValidatingTemplate(template);\n}\n\n/**\n * Begin an asynchronous invalidation cycle.\n * This should be called after every validation of a template\n *\n * After one microtask, the template will be marked as valid until the next call to `invalidateTemplate`\n * @param {HTMLTemplateElement} template\n */\nexport function startValidatingTemplate(template) {\n // remember that the current \"next version\" is the reason for this validation cycle\n template[VALIDATING_VERSION] = template[NEXT_VERSION];\n // however, there only needs to be one async task to clear the counters\n if (!template._validating) {\n template._validating = true;\n promise.then(function() {\n // sync the current version to let future invalidations cause a refresh cycle\n template[CURRENT_VERSION] = template[NEXT_VERSION];\n template._validating = false;\n });\n }\n}\n\n/**\n * @return {boolean}\n */\nexport function elementsAreInvalid() {\n for (let elementName in templateMap) {\n let template = templateMap[elementName];\n if (!templateIsValid(template)) {\n return true;\n }\n }\n return false;\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport { MIXIN_MATCH, VAR_ASSIGN } from './common-regex.js';\n\n/**\n * @param {Element} element\n * @param {Object=} properties\n */\nexport function updateNativeProperties(element, properties) {\n // remove previous properties\n for (let p in properties) {\n // NOTE: for bc with shim, don't apply null values.\n if (p === null) {\n element.style.removeProperty(p);\n } else {\n element.style.setProperty(p, properties[p]);\n }\n }\n}\n\n/**\n * @param {Element} element\n * @param {string} property\n * @return {string}\n */\nexport function getComputedStyleValue(element, property) {\n /**\n * @const {string}\n */\n const value = window.getComputedStyle(element).getPropertyValue(property);\n if (!value) {\n return '';\n } else {\n return value.trim();\n }\n}\n\n/**\n * return true if `cssText` contains a mixin definition or consumption\n * @param {string} cssText\n * @return {boolean}\n */\nexport function detectMixin(cssText) {\n const has = MIXIN_MATCH.test(cssText) || VAR_ASSIGN.test(cssText);\n // reset state of the regexes\n MIXIN_MATCH.lastIndex = 0;\n VAR_ASSIGN.lastIndex = 0;\n return has;\n}\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n\n'use strict';\n\nimport ScopingShim from '../src/scoping-shim.js';\nimport {nativeCssVariables, nativeShadow, cssBuild} from '../src/style-settings.js';\n\n/** @const {ScopingShim} */\nconst scopingShim = new ScopingShim();\n\nlet ApplyShim, CustomStyleInterface;\n\nif (window['ShadyCSS']) {\n ApplyShim = window['ShadyCSS']['ApplyShim'];\n CustomStyleInterface = window['ShadyCSS']['CustomStyleInterface'];\n}\n\nwindow.ShadyCSS = {\n ScopingShim: scopingShim,\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} elementExtends\n */\n prepareTemplate(template, elementName, elementExtends) {\n scopingShim.flushCustomStyles();\n scopingShim.prepareTemplate(template, elementName, elementExtends)\n },\n\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n */\n prepareTemplateDom(template, elementName) {\n scopingShim.prepareTemplateDom(template, elementName);\n },\n\n /**\n * @param {!HTMLTemplateElement} template\n * @param {string} elementName\n * @param {string=} elementExtends\n */\n prepareTemplateStyles(template, elementName, elementExtends) {\n scopingShim.flushCustomStyles();\n scopingShim.prepareTemplateStyles(template, elementName, elementExtends)\n },\n /**\n * @param {!HTMLElement} element\n * @param {Object=} properties\n */\n styleSubtree(element, properties) {\n scopingShim.flushCustomStyles();\n scopingShim.styleSubtree(element, properties);\n },\n\n /**\n * @param {!HTMLElement} element\n */\n styleElement(element) {\n scopingShim.flushCustomStyles();\n scopingShim.styleElement(element);\n },\n\n /**\n * @param {Object=} properties\n */\n styleDocument(properties) {\n scopingShim.flushCustomStyles();\n scopingShim.styleDocument(properties);\n },\n\n flushCustomStyles() {\n scopingShim.flushCustomStyles();\n },\n\n /**\n * @param {Element} element\n * @param {string} property\n * @return {string}\n */\n getComputedStyleValue(element, property) {\n return scopingShim.getComputedStyleValue(element, property);\n },\n\n nativeCss: nativeCssVariables,\n\n nativeShadow: nativeShadow,\n\n cssBuild: cssBuild\n};\n\nif (ApplyShim) {\n window.ShadyCSS.ApplyShim = ApplyShim;\n}\n\nif (CustomStyleInterface) {\n window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;\n}"]} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/src/apply-shim-utils.js b/catapult/third_party/polymer/components/shadycss/src/apply-shim-utils.js
new file mode 100644
index 00000000..c5c27a2b
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/apply-shim-utils.js
@@ -0,0 +1,149 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+import templateMap from './template-map.js';
+import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
+
+/*
+ * Utilities for handling invalidating apply-shim mixins for a given template.
+ *
+ * The invalidation strategy involves keeping track of the "current" version of a template's mixins, and updating that count when a mixin is invalidated.
+ * The template
+ */
+
+/** @const {string} */
+const CURRENT_VERSION = '_applyShimCurrentVersion';
+
+/** @const {string} */
+const NEXT_VERSION = '_applyShimNextVersion';
+
+/** @const {string} */
+const VALIDATING_VERSION = '_applyShimValidatingVersion';
+
+/**
+ * @const {Promise<void>}
+ */
+const promise = Promise.resolve();
+
+/**
+ * @param {string} elementName
+ */
+export function invalidate(elementName){
+ let template = templateMap[elementName];
+ if (template) {
+ invalidateTemplate(template);
+ }
+}
+
+/**
+ * This function can be called multiple times to mark a template invalid
+ * and signal that the style inside must be regenerated.
+ *
+ * Use `startValidatingTemplate` to begin an asynchronous validation cycle.
+ * During that cycle, call `templateIsValidating` to see if the template must
+ * be revalidated
+ * @param {HTMLTemplateElement} template
+ */
+export function invalidateTemplate(template) {
+ // default the current version to 0
+ template[CURRENT_VERSION] = template[CURRENT_VERSION] || 0;
+ // ensure the "validating for" flag exists
+ template[VALIDATING_VERSION] = template[VALIDATING_VERSION] || 0;
+ // increment the next version
+ template[NEXT_VERSION] = (template[NEXT_VERSION] || 0) + 1;
+}
+
+/**
+ * @param {string} elementName
+ * @return {boolean}
+ */
+export function isValid(elementName) {
+ let template = templateMap[elementName];
+ if (template) {
+ return templateIsValid(template);
+ }
+ return true;
+}
+
+/**
+ * @param {HTMLTemplateElement} template
+ * @return {boolean}
+ */
+export function templateIsValid(template) {
+ return template[CURRENT_VERSION] === template[NEXT_VERSION];
+}
+
+/**
+ * @param {string} elementName
+ * @return {boolean}
+ */
+export function isValidating(elementName) {
+ let template = templateMap[elementName];
+ if (template) {
+ return templateIsValidating(template);
+ }
+ return false;
+}
+
+/**
+ * Returns true if the template is currently invalid and `startValidating` has been called since the last invalidation.
+ * If false, the template must be validated.
+ * @param {HTMLTemplateElement} template
+ * @return {boolean}
+ */
+export function templateIsValidating(template) {
+ return !templateIsValid(template) && template[VALIDATING_VERSION] === template[NEXT_VERSION];
+}
+
+/**
+ * the template is marked as `validating` for one microtask so that all instances
+ * found in the tree crawl of `applyStyle` will update themselves,
+ * but the template will only be updated once.
+ * @param {string} elementName
+*/
+export function startValidating(elementName) {
+ let template = templateMap[elementName];
+ startValidatingTemplate(template);
+}
+
+/**
+ * Begin an asynchronous invalidation cycle.
+ * This should be called after every validation of a template
+ *
+ * After one microtask, the template will be marked as valid until the next call to `invalidateTemplate`
+ * @param {HTMLTemplateElement} template
+ */
+export function startValidatingTemplate(template) {
+ // remember that the current "next version" is the reason for this validation cycle
+ template[VALIDATING_VERSION] = template[NEXT_VERSION];
+ // however, there only needs to be one async task to clear the counters
+ if (!template._validating) {
+ template._validating = true;
+ promise.then(function() {
+ // sync the current version to let future invalidations cause a refresh cycle
+ template[CURRENT_VERSION] = template[NEXT_VERSION];
+ template._validating = false;
+ });
+ }
+}
+
+/**
+ * @return {boolean}
+ */
+export function elementsAreInvalid() {
+ for (let elementName in templateMap) {
+ let template = templateMap[elementName];
+ if (!templateIsValid(template)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/catapult/third_party/polymer/components/shadycss/src/apply-shim.js b/catapult/third_party/polymer/components/shadycss/src/apply-shim.js
new file mode 100644
index 00000000..e4bc9cdf
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/apply-shim.js
@@ -0,0 +1,525 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+/*
+ * The apply shim simulates the behavior of `@apply` proposed at
+ * https://tabatkins.github.io/specs/css-apply-rule/.
+ * The approach is to convert a property like this:
+ *
+ * --foo: {color: red; background: blue;}
+ *
+ * to this:
+ *
+ * --foo_-_color: red;
+ * --foo_-_background: blue;
+ *
+ * Then where `@apply --foo` is used, that is converted to:
+ *
+ * color: var(--foo_-_color);
+ * background: var(--foo_-_background);
+ *
+ * This approach generally works but there are some issues and limitations.
+ * Consider, for example, that somewhere *between* where `--foo` is set and used,
+ * another element sets it to:
+ *
+ * --foo: { border: 2px solid red; }
+ *
+ * We must now ensure that the color and background from the previous setting
+ * do not apply. This is accomplished by changing the property set to this:
+ *
+ * --foo_-_border: 2px solid red;
+ * --foo_-_color: initial;
+ * --foo_-_background: initial;
+ *
+ * This works but introduces one new issue.
+ * Consider this setup at the point where the `@apply` is used:
+ *
+ * background: orange;
+ * `@apply` --foo;
+ *
+ * In this case the background will be unset (initial) rather than the desired
+ * `orange`. We address this by altering the property set to use a fallback
+ * value like this:
+ *
+ * color: var(--foo_-_color);
+ * background: var(--foo_-_background, orange);
+ * border: var(--foo_-_border);
+ *
+ * Note that the default is retained in the property set and the `background` is
+ * the desired `orange`. This leads us to a limitation.
+ *
+ * Limitation 1:
+
+ * Only properties in the rule where the `@apply`
+ * is used are considered as default values.
+ * If another rule matches the element and sets `background` with
+ * less specificity than the rule in which `@apply` appears,
+ * the `background` will not be set.
+ *
+ * Limitation 2:
+ *
+ * When using Polymer's `updateStyles` api, new properties may not be set for
+ * `@apply` properties.
+
+*/
+
+'use strict';
+
+import {forEachRule, processVariableAndFallback, rulesForStyle, toCssText, gatherStyleText} from './style-util.js';
+import {MIXIN_MATCH, VAR_ASSIGN} from './common-regex.js';
+import {detectMixin} from './common-utils.js';
+import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
+
+const APPLY_NAME_CLEAN = /;\s*/m;
+const INITIAL_INHERIT = /^\s*(initial)|(inherit)\s*$/;
+const IMPORTANT = /\s*!important/;
+
+// separator used between mixin-name and mixin-property-name when producing properties
+// NOTE: plain '-' may cause collisions in user styles
+const MIXIN_VAR_SEP = '_-_';
+
+/**
+ * @typedef {!Object<string, string>}
+ */
+let PropertyEntry; // eslint-disable-line no-unused-vars
+
+/**
+ * @typedef {!Object<string, boolean>}
+ */
+let DependantsEntry; // eslint-disable-line no-unused-vars
+
+/** @typedef {{
+ * properties: PropertyEntry,
+ * dependants: DependantsEntry
+ * }}
+ */
+let MixinMapEntry; // eslint-disable-line no-unused-vars
+
+// map of mixin to property names
+// --foo: {border: 2px} -> {properties: {(--foo, ['border'])}, dependants: {'element-name': proto}}
+class MixinMap {
+ constructor() {
+ /** @type {!Object<string, !MixinMapEntry>} */
+ this._map = {};
+ }
+ /**
+ * @param {string} name
+ * @param {!PropertyEntry} props
+ */
+ set(name, props) {
+ name = name.trim();
+ this._map[name] = {
+ properties: props,
+ dependants: {}
+ }
+ }
+ /**
+ * @param {string} name
+ * @return {MixinMapEntry}
+ */
+ get(name) {
+ name = name.trim();
+ return this._map[name] || null;
+ }
+}
+
+/**
+ * Callback for when an element is marked invalid
+ * @type {?function(string)}
+ */
+let invalidCallback = null;
+
+/** @unrestricted */
+class ApplyShim {
+ constructor() {
+ /** @type {?string} */
+ this._currentElement = null;
+ /** @type {HTMLMetaElement} */
+ this._measureElement = null;
+ this._map = new MixinMap();
+ }
+ /**
+ * return true if `cssText` contains a mixin definition or consumption
+ * @param {string} cssText
+ * @return {boolean}
+ */
+ detectMixin(cssText) {
+ return detectMixin(cssText);
+ }
+
+ /**
+ * Gather styles into one style for easier processing
+ * @param {!HTMLTemplateElement} template
+ * @return {HTMLStyleElement}
+ */
+ gatherStyles(template) {
+ const styleText = gatherStyleText(template.content);
+ if (styleText) {
+ const style = /** @type {!HTMLStyleElement} */(document.createElement('style'));
+ style.textContent = styleText;
+ template.content.insertBefore(style, template.content.firstChild);
+ return style;
+ }
+ return null;
+ }
+ /**
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @return {StyleNode}
+ */
+ transformTemplate(template, elementName) {
+ if (template._gatheredStyle === undefined) {
+ template._gatheredStyle = this.gatherStyles(template);
+ }
+ /** @type {HTMLStyleElement} */
+ const style = template._gatheredStyle;
+ return style ? this.transformStyle(style, elementName) : null;
+ }
+ /**
+ * @param {!HTMLStyleElement} style
+ * @param {string} elementName
+ * @return {StyleNode}
+ */
+ transformStyle(style, elementName = '') {
+ let ast = rulesForStyle(style);
+ this.transformRules(ast, elementName);
+ style.textContent = toCssText(ast);
+ return ast;
+ }
+ /**
+ * @param {!HTMLStyleElement} style
+ * @return {StyleNode}
+ */
+ transformCustomStyle(style) {
+ let ast = rulesForStyle(style);
+ forEachRule(ast, (rule) => {
+ if (rule['selector'] === ':root') {
+ rule['selector'] = 'html';
+ }
+ this.transformRule(rule);
+ })
+ style.textContent = toCssText(ast);
+ return ast;
+ }
+ /**
+ * @param {StyleNode} rules
+ * @param {string} elementName
+ */
+ transformRules(rules, elementName) {
+ this._currentElement = elementName;
+ forEachRule(rules, (r) => {
+ this.transformRule(r);
+ });
+ this._currentElement = null;
+ }
+ /**
+ * @param {!StyleNode} rule
+ */
+ transformRule(rule) {
+ rule['cssText'] = this.transformCssText(rule['parsedCssText'], rule);
+ // :root was only used for variable assignment in property shim,
+ // but generates invalid selectors with real properties.
+ // replace with `:host > *`, which serves the same effect
+ if (rule['selector'] === ':root') {
+ rule['selector'] = ':host > *';
+ }
+ }
+ /**
+ * @param {string} cssText
+ * @param {!StyleNode} rule
+ * @return {string}
+ */
+ transformCssText(cssText, rule) {
+ // produce variables
+ cssText = cssText.replace(VAR_ASSIGN, (matchText, propertyName, valueProperty, valueMixin) =>
+ this._produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule));
+ // consume mixins
+ return this._consumeCssProperties(cssText, rule);
+ }
+ /**
+ * @param {string} property
+ * @return {string}
+ */
+ _getInitialValueForProperty(property) {
+ if (!this._measureElement) {
+ this._measureElement = /** @type {HTMLMetaElement} */(document.createElement('meta'));
+ this._measureElement.setAttribute('apply-shim-measure', '');
+ this._measureElement.style.all = 'initial';
+ document.head.appendChild(this._measureElement);
+ }
+ return window.getComputedStyle(this._measureElement).getPropertyValue(property);
+ }
+ /**
+ * Walk over all rules before this rule to find fallbacks for mixins
+ *
+ * @param {!StyleNode} startRule
+ * @return {!Object}
+ */
+ _fallbacksFromPreviousRules(startRule) {
+ // find the "top" rule
+ let topRule = startRule;
+ while (topRule['parent']) {
+ topRule = topRule['parent'];
+ }
+ const fallbacks = {};
+ let seenStartRule = false;
+ forEachRule(topRule, (r) => {
+ // stop when we hit the input rule
+ seenStartRule = seenStartRule || r === startRule;
+ if (seenStartRule) {
+ return;
+ }
+ // NOTE: Only matching selectors are "safe" for this fallback processing
+ // It would be prohibitive to run `matchesSelector()` on each selector,
+ // so we cheat and only check if the same selector string is used, which
+ // guarantees things like specificity matching
+ if (r['selector'] === startRule['selector']) {
+ Object.assign(fallbacks, this._cssTextToMap(r['parsedCssText']));
+ }
+ });
+ return fallbacks;
+ }
+ /**
+ * replace mixin consumption with variable consumption
+ * @param {string} text
+ * @param {!StyleNode=} rule
+ * @return {string}
+ */
+ _consumeCssProperties(text, rule) {
+ /** @type {Array} */
+ let m = null;
+ // loop over text until all mixins with defintions have been applied
+ while((m = MIXIN_MATCH.exec(text))) {
+ let matchText = m[0];
+ let mixinName = m[1];
+ let idx = m.index;
+ // collect properties before apply to be "defaults" if mixin might override them
+ // match includes a "prefix", so find the start and end positions of @apply
+ let applyPos = idx + matchText.indexOf('@apply');
+ let afterApplyPos = idx + matchText.length;
+ // find props defined before this @apply
+ let textBeforeApply = text.slice(0, applyPos);
+ let textAfterApply = text.slice(afterApplyPos);
+ let defaults = rule ? this._fallbacksFromPreviousRules(rule) : {};
+ Object.assign(defaults, this._cssTextToMap(textBeforeApply));
+ let replacement = this._atApplyToCssProperties(mixinName, defaults);
+ // use regex match position to replace mixin, keep linear processing time
+ text = `${textBeforeApply}${replacement}${textAfterApply}`;
+ // move regex search to _after_ replacement
+ MIXIN_MATCH.lastIndex = idx + replacement.length;
+ }
+ return text;
+ }
+ /**
+ * produce variable consumption at the site of mixin consumption
+ * `@apply` --foo; -> for all props (${propname}: var(--foo_-_${propname}, ${fallback[propname]}}))
+ * Example:
+ * border: var(--foo_-_border); padding: var(--foo_-_padding, 2px)
+ *
+ * @param {string} mixinName
+ * @param {Object} fallbacks
+ * @return {string}
+ */
+ _atApplyToCssProperties(mixinName, fallbacks) {
+ mixinName = mixinName.replace(APPLY_NAME_CLEAN, '');
+ let vars = [];
+ let mixinEntry = this._map.get(mixinName);
+ // if we depend on a mixin before it is created
+ // make a sentinel entry in the map to add this element as a dependency for when it is defined.
+ if (!mixinEntry) {
+ this._map.set(mixinName, {});
+ mixinEntry = this._map.get(mixinName);
+ }
+ if (mixinEntry) {
+ if (this._currentElement) {
+ mixinEntry.dependants[this._currentElement] = true;
+ }
+ let p, parts, f;
+ const properties = mixinEntry.properties;
+ for (p in properties) {
+ f = fallbacks && fallbacks[p];
+ parts = [p, ': var(', mixinName, MIXIN_VAR_SEP, p];
+ if (f) {
+ parts.push(',', f.replace(IMPORTANT, ''));
+ }
+ parts.push(')');
+ if (IMPORTANT.test(properties[p])) {
+ parts.push(' !important');
+ }
+ vars.push(parts.join(''));
+ }
+ }
+ return vars.join('; ');
+ }
+
+ /**
+ * @param {string} property
+ * @param {string} value
+ * @return {string}
+ */
+ _replaceInitialOrInherit(property, value) {
+ let match = INITIAL_INHERIT.exec(value);
+ if (match) {
+ if (match[1]) {
+ // initial
+ // replace `initial` with the concrete initial value for this property
+ value = this._getInitialValueForProperty(property);
+ } else {
+ // inherit
+ // with this purposfully illegal value, the variable will be invalid at
+ // compute time (https://www.w3.org/TR/css-variables/#invalid-at-computed-value-time)
+ // and for inheriting values, will behave similarly
+ // we cannot support the same behavior for non inheriting values like 'border'
+ value = 'apply-shim-inherit';
+ }
+ }
+ return value;
+ }
+
+ /**
+ * "parse" a mixin definition into a map of properties and values
+ * cssTextToMap('border: 2px solid black') -> ('border', '2px solid black')
+ * @param {string} text
+ * @param {boolean=} replaceInitialOrInherit
+ * @return {!Object<string, string>}
+ */
+ _cssTextToMap(text, replaceInitialOrInherit = false) {
+ let props = text.split(';');
+ let property, value;
+ let out = {};
+ for (let i = 0, p, sp; i < props.length; i++) {
+ p = props[i];
+ if (p) {
+ sp = p.split(':');
+ // ignore lines that aren't definitions like @media
+ if (sp.length > 1) {
+ property = sp[0].trim();
+ // some properties may have ':' in the value, like data urls
+ value = sp.slice(1).join(':');
+ if (replaceInitialOrInherit) {
+ value = this._replaceInitialOrInherit(property, value);
+ }
+ out[property] = value;
+ }
+ }
+ }
+ return out;
+ }
+
+ /**
+ * @param {MixinMapEntry} mixinEntry
+ */
+ _invalidateMixinEntry(mixinEntry) {
+ if (!invalidCallback) {
+ return;
+ }
+ for (let elementName in mixinEntry.dependants) {
+ if (elementName !== this._currentElement) {
+ invalidCallback(elementName);
+ }
+ }
+ }
+
+ /**
+ * @param {string} matchText
+ * @param {string} propertyName
+ * @param {?string} valueProperty
+ * @param {?string} valueMixin
+ * @param {!StyleNode} rule
+ * @return {string}
+ */
+ _produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule) {
+ // handle case where property value is a mixin
+ if (valueProperty) {
+ // form: --mixin2: var(--mixin1), where --mixin1 is in the map
+ processVariableAndFallback(valueProperty, (prefix, value) => {
+ if (value && this._map.get(value)) {
+ valueMixin = `@apply ${value};`
+ }
+ });
+ }
+ if (!valueMixin) {
+ return matchText;
+ }
+ let mixinAsProperties = this._consumeCssProperties('' + valueMixin, rule);
+ let prefix = matchText.slice(0, matchText.indexOf('--'));
+ // `initial` and `inherit` as properties in a map should be replaced because
+ // these keywords are eagerly evaluated when the mixin becomes CSS Custom Properties,
+ // and would set the variable value, rather than carry the keyword to the `var()` usage.
+ let mixinValues = this._cssTextToMap(mixinAsProperties, true);
+ let combinedProps = mixinValues;
+ let mixinEntry = this._map.get(propertyName);
+ let oldProps = mixinEntry && mixinEntry.properties;
+ if (oldProps) {
+ // NOTE: since we use mixin, the map of properties is updated here
+ // and this is what we want.
+ combinedProps = Object.assign(Object.create(oldProps), mixinValues);
+ } else {
+ this._map.set(propertyName, combinedProps);
+ }
+ let out = [];
+ let p, v;
+ // set variables defined by current mixin
+ let needToInvalidate = false;
+ for (p in combinedProps) {
+ v = mixinValues[p];
+ // if property not defined by current mixin, set initial
+ if (v === undefined) {
+ v = 'initial';
+ }
+ if (oldProps && !(p in oldProps)) {
+ needToInvalidate = true;
+ }
+ out.push(`${propertyName}${MIXIN_VAR_SEP}${p}: ${v}`);
+ }
+ if (needToInvalidate) {
+ this._invalidateMixinEntry(mixinEntry);
+ }
+ if (mixinEntry) {
+ mixinEntry.properties = combinedProps;
+ }
+ // because the mixinMap is global, the mixin might conflict with
+ // a different scope's simple variable definition:
+ // Example:
+ // some style somewhere:
+ // --mixin1:{ ... }
+ // --mixin2: var(--mixin1);
+ // some other element:
+ // --mixin1: 10px solid red;
+ // --foo: var(--mixin1);
+ // In this case, we leave the original variable definition in place.
+ if (valueProperty) {
+ prefix = `${matchText};${prefix}`;
+ }
+ return `${prefix}${out.join('; ')};`;
+ }
+}
+
+/* exports */
+/* eslint-disable no-self-assign */
+ApplyShim.prototype['detectMixin'] = ApplyShim.prototype.detectMixin;
+ApplyShim.prototype['transformStyle'] = ApplyShim.prototype.transformStyle;
+ApplyShim.prototype['transformCustomStyle'] = ApplyShim.prototype.transformCustomStyle;
+ApplyShim.prototype['transformRules'] = ApplyShim.prototype.transformRules;
+ApplyShim.prototype['transformRule'] = ApplyShim.prototype.transformRule;
+ApplyShim.prototype['transformTemplate'] = ApplyShim.prototype.transformTemplate;
+ApplyShim.prototype['_separator'] = MIXIN_VAR_SEP;
+/* eslint-enable no-self-assign */
+Object.defineProperty(ApplyShim.prototype, 'invalidCallback', {
+ /** @return {?function(string)} */
+ get() {
+ return invalidCallback;
+ },
+ /** @param {?function(string)} cb */
+ set(cb) {
+ invalidCallback = cb;
+ }
+});
+
+export default ApplyShim;
diff --git a/catapult/third_party/polymer/components/shadycss/src/common-regex.js b/catapult/third_party/polymer/components/shadycss/src/common-regex.js
new file mode 100644
index 00000000..126fc15c
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/common-regex.js
@@ -0,0 +1,19 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+export const VAR_ASSIGN = /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi;
+export const MIXIN_MATCH = /(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi;
+export const VAR_CONSUMED = /(--[\w-]+)\s*([:,;)]|$)/gi;
+export const ANIMATION_MATCH = /(animation\s*:)|(animation-name\s*:)/;
+export const MEDIA_MATCH = /@media\s(.*)/;
+export const IS_VAR = /^--/;
+export const BRACKETED = /\{[^}]*\}/g;
+export const HOST_PREFIX = '(?:^|[^.#[:])';
+export const HOST_SUFFIX = '($|[.:[\\s>+~])';
diff --git a/catapult/third_party/polymer/components/shadycss/src/common-utils.js b/catapult/third_party/polymer/components/shadycss/src/common-utils.js
new file mode 100644
index 00000000..863250be
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/common-utils.js
@@ -0,0 +1,59 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import { MIXIN_MATCH, VAR_ASSIGN } from './common-regex.js';
+
+/**
+ * @param {Element} element
+ * @param {Object=} properties
+ */
+export function updateNativeProperties(element, properties) {
+ // remove previous properties
+ for (let p in properties) {
+ // NOTE: for bc with shim, don't apply null values.
+ if (p === null) {
+ element.style.removeProperty(p);
+ } else {
+ element.style.setProperty(p, properties[p]);
+ }
+ }
+}
+
+/**
+ * @param {Element} element
+ * @param {string} property
+ * @return {string}
+ */
+export function getComputedStyleValue(element, property) {
+ /**
+ * @const {string}
+ */
+ const value = window.getComputedStyle(element).getPropertyValue(property);
+ if (!value) {
+ return '';
+ } else {
+ return value.trim();
+ }
+}
+
+/**
+ * return true if `cssText` contains a mixin definition or consumption
+ * @param {string} cssText
+ * @return {boolean}
+ */
+export function detectMixin(cssText) {
+ const has = MIXIN_MATCH.test(cssText) || VAR_ASSIGN.test(cssText);
+ // reset state of the regexes
+ MIXIN_MATCH.lastIndex = 0;
+ VAR_ASSIGN.lastIndex = 0;
+ return has;
+}
diff --git a/catapult/third_party/polymer/components/shadycss/src/css-parse.js b/catapult/third_party/polymer/components/shadycss/src/css-parse.js
new file mode 100644
index 00000000..8a8fb1cd
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/css-parse.js
@@ -0,0 +1,264 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+/*
+Extremely simple css parser. Intended to be not more than what we need
+and definitely not necessarily correct =).
+*/
+
+'use strict';
+
+/** @unrestricted */
+class StyleNode {
+ constructor() {
+ /** @type {number} */
+ this['start'] = 0;
+ /** @type {number} */
+ this['end'] = 0;
+ /** @type {StyleNode} */
+ this['previous'] = null;
+ /** @type {StyleNode} */
+ this['parent'] = null;
+ /** @type {Array<StyleNode>} */
+ this['rules'] = null;
+ /** @type {string} */
+ this['parsedCssText'] = '';
+ /** @type {string} */
+ this['cssText'] = '';
+ /** @type {boolean} */
+ this['atRule'] = false;
+ /** @type {number} */
+ this['type'] = 0;
+ /** @type {string} */
+ this['keyframesName'] = '';
+ /** @type {string} */
+ this['selector'] = '';
+ /** @type {string} */
+ this['parsedSelector'] = '';
+ }
+}
+
+export {StyleNode}
+
+// given a string of css, return a simple rule tree
+/**
+ * @param {string} text
+ * @return {StyleNode}
+ */
+export function parse(text) {
+ text = clean(text);
+ return parseCss(lex(text), text);
+}
+
+// remove stuff we don't care about that may hinder parsing
+/**
+ * @param {string} cssText
+ * @return {string}
+ */
+function clean(cssText) {
+ return cssText.replace(RX.comments, '').replace(RX.port, '');
+}
+
+// super simple {...} lexer that returns a node tree
+/**
+ * @param {string} text
+ * @return {StyleNode}
+ */
+function lex(text) {
+ let root = new StyleNode();
+ root['start'] = 0;
+ root['end'] = text.length
+ let n = root;
+ for (let i = 0, l = text.length; i < l; i++) {
+ if (text[i] === OPEN_BRACE) {
+ if (!n['rules']) {
+ n['rules'] = [];
+ }
+ let p = n;
+ let previous = p['rules'][p['rules'].length - 1] || null;
+ n = new StyleNode();
+ n['start'] = i + 1;
+ n['parent'] = p;
+ n['previous'] = previous;
+ p['rules'].push(n);
+ } else if (text[i] === CLOSE_BRACE) {
+ n['end'] = i + 1;
+ n = n['parent'] || root;
+ }
+ }
+ return root;
+}
+
+// add selectors/cssText to node tree
+/**
+ * @param {StyleNode} node
+ * @param {string} text
+ * @return {StyleNode}
+ */
+function parseCss(node, text) {
+ let t = text.substring(node['start'], node['end'] - 1);
+ node['parsedCssText'] = node['cssText'] = t.trim();
+ if (node['parent']) {
+ let ss = node['previous'] ? node['previous']['end'] : node['parent']['start'];
+ t = text.substring(ss, node['start'] - 1);
+ t = _expandUnicodeEscapes(t);
+ t = t.replace(RX.multipleSpaces, ' ');
+ // TODO(sorvell): ad hoc; make selector include only after last ;
+ // helps with mixin syntax
+ t = t.substring(t.lastIndexOf(';') + 1);
+ let s = node['parsedSelector'] = node['selector'] = t.trim();
+ node['atRule'] = (s.indexOf(AT_START) === 0);
+ // note, support a subset of rule types...
+ if (node['atRule']) {
+ if (s.indexOf(MEDIA_START) === 0) {
+ node['type'] = types.MEDIA_RULE;
+ } else if (s.match(RX.keyframesRule)) {
+ node['type'] = types.KEYFRAMES_RULE;
+ node['keyframesName'] =
+ node['selector'].split(RX.multipleSpaces).pop();
+ }
+ } else {
+ if (s.indexOf(VAR_START) === 0) {
+ node['type'] = types.MIXIN_RULE;
+ } else {
+ node['type'] = types.STYLE_RULE;
+ }
+ }
+ }
+ let r$ = node['rules'];
+ if (r$) {
+ for (let i = 0, l = r$.length, r;
+ (i < l) && (r = r$[i]); i++) {
+ parseCss(r, text);
+ }
+ }
+ return node;
+}
+
+/**
+ * conversion of sort unicode escapes with spaces like `\33 ` (and longer) into
+ * expanded form that doesn't require trailing space `\000033`
+ * @param {string} s
+ * @return {string}
+ */
+function _expandUnicodeEscapes(s) {
+ return s.replace(/\\([0-9a-f]{1,6})\s/gi, function() {
+ let code = arguments[1],
+ repeat = 6 - code.length;
+ while (repeat--) {
+ code = '0' + code;
+ }
+ return '\\' + code;
+ });
+}
+
+/**
+ * stringify parsed css.
+ * @param {StyleNode} node
+ * @param {boolean=} preserveProperties
+ * @param {string=} text
+ * @return {string}
+ */
+export function stringify(node, preserveProperties, text = '') {
+ // calc rule cssText
+ let cssText = '';
+ if (node['cssText'] || node['rules']) {
+ let r$ = node['rules'];
+ if (r$ && !_hasMixinRules(r$)) {
+ for (let i = 0, l = r$.length, r;
+ (i < l) && (r = r$[i]); i++) {
+ cssText = stringify(r, preserveProperties, cssText);
+ }
+ } else {
+ cssText = preserveProperties ? node['cssText'] :
+ removeCustomProps(node['cssText']);
+ cssText = cssText.trim();
+ if (cssText) {
+ cssText = ' ' + cssText + '\n';
+ }
+ }
+ }
+ // emit rule if there is cssText
+ if (cssText) {
+ if (node['selector']) {
+ text += node['selector'] + ' ' + OPEN_BRACE + '\n';
+ }
+ text += cssText;
+ if (node['selector']) {
+ text += CLOSE_BRACE + '\n\n';
+ }
+ }
+ return text;
+}
+
+/**
+ * @param {Array<StyleNode>} rules
+ * @return {boolean}
+ */
+function _hasMixinRules(rules) {
+ let r = rules[0];
+ return Boolean(r) && Boolean(r['selector']) && r['selector'].indexOf(VAR_START) === 0;
+}
+
+/**
+ * @param {string} cssText
+ * @return {string}
+ */
+function removeCustomProps(cssText) {
+ cssText = removeCustomPropAssignment(cssText);
+ return removeCustomPropApply(cssText);
+}
+
+/**
+ * @param {string} cssText
+ * @return {string}
+ */
+export function removeCustomPropAssignment(cssText) {
+ return cssText
+ .replace(RX.customProp, '')
+ .replace(RX.mixinProp, '');
+}
+
+/**
+ * @param {string} cssText
+ * @return {string}
+ */
+function removeCustomPropApply(cssText) {
+ return cssText
+ .replace(RX.mixinApply, '')
+ .replace(RX.varApply, '');
+}
+
+/** @enum {number} */
+export const types = {
+ STYLE_RULE: 1,
+ KEYFRAMES_RULE: 7,
+ MEDIA_RULE: 4,
+ MIXIN_RULE: 1000
+}
+
+const OPEN_BRACE = '{';
+const CLOSE_BRACE = '}';
+
+// helper regexp's
+const RX = {
+ comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
+ port: /@import[^;]*;/gim,
+ customProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,
+ mixinProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,
+ mixinApply: /@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,
+ varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,
+ keyframesRule: /^@[^\s]*keyframes/,
+ multipleSpaces: /\s+/g
+}
+
+const VAR_START = '--';
+const MEDIA_START = '@media';
+const AT_START = '@';
diff --git a/catapult/third_party/polymer/components/shadycss/src/custom-style-interface.js b/catapult/third_party/polymer/components/shadycss/src/custom-style-interface.js
new file mode 100644
index 00000000..257433bf
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/custom-style-interface.js
@@ -0,0 +1,164 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import documentWait from './document-wait.js';
+
+/**
+ * @typedef {HTMLStyleElement | {getStyle: function():HTMLStyleElement}}
+ */
+export let CustomStyleProvider;
+
+const SEEN_MARKER = '__seenByShadyCSS';
+const CACHED_STYLE = '__shadyCSSCachedStyle';
+
+/** @type {?function(!HTMLStyleElement)} */
+let transformFn = null;
+
+/** @type {?function()} */
+let validateFn = null;
+
+/**
+This interface is provided to add document-level <style> elements to ShadyCSS for processing.
+These styles must be processed by ShadyCSS to simulate ShadowRoot upper-bound encapsulation from outside styles
+In addition, these styles may also need to be processed for @apply rules and CSS Custom Properties
+
+To add document-level styles to ShadyCSS, one can call `ShadyCSS.addDocumentStyle(styleElement)` or `ShadyCSS.addDocumentStyle({getStyle: () => styleElement})`
+
+In addition, if the process used to discover document-level styles can be synchronously flushed, one should set `ShadyCSS.documentStyleFlush`.
+This function will be called when calculating styles.
+
+An example usage of the document-level styling api can be found in `examples/document-style-lib.js`
+
+@unrestricted
+*/
+export default class CustomStyleInterface {
+ constructor() {
+ /** @type {!Array<!CustomStyleProvider>} */
+ this['customStyles'] = [];
+ this['enqueued'] = false;
+ // NOTE(dfreedm): use quotes here to prevent closure inlining to `function(){}`;
+ documentWait(() => {
+ if (window['ShadyCSS']['flushCustomStyles']) {
+ window['ShadyCSS']['flushCustomStyles']();
+ }
+ })
+ }
+ /**
+ * Queue a validation for new custom styles to batch style recalculations
+ */
+ enqueueDocumentValidation() {
+ if (this['enqueued'] || !validateFn) {
+ return;
+ }
+ this['enqueued'] = true;
+ documentWait(validateFn);
+ }
+ /**
+ * @param {!HTMLStyleElement} style
+ */
+ addCustomStyle(style) {
+ if (!style[SEEN_MARKER]) {
+ style[SEEN_MARKER] = true;
+ this['customStyles'].push(style);
+ this.enqueueDocumentValidation();
+ }
+ }
+ /**
+ * @param {!CustomStyleProvider} customStyle
+ * @return {HTMLStyleElement}
+ */
+ getStyleForCustomStyle(customStyle) {
+ if (customStyle[CACHED_STYLE]) {
+ return customStyle[CACHED_STYLE];
+ }
+ let style;
+ if (customStyle['getStyle']) {
+ style = customStyle['getStyle']();
+ } else {
+ style = customStyle;
+ }
+ return style;
+ }
+ /**
+ * @return {!Array<!CustomStyleProvider>}
+ */
+ processStyles() {
+ const cs = this['customStyles'];
+ for (let i = 0; i < cs.length; i++) {
+ const customStyle = cs[i];
+ if (customStyle[CACHED_STYLE]) {
+ continue;
+ }
+ const style = this.getStyleForCustomStyle(customStyle);
+ if (style) {
+ // HTMLImports polyfill may have cloned the style into the main document,
+ // which is referenced with __appliedElement.
+ const styleToTransform = /** @type {!HTMLStyleElement} */(style['__appliedElement'] || style);
+ if (transformFn) {
+ transformFn(styleToTransform);
+ }
+ customStyle[CACHED_STYLE] = styleToTransform;
+ }
+ }
+ return cs;
+ }
+}
+
+/* eslint-disable no-self-assign */
+CustomStyleInterface.prototype['addCustomStyle'] = CustomStyleInterface.prototype.addCustomStyle;
+CustomStyleInterface.prototype['getStyleForCustomStyle'] = CustomStyleInterface.prototype.getStyleForCustomStyle;
+CustomStyleInterface.prototype['processStyles'] = CustomStyleInterface.prototype.processStyles;
+/* eslint-enable no-self-assign */
+
+Object.defineProperties(CustomStyleInterface.prototype, {
+ 'transformCallback': {
+ /** @return {?function(!HTMLStyleElement)} */
+ get() {
+ return transformFn;
+ },
+ /** @param {?function(!HTMLStyleElement)} fn */
+ set(fn) {
+ transformFn = fn;
+ }
+ },
+ 'validateCallback': {
+ /** @return {?function()} */
+ get() {
+ return validateFn;
+ },
+ /**
+ * @param {?function()} fn
+ * @this {CustomStyleInterface}
+ */
+ set(fn) {
+ let needsEnqueue = false;
+ if (!validateFn) {
+ needsEnqueue = true;
+ }
+ validateFn = fn;
+ if (needsEnqueue) {
+ this.enqueueDocumentValidation();
+ }
+ },
+ }
+})
+
+/** @typedef {{
+ * customStyles: !Array<!CustomStyleProvider>,
+ * addCustomStyle: function(!CustomStyleProvider),
+ * getStyleForCustomStyle: function(!CustomStyleProvider): HTMLStyleElement,
+ * findStyles: function(),
+ * transformCallback: ?function(!HTMLStyleElement),
+ * validateCallback: ?function()
+ * }}
+ */
+export const CustomStyleInterfaceInterface = {};
diff --git a/catapult/third_party/polymer/components/shadycss/src/document-wait.js b/catapult/third_party/polymer/components/shadycss/src/document-wait.js
new file mode 100644
index 00000000..398ca056
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/document-wait.js
@@ -0,0 +1,45 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+/** @type {Promise<void>} */
+let readyPromise = null;
+
+/** @type {?function(?function())} */
+let whenReady = window['HTMLImports'] && window['HTMLImports']['whenReady'] || null;
+
+/** @type {function()} */
+let resolveFn;
+
+/**
+ * @param {?function()} callback
+ */
+export default function documentWait(callback) {
+ requestAnimationFrame(function() {
+ if (whenReady) {
+ whenReady(callback)
+ } else {
+ if (!readyPromise) {
+ readyPromise = new Promise((resolve) => {resolveFn = resolve});
+ if (document.readyState === 'complete') {
+ resolveFn();
+ } else {
+ document.addEventListener('readystatechange', () => {
+ if (document.readyState === 'complete') {
+ resolveFn();
+ }
+ });
+ }
+ }
+ readyPromise.then(function(){ callback && callback(); });
+ }
+ });
+}
diff --git a/catapult/third_party/polymer/components/shadycss/src/document-watcher.js b/catapult/third_party/polymer/components/shadycss/src/document-watcher.js
new file mode 100644
index 00000000..9cf34f05
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/document-watcher.js
@@ -0,0 +1,198 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {nativeShadow} from './style-settings.js';
+import StyleTransformer from './style-transformer.js';
+import {getIsExtends, elementHasBuiltCss, wrap} from './style-util.js';
+
+export let flush = function() {};
+
+/**
+ * @param {!Element} element
+ * @return {string}
+ */
+function getClasses(element) {
+ if (element.classList && element.classList.value) {
+ return element.classList.value;
+ } else {
+ // NOTE: className is patched to remove scoping classes in ShadyDOM
+ // use getAttribute('class') instead, which is unpatched
+ return element.getAttribute('class') || '';
+ }
+}
+
+const scopeRegExp = new RegExp(`${StyleTransformer.SCOPE_NAME}\\s*([^\\s]*)`);
+
+/**
+ * @param {!Element} element
+ * @return {string}
+ */
+export function getCurrentScope(element) {
+ const match = getClasses(element).match(scopeRegExp);
+ if (match) {
+ return match[1];
+ } else {
+ return '';
+ }
+}
+
+/**
+ * @param {!Node} node
+ */
+export function getOwnerScope(node) {
+ const ownerRoot = wrap(node).getRootNode();
+ if (ownerRoot === node || ownerRoot === node.ownerDocument) {
+ return '';
+ }
+ const host = /** @type {!ShadowRoot} */(ownerRoot).host;
+ if (!host) {
+ // this may actually be a document fragment
+ return '';
+ }
+ return getIsExtends(host).is;
+}
+
+/**
+ * @param {!Element} element
+ */
+export function ensureCorrectScope(element) {
+ const currentScope = getCurrentScope(element);
+ const ownerRoot = wrap(element).getRootNode();
+ if (ownerRoot === element) {
+ return;
+ }
+ if (currentScope && ownerRoot === element.ownerDocument) {
+ // node was scoped, but now is in document
+ StyleTransformer.domRemoveScope(element, currentScope);
+ } else if (ownerRoot instanceof ShadowRoot) {
+ const ownerScope = getOwnerScope(element);
+ if (ownerScope !== currentScope) {
+ // node was scoped, but not by its current owner
+ StyleTransformer.domReplaceScope(element, currentScope, ownerScope);
+ }
+ }
+}
+
+/**
+ * @param {!HTMLElement|!HTMLDocument} element
+ */
+export function ensureCorrectSubtreeScoping(element) {
+ // find unscoped subtree nodes
+ const unscopedNodes = window['ShadyDOM']['nativeMethods']['querySelectorAll'].call(
+ element, `:not(.${StyleTransformer.SCOPE_NAME})`);
+
+ for (let j = 0; j < unscopedNodes.length; j++) {
+ // it's possible, during large batch inserts, that nodes that aren't
+ // scoped within the current scope were added.
+ // To make sure that any unscoped nodes that were inserted in the current batch are correctly styled,
+ // query all unscoped nodes and force their style-scope to be applied.
+ // This could happen if a sub-element appended an unscoped node in its shadowroot and this function
+ // runs on a parent element of the host of that unscoped node:
+ // parent-element -> element -> unscoped node
+ // Here unscoped node should have the style-scope element, not parent-element.
+ const unscopedNode = unscopedNodes[j];
+ const scopeForPreviouslyUnscopedNode = getOwnerScope(unscopedNode);
+ if (scopeForPreviouslyUnscopedNode) {
+ StyleTransformer.element(unscopedNode, scopeForPreviouslyUnscopedNode);
+ }
+ }
+}
+
+/**
+ * @param {HTMLElement} el
+ * @return {boolean}
+ */
+function isElementWithBuiltCss(el) {
+ if (el.localName === 'style' || el.localName === 'template') {
+ return elementHasBuiltCss(el);
+ }
+ return false;
+}
+
+/**
+ * @param {Array<MutationRecord|null>|null} mxns
+ */
+function handler(mxns) {
+ for (let x=0; x < mxns.length; x++) {
+ let mxn = mxns[x];
+ if (mxn.target === document.documentElement ||
+ mxn.target === document.head) {
+ continue;
+ }
+ for (let i=0; i < mxn.addedNodes.length; i++) {
+ let n = mxn.addedNodes[i];
+ if (n.nodeType !== Node.ELEMENT_NODE) {
+ continue;
+ }
+ n = /** @type {HTMLElement} */(n); // eslint-disable-line no-self-assign
+ let root = n.getRootNode();
+ let currentScope = getCurrentScope(n);
+ // node was scoped, but now is in document
+ // If this element has built css, we must not remove scoping as this node
+ // will be used as a template or style without re - applying scoping as an optimization
+ if (currentScope && root === n.ownerDocument && !isElementWithBuiltCss(n)) {
+ StyleTransformer.domRemoveScope(n, currentScope);
+ } else if (root instanceof ShadowRoot) {
+ const newScope = getOwnerScope(n);
+ // rescope current node and subtree if necessary
+ if (newScope !== currentScope) {
+ StyleTransformer.domReplaceScope(n, currentScope, newScope);
+ }
+ // make sure all the subtree elements are scoped correctly
+ ensureCorrectSubtreeScoping(n);
+ }
+ }
+ }
+}
+
+// if native Shadow DOM is being used, or ShadyDOM handles dynamic scoiping, do not activate the MutationObserver
+if (!nativeShadow && !(window['ShadyDOM'] && window['ShadyDOM']['handlesDynamicScoping'])) {
+ let observer = new MutationObserver(handler);
+ let start = (node) => {
+ observer.observe(node, {childList: true, subtree: true});
+ }
+ let nativeCustomElements = (window['customElements'] &&
+ !window['customElements']['polyfillWrapFlushCallback']);
+ // need to start immediately with native custom elements
+ // TODO(dfreedm): with polyfilled HTMLImports and native custom elements
+ // excessive mutations may be observed; this can be optimized via cooperation
+ // with the HTMLImports polyfill.
+ if (nativeCustomElements) {
+ start(document);
+ } else {
+ let delayedStart = () => {
+ start(document.body);
+ }
+ // use polyfill timing if it's available
+ if (window['HTMLImports']) {
+ window['HTMLImports']['whenReady'](delayedStart);
+ // otherwise push beyond native imports being ready
+ // which requires RAF + readystate interactive.
+ } else {
+ requestAnimationFrame(function() {
+ if (document.readyState === 'loading') {
+ let listener = function() {
+ delayedStart();
+ document.removeEventListener('readystatechange', listener);
+ }
+ document.addEventListener('readystatechange', listener);
+ } else {
+ delayedStart();
+ }
+ });
+ }
+ }
+
+ flush = function() {
+ handler(observer.takeRecords());
+ }
+}
diff --git a/catapult/third_party/polymer/components/shadycss/src/scoping-shim.js b/catapult/third_party/polymer/components/shadycss/src/scoping-shim.js
new file mode 100644
index 00000000..d439bb36
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/scoping-shim.js
@@ -0,0 +1,570 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {parse, StyleNode} from './css-parse.js';
+import {nativeShadow, nativeCssVariables} from './style-settings.js';
+import StyleTransformer from './style-transformer.js';
+import * as StyleUtil from './style-util.js';
+import StyleProperties from './style-properties.js';
+import {ensureStylePlaceholder, getStylePlaceholder} from './style-placeholder.js';
+import StyleInfo from './style-info.js';
+import StyleCache from './style-cache.js';
+import {flush as watcherFlush, getOwnerScope, getCurrentScope} from './document-watcher.js';
+import templateMap from './template-map.js';
+import * as ApplyShimUtils from './apply-shim-utils.js';
+import {updateNativeProperties, detectMixin} from './common-utils.js';
+import {CustomStyleInterfaceInterface} from './custom-style-interface.js'; // eslint-disable-line no-unused-vars
+
+/**
+ * @const {StyleCache}
+ */
+const styleCache = new StyleCache();
+
+export default class ScopingShim {
+ constructor() {
+ this._scopeCounter = {};
+ this._documentOwner = /** @type {!HTMLElement} */(document.documentElement);
+ let ast = new StyleNode();
+ ast['rules'] = [];
+ this._documentOwnerStyleInfo = StyleInfo.set(this._documentOwner, new StyleInfo(ast));
+ this._elementsHaveApplied = false;
+ /** @type {?Object} */
+ this._applyShim = null;
+ /** @type {?CustomStyleInterfaceInterface} */
+ this._customStyleInterface = null;
+ }
+ flush() {
+ watcherFlush();
+ }
+ _generateScopeSelector(name) {
+ let id = this._scopeCounter[name] = (this._scopeCounter[name] || 0) + 1;
+ return `${name}-${id}`;
+ }
+ getStyleAst(style) {
+ return StyleUtil.rulesForStyle(style);
+ }
+ styleAstToString(ast) {
+ return StyleUtil.toCssText(ast);
+ }
+ _gatherStyles(template) {
+ return StyleUtil.gatherStyleText(template.content);
+ }
+ /**
+ * Prepare the styling and template for the given element type
+ *
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} typeExtension
+ */
+ prepareTemplate(template, elementName, typeExtension) {
+ this.prepareTemplateDom(template, elementName);
+ this.prepareTemplateStyles(template, elementName, typeExtension);
+ }
+ /**
+ * Prepare styling for the given element type
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ * @param {string=} typeExtension
+ */
+ prepareTemplateStyles(template, elementName, typeExtension) {
+ if (template._prepared) {
+ return;
+ }
+ // style placeholders are only used when ShadyDOM is active
+ if (!nativeShadow) {
+ ensureStylePlaceholder(elementName);
+ }
+ template._prepared = true;
+ template.name = elementName;
+ template.extends = typeExtension;
+ templateMap[elementName] = template;
+ let cssBuild = StyleUtil.getCssBuild(template);
+ const optimalBuild = StyleUtil.isOptimalCssBuild(cssBuild);
+ let info = {
+ is: elementName,
+ extends: typeExtension,
+ };
+ let cssText = this._gatherStyles(template);
+ // check if the styling has mixin definitions or uses
+ this._ensure();
+ if (!optimalBuild) {
+ let hasMixins = !cssBuild && detectMixin(cssText);
+ let ast = parse(cssText);
+ // only run the applyshim transforms if there is a mixin involved
+ if (hasMixins && nativeCssVariables && this._applyShim) {
+ this._applyShim['transformRules'](ast, elementName);
+ }
+ template['_styleAst'] = ast;
+ }
+ let ownPropertyNames = [];
+ if (!nativeCssVariables) {
+ ownPropertyNames = StyleProperties.decorateStyles(template['_styleAst']);
+ }
+ if (!ownPropertyNames.length || nativeCssVariables) {
+ let root = nativeShadow ? template.content : null;
+ let placeholder = getStylePlaceholder(elementName);
+ let style = this._generateStaticStyle(info, template['_styleAst'], root, placeholder, cssBuild, optimalBuild ? cssText : '');
+ template._style = style;
+ }
+ template._ownPropertyNames = ownPropertyNames;
+ }
+ /**
+ * Prepare template for the given element type
+ * @param {!HTMLTemplateElement} template
+ * @param {string} elementName
+ */
+ prepareTemplateDom(template, elementName) {
+ const cssBuild = StyleUtil.getCssBuild(template);
+ if (!nativeShadow && cssBuild !== 'shady' && !template._domPrepared) {
+ template._domPrepared = true;
+ StyleTransformer.domAddScope(template.content, elementName);
+ }
+ }
+ /**
+ * @param {!{is: string, extends: (string|undefined)}} info
+ * @param {!StyleNode} rules
+ * @param {DocumentFragment} shadowroot
+ * @param {Node} placeholder
+ * @param {string} cssBuild
+ * @param {string=} cssText
+ * @return {?HTMLStyleElement}
+ */
+ _generateStaticStyle(info, rules, shadowroot, placeholder, cssBuild, cssText) {
+ cssText = StyleTransformer.elementStyles(info, rules, null, cssBuild, cssText);
+ if (cssText.length) {
+ return StyleUtil.applyCss(cssText, info.is, shadowroot, placeholder);
+ }
+ return null;
+ }
+ _prepareHost(host) {
+ const {is, typeExtension} = StyleUtil.getIsExtends(host);
+ const placeholder = getStylePlaceholder(is);
+ const template = templateMap[is];
+ if (!template) {
+ return;
+ }
+ const ast = template['_styleAst'];
+ const ownStylePropertyNames = template._ownPropertyNames;
+ const cssBuild = StyleUtil.getCssBuild(template);
+ const styleInfo = new StyleInfo(
+ ast,
+ placeholder,
+ ownStylePropertyNames,
+ is,
+ typeExtension,
+ cssBuild
+ );
+ StyleInfo.set(host, styleInfo);
+ return styleInfo;
+ }
+ _ensureApplyShim() {
+ if (this._applyShim) {
+ return;
+ } else if (window.ShadyCSS && window.ShadyCSS.ApplyShim) {
+ this._applyShim = /** @type {!Object} */ (window.ShadyCSS.ApplyShim);
+ this._applyShim['invalidCallback'] = ApplyShimUtils.invalidate;
+ }
+ }
+ _ensureCustomStyleInterface() {
+ if (this._customStyleInterface) {
+ return;
+ } else if (window.ShadyCSS && window.ShadyCSS.CustomStyleInterface) {
+ this._customStyleInterface = /** @type {!CustomStyleInterfaceInterface} */(window.ShadyCSS.CustomStyleInterface);
+ /** @type {function(!HTMLStyleElement)} */
+ this._customStyleInterface['transformCallback'] = (style) => {this.transformCustomStyleForDocument(style)};
+ this._customStyleInterface['validateCallback'] = () => {
+ requestAnimationFrame(() => {
+ if (this._customStyleInterface['enqueued'] || this._elementsHaveApplied) {
+ this.flushCustomStyles();
+ }
+ })
+ };
+ }
+ }
+ _ensure() {
+ this._ensureApplyShim();
+ this._ensureCustomStyleInterface();
+ }
+ /**
+ * Flush and apply custom styles to document
+ */
+ flushCustomStyles() {
+ this._ensure();
+ if (!this._customStyleInterface) {
+ return;
+ }
+ let customStyles = this._customStyleInterface['processStyles']();
+ // early return if custom-styles don't need validation
+ if (!this._customStyleInterface['enqueued']) {
+ return;
+ }
+ // bail if custom styles are built optimally
+ if (StyleUtil.isOptimalCssBuild(this._documentOwnerStyleInfo.cssBuild)) {
+ return;
+ }
+ if (!nativeCssVariables) {
+ this._updateProperties(this._documentOwner, this._documentOwnerStyleInfo);
+ this._applyCustomStyles(customStyles);
+ if (this._elementsHaveApplied) {
+ // if custom elements have upgraded and there are no native css variables, we must recalculate the whole tree
+ this.styleDocument();
+ }
+ } else if (!this._documentOwnerStyleInfo.cssBuild) {
+ this._revalidateCustomStyleApplyShim(customStyles);
+ }
+ this._customStyleInterface['enqueued'] = false;
+ }
+ /**
+ * Apply styles for the given element
+ *
+ * @param {!HTMLElement} host
+ * @param {Object=} overrideProps
+ */
+ styleElement(host, overrideProps) {
+ const styleInfo = StyleInfo.get(host) || this._prepareHost(host);
+ // if there is no style info at this point, bail
+ if (!styleInfo) {
+ return;
+ }
+ // Only trip the `elementsHaveApplied` flag if a node other that the root document has `applyStyle` called
+ if (!this._isRootOwner(host)) {
+ this._elementsHaveApplied = true;
+ }
+ if (overrideProps) {
+ styleInfo.overrideStyleProperties =
+ styleInfo.overrideStyleProperties || {};
+ Object.assign(styleInfo.overrideStyleProperties, overrideProps);
+ }
+ if (!nativeCssVariables) {
+ this.styleElementShimVariables(host, styleInfo);
+ } else {
+ this.styleElementNativeVariables(host, styleInfo);
+ }
+ }
+ /**
+ * @param {!HTMLElement} host
+ * @param {!StyleInfo} styleInfo
+ */
+ styleElementShimVariables(host, styleInfo) {
+ this.flush();
+ this._updateProperties(host, styleInfo);
+ if (styleInfo.ownStylePropertyNames && styleInfo.ownStylePropertyNames.length) {
+ this._applyStyleProperties(host, styleInfo);
+ }
+ }
+ /**
+ * @param {!HTMLElement} host
+ * @param {!StyleInfo} styleInfo
+ */
+ styleElementNativeVariables(host, styleInfo) {
+ const { is } = StyleUtil.getIsExtends(host);
+ if (styleInfo.overrideStyleProperties) {
+ updateNativeProperties(host, styleInfo.overrideStyleProperties);
+ }
+ const template = templateMap[is];
+ // bail early if there is no shadowroot for this element
+ if (!template && !this._isRootOwner(host)) {
+ return;
+ }
+ // bail early if the template was built with polymer-css-build
+ if (template && StyleUtil.elementHasBuiltCss(template)) {
+ return;
+ }
+ if (template && template._style && !ApplyShimUtils.templateIsValid(template)) {
+ // update template
+ if (!ApplyShimUtils.templateIsValidating(template)) {
+ this._ensure();
+ this._applyShim && this._applyShim['transformRules'](template['_styleAst'], is);
+ template._style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules);
+ ApplyShimUtils.startValidatingTemplate(template);
+ }
+ // update instance if native shadowdom
+ if (nativeShadow) {
+ let root = host.shadowRoot;
+ if (root) {
+ let style = root.querySelector('style');
+ if (style) {
+ style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules);
+ }
+ }
+ }
+ styleInfo.styleRules = template['_styleAst'];
+ }
+ }
+ _styleOwnerForNode(node) {
+ let root = StyleUtil.wrap(node).getRootNode();
+ let host = root.host;
+ if (host) {
+ if (StyleInfo.get(host) || this._prepareHost(host)) {
+ return host;
+ } else {
+ return this._styleOwnerForNode(host);
+ }
+ }
+ return this._documentOwner;
+ }
+ _isRootOwner(node) {
+ return (node === this._documentOwner);
+ }
+ _applyStyleProperties(host, styleInfo) {
+ let is = StyleUtil.getIsExtends(host).is;
+ let cacheEntry = styleCache.fetch(is, styleInfo.styleProperties, styleInfo.ownStylePropertyNames);
+ let cachedScopeSelector = cacheEntry && cacheEntry.scopeSelector;
+ let cachedStyle = cacheEntry ? cacheEntry.styleElement : null;
+ let oldScopeSelector = styleInfo.scopeSelector;
+ // only generate new scope if cached style is not found
+ styleInfo.scopeSelector = cachedScopeSelector || this._generateScopeSelector(is);
+ let style = StyleProperties.applyElementStyle(host, styleInfo.styleProperties, styleInfo.scopeSelector, cachedStyle);
+ if (!nativeShadow) {
+ StyleProperties.applyElementScopeSelector(host, styleInfo.scopeSelector, oldScopeSelector);
+ }
+ if (!cacheEntry) {
+ styleCache.store(is, styleInfo.styleProperties, style, styleInfo.scopeSelector);
+ }
+ return style;
+ }
+ _updateProperties(host, styleInfo) {
+ let owner = this._styleOwnerForNode(host);
+ let ownerStyleInfo = StyleInfo.get(owner);
+ let ownerProperties = ownerStyleInfo.styleProperties;
+ // style owner has not updated properties yet
+ // go up the chain and force property update,
+ // except if the owner is the document
+ if (owner !== this._documentOwner && !ownerProperties) {
+ this._updateProperties(owner, ownerStyleInfo);
+ ownerProperties = ownerStyleInfo.styleProperties;
+ }
+ let props = Object.create(ownerProperties || null);
+ let hostAndRootProps = StyleProperties.hostAndRootPropertiesForScope(host, styleInfo.styleRules, styleInfo.cssBuild);
+ let propertyData = StyleProperties.propertyDataFromStyles(ownerStyleInfo.styleRules, host);
+ let propertiesMatchingHost = propertyData.properties
+ Object.assign(
+ props,
+ hostAndRootProps.hostProps,
+ propertiesMatchingHost,
+ hostAndRootProps.rootProps
+ );
+ this._mixinOverrideStyles(props, styleInfo.overrideStyleProperties);
+ StyleProperties.reify(props);
+ styleInfo.styleProperties = props;
+ }
+ _mixinOverrideStyles(props, overrides) {
+ for (let p in overrides) {
+ let v = overrides[p];
+ // skip override props if they are not truthy or 0
+ // in order to fall back to inherited values
+ if (v || v === 0) {
+ props[p] = v;
+ }
+ }
+ }
+ /**
+ * Update styles of the whole document
+ *
+ * @param {Object=} properties
+ */
+ styleDocument(properties) {
+ this.styleSubtree(this._documentOwner, properties);
+ }
+ /**
+ * Update styles of a subtree
+ *
+ * @param {!HTMLElement} host
+ * @param {Object=} properties
+ */
+ styleSubtree(host, properties) {
+ let root = host.shadowRoot;
+ if (root || this._isRootOwner(host)) {
+ this.styleElement(host, properties);
+ }
+ // process the shadowdom children of `host`
+ let shadowChildren =
+ root && (/** @type {!ParentNode} */ (root).children || root.childNodes);
+ if (shadowChildren) {
+ for (let i = 0; i < shadowChildren.length; i++) {
+ let c = /** @type {!HTMLElement} */(shadowChildren[i]);
+ this.styleSubtree(c);
+ }
+ } else {
+ // process the lightdom children of `host`
+ let children = host.children || host.childNodes;
+ if (children) {
+ for (let i = 0; i < children.length; i++) {
+ let c = /** @type {!HTMLElement} */(children[i]);
+ this.styleSubtree(c);
+ }
+ }
+ }
+ }
+ /* Custom Style operations */
+ _revalidateCustomStyleApplyShim(customStyles) {
+ for (let i = 0; i < customStyles.length; i++) {
+ let c = customStyles[i];
+ let s = this._customStyleInterface['getStyleForCustomStyle'](c);
+ if (s) {
+ this._revalidateApplyShim(s);
+ }
+ }
+ }
+ _applyCustomStyles(customStyles) {
+ for (let i = 0; i < customStyles.length; i++) {
+ let c = customStyles[i];
+ let s = this._customStyleInterface['getStyleForCustomStyle'](c);
+ if (s) {
+ StyleProperties.applyCustomStyle(s, this._documentOwnerStyleInfo.styleProperties);
+ }
+ }
+ }
+ transformCustomStyleForDocument(style) {
+ const cssBuild = StyleUtil.getCssBuild(style);
+ if (cssBuild !== this._documentOwnerStyleInfo.cssBuild) {
+ this._documentOwnerStyleInfo.cssBuild = cssBuild;
+ }
+ if (StyleUtil.isOptimalCssBuild(cssBuild)) {
+ return;
+ }
+ let ast = StyleUtil.rulesForStyle(style);
+ StyleUtil.forEachRule(ast, (rule) => {
+ if (nativeShadow) {
+ StyleTransformer.normalizeRootSelector(rule);
+ } else {
+ StyleTransformer.documentRule(rule);
+ }
+ if (nativeCssVariables && cssBuild === '') {
+ this._ensure();
+ this._applyShim && this._applyShim['transformRule'](rule);
+ }
+ });
+ if (nativeCssVariables) {
+ style.textContent = StyleUtil.toCssText(ast);
+ } else {
+ this._documentOwnerStyleInfo.styleRules['rules'].push(ast);
+ }
+ }
+ _revalidateApplyShim(style) {
+ if (nativeCssVariables && this._applyShim) {
+ let ast = StyleUtil.rulesForStyle(style);
+ this._ensure();
+ this._applyShim['transformRules'](ast);
+ style.textContent = StyleUtil.toCssText(ast);
+ }
+ }
+ getComputedStyleValue(element, property) {
+ let value;
+ if (!nativeCssVariables) {
+ // element is either a style host, or an ancestor of a style host
+ let styleInfo = StyleInfo.get(element) || StyleInfo.get(this._styleOwnerForNode(element));
+ value = styleInfo.styleProperties[property];
+ }
+ // fall back to the property value from the computed styling
+ value = value || window.getComputedStyle(element).getPropertyValue(property);
+ // trim whitespace that can come after the `:` in css
+ // example: padding: 2px -> " 2px"
+ return value ? value.trim() : '';
+ }
+ // given an element and a classString, replaces
+ // the element's class with the provided classString and adds
+ // any necessary ShadyCSS static and property based scoping selectors
+ setElementClass(element, classString) {
+ let root = StyleUtil.wrap(element).getRootNode();
+ let classes = classString ? classString.split(/\s/) : [];
+ let scopeName = root.host && root.host.localName;
+ // If no scope, try to discover scope name from existing class.
+ // This can occur if, for example, a template stamped element that
+ // has been scoped is manipulated when not in a root.
+ if (!scopeName) {
+ var classAttr = element.getAttribute('class');
+ if (classAttr) {
+ let k$ = classAttr.split(/\s/);
+ for (let i=0; i < k$.length; i++) {
+ if (k$[i] === StyleTransformer.SCOPE_NAME) {
+ scopeName = k$[i+1];
+ break;
+ }
+ }
+ }
+ }
+ if (scopeName) {
+ classes.push(StyleTransformer.SCOPE_NAME, scopeName);
+ }
+ if (!nativeCssVariables) {
+ let styleInfo = StyleInfo.get(element);
+ if (styleInfo && styleInfo.scopeSelector) {
+ classes.push(StyleProperties.XSCOPE_NAME, styleInfo.scopeSelector);
+ }
+ }
+ StyleUtil.setElementClassRaw(element, classes.join(' '));
+ }
+ _styleInfoForNode(node) {
+ return StyleInfo.get(node);
+ }
+ /**
+ * @param {!Element} node
+ * @param {string} scope
+ */
+ scopeNode(node, scope) {
+ StyleTransformer.element(node, scope);
+ }
+ /**
+ * @param {!Element} node
+ * @param {string} scope
+ */
+ unscopeNode(node, scope) {
+ StyleTransformer.element(node, scope, true);
+ }
+ /**
+ * @param {!Node} node
+ * @return {string}
+ */
+ scopeForNode(node) {
+ return getOwnerScope(node);
+ }
+ /**
+ * @param {!Element} node
+ * @return {string}
+ */
+ currentScopeForNode(node) {
+ return getCurrentScope(node);
+ }
+}
+
+/* exports */
+/* eslint-disable no-self-assign */
+ScopingShim.prototype['flush'] = ScopingShim.prototype.flush;
+ScopingShim.prototype['prepareTemplate'] = ScopingShim.prototype.prepareTemplate;
+ScopingShim.prototype['styleElement'] = ScopingShim.prototype.styleElement;
+ScopingShim.prototype['styleDocument'] = ScopingShim.prototype.styleDocument;
+ScopingShim.prototype['styleSubtree'] = ScopingShim.prototype.styleSubtree;
+ScopingShim.prototype['getComputedStyleValue'] = ScopingShim.prototype.getComputedStyleValue;
+ScopingShim.prototype['setElementClass'] = ScopingShim.prototype.setElementClass;
+ScopingShim.prototype['_styleInfoForNode'] = ScopingShim.prototype._styleInfoForNode;
+ScopingShim.prototype['transformCustomStyleForDocument'] = ScopingShim.prototype.transformCustomStyleForDocument;
+ScopingShim.prototype['getStyleAst'] = ScopingShim.prototype.getStyleAst;
+ScopingShim.prototype['styleAstToString'] = ScopingShim.prototype.styleAstToString;
+ScopingShim.prototype['flushCustomStyles'] = ScopingShim.prototype.flushCustomStyles;
+ScopingShim.prototype['scopeNode'] = ScopingShim.prototype.scopeNode;
+ScopingShim.prototype['unscopeNode'] = ScopingShim.prototype.unscopeNode;
+ScopingShim.prototype['scopeForNode'] = ScopingShim.prototype.scopeForNode;
+ScopingShim.prototype['currentScopeForNode'] = ScopingShim.prototype.currentScopeForNode;
+/* eslint-enable no-self-assign */
+Object.defineProperties(ScopingShim.prototype, {
+ 'nativeShadow': {
+ get() {
+ return nativeShadow;
+ }
+ },
+ 'nativeCss': {
+ get() {
+ return nativeCssVariables;
+ }
+ }
+});
diff --git a/catapult/third_party/polymer/components/shadycss/src/style-cache.js b/catapult/third_party/polymer/components/shadycss/src/style-cache.js
new file mode 100644
index 00000000..0006a0bc
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/style-cache.js
@@ -0,0 +1,52 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+'use strict';
+
+export default class StyleCache {
+ constructor(typeMax = 100) {
+ // map element name -> [{properties, styleElement, scopeSelector}]
+ this.cache = {};
+ /** @type {number} */
+ this.typeMax = typeMax;
+ }
+
+ _validate(cacheEntry, properties, ownPropertyNames) {
+ for (let idx = 0; idx < ownPropertyNames.length; idx++) {
+ let pn = ownPropertyNames[idx];
+ if (cacheEntry.properties[pn] !== properties[pn]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ store(tagname, properties, styleElement, scopeSelector) {
+ let list = this.cache[tagname] || [];
+ list.push({properties, styleElement, scopeSelector});
+ if (list.length > this.typeMax) {
+ list.shift();
+ }
+ this.cache[tagname] = list;
+ }
+
+ fetch(tagname, properties, ownPropertyNames) {
+ let list = this.cache[tagname];
+ if (!list) {
+ return;
+ }
+ // reverse list for most-recent lookups
+ for (let idx = list.length - 1; idx >= 0; idx--) {
+ let entry = list[idx];
+ if (this._validate(entry, properties, ownPropertyNames)) {
+ return entry;
+ }
+ }
+ }
+}
diff --git a/catapult/third_party/polymer/components/shadycss/src/style-info.js b/catapult/third_party/polymer/components/shadycss/src/style-info.js
new file mode 100644
index 00000000..fd5b14b2
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/style-info.js
@@ -0,0 +1,75 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
+
+/** @const {string} */
+const infoKey = '__styleInfo';
+
+export default class StyleInfo {
+ /**
+ * @param {Element} node
+ * @return {StyleInfo}
+ */
+ static get(node) {
+ if (node) {
+ return node[infoKey];
+ } else {
+ return null;
+ }
+ }
+ /**
+ * @param {!Element} node
+ * @param {StyleInfo} styleInfo
+ * @return {StyleInfo}
+ */
+ static set(node, styleInfo) {
+ node[infoKey] = styleInfo;
+ return styleInfo;
+ }
+ /**
+ * @param {StyleNode} ast
+ * @param {Node=} placeholder
+ * @param {Array<string>=} ownStylePropertyNames
+ * @param {string=} elementName
+ * @param {string=} typeExtension
+ * @param {string=} cssBuild
+ */
+ constructor(ast, placeholder, ownStylePropertyNames, elementName, typeExtension, cssBuild) {
+ /** @type {StyleNode} */
+ this.styleRules = ast || null;
+ /** @type {Node} */
+ this.placeholder = placeholder || null;
+ /** @type {!Array<string>} */
+ this.ownStylePropertyNames = ownStylePropertyNames || [];
+ /** @type {Array<Object>} */
+ this.overrideStyleProperties = null;
+ /** @type {string} */
+ this.elementName = elementName || '';
+ /** @type {string} */
+ this.cssBuild = cssBuild || '';
+ /** @type {string} */
+ this.typeExtension = typeExtension || '';
+ /** @type {Object<string, string>} */
+ this.styleProperties = null;
+ /** @type {?string} */
+ this.scopeSelector = null;
+ /** @type {HTMLStyleElement} */
+ this.customStyle = null;
+ }
+ _getStyleRules() {
+ return this.styleRules;
+ }
+}
+
+/* eslint-disable-next-line no-self-assign */
+StyleInfo.prototype['_getStyleRules'] = StyleInfo.prototype._getStyleRules;
diff --git a/catapult/third_party/polymer/components/shadycss/src/style-placeholder.js b/catapult/third_party/polymer/components/shadycss/src/style-placeholder.js
new file mode 100644
index 00000000..72be92dc
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/style-placeholder.js
@@ -0,0 +1,55 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {applyStylePlaceHolder} from './style-util.js';
+import {nativeShadow} from './style-settings.js';
+
+/** @type {!Object<string, !Node>} */
+const placeholderMap = {};
+
+/**
+ * @param {string} elementName
+ * @return {Node}
+ */
+export function getStylePlaceholder(elementName) {
+ return placeholderMap[elementName] || null;
+}
+
+/**
+ * @param {string} elementName
+ */
+export function ensureStylePlaceholder(elementName) {
+ if (!placeholderMap[elementName]) {
+ placeholderMap[elementName] = applyStylePlaceHolder(elementName);
+ }
+}
+
+/**
+ * @const {CustomElementRegistry}
+ */
+const ce = window['customElements'];
+if (ce && !nativeShadow) {
+ /**
+ * @const {function(this:CustomElementRegistry, string,function(new:HTMLElement),{extends: string}=)}
+ */
+ const origDefine = ce['define'];
+ /**
+ * @param {string} name
+ * @param {function(new:HTMLElement)} clazz
+ * @param {{extends: string}=} options
+ */
+ const wrappedDefine = (name, clazz, options) => {
+ ensureStylePlaceholder(name);
+ origDefine.call(/** @type {!CustomElementRegistry} */(ce), name, clazz, options);
+ };
+ ce['define'] = wrappedDefine;
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/src/style-properties.js b/catapult/third_party/polymer/components/shadycss/src/style-properties.js
new file mode 100644
index 00000000..ecaa3891
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/style-properties.js
@@ -0,0 +1,608 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {removeCustomPropAssignment, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
+import {nativeShadow} from './style-settings.js';
+import StyleTransformer from './style-transformer.js';
+import * as StyleUtil from './style-util.js';
+import * as RX from './common-regex.js';
+import StyleInfo from './style-info.js';
+
+// TODO: dedupe with shady
+/**
+ * @param {string} selector
+ * @return {boolean}
+ * @this {Element}
+ */
+const matchesSelector = function(selector) {
+ const method = this.matches || this.matchesSelector ||
+ this.mozMatchesSelector || this.msMatchesSelector ||
+ this.oMatchesSelector || this.webkitMatchesSelector;
+ return method && method.call(this, selector);
+};
+
+const IS_IE = navigator.userAgent.match('Trident');
+
+const XSCOPE_NAME = 'x-scope';
+
+class StyleProperties {
+ get XSCOPE_NAME() {
+ return XSCOPE_NAME;
+ }
+/**
+ * decorates styles with rule info and returns an array of used style property names
+ *
+ * @param {StyleNode} rules
+ * @return {Array<string>}
+ */
+ decorateStyles(rules) {
+ let self = this, props = {}, keyframes = [], ruleIndex = 0;
+ StyleUtil.forEachRule(rules, function(rule) {
+ self.decorateRule(rule);
+ // mark in-order position of ast rule in styles block, used for cache key
+ rule.index = ruleIndex++;
+ self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
+ }, function onKeyframesRule(rule) {
+ keyframes.push(rule);
+ });
+ // Cache all found keyframes rules for later reference:
+ rules._keyframes = keyframes;
+ // return this list of property names *consumes* in these styles.
+ let names = [];
+ for (let i in props) {
+ names.push(i);
+ }
+ return names;
+ }
+
+ // decorate a single rule with property info
+ decorateRule(rule) {
+ if (rule.propertyInfo) {
+ return rule.propertyInfo;
+ }
+ let info = {}, properties = {};
+ let hasProperties = this.collectProperties(rule, properties);
+ if (hasProperties) {
+ info.properties = properties;
+ // TODO(sorvell): workaround parser seeing mixins as additional rules
+ rule['rules'] = null;
+ }
+ info.cssText = this.collectCssText(rule);
+ rule.propertyInfo = info;
+ return info;
+ }
+
+ // collects the custom properties from a rule's cssText
+ collectProperties(rule, properties) {
+ let info = rule.propertyInfo;
+ if (info) {
+ if (info.properties) {
+ Object.assign(properties, info.properties);
+ return true;
+ }
+ } else {
+ let m, rx = RX.VAR_ASSIGN;
+ let cssText = rule['parsedCssText'];
+ let value;
+ let any;
+ while ((m = rx.exec(cssText))) {
+ // note: group 2 is var, 3 is mixin
+ value = (m[2] || m[3]).trim();
+ // value of 'inherit' or 'unset' is equivalent to not setting the property here
+ if (value !== 'inherit' || value !== 'unset') {
+ properties[m[1].trim()] = value;
+ }
+ any = true;
+ }
+ return any;
+ }
+
+ }
+
+ // returns cssText of properties that consume variables/mixins
+ collectCssText(rule) {
+ return this.collectConsumingCssText(rule['parsedCssText']);
+ }
+
+ // NOTE: we support consumption inside mixin assignment
+ // but not production, so strip out {...}
+ collectConsumingCssText(cssText) {
+ return cssText.replace(RX.BRACKETED, '')
+ .replace(RX.VAR_ASSIGN, '');
+ }
+
+ collectPropertiesInCssText(cssText, props) {
+ let m;
+ while ((m = RX.VAR_CONSUMED.exec(cssText))) {
+ let name = m[1];
+ // This regex catches all variable names, and following non-whitespace char
+ // If next char is not ':', then variable is a consumer
+ if (m[2] !== ':') {
+ props[name] = true;
+ }
+ }
+ }
+
+ // turns custom properties into realized values.
+ reify(props) {
+ // big perf optimization here: reify only *own* properties
+ // since this object has __proto__ of the element's scope properties
+ let names = Object.getOwnPropertyNames(props);
+ for (let i=0, n; i < names.length; i++) {
+ n = names[i];
+ props[n] = this.valueForProperty(props[n], props);
+ }
+ }
+
+ // given a property value, returns the reified value
+ // a property value may be:
+ // (1) a literal value like: red or 5px;
+ // (2) a variable value like: var(--a), var(--a, red), or var(--a, --b) or
+ // var(--a, var(--b));
+ // (3) a literal mixin value like { properties }. Each of these properties
+ // can have values that are: (a) literal, (b) variables, (c) @apply mixins.
+ valueForProperty(property, props) {
+ // case (1) default
+ // case (3) defines a mixin and we have to reify the internals
+ if (property) {
+ if (property.indexOf(';') >=0) {
+ property = this.valueForProperties(property, props);
+ } else {
+ // case (2) variable
+ let self = this;
+ let fn = function(prefix, value, fallback, suffix) {
+ if (!value) {
+ return prefix + suffix;
+ }
+ let propertyValue = self.valueForProperty(props[value], props);
+ // if value is "initial", then the variable should be treated as unset
+ if (!propertyValue || propertyValue === 'initial') {
+ // fallback may be --a or var(--a) or literal
+ propertyValue = self.valueForProperty(props[fallback] || fallback, props) ||
+ fallback;
+ } else if (propertyValue === 'apply-shim-inherit') {
+ // CSS build will replace `inherit` with `apply-shim-inherit`
+ // for use with native css variables.
+ // Since we have full control, we can use `inherit` directly.
+ propertyValue = 'inherit';
+ }
+ return prefix + (propertyValue || '') + suffix;
+ };
+ property = StyleUtil.processVariableAndFallback(property, fn);
+ }
+ }
+ return property && property.trim() || '';
+ }
+
+ // note: we do not yet support mixin within mixin
+ valueForProperties(property, props) {
+ let parts = property.split(';');
+ for (let i=0, p, m; i<parts.length; i++) {
+ if ((p = parts[i])) {
+ RX.MIXIN_MATCH.lastIndex = 0;
+ m = RX.MIXIN_MATCH.exec(p);
+ if (m) {
+ p = this.valueForProperty(props[m[1]], props);
+ } else {
+ let colon = p.indexOf(':');
+ if (colon !== -1) {
+ let pp = p.substring(colon);
+ pp = pp.trim();
+ pp = this.valueForProperty(pp, props) || pp;
+ p = p.substring(0, colon) + pp;
+ }
+ }
+ parts[i] = (p && p.lastIndexOf(';') === p.length - 1) ?
+ // strip trailing ;
+ p.slice(0, -1) :
+ p || '';
+ }
+ }
+ return parts.join(';');
+ }
+
+ applyProperties(rule, props) {
+ let output = '';
+ // dynamically added sheets may not be decorated so ensure they are.
+ if (!rule.propertyInfo) {
+ this.decorateRule(rule);
+ }
+ if (rule.propertyInfo.cssText) {
+ output = this.valueForProperties(rule.propertyInfo.cssText, props);
+ }
+ rule['cssText'] = output;
+ }
+
+ // Apply keyframe transformations to the cssText of a given rule. The
+ // keyframeTransforms object is a map of keyframe names to transformer
+ // functions which take in cssText and spit out transformed cssText.
+ applyKeyframeTransforms(rule, keyframeTransforms) {
+ let input = rule['cssText'];
+ let output = rule['cssText'];
+ if (rule.hasAnimations == null) {
+ // Cache whether or not the rule has any animations to begin with:
+ rule.hasAnimations = RX.ANIMATION_MATCH.test(input);
+ }
+ // If there are no animations referenced, we can skip transforms:
+ if (rule.hasAnimations) {
+ let transform;
+ // If we haven't transformed this rule before, we iterate over all
+ // transforms:
+ if (rule.keyframeNamesToTransform == null) {
+ rule.keyframeNamesToTransform = [];
+ for (let keyframe in keyframeTransforms) {
+ transform = keyframeTransforms[keyframe];
+ output = transform(input);
+ // If the transform actually changed the CSS text, we cache the
+ // transform name for future use:
+ if (input !== output) {
+ input = output;
+ rule.keyframeNamesToTransform.push(keyframe);
+ }
+ }
+ } else {
+ // If we already have a list of keyframe names that apply to this
+ // rule, we apply only those keyframe name transforms:
+ for (let i = 0; i < rule.keyframeNamesToTransform.length; ++i) {
+ transform = keyframeTransforms[rule.keyframeNamesToTransform[i]];
+ input = transform(input);
+ }
+ output = input;
+ }
+ }
+ rule['cssText'] = output;
+ }
+
+ // Test if the rules in these styles matches the given `element` and if so,
+ // collect any custom properties into `props`.
+ /**
+ * @param {StyleNode} rules
+ * @param {Element} element
+ */
+ propertyDataFromStyles(rules, element) {
+ let props = {};
+ // generates a unique key for these matches
+ let o = [];
+ // note: active rules excludes non-matching @media rules
+ StyleUtil.forEachRule(rules, (rule) => {
+ // TODO(sorvell): we could trim the set of rules at declaration
+ // time to only include ones that have properties
+ if (!rule.propertyInfo) {
+ this.decorateRule(rule);
+ }
+ // match element against transformedSelector: selector may contain
+ // unwanted uniquification and parsedSelector does not directly match
+ // for :host selectors.
+ let selectorToMatch = rule.transformedSelector || rule['parsedSelector'];
+ if (element && rule.propertyInfo.properties && selectorToMatch) {
+ if (matchesSelector.call(element, selectorToMatch)) {
+ this.collectProperties(rule, props);
+ // produce numeric key for these matches for lookup
+ addToBitMask(rule.index, o);
+ }
+ }
+ }, null, true);
+ return {properties: props, key: o};
+ }
+
+ /**
+ * @param {Element} scope
+ * @param {StyleNode} rule
+ * @param {string} cssBuild
+ * @param {function(Object)} callback
+ */
+ whenHostOrRootRule(scope, rule, cssBuild, callback) {
+ if (!rule.propertyInfo) {
+ this.decorateRule(rule);
+ }
+ if (!rule.propertyInfo.properties) {
+ return;
+ }
+ let {is, typeExtension} = StyleUtil.getIsExtends(scope);
+ let hostScope = is ?
+ StyleTransformer._calcHostScope(is, typeExtension) :
+ 'html';
+ let parsedSelector = rule['parsedSelector'];
+ let isRoot = (parsedSelector === ':host > *' || parsedSelector === 'html');
+ let isHost = parsedSelector.indexOf(':host') === 0 && !isRoot;
+ // build info is either in scope (when scope is an element) or in the style
+ // when scope is the default scope; note: this allows default scope to have
+ // mixed mode built and unbuilt styles.
+ if (cssBuild === 'shady') {
+ // :root -> x-foo > *.x-foo for elements and html for custom-style
+ isRoot = parsedSelector === (hostScope + ' > *.' + hostScope) || parsedSelector.indexOf('html') !== -1;
+ // :host -> x-foo for elements, but sub-rules have .x-foo in them
+ isHost = !isRoot && parsedSelector.indexOf(hostScope) === 0;
+ }
+ if (!isRoot && !isHost) {
+ return;
+ }
+ let selectorToMatch = hostScope;
+ if (isHost) {
+ // need to transform :host because `:host` does not work with `matches`
+ if (!rule.transformedSelector) {
+ // transform :host into a matchable selector
+ rule.transformedSelector =
+ StyleTransformer._transformRuleCss(
+ rule,
+ StyleTransformer._transformComplexSelector,
+ StyleTransformer._calcElementScope(is),
+ hostScope
+ );
+ }
+ selectorToMatch = rule.transformedSelector || hostScope;
+ }
+ callback({
+ selector: selectorToMatch,
+ isHost: isHost,
+ isRoot: isRoot
+ });
+ }
+/**
+ * @param {Element} scope
+ * @param {StyleNode} rules
+ * @param {string} cssBuild
+ * @return {Object}
+ */
+ hostAndRootPropertiesForScope(scope, rules, cssBuild) {
+ let hostProps = {}, rootProps = {};
+ // note: active rules excludes non-matching @media rules
+ StyleUtil.forEachRule(rules, (rule) => {
+ // if scope is StyleDefaults, use _element for matchesSelector
+ this.whenHostOrRootRule(scope, rule, cssBuild, (info) => {
+ let element = scope._element || scope;
+ if (matchesSelector.call(element, info.selector)) {
+ if (info.isHost) {
+ this.collectProperties(rule, hostProps);
+ } else {
+ this.collectProperties(rule, rootProps);
+ }
+ }
+ });
+ }, null, true);
+ return {rootProps: rootProps, hostProps: hostProps};
+ }
+
+ /**
+ * @param {Element} element
+ * @param {Object} properties
+ * @param {string} scopeSelector
+ */
+ transformStyles(element, properties, scopeSelector) {
+ let self = this;
+ let {is, typeExtension} = StyleUtil.getIsExtends(element);
+ let hostSelector = StyleTransformer
+ ._calcHostScope(is, typeExtension);
+ let rxHostSelector = element.extends ?
+ '\\' + hostSelector.slice(0, -1) + '\\]' :
+ hostSelector;
+ let hostRx = new RegExp(RX.HOST_PREFIX + rxHostSelector +
+ RX.HOST_SUFFIX);
+ let {styleRules: rules, cssBuild} = StyleInfo.get(element);
+ let keyframeTransforms =
+ this._elementKeyframeTransforms(element, rules, scopeSelector);
+ return StyleTransformer.elementStyles(element, rules, function(rule) {
+ self.applyProperties(rule, properties);
+ if (!nativeShadow &&
+ !StyleUtil.isKeyframesSelector(rule) &&
+ rule['cssText']) {
+ // NOTE: keyframe transforms only scope munge animation names, so it
+ // is not necessary to apply them in ShadowDOM.
+ self.applyKeyframeTransforms(rule, keyframeTransforms);
+ self._scopeSelector(rule, hostRx, hostSelector, scopeSelector);
+ }
+ }, cssBuild);
+ }
+
+ /**
+ * @param {Element} element
+ * @param {StyleNode} rules
+ * @param {string} scopeSelector
+ * @return {Object}
+ */
+ _elementKeyframeTransforms(element, rules, scopeSelector) {
+ let keyframesRules = rules._keyframes;
+ let keyframeTransforms = {};
+ if (!nativeShadow && keyframesRules) {
+ // For non-ShadowDOM, we transform all known keyframes rules in
+ // advance for the current scope. This allows us to catch keyframes
+ // rules that appear anywhere in the stylesheet:
+ for (let i = 0, keyframesRule = keyframesRules[i];
+ i < keyframesRules.length;
+ keyframesRule = keyframesRules[++i]) {
+ this._scopeKeyframes(keyframesRule, scopeSelector);
+ keyframeTransforms[keyframesRule['keyframesName']] =
+ this._keyframesRuleTransformer(keyframesRule);
+ }
+ }
+ return keyframeTransforms;
+ }
+
+ // Generate a factory for transforming a chunk of CSS text to handle a
+ // particular scoped keyframes rule.
+ /**
+ * @param {StyleNode} keyframesRule
+ * @return {function(string):string}
+ */
+ _keyframesRuleTransformer(keyframesRule) {
+ return function(cssText) {
+ return cssText.replace(
+ keyframesRule.keyframesNameRx,
+ keyframesRule.transformedKeyframesName);
+ };
+ }
+
+/**
+ * Transforms `@keyframes` names to be unique for the current host.
+ * Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0
+ *
+ * @param {StyleNode} rule
+ * @param {string} scopeId
+ */
+ _scopeKeyframes(rule, scopeId) {
+ // Animation names are of the form [\w-], so ensure that the name regex does not partially apply
+ // to similarly named keyframe names by checking for a word boundary at the beginning and
+ // a non-word boundary or `-` at the end.
+ rule.keyframesNameRx = new RegExp(`\\b${rule['keyframesName']}(?!\\B|-)`, 'g');
+ rule.transformedKeyframesName = rule['keyframesName'] + '-' + scopeId;
+ rule.transformedSelector = rule.transformedSelector || rule['selector'];
+ rule['selector'] = rule.transformedSelector.replace(
+ rule['keyframesName'], rule.transformedKeyframesName);
+ }
+
+ // Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes):
+ // non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo
+ // host selector: x-foo.wide -> .x-foo-42.wide
+ // note: we use only the scope class (.x-foo-42) and not the hostSelector
+ // (x-foo) to scope :host rules; this helps make property host rules
+ // have low specificity. They are overrideable by class selectors but,
+ // unfortunately, not by type selectors (e.g. overriding via
+ // `.special` is ok, but not by `x-foo`).
+ /**
+ * @param {StyleNode} rule
+ * @param {RegExp} hostRx
+ * @param {string} hostSelector
+ * @param {string} scopeId
+ */
+ _scopeSelector(rule, hostRx, hostSelector, scopeId) {
+ rule.transformedSelector = rule.transformedSelector || rule['selector'];
+ let selector = rule.transformedSelector;
+ let scope = '.' + scopeId;
+ let parts = StyleUtil.splitSelectorList(selector);
+ for (let i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
+ parts[i] = p.match(hostRx) ?
+ p.replace(hostSelector, scope) :
+ scope + ' ' + p;
+ }
+ rule['selector'] = parts.join(',');
+ }
+
+ /**
+ * @param {Element} element
+ * @param {string} selector
+ * @param {string} old
+ */
+ applyElementScopeSelector(element, selector, old) {
+ let c = element.getAttribute('class') || '';
+ let v = c;
+ if (old) {
+ v = c.replace(
+ new RegExp('\\s*' + XSCOPE_NAME + '\\s*' + old + '\\s*', 'g'), ' ');
+ }
+ v += (v ? ' ' : '') + XSCOPE_NAME + ' ' + selector;
+ if (c !== v) {
+ StyleUtil.setElementClassRaw(element, v);
+ }
+ }
+
+ /**
+ * @param {HTMLElement} element
+ * @param {Object} properties
+ * @param {string} selector
+ * @param {HTMLStyleElement} style
+ * @return {HTMLStyleElement}
+ */
+ applyElementStyle(element, properties, selector, style) {
+ // calculate cssText to apply
+ let cssText = style ? style.textContent || '' :
+ this.transformStyles(element, properties, selector);
+ // if shady and we have a cached style that is not style, decrement
+ let styleInfo = StyleInfo.get(element);
+ let s = styleInfo.customStyle;
+ if (s && !nativeShadow && (s !== style)) {
+ s['_useCount']--;
+ if (s['_useCount'] <= 0 && s.parentNode) {
+ s.parentNode.removeChild(s);
+ }
+ }
+ // apply styling always under native or if we generated style
+ // or the cached style is not in document(!)
+ if (nativeShadow) {
+ // update existing style only under native
+ if (styleInfo.customStyle) {
+ styleInfo.customStyle.textContent = cssText;
+ style = styleInfo.customStyle;
+ // otherwise, if we have css to apply, do so
+ } else if (cssText) {
+ // apply css after the scope style of the element to help with
+ // style precedence rules.
+ style = StyleUtil.applyCss(cssText, selector, element.shadowRoot,
+ styleInfo.placeholder);
+ }
+ } else {
+ // shady and no cache hit
+ if (!style) {
+ // apply css after the scope style of the element to help with
+ // style precedence rules.
+ if (cssText) {
+ style = StyleUtil.applyCss(cssText, selector, null,
+ styleInfo.placeholder);
+ }
+ // shady and cache hit but not in document
+ } else if (!style.parentNode) {
+ if (IS_IE && cssText.indexOf('@media') > -1) {
+ // @media rules may be stale in IE 10 and 11
+ // refresh the text content of the style to revalidate them.
+ style.textContent = cssText;
+ }
+ StyleUtil.applyStyle(style, null, styleInfo.placeholder);
+ }
+ }
+ // ensure this style is our custom style and increment its use count.
+ if (style) {
+ style['_useCount'] = style['_useCount'] || 0;
+ // increment use count if we changed styles
+ if (styleInfo.customStyle != style) {
+ style['_useCount']++;
+ }
+ styleInfo.customStyle = style;
+ }
+ return style;
+ }
+
+ /**
+ * @param {Element} style
+ * @param {Object} properties
+ */
+ applyCustomStyle(style, properties) {
+ let rules = StyleUtil.rulesForStyle(/** @type {HTMLStyleElement} */(style));
+ let self = this;
+ style.textContent = StyleUtil.toCssText(rules, function(/** StyleNode */rule) {
+ let css = rule['cssText'] = rule['parsedCssText'];
+ if (rule.propertyInfo && rule.propertyInfo.cssText) {
+ // remove property assignments
+ // so next function isn't confused
+ // NOTE: we have 3 categories of css:
+ // (1) normal properties,
+ // (2) custom property assignments (--foo: red;),
+ // (3) custom property usage: border: var(--foo); @apply(--foo);
+ // In elements, 1 and 3 are separated for efficiency; here they
+ // are not and this makes this case unique.
+ css = removeCustomPropAssignment(/** @type {string} */(css));
+ // replace with reified properties, scenario is same as mixin
+ rule['cssText'] = self.valueForProperties(css, properties);
+ }
+ });
+ }
+}
+
+/**
+ * @param {number} n
+ * @param {Array<number>} bits
+ */
+function addToBitMask(n, bits) {
+ let o = parseInt(n / 32, 10);
+ let v = 1 << (n % 32);
+ bits[o] = (bits[o] || 0) | v;
+}
+
+export default new StyleProperties(); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/src/style-settings.js b/catapult/third_party/polymer/components/shadycss/src/style-settings.js
new file mode 100644
index 00000000..55757067
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/style-settings.js
@@ -0,0 +1,53 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+export const nativeShadow = !(window['ShadyDOM'] && window['ShadyDOM']['inUse']);
+let nativeCssVariables_;
+
+/**
+ * @param {(ShadyCSSOptions | ShadyCSSInterface)=} settings
+ */
+function calcCssVariables(settings) {
+ if (settings && settings['shimcssproperties']) {
+ nativeCssVariables_ = false;
+ } else {
+ // chrome 49 has semi-working css vars, check if box-shadow works
+ // safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782
+ // However, shim css custom properties are only supported with ShadyDOM enabled,
+ // so fall back on native if we do not detect ShadyDOM
+ // Edge 15: custom properties used in ::before and ::after will also be used in the parent element
+ // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12414257/
+ nativeCssVariables_ = nativeShadow || Boolean(!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/) &&
+ window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)'));
+ }
+}
+
+/** @type {string | undefined} */
+export let cssBuild;
+if (window.ShadyCSS && window.ShadyCSS.cssBuild !== undefined) {
+ cssBuild = window.ShadyCSS.cssBuild;
+}
+
+if (window.ShadyCSS && window.ShadyCSS.nativeCss !== undefined) {
+ nativeCssVariables_ = window.ShadyCSS.nativeCss;
+} else if (window.ShadyCSS) {
+ calcCssVariables(window.ShadyCSS);
+ // reset window variable to let ShadyCSS API take its place
+ window.ShadyCSS = undefined;
+} else {
+ calcCssVariables(window['WebComponents'] && window['WebComponents']['flags']);
+}
+
+// Hack for type error under new type inference which doesn't like that
+// nativeCssVariables is updated in a function and assigns the type
+// `function(): ?` instead of `boolean`.
+export const nativeCssVariables = /** @type {boolean} */(nativeCssVariables_); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/src/style-transformer.js b/catapult/third_party/polymer/components/shadycss/src/style-transformer.js
new file mode 100644
index 00000000..a4623a6b
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/style-transformer.js
@@ -0,0 +1,487 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
+import * as StyleUtil from './style-util.js';
+import {nativeShadow} from './style-settings.js';
+
+/* Transforms ShadowDOM styling into ShadyDOM styling
+
+* scoping:
+
+ * elements in scope get scoping selector class="x-foo-scope"
+ * selectors re-written as follows:
+
+ div button -> div.x-foo-scope button.x-foo-scope
+
+* :host -> scopeName
+
+* :host(...) -> scopeName...
+
+* ::slotted(...) -> scopeName > ...
+
+* ...:dir(ltr|rtl) -> [dir="ltr|rtl"] ..., ...[dir="ltr|rtl"]
+
+* :host(:dir[rtl]) -> scopeName:dir(rtl) -> [dir="rtl"] scopeName, scopeName[dir="rtl"]
+
+*/
+const SCOPE_NAME = 'style-scope';
+
+class StyleTransformer {
+ get SCOPE_NAME() {
+ return SCOPE_NAME;
+ }
+ /**
+ * Given a node and scope name, add a scoping class to each node
+ * in the tree. This facilitates transforming css into scoped rules.
+ * @param {!Node} node
+ * @param {string} scope
+ * @param {boolean=} shouldRemoveScope
+ * @deprecated
+ */
+ dom(node, scope, shouldRemoveScope) {
+ const fn = (node) => {
+ this.element(node, scope || '', shouldRemoveScope);
+ };
+ this._transformDom(node, fn);
+ }
+
+ /**
+ * Given a node and scope name, add a scoping class to each node in the tree.
+ * @param {!Node} node
+ * @param {string} scope
+ */
+ domAddScope(node, scope) {
+ const fn = (node) => {
+ this.element(node, scope || '');
+ };
+ this._transformDom(node, fn);
+ }
+
+ /**
+ * @param {!Node} startNode
+ * @param {!function(!Node)} transformer
+ */
+ _transformDom(startNode, transformer) {
+ if (startNode.nodeType === Node.ELEMENT_NODE) {
+ transformer(startNode)
+ }
+ let c$;
+ if (startNode.localName === 'template') {
+ const template = /** @type {!HTMLTemplateElement} */ (startNode);
+ // In case the template is in svg context, fall back to the node
+ // since it won't be an HTMLTemplateElement with a .content property
+ c$ = (template.content || template._content || template).childNodes;
+ } else {
+ c$ = /** @type {!ParentNode} */ (startNode).children ||
+ startNode.childNodes;
+ }
+ if (c$) {
+ for (let i = 0; i < c$.length; i++) {
+ this._transformDom(c$[i], transformer);
+ }
+ }
+ }
+
+ /**
+ * @param {?} element
+ * @param {?} scope
+ * @param {?=} shouldRemoveScope
+ */
+ element(element, scope, shouldRemoveScope) {
+ // note: if using classes, we add both the general 'style-scope' class
+ // as well as the specific scope. This enables easy filtering of all
+ // `style-scope` elements
+ if (scope) {
+ // note: svg on IE does not have classList so fallback to class
+ if (element.classList) {
+ if (shouldRemoveScope) {
+ element.classList.remove(SCOPE_NAME);
+ element.classList.remove(scope);
+ } else {
+ element.classList.add(SCOPE_NAME);
+ element.classList.add(scope);
+ }
+ } else if (element.getAttribute) {
+ let c = element.getAttribute(CLASS);
+ if (shouldRemoveScope) {
+ if (c) {
+ let newValue = c.replace(SCOPE_NAME, '').replace(scope, '');
+ StyleUtil.setElementClassRaw(element, newValue);
+ }
+ } else {
+ let newValue = (c ? c + ' ' : '') + SCOPE_NAME + ' ' + scope;
+ StyleUtil.setElementClassRaw(element, newValue);
+ }
+ }
+ }
+ }
+
+ /**
+ * Given a node, replace the scoping class to each subnode in the tree.
+ * @param {!Node} node
+ * @param {string} oldScope
+ * @param {string} newScope
+ */
+ domReplaceScope(node, oldScope, newScope) {
+ const fn = (node) => {
+ this.element(node, oldScope, true);
+ this.element(node, newScope);
+ };
+ this._transformDom(node, fn);
+ }
+ /**
+ * Given a node, remove the scoping class to each subnode in the tree.
+ * @param {!Node} node
+ * @param {string} oldScope
+ */
+ domRemoveScope(node, oldScope) {
+ const fn = (node) => {
+ this.element(node, oldScope || '', true);
+ };
+ this._transformDom(node, fn);
+ }
+
+ /**
+ * @param {?} element
+ * @param {?} styleRules
+ * @param {?=} callback
+ * @param {string=} cssBuild
+ * @param {string=} cssText
+ * @return {string}
+ */
+ elementStyles(element, styleRules, callback, cssBuild = '', cssText = '') {
+ // no need to shim selectors if settings.useNativeShadow, also
+ // a shady css build will already have transformed selectors
+ // NOTE: This method may be called as part of static or property shimming.
+ // When there is a targeted build it will not be called for static shimming,
+ // but when the property shim is used it is called and should opt out of
+ // static shimming work when a proper build exists.
+ if (cssText === '') {
+ if (nativeShadow || cssBuild === 'shady') {
+ cssText = StyleUtil.toCssText(styleRules, callback);
+ } else {
+ let {is, typeExtension} = StyleUtil.getIsExtends(element);
+ cssText = this.css(styleRules, is, typeExtension, callback) + '\n\n';
+ }
+ }
+ return cssText.trim();
+ }
+
+ // Given a string of cssText and a scoping string (scope), returns
+ // a string of scoped css where each selector is transformed to include
+ // a class created from the scope. ShadowDOM selectors are also transformed
+ // (e.g. :host) to use the scoping selector.
+ css(rules, scope, ext, callback) {
+ let hostScope = this._calcHostScope(scope, ext);
+ scope = this._calcElementScope(scope);
+ let self = this;
+ return StyleUtil.toCssText(rules, function(/** StyleNode */rule) {
+ if (!rule.isScoped) {
+ self.rule(rule, scope, hostScope);
+ rule.isScoped = true;
+ }
+ if (callback) {
+ callback(rule, scope, hostScope);
+ }
+ });
+ }
+
+ _calcElementScope(scope) {
+ if (scope) {
+ return CSS_CLASS_PREFIX + scope;
+ } else {
+ return '';
+ }
+ }
+
+ _calcHostScope(scope, ext) {
+ return ext ? `[is=${scope}]` : scope;
+ }
+
+ rule(rule, scope, hostScope) {
+ this._transformRule(rule, this._transformComplexSelector,
+ scope, hostScope);
+ }
+
+ /**
+ * transforms a css rule to a scoped rule.
+ *
+ * @param {StyleNode} rule
+ * @param {Function} transformer
+ * @param {string=} scope
+ * @param {string=} hostScope
+ */
+ _transformRule(rule, transformer, scope, hostScope) {
+ // NOTE: save transformedSelector for subsequent matching of elements
+ // against selectors (e.g. when calculating style properties)
+ rule['selector'] = rule.transformedSelector =
+ this._transformRuleCss(rule, transformer, scope, hostScope);
+ }
+
+ /**
+ * @param {StyleNode} rule
+ * @param {Function} transformer
+ * @param {string=} scope
+ * @param {string=} hostScope
+ */
+ _transformRuleCss(rule, transformer, scope, hostScope) {
+ let p$ = StyleUtil.splitSelectorList(rule['selector']);
+ // we want to skip transformation of rules that appear in keyframes,
+ // because they are keyframe selectors, not element selectors.
+ if (!StyleUtil.isKeyframesSelector(rule)) {
+ for (let i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {
+ p$[i] = transformer.call(this, p, scope, hostScope);
+ }
+ }
+ return p$.filter((part) => Boolean(part)).join(COMPLEX_SELECTOR_SEP);
+ }
+
+ /**
+ * @param {string} selector
+ * @return {string}
+ */
+ _twiddleNthPlus(selector) {
+ return selector.replace(NTH, (m, type, inside) => {
+ if (inside.indexOf('+') > -1) {
+ inside = inside.replace(/\+/g, '___');
+ } else if (inside.indexOf('___') > -1) {
+ inside = inside.replace(/___/g, '+');
+ }
+ return `:${type}(${inside})`;
+ });
+ }
+
+ /**
+ * Preserve `:matches()` selectors by replacing them with MATCHES_REPLACMENT
+ * and returning an array of `:matches()` selectors.
+ * Use `_replacesMatchesPseudo` to replace the `:matches()` parts
+ *
+ * @param {string} selector
+ * @return {{selector: string, matches: !Array<string>}}
+ */
+ _preserveMatchesPseudo(selector) {
+ /** @type {!Array<string>} */
+ const matches = [];
+ let match;
+ while ((match = selector.match(MATCHES))) {
+ const start = match.index;
+ const end = StyleUtil.findMatchingParen(selector, start);
+ if (end === -1) {
+ throw new Error(`${match.input} selector missing ')'`)
+ }
+ const part = selector.slice(start, end + 1);
+ selector = selector.replace(part, MATCHES_REPLACEMENT);
+ matches.push(part);
+ }
+ return {selector, matches};
+ }
+
+ /**
+ * Replace MATCHES_REPLACMENT character with the given set of `:matches()`
+ * selectors.
+ *
+ * @param {string} selector
+ * @param {!Array<string>} matches
+ * @return {string}
+ */
+ _replaceMatchesPseudo(selector, matches) {
+ const parts = selector.split(MATCHES_REPLACEMENT);
+ return matches.reduce((acc, cur, idx) => acc + cur + parts[idx + 1], parts[0]);
+ }
+
+/**
+ * @param {string} selector
+ * @param {string} scope
+ * @param {string=} hostScope
+ */
+ _transformComplexSelector(selector, scope, hostScope) {
+ let stop = false;
+ selector = selector.trim();
+ // Remove spaces inside of selectors like `:nth-of-type` because it confuses SIMPLE_SELECTOR_SEP
+ let isNth = NTH.test(selector);
+ if (isNth) {
+ selector = selector.replace(NTH, (m, type, inner) => `:${type}(${inner.replace(/\s/g, '')})`)
+ selector = this._twiddleNthPlus(selector);
+ }
+ // Preserve selectors like `:-webkit-any` so that SIMPLE_SELECTOR_SEP does
+ // not get confused by spaces inside the pseudo selector
+ const isMatches = MATCHES.test(selector);
+ /** @type {!Array<string>} */
+ let matches;
+ if (isMatches) {
+ ({selector, matches} = this._preserveMatchesPseudo(selector));
+ }
+ selector = selector.replace(SLOTTED_START, `${HOST} $1`);
+ selector = selector.replace(SIMPLE_SELECTOR_SEP, (m, c, s) => {
+ if (!stop) {
+ let info = this._transformCompoundSelector(s, c, scope, hostScope);
+ stop = stop || info.stop;
+ c = info.combinator;
+ s = info.value;
+ }
+ return c + s;
+ });
+ // replace `:matches()` selectors
+ if (isMatches) {
+ selector = this._replaceMatchesPseudo(selector, matches);
+ }
+ if (isNth) {
+ selector = this._twiddleNthPlus(selector);
+ }
+ return selector;
+ }
+
+ _transformCompoundSelector(selector, combinator, scope, hostScope) {
+ // replace :host with host scoping class
+ let slottedIndex = selector.indexOf(SLOTTED);
+ if (selector.indexOf(HOST) >= 0) {
+ selector = this._transformHostSelector(selector, hostScope);
+ // replace other selectors with scoping class
+ } else if (slottedIndex !== 0) {
+ selector = scope ? this._transformSimpleSelector(selector, scope) :
+ selector;
+ }
+ // mark ::slotted() scope jump to replace with descendant selector + arg
+ // also ignore left-side combinator
+ let slotted = false;
+ if (slottedIndex >= 0) {
+ combinator = '';
+ slotted = true;
+ }
+ // process scope jumping selectors up to the scope jump and then stop
+ let stop;
+ if (slotted) {
+ stop = true;
+ if (slotted) {
+ // .zonk ::slotted(.foo) -> .zonk.scope > .foo
+ selector = selector.replace(SLOTTED_PAREN, (m, paren) => ` > ${paren}`);
+ }
+ }
+ selector = selector.replace(DIR_PAREN, (m, before, dir) =>
+ `[dir="${dir}"] ${before}, ${before}[dir="${dir}"]`);
+ return {value: selector, combinator, stop};
+ }
+
+ _transformSimpleSelector(selector, scope) {
+ const attributes = selector.split(/(\[.+?\])/);
+
+ const output = [];
+ for (let i = 0; i < attributes.length; i++) {
+ // Do not attempt to transform any attribute selector content
+ if ((i % 2) === 1) {
+ output.push(attributes[i]);
+ } else {
+ const part = attributes[i];
+
+ if (!(part === '' && i === attributes.length - 1)) {
+ let p$ = part.split(PSEUDO_PREFIX);
+ p$[0] += scope;
+ output.push(p$.join(PSEUDO_PREFIX));
+ }
+ }
+ }
+
+ return output.join('');
+ }
+
+ // :host(...) -> scopeName...
+ _transformHostSelector(selector, hostScope) {
+ let m = selector.match(HOST_PAREN);
+ let paren = m && m[2].trim() || '';
+ if (paren) {
+ if (!paren[0].match(SIMPLE_SELECTOR_PREFIX)) {
+ // paren starts with a type selector
+ let typeSelector = paren.split(SIMPLE_SELECTOR_PREFIX)[0];
+ // if the type selector is our hostScope then avoid pre-pending it
+ if (typeSelector === hostScope) {
+ return paren;
+ // otherwise, this selector should not match in this scope so
+ // output a bogus selector.
+ } else {
+ return SELECTOR_NO_MATCH;
+ }
+ } else {
+ // make sure to do a replace here to catch selectors like:
+ // `:host(.foo)::before`
+ return selector.replace(HOST_PAREN, function(m, host, paren) {
+ return hostScope + paren;
+ });
+ }
+ // if no paren, do a straight :host replacement.
+ // TODO(sorvell): this should not strictly be necessary but
+ // it's needed to maintain support for `:host[foo]` type selectors
+ // which have been improperly used under Shady DOM. This should be
+ // deprecated.
+ } else {
+ return selector.replace(HOST, hostScope);
+ }
+ }
+
+ /**
+ * @param {StyleNode} rule
+ */
+ documentRule(rule) {
+ // reset selector in case this is redone.
+ rule['selector'] = rule['parsedSelector'];
+ this.normalizeRootSelector(rule);
+ this._transformRule(rule, this._transformDocumentSelector);
+ }
+
+ /**
+ * @param {StyleNode} rule
+ */
+ normalizeRootSelector(rule) {
+ if (rule['selector'] === ROOT) {
+ rule['selector'] = 'html';
+ }
+ }
+
+/**
+ * @param {string} selector
+ */
+ _transformDocumentSelector(selector) {
+ if (selector.match(HOST)) {
+ // remove ':host' type selectors in document rules
+ return '';
+ } else if (selector.match(SLOTTED)) {
+ return this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR)
+ } else {
+ return this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR);
+ }
+ }
+}
+
+const NTH = /:(nth[-\w]+)\(([^)]+)\)/;
+const SCOPE_DOC_SELECTOR = `:not(.${SCOPE_NAME})`;
+const COMPLEX_SELECTOR_SEP = ',';
+const SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g;
+const SIMPLE_SELECTOR_PREFIX = /[[.:#*]/;
+const HOST = ':host';
+const ROOT = ':root';
+const SLOTTED = '::slotted';
+const SLOTTED_START = new RegExp(`^(${SLOTTED})`);
+// NOTE: this supports 1 nested () pair for things like
+// :host(:not([selected]), more general support requires
+// parsing which seems like overkill
+const HOST_PAREN = /(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/;
+// similar to HOST_PAREN
+const SLOTTED_PAREN = /(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/;
+const DIR_PAREN = /(.*):dir\((?:(ltr|rtl))\)/;
+const CSS_CLASS_PREFIX = '.';
+const PSEUDO_PREFIX = ':';
+const CLASS = 'class';
+const SELECTOR_NO_MATCH = 'should_not_match';
+const MATCHES = /:(?:matches|any|-(?:webkit|moz)-any)/;
+const MATCHES_REPLACEMENT = '\u{e000}';
+
+export default new StyleTransformer()
diff --git a/catapult/third_party/polymer/components/shadycss/src/style-util.js b/catapult/third_party/polymer/components/shadycss/src/style-util.js
new file mode 100644
index 00000000..230a3542
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/style-util.js
@@ -0,0 +1,411 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {nativeShadow, nativeCssVariables, cssBuild} from './style-settings.js';
+import {parse, stringify, types, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
+import {MEDIA_MATCH} from './common-regex.js';
+import {processUnscopedStyle, isUnscopedStyle} from './unscoped-style-handler.js';
+
+/**
+ * @param {string|StyleNode} rules
+ * @param {function(StyleNode)=} callback
+ * @return {string}
+ */
+export function toCssText (rules, callback) {
+ if (!rules) {
+ return '';
+ }
+ if (typeof rules === 'string') {
+ rules = parse(rules);
+ }
+ if (callback) {
+ forEachRule(rules, callback);
+ }
+ return stringify(rules, nativeCssVariables);
+}
+
+/**
+ * @param {HTMLStyleElement} style
+ * @return {StyleNode}
+ */
+export function rulesForStyle(style) {
+ if (!style['__cssRules'] && style.textContent) {
+ style['__cssRules'] = parse(style.textContent);
+ }
+ return style['__cssRules'] || null;
+}
+
+// Tests if a rule is a keyframes selector, which looks almost exactly
+// like a normal selector but is not (it has nothing to do with scoping
+// for example).
+/**
+ * @param {StyleNode} rule
+ * @return {boolean}
+ */
+export function isKeyframesSelector(rule) {
+ return Boolean(rule['parent']) &&
+ rule['parent']['type'] === types.KEYFRAMES_RULE;
+}
+
+/**
+ * @param {StyleNode} node
+ * @param {Function=} styleRuleCallback
+ * @param {Function=} keyframesRuleCallback
+ * @param {boolean=} onlyActiveRules
+ */
+export function forEachRule(node, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) {
+ if (!node) {
+ return;
+ }
+ let skipRules = false;
+ let type = node['type'];
+ if (onlyActiveRules) {
+ if (type === types.MEDIA_RULE) {
+ let matchMedia = node['selector'].match(MEDIA_MATCH);
+ if (matchMedia) {
+ // if rule is a non matching @media rule, skip subrules
+ if (!window.matchMedia(matchMedia[1]).matches) {
+ skipRules = true;
+ }
+ }
+ }
+ }
+ if (type === types.STYLE_RULE) {
+ styleRuleCallback(node);
+ } else if (keyframesRuleCallback &&
+ type === types.KEYFRAMES_RULE) {
+ keyframesRuleCallback(node);
+ } else if (type === types.MIXIN_RULE) {
+ skipRules = true;
+ }
+ let r$ = node['rules'];
+ if (r$ && !skipRules) {
+ for (let i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {
+ forEachRule(r, styleRuleCallback, keyframesRuleCallback, onlyActiveRules);
+ }
+ }
+}
+
+// add a string of cssText to the document.
+/**
+ * @param {string} cssText
+ * @param {string} moniker
+ * @param {Node} target
+ * @param {Node} contextNode
+ * @return {HTMLStyleElement}
+ */
+export function applyCss(cssText, moniker, target, contextNode) {
+ let style = createScopeStyle(cssText, moniker);
+ applyStyle(style, target, contextNode);
+ return style;
+}
+
+/**
+ * @param {string} cssText
+ * @param {string} moniker
+ * @return {HTMLStyleElement}
+ */
+export function createScopeStyle(cssText, moniker) {
+ let style = /** @type {HTMLStyleElement} */(document.createElement('style'));
+ if (moniker) {
+ style.setAttribute('scope', moniker);
+ }
+ style.textContent = cssText;
+ return style;
+}
+
+/**
+ * Track the position of the last added style for placing placeholders
+ * @type {Node}
+ */
+let lastHeadApplyNode = null;
+
+// insert a comment node as a styling position placeholder.
+/**
+ * @param {string} moniker
+ * @return {!Comment}
+ */
+export function applyStylePlaceHolder(moniker) {
+ let placeHolder = document.createComment(' Shady DOM styles for ' +
+ moniker + ' ');
+ let after = lastHeadApplyNode ?
+ lastHeadApplyNode['nextSibling'] : null;
+ let scope = document.head;
+ scope.insertBefore(placeHolder, after || scope.firstChild);
+ lastHeadApplyNode = placeHolder;
+ return placeHolder;
+}
+
+/**
+ * @param {HTMLStyleElement} style
+ * @param {?Node} target
+ * @param {?Node} contextNode
+ */
+export function applyStyle(style, target, contextNode) {
+ target = target || document.head;
+ let after = (contextNode && contextNode.nextSibling) ||
+ target.firstChild;
+ target.insertBefore(style, after);
+ if (!lastHeadApplyNode) {
+ lastHeadApplyNode = style;
+ } else {
+ // only update lastHeadApplyNode if the new style is inserted after the old lastHeadApplyNode
+ let position = style.compareDocumentPosition(lastHeadApplyNode);
+ if (position === Node.DOCUMENT_POSITION_PRECEDING) {
+ lastHeadApplyNode = style;
+ }
+ }
+}
+
+/**
+ * @param {string} buildType
+ * @return {boolean}
+ */
+export function isTargetedBuild(buildType) {
+ return nativeShadow ? buildType === 'shadow' : buildType === 'shady';
+}
+
+/**
+ * Walk from text[start] matching parens and
+ * returns position of the outer end paren
+ * @param {string} text
+ * @param {number} start
+ * @return {number}
+ */
+export function findMatchingParen(text, start) {
+ let level = 0;
+ for (let i=start, l=text.length; i < l; i++) {
+ if (text[i] === '(') {
+ level++;
+ } else if (text[i] === ')') {
+ if (--level === 0) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+/**
+ * @param {string} str
+ * @param {function(string, string, string, string)} callback
+ */
+export function processVariableAndFallback(str, callback) {
+ // find 'var('
+ let start = str.indexOf('var(');
+ if (start === -1) {
+ // no var?, everything is prefix
+ return callback(str, '', '', '');
+ }
+ //${prefix}var(${inner})${suffix}
+ let end = findMatchingParen(str, start + 3);
+ let inner = str.substring(start + 4, end);
+ let prefix = str.substring(0, start);
+ // suffix may have other variables
+ let suffix = processVariableAndFallback(str.substring(end + 1), callback);
+ let comma = inner.indexOf(',');
+ // value and fallback args should be trimmed to match in property lookup
+ if (comma === -1) {
+ // variable, no fallback
+ return callback(prefix, inner.trim(), '', suffix);
+ }
+ // var(${value},${fallback})
+ let value = inner.substring(0, comma).trim();
+ let fallback = inner.substring(comma + 1).trim();
+ return callback(prefix, value, fallback, suffix);
+}
+
+/**
+ * @param {Element} element
+ * @param {string} value
+ */
+export function setElementClassRaw(element, value) {
+ // use native setAttribute provided by ShadyDOM when setAttribute is patched
+ if (nativeShadow) {
+ element.setAttribute('class', value);
+ } else {
+ window['ShadyDOM']['nativeMethods']['setAttribute'].call(element, 'class', value);
+ }
+}
+
+export const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] || ((node) => node);
+
+/**
+ * @param {Element | {is: string, extends: string}} element
+ * @return {{is: string, typeExtension: string}}
+ */
+export function getIsExtends(element) {
+ let localName = element['localName'];
+ let is = '', typeExtension = '';
+ /*
+ NOTE: technically, this can be wrong for certain svg elements
+ with `-` in the name like `<font-face>`
+ */
+ if (localName) {
+ if (localName.indexOf('-') > -1) {
+ is = localName;
+ } else {
+ typeExtension = localName;
+ is = (element.getAttribute && element.getAttribute('is')) || '';
+ }
+ } else {
+ is = /** @type {?} */(element).is;
+ typeExtension = /** @type {?} */(element).extends;
+ }
+ return {is, typeExtension};
+}
+
+/**
+ * @param {Element|DocumentFragment} element
+ * @return {string}
+ */
+export function gatherStyleText(element) {
+ /** @type {!Array<string>} */
+ const styleTextParts = [];
+ const styles = /** @type {!NodeList<!HTMLStyleElement>} */(element.querySelectorAll('style'));
+ for (let i = 0; i < styles.length; i++) {
+ const style = styles[i];
+ if (isUnscopedStyle(style)) {
+ if (!nativeShadow) {
+ processUnscopedStyle(style);
+ style.parentNode.removeChild(style);
+ }
+ } else {
+ styleTextParts.push(style.textContent);
+ style.parentNode.removeChild(style);
+ }
+ }
+ return styleTextParts.join('').trim();
+}
+
+/**
+ * Split a selector separated by commas into an array in a smart way
+ * @param {string} selector
+ * @return {!Array<string>}
+ */
+export function splitSelectorList(selector) {
+ const parts = [];
+ let part = '';
+ for (let i = 0; i >= 0 && i < selector.length; i++) {
+ // A selector with parentheses will be one complete part
+ if (selector[i] === '(') {
+ // find the matching paren
+ const end = findMatchingParen(selector, i);
+ // push the paren block into the part
+ part += selector.slice(i, end + 1);
+ // move the index to after the paren block
+ i = end;
+ } else if (selector[i] === ',') {
+ parts.push(part);
+ part = '';
+ } else {
+ part += selector[i];
+ }
+ }
+ // catch any pieces after the last comma
+ if (part) {
+ parts.push(part);
+ }
+ return parts;
+}
+
+const CSS_BUILD_ATTR = 'css-build';
+
+/**
+ * Return the polymer-css-build "build type" applied to this element
+ *
+ * @param {!HTMLElement} element
+ * @return {string} Can be "", "shady", or "shadow"
+ */
+export function getCssBuild(element) {
+ if (cssBuild !== undefined) {
+ return /** @type {string} */(cssBuild);
+ }
+ if (element.__cssBuild === undefined) {
+ // try attribute first, as it is the common case
+ const attrValue = element.getAttribute(CSS_BUILD_ATTR);
+ if (attrValue) {
+ element.__cssBuild = attrValue;
+ } else {
+ const buildComment = getBuildComment(element);
+ if (buildComment !== '') {
+ // remove build comment so it is not needlessly copied into every element instance
+ removeBuildComment(element);
+ }
+ element.__cssBuild = buildComment;
+ }
+ }
+ return element.__cssBuild || '';
+}
+
+/**
+ * Check if the given element, either a <template> or <style>, has been processed
+ * by polymer-css-build.
+ *
+ * If so, then we can make a number of optimizations:
+ * - polymer-css-build will decompose mixins into individual CSS Custom Properties,
+ * so the ApplyShim can be skipped entirely.
+ * - Under native ShadowDOM, the style text can just be copied into each instance
+ * without modification
+ * - If the build is "shady" and ShadyDOM is in use, the styling does not need
+ * scoping beyond the shimming of CSS Custom Properties
+ *
+ * @param {!HTMLElement} element
+ * @return {boolean}
+ */
+export function elementHasBuiltCss(element) {
+ return getCssBuild(element) !== '';
+}
+
+/**
+ * For templates made with tagged template literals, polymer-css-build will
+ * insert a comment of the form `<!--css-build:shadow-->`
+ *
+ * @param {!HTMLElement} element
+ * @return {string}
+ */
+export function getBuildComment(element) {
+ const buildComment = element.localName === 'template' ?
+ /** @type {!HTMLTemplateElement} */ (element).content.firstChild :
+ element.firstChild;
+ if (buildComment instanceof Comment) {
+ const commentParts = buildComment.textContent.trim().split(':');
+ if (commentParts[0] === CSS_BUILD_ATTR) {
+ return commentParts[1];
+ }
+ }
+ return '';
+}
+
+/**
+ * Check if the css build status is optimal, and do no unneeded work.
+ *
+ * @param {string=} cssBuild CSS build status
+ * @return {boolean} css build is optimal or not
+ */
+export function isOptimalCssBuild(cssBuild = '') {
+ // CSS custom property shim always requires work
+ if (cssBuild === '' || !nativeCssVariables) {
+ return false;
+ }
+ return nativeShadow ? cssBuild === 'shadow' : cssBuild === 'shady';
+}
+
+/**
+ * @param {!HTMLElement} element
+ */
+function removeBuildComment(element) {
+ const buildComment = element.localName === 'template' ?
+ /** @type {!HTMLTemplateElement} */ (element).content.firstChild :
+ element.firstChild;
+ buildComment.parentNode.removeChild(buildComment);
+}
diff --git a/catapult/third_party/polymer/components/shadycss/src/template-map.js b/catapult/third_party/polymer/components/shadycss/src/template-map.js
new file mode 100644
index 00000000..d3eb8409
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/template-map.js
@@ -0,0 +1,17 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+/**
+ * @const {!Object<string, !HTMLTemplateElement>}
+ */
+const templateMap = {};
+export default templateMap;
diff --git a/catapult/third_party/polymer/components/shadycss/src/unscoped-style-handler.js b/catapult/third_party/polymer/components/shadycss/src/unscoped-style-handler.js
new file mode 100644
index 00000000..95e994fa
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/src/unscoped-style-handler.js
@@ -0,0 +1,40 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+/** @type {!Set<string>} */
+const styleTextSet = new Set();
+
+export const scopingAttribute = 'shady-unscoped';
+
+/**
+ * Add a specifically-marked style to the document directly, and only one copy of that style.
+ *
+ * @param {!HTMLStyleElement} style
+ * @return {undefined}
+ */
+export function processUnscopedStyle(style) {
+ const text = style.textContent;
+ if (!styleTextSet.has(text)) {
+ styleTextSet.add(text);
+ const newStyle = style.cloneNode(true);
+ document.head.appendChild(newStyle);
+ }
+}
+
+/**
+ * Check if a style is supposed to be unscoped
+ * @param {!HTMLStyleElement} style
+ * @return {boolean} true if the style has the unscoping attribute
+ */
+export function isUnscopedStyle(style) {
+ return style.hasAttribute(scopingAttribute);
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/.eslintrc.json b/catapult/third_party/polymer/components/shadycss/tests/.eslintrc.json
new file mode 100644
index 00000000..f6ee2a64
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/.eslintrc.json
@@ -0,0 +1,11 @@
+{
+ "env": {
+ "mocha": true
+ },
+ "globals": {
+ "assert": true,
+ "sinon": true,
+ "WCT": true,
+ "fixture": true
+ }
+}
diff --git a/catapult/third_party/polymer/components/shadycss/tests/apply-shim.html b/catapult/third_party/polymer/components/shadycss/tests/apply-shim.html
new file mode 100644
index 00000000..dd221367
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/apply-shim.html
@@ -0,0 +1,343 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script>
+ if (customElements.polyfillWrapFlushCallback) {
+ // delay definition of custom-style until after template polyfill loads
+ customElements.polyfillWrapFlushCallback(function(cb) {
+ HTMLImports.whenReady(cb);
+ });
+ }
+ </script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/custom-style-element.js"></script>
+ <script src="module/generated/make-element.js"></script>
+ <title>Apply Shim</title>
+
+</head>
+<body>
+ <template id="basic">
+ <style>
+ :host {
+ --mixin: {
+ border: 2px solid black;
+ };
+ }
+ div {
+ @apply --mixin;
+ }
+ </style>
+ </template>
+
+ <template id="defaults">
+ <style>
+ :host {
+ --mixin: {
+ border: 2px solid black;
+ }
+ }
+ div {
+ border: 1px dotted orange;
+ @apply --mixin;
+ }
+ span {
+ border: inherit;
+ @apply --mixin;
+ }
+ span {
+ border: initial;
+ @apply --mixin;
+ }
+ </style>
+ </template>
+
+ <template id="override">
+ <style>
+ :host {
+ --override: {
+ padding: 2px;
+ };
+ }
+ :host([override]) {
+ --override: {
+ border: 2px solid black;
+ };
+ }
+ div {
+ @apply --override;
+ }
+ </style>
+ </template>
+
+ <template id="override-with-property">
+ <style>
+ :root {
+ --prop-mixin: {
+ border: 2px solid black;
+ };
+ }
+ x-foo {
+ --prop-mixin: blue;
+ color: var(--prop-mixin);
+ }
+ div {
+ @apply --prop-mixin;
+ }
+ </style>
+ </template>
+
+ <template id="define-with-var">
+ <style>
+ :root {
+ --mixin-var: {
+ border: 2px solid black;
+ };
+ }
+ div {
+ --mixin-var2: var(--mixin-var);
+ }
+ span {
+ --mixin-var: 20px;
+ --variable: var(--mixin-var);
+ }
+ </style>
+ </template>
+
+ <template id="x-element">
+ <style>
+ :host {
+ @apply --my-mixin;
+ }
+ </style>
+ </template>
+
+ <template id="x-element2">
+ <custom-style>
+ <style>
+ html {
+ --my-mixin: {
+ border: 2px solid black;
+ };
+ }
+ </style>
+ </custom-style>
+ </template>
+ <template id="important">
+ <style>
+ :host {
+ --mixin-important: {
+ background-color: white;
+ border: 2px solid black !important;
+ color: white !important;
+ };
+ --mixin: {
+ background-color: red;
+ border: 1px dotted orange;
+ color: black !important;
+ };
+ }
+ div {
+ @apply --mixin-important;
+ @apply --mixin;
+ }
+ </style>
+ </template>
+ <script>
+ suite('Apply Shim', function() {
+ function copy(name) {
+ var template = document.querySelector('template#' + name);
+ return template.content.cloneNode(true);
+ }
+
+ function prep(templateName, elementName) {
+ var style = copy(templateName).querySelector('style');
+ var ast = window.ShadyCSS.ApplyShim.transformStyle(style, elementName);
+ return {style: style, ast: ast};
+ }
+
+ suite('Basic', function() {
+ var style, ast;
+ suiteSetup(function() {
+ var info = prep('basic');
+ style = info.style;
+ ast = info.ast;
+ style.textContent = window.ShadyCSS.ScopingShim.styleAstToString(ast);
+ });
+
+ test('style is transformed', function() {
+ var orig = copy('basic').querySelector('style');
+ assert.notEqual(style.textContent, orig.textContent);
+ });
+
+ test('mixin became custom properties', function() {
+ var definition = ast.rules[0];
+ var application = ast.rules[1];
+ assert.match(definition.cssText, /--mixin_-_border:\s*2px solid black/);
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border\)/);
+ });
+ });
+ suite('Defaults', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('defaults');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('properties defined before mixin are used as defaults', function() {
+ var application = ast.rules[1];
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*1px dotted orange\)/);
+ });
+
+ test('inherit and initial default values are preserved', function() {
+ var application = ast.rules[2];
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*inherit\)/);
+ application = ast.rules[3];
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*initial\)/);
+ });
+ });
+
+ suite('override', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('override');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('mixin redefinition sets unused properties to initial', function() {
+ var def1 = ast.rules[0];
+ assert.match(def1.cssText, /--override_-_padding:\s*2px/);
+ var def2 = ast.rules[1];
+ assert.match(def2.cssText, /--override_-_padding:\s*initial/);
+ assert.match(def2.cssText, /--override_-_border:\s*2px solid black/);
+ });
+
+ test('mixin application includes all values', function() {
+ var application = ast.rules[2];
+ assert.match(application.cssText, /padding:\s*var\(--override_-_padding\)/);
+ assert.match(application.cssText, /border:\s*var\(--override_-_border\)/);
+ });
+ });
+
+ suite('override with property', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('override-with-property');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('mixin definition defers to property definition', function() {
+ var def = ast.rules[1];
+ assert.notMatch(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
+ });
+
+ test('mixin can still be used by other parts of the page', function() {
+ var def = ast.rules[2];
+ assert.match(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
+ });
+ });
+
+ suite('define with var()', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('define-with-var');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('mixin-var2 is defined with mixin-var\'s values', function() {
+ var def = ast.rules[1];
+ assert.match(def.cssText, /--mixin-var2_-_border:\s*var\(--mixin-var_-_border\)/);
+ });
+
+ test('var usage of mixin is not removed, preserving override functionality', function() {
+ var def = ast.rules[2];
+ assert.match(def.cssText, /--variable:\s*var\(--mixin-var\)/);
+ });
+ });
+
+ suite('invalidation on new definitions', function() {
+ var style, ast, element;
+ suiteSetup(function() {
+ makeElement('x-element');
+ element = document.createElement('x-element');
+ document.body.appendChild(element);
+ style = element.shadowRoot ? element.shadowRoot.querySelector('style') : document.head.querySelector('style[scope=x-element]');
+ });
+
+ test('element initially has no definition', function() {
+ var ast = window.ShadyCSS.ScopingShim._styleInfoForNode(element)._getStyleRules();
+ assert.equal(ast.rules[0].cssText, ';');
+ });
+
+ test('Revalidating Apply Shim on element template fills in properties', function() {
+ var nodes = copy('x-element2');
+ document.body.appendChild(nodes);
+ window.ShadyCSS.styleDocument();
+ var ast = window.ShadyCSS.ScopingShim._styleInfoForNode(element)._getStyleRules();
+ if (window.ShadyCSS.nativeCss) {
+ assert.match(ast.rules[0].cssText, /border:\s*var\(--my-mixin_-_border\)/);
+ } else {
+ assert.match(ast.rules[0].cssText, /border:\s*2px solid black/);
+ }
+ });
+ });
+ suite('!important', function() {
+ var ast;
+ suiteSetup(function() {
+ var info = prep('important');
+ ast = info.ast;
+ });
+
+ test('!important in mixin correctly translates to !important in resulting custom property', function() {
+ var application = ast.rules[1];
+ assert.match(application.cssText, /border:\s*var\(--mixin-important_-_border\)\s*!important/);
+ });
+ test("Fallback of related property without !important is kept without !important in resulting custom property", function() {
+ var application = ast.rules[1];
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*var\(--mixin-important_-_border\)\)/);
+ });
+ test('Two mixins with both !important are treated in correct order while also preserving !important in resulting custom property', function () {
+ var application = ast.rules[1];
+ assert.match(application.cssText, /color:\s*var\(--mixin-important_-_color\)\s*!important/);
+ assert.match(application.cssText, /color:\s*var\(--mixin_-_color,\s*var\(--mixin-important_-_color\)\)\s*!important/);
+ })
+ test('Properties without !important in a mixin with !important are treated independently', function() {
+ var application = ast.rules[1];
+ assert.match(application.cssText, /background-color:\s*var\(--mixin_-_background-color,\s*var\(--mixin-important_-_background-color\)\)/);
+ assert.notMatch(application.cssText, /background-color:\s*var\(--mixin_-_background-color,\s*var\(--mixin-important_-_background-color\)\)\s*!important/);
+
+ assert.match(application.cssText, /background-color:\s*var\(--mixin-important_-_background-color\)/);
+ assert.notMatch(application.cssText, /background-color:\s*var\(--mixin-important_-_background-color\)\s*!important/);
+ });
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/async-loading.html b/catapult/third_party/polymer/components/shadycss/tests/async-loading.html
new file mode 100644
index 00000000..c285ce31
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/async-loading.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+</script>
+<script src="./test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js" defer></script>
+<script src="../scoping-shim.min.js" defer></script>
+<script>
+ suite('defered loading', () => {
+ test('Loading works as expected', () => {
+ });
+ })
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/chrome-devtools.html b/catapult/third_party/polymer/components/shadycss/tests/chrome-devtools.html
new file mode 100644
index 00000000..602c207b
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/chrome-devtools.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<title>Chrome Dev Tools emulation</title>
+<script>
+ // define user agent to be Safari 9
+ Object.defineProperty(navigator, 'userAgent', { value: 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1' });
+</script>
+<script src="test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="module/generated/style-settings.js"></script>
+<script>
+ suite('Chrome Devtools', () => {
+ test('Emaulating iOS, native css variables are tied to native shadowdom support', () => {
+ if (!window.ShadyDOM || !window.ShadyDOM.inUse) {
+ assert.isTrue(window.StyleSettings.nativeCssVariables);
+ }
+ });
+ })
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/complicated-mixin-ordering.html b/catapult/third_party/polymer/components/shadycss/tests/complicated-mixin-ordering.html
new file mode 100644
index 00000000..552a1e61
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/complicated-mixin-ordering.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<html>
+
+<head>
+
+ <meta charset="utf-8">
+ <script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script>
+ if (customElements.polyfillWrapFlushCallback) {
+ // delay definition of custom-style until after template polyfill loads
+ customElements.polyfillWrapFlushCallback(function (cb) {
+ HTMLImports.whenReady(cb);
+ });
+ }
+ </script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/custom-style-element.js"></script>
+ <script src="module/generated/make-element.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <title>Complicated Order</title>
+
+</head>
+
+<body>
+ <template id="child-element">
+ <style>
+ p {
+ @apply --my-mixin;
+ }
+ </style>
+ <p>I'm a DOM element. This is my local DOM!</p>
+ </template>
+
+ <template id="container-element">
+ <style>
+ child-element {
+ --my-mixin: {
+ background-color: rgb(255, 0, 0);
+ }
+ }
+ </style>
+ <child-element></child-element>
+ </template>
+
+ <template id="other-container-element">
+ <style>
+ child-element {
+ --my-mixin: {
+ font-size: 40px;
+ background-color: rgb(0, 255, 0);
+ }
+ }
+ </style>
+ <child-element></child-element>
+ </template>
+
+ <container-element></container-element>
+ <other-container-element></other-container-element>
+
+ <script>
+ suite('Complicated Order', () => {
+ function assertComputed(node, property, expectedValue, msg) {
+ assert.equal(getComputedStyle(node).getPropertyValue(property).trim(), expectedValue, msg);
+ }
+ suiteSetup(() => {
+ makeElement('child-element');
+ makeElement('container-element');
+ makeElement('other-container-element');
+ });
+ test('complicated ordering works as expected', () => {
+ let initialFontSize = getComputedStyle(document.head).getPropertyValue('font-size').trim();
+ let con = document.querySelector('container-element');
+ let oth = document.querySelector('other-container-element');
+ assertComputed(con.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'background-color', 'rgb(255, 0, 0)');
+ assertComputed(con.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'font-size', initialFontSize);
+ assertComputed(oth.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'background-color', 'rgb(0, 255, 0)');
+ assertComputed(oth.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'font-size', '40px');
+ con.parentNode.removeChild(con);
+ oth.parentNode.removeChild(oth);
+ })
+ })
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/css-parse.html b/catapult/third_party/polymer/components/shadycss/tests/css-parse.html
new file mode 100644
index 00000000..6fb98465
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/css-parse.html
@@ -0,0 +1,188 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="module/generated/css-parse.js"></script>
+
+ <title>css-parse</title>
+
+</head>
+<body>
+
+ <style id="test">
+ :host {
+ background: red;
+ }
+
+ .foo .bar .baz, zonk[happy]:focus {
+ font-family: sans-serif;
+ font-size: 15px;
+ }
+
+ @-webkit-keyframes fill-unfill-rotate {
+ 12.5% { transform: rotate(135deg); }
+ 25% { transform: rotate(270deg); }
+ 37.5% { transform: rotate(405deg); }
+ 50% { transform: rotate(540deg); }
+ 62.5% { transform: rotate(675deg); }
+ 75% { transform: rotate(810deg); }
+ 87.5% { transform: rotate(945deg); }
+ to { transform: rotate(1080deg); }
+ }
+
+ @media (max-width: 400px) {
+ div {
+ margin-left: 0 !important;
+ }
+ }
+ </style>
+
+ <style id="test-ignore">
+ @import '';
+
+ /* comment */
+ .stuff {
+ background: red;
+ }
+ /* comment */
+
+ /*
+ This is a multi-line comment
+ */
+
+ /*.aclassThatShouldBeIgnored {
+ someProperty: thatMustNotShowUp
+ }*/
+ </style>
+
+ <style id="short-escape-sequence">
+ .\33 d-model {
+ border-top: 3px solid red;
+ }
+ .\a33 d-model {
+ border-top: 3px solid red;
+ }
+ .\b333 d-model {
+ border-top: 3px solid red;
+ }
+ .\c3333 d-model {
+ border-top: 3px solid red;
+ }
+ .\d33333 d-model {
+ border-top: 3px solid red;
+ }
+ .\e33333d-model {
+ border-top: 3px solid red;
+ }
+ </style>
+
+ <style id="multiple-spaces">
+ .foo .bar {}
+ .foo .bar {}
+ .foo
+
+
+ .bar {}
+ </style>
+
+ <style id="empty"></style>
+<script>
+
+ function sanitizeCss(text) {
+ return text.replace(/[\s]+/g, ' ').trim();
+ }
+
+ suite('css-parse', function() {
+ var s, tree;
+
+ setup(function() {
+ s = document.querySelector('style#test');
+ tree = window.CssParse.parse(s.textContent);
+ });
+
+ test('window.CssParse rules parse', function() {
+ assert.equal(tree.rules.length, 4, 'unexpected number of rules');
+ assert.equal(tree.rules[2].rules.length, 8, 'unexpected number of rules in keyframes');
+ assert.equal(tree.rules[3].rules.length, 1, 'unexpected number of rules in @media');
+ });
+
+ test('rule selectors parse', function() {
+ assert.equal(tree.rules[0].selector, ':host', 'unexpected selector');
+ assert.equal(tree.rules[2].selector, '@-webkit-keyframes fill-unfill-rotate', 'unexpected selector in keyframes');
+ assert.equal(tree.rules[3].selector, '@media (max-width: 400px)', 'unexpected selector in @media');
+ });
+
+ test('rule cssText parse', function() {
+ assert.equal(tree.rules[0].cssText, 'background: red;', 'unexpected cssText');
+ assert.match(tree.rules[2].cssText, /^12.5%/, 'unexpected cssText in keyframes');
+ assert.equal(tree.rules[2].rules[2].cssText, 'transform: rotate(405deg);', 'unexpected cssText in keyframes');
+ assert.match(tree.rules[3].cssText, /^div/, 'unexpected cssText in @media');
+ assert.equal(tree.rules[3].rules[0].cssText, 'margin-left: 0 !important;', 'unexpected cssText in @media');
+ });
+
+ test('rule types', function() {
+ assert.equal(tree.rules[0].type, window.CssParse.types.STYLE_RULE);
+ assert.equal(tree.rules[1].type, window.CssParse.types.STYLE_RULE);
+ assert.equal(tree.rules[2].type, window.CssParse.types.KEYFRAMES_RULE);
+ assert.equal(tree.rules[3].type, window.CssParse.types.MEDIA_RULE);
+ assert.equal(tree.rules[3].rules[0].type, window.CssParse.types.STYLE_RULE);
+
+ });
+
+ test('rules stringify', function() {
+ var orig = sanitizeCss(s.textContent);
+ var result = sanitizeCss(window.CssParse.stringify(tree));
+ assert.equal(result, orig, 'unexpected stringified output');
+ });
+
+ test('parse correctly ignores @import and comments', function() {
+ var s2 = document.querySelector('#test-ignore');
+ var t = window.CssParse.parse(s2.textContent);
+ assert.equal(t.rules[0].selector, '.stuff', 'unexpected rule selector');
+ assert.equal(t.rules[0].cssText, 'background: red;', 'unexpected rule cssText');
+ var result = sanitizeCss(window.CssParse.stringify(t));
+ assert.equal(result, '.stuff { background: red; }', 'unexpected stringified output');
+ });
+
+ test('short escape sequences', function() {
+ var s3 = document.querySelector('#short-escape-sequence');
+ var t = window.CssParse.parse(s3.textContent);
+ assert.equal(t.rules[0].selector, '.\\000033d-model');
+ assert.equal(t.rules[1].selector, '.\\000a33d-model');
+ assert.equal(t.rules[2].selector, '.\\00b333d-model');
+ assert.equal(t.rules[3].selector, '.\\0c3333d-model');
+ assert.equal(t.rules[4].selector, '.\\d33333d-model');
+ assert.equal(t.rules[5].selector, '.\\e33333d-model');
+ });
+
+ test('multiple consequent spaces in CSS selector', function() {
+ var s4 = document.querySelector('#multiple-spaces');
+ var t = window.CssParse.parse(s4.textContent);
+ assert.equal(t.rules[0].selector, '.foo .bar');
+ assert.equal(t.rules[1].selector, '.foo .bar');
+ assert.equal(t.rules[2].selector, '.foo .bar');
+ });
+
+ test('empty styles are are handled', function() {
+ var s = document.querySelector('#empty');
+ var t = window.CssParse.parse(s.textContent);
+ window.CssParse.stringify(t);
+ });
+
+ });
+</script>
+
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/custom-style-import.html b/catapult/third_party/polymer/components/shadycss/tests/custom-style-import.html
new file mode 100644
index 00000000..15e34143
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/custom-style-import.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="./test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../scoping-shim.min.js"></script>
+<script src="../apply-shim.min.js"></script>
+<script src="../custom-style-interface.min.js"></script>
+<script src="module/generated/make-element.js"></script>
+<script src="module/generated/custom-style-element.js"></script>
+<link rel="import" href="html-imports/custom-style-import.html">
+
+<template id="x-import">
+ <style>
+ :host {
+ border: 2px solid var(--color, black);
+ }
+ </style>
+</template>
+
+<x-import id="target"></x-import>
+
+<script>
+ suite('Custom Style upgrades', function() {
+ suiteSetup(function() {
+ makeElement('x-import');
+ });
+ test('custom-style in import provides styling', function() {
+ var target = document.querySelector('#target');
+ assert.equal(getComputedStyle(target).borderColor, 'rgb(0, 0, 255)');
+ });
+ });
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/custom-style-late.html b/catapult/third_party/polymer/components/shadycss/tests/custom-style-late.html
new file mode 100644
index 00000000..3aac557f
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/custom-style-late.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="./test-flags.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../scoping-shim.min.js"></script>
+<script src="../apply-shim.min.js"></script>
+ <script>
+ if (customElements.polyfillWrapFlushCallback) {
+ // delay definition of custom-style until after template polyfill loads
+ customElements.polyfillWrapFlushCallback(function(cb) {
+ HTMLImports.whenReady(cb);
+ });
+ }
+ </script>
+<script src="../custom-style-interface.min.js"></script>
+<script src="module/generated/make-element.js"></script>
+<script src="module/generated/custom-style-element.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+
+<template id="late">
+ <custom-style class="late-style">
+ <style>
+ html {
+ --late: {
+ border: 2px solid red;
+ };
+ }
+ </style>
+ </custom-style>
+</template>
+
+<template id="x-late">
+ <style>
+ :host {
+ display: block;
+ @apply --late;
+ }
+ </style>
+ <div>late</div>
+</template>
+
+<template id="x-host">
+ <style>
+ :host {
+ display: block;
+ padding: 4px;
+ }
+ </style>
+ <x-late></x-late>
+</template>
+
+<x-host></x-host>
+
+<script>
+suite('Async custom-style', function() {
+ suiteSetup(function() {
+ makeElement('x-host');
+ });
+ test('late custom-style updates elements', function(done) {
+ var lateTemplate = document.querySelector('template#late');
+ var host = document.querySelector('x-host');
+ var inner = host.shadowRoot.querySelector('x-late');
+ requestAnimationFrame(function() {
+ document.body.appendChild(document.importNode(lateTemplate.content, true));
+ makeElement('x-late');
+ requestAnimationFrame(function() {
+ assert.equal(getComputedStyle(inner).borderTopWidth.trim(), '2px');
+ done();
+ });
+ });
+ })
+})
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/custom-style-only.html b/catapult/third_party/polymer/components/shadycss/tests/custom-style-only.html
new file mode 100644
index 00000000..016469bb
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/custom-style-only.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script src="./test-flags.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+</script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../scoping-shim.min.js"></script>
+<script src="../apply-shim.min.js"></script>
+<script>
+if (window.customElements && customElements.polyfillWrapFlushCallback) {
+ // delay definition of custom-style until after template polyfill loads
+ customElements.polyfillWrapFlushCallback(function(cb) {
+ HTMLImports.whenReady(cb);
+ });
+}
+</script>
+<script src="../custom-style-interface.min.js"></script>
+<script src="module/generated/custom-style-element.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+
+<custom-style id="indoc">
+ <style>
+ #target {
+ display: block;
+ @apply --late;
+ }
+ </style>
+</custom-style>
+
+<template id="late">
+ <custom-style class="late-style">
+ <style>
+ html {
+ --late: {
+ border: 2px solid red;
+ };
+ }
+ </style>
+ </custom-style>
+</template>
+
+<div id="target"></div>
+
+<script>
+suite('custom-style only', function() {
+ var host = document.querySelector('#target');
+ test('custom-style by itself works as expected', function() {
+ assert.equal(getComputedStyle(host).getPropertyValue('display').trim(), 'block');
+ });
+ test('late custom-style updates styling', function(done) {
+ var lateTemplate = document.querySelector('template#late');
+ document.body.appendChild(document.importNode(lateTemplate.content, true));
+ // two rAF to wait for after custom-style-interface's batching
+ requestAnimationFrame(function(){
+ requestAnimationFrame(function(){
+ assert.equal(getComputedStyle(host).borderTopWidth.trim(), '2px');
+ done();
+ });
+ });
+ });
+})
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/custom-style.html b/catapult/third_party/polymer/components/shadycss/tests/custom-style.html
new file mode 100644
index 00000000..8faa10fc
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/custom-style.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="./test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../scoping-shim.min.js"></script>
+<script src="../apply-shim.min.js"></script>
+<script src="../custom-style-interface.min.js"></script>
+<script src="module/generated/make-element.js"></script>
+<script src="module/generated/custom-style-element.js"></script>
+
+<custom-style>
+ <style>
+ html {
+ --foo: rgb(123, 123, 123);
+ --bar: {
+ border: 2px solid red;
+ }
+ }
+
+ </style>
+</custom-style>
+<custom-style>
+ <style>
+ :root {
+ --svg-icon: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
+ --svg-icon-2: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
+ }
+ .base64 {
+ background-image: var(--svg-icon);
+ }
+ </style>
+</custom-style>
+
+<template id="x-inner">
+ <style>
+ :host {
+ display: block;
+ height: 100px;
+ width: 100px;
+ border: 4px solid blue;
+ background-color: var(--foo);
+ @apply --bar;
+ }
+ </style>
+</template>
+
+<template id="x-outer">
+ <style>
+ :host {
+ display: block;
+ @apply --bar;
+ }
+ </style>
+ <x-inner></x-inner>
+</template>
+
+<x-outer id="target"></x-outer>
+
+<div class="base64">base64</div>
+
+<script>
+ suite('Custom Style upgrades', function() {
+ suiteSetup(function() {
+ makeElement('x-inner');
+ makeElement('x-outer');
+ });
+ test('custom-style applies to deeply nested elements', function() {
+ var target = document.querySelector('#target');
+ var inner = target.shadowRoot.querySelector('x-inner');
+ assert.equal(getComputedStyle(inner).backgroundColor, 'rgb(123, 123, 123)');
+ });
+ test('custom-style applied mixins update', function() {
+ var target = document.querySelector('#target');
+ var inner = target.shadowRoot.querySelector('x-inner');
+ assert.equal(getComputedStyle(target).borderTopWidth.trim(), '2px');
+ assert.equal(getComputedStyle(inner).borderTopWidth.trim(), '2px');
+ });
+ test('custom-style with base64 in variable', function() {
+ var target = document.querySelector('.base64');
+ assert.match(getComputedStyle(target).backgroundImage, /url\("?data\:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"?\)/);
+ });
+ });
+</script>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/deferred-apply.html b/catapult/third_party/polymer/components/shadycss/tests/deferred-apply.html
new file mode 100644
index 00000000..1b3af210
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/deferred-apply.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2018 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
+-->
+<html>
+
+<head>
+
+ <meta charset="utf-8">
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/custom-style-element.js"></script>
+ <script src="module/generated/make-element.js"></script>
+ <title>Apply Shim Deferred</title>
+</head>
+<body>
+ <custom-style>
+ <style>
+ html {
+ --foo: {
+ background-color: rgb(0, 0, 255);
+ }
+ }
+ </style>
+ </custom-style>
+ <template id="x-foo">
+ <style>
+ :host {
+ display: block;
+ height: 100px;
+ width: 100px;
+ background-color: rgb(255, 0, 0);
+ @apply --foo;
+ }
+ </style>
+ </template>
+ <x-foo></x-foo>
+ <!-- emulate apply-shim imported into a module context -->
+ <script id="applyScript" src="../apply-shim.min.js" defer></script>
+ <script>
+ suite('Deferred Apply Shim', function() {
+ test('Styling works as expected', function() {
+ let resolveFn = null;
+ const promise = new Promise((resolve) => {resolveFn = resolve}).then(() => {
+ // IE 11 timing issue
+ if (window.HTMLTemplateElement.bootstrap) {
+ HTMLTemplateElement.bootstrap(document);
+ }
+ window.makeElement('x-foo');
+ }).then(() => {
+ const el = document.querySelector('x-foo');
+ assert.equal(getComputedStyle(el).getPropertyValue('background-color').trim(), 'rgb(0, 0, 255)');
+ });
+ if (document.readyState === 'complete') {
+ resolveFn();
+ } else {
+ window.addEventListener('load', resolveFn);
+ window.addEventListener('DOMContentLoaded', () => {
+ window.removeEventListener('load', resolveFn);
+ resolveFn();
+ });
+ }
+ return promise;
+ })
+ });
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/dynamic-scoping.html b/catapult/third_party/polymer/components/shadycss/tests/dynamic-scoping.html
new file mode 100644
index 00000000..e94fc2b3
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/dynamic-scoping.html
@@ -0,0 +1,296 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/make-element.js"></script>
+ <script src="module/generated/custom-style-element.js"></script>
+</head>
+<body>
+
+ <custom-style>
+ <style>
+ x-container, x-sample, x-sample-dynamic, x-container-dynamic {
+ display: block;
+ padding: 10px;
+ margin: 10px;
+ border: 1px solid black;
+ }
+
+ .target {
+ background-color: rgb(0, 255, 0);
+ }
+ </style>
+ </custom-style>
+
+ <template id="x-sample">
+ <style>
+ .target {
+ background-color: rgb(255, 0, 0);
+ }
+ </style>
+ <h2></h2>
+ <p>here .target elements are red</p>
+ <div class="target">I'm red</div>
+ <template id="renderer">
+ <div class="target"></div>
+ </template>
+ </template>
+
+ <template id="x-container">
+ <style>
+ .target {
+ background-color: rgb(123, 123, 123);
+ }
+ </style>
+ <h1>x-container</h1>
+ <p>here .target elements are gray</p>
+ <div class="target">I'm gray</div>
+ <slot></slot>
+ </template>
+
+ <h2>body</h2>
+ <p>here .target elements are green</p>
+
+ <div class="target">I'm green</div>
+
+ <x-sample id="inBody"></x-sample>
+
+ <x-sample id="inContainer"></x-sample>
+
+ <x-container></x-container>
+
+ <template id="x-dynamic">
+ <style>
+ span {
+ background-color: rgb(123, 123, 123);
+ }
+ </style>
+ <div id="container">
+ </div>
+ </template>
+
+ <x-dynamic></x-dynamic>
+
+ <template id="out-of-band">
+ <style>
+ div {
+ color: var(--foo);
+ }
+ </style>
+ <div>oob shadowed</div>
+ </template>
+
+ <template id="oob-parent">
+ <style>
+ out-of-band {
+ --foo: rgb(0, 0, 255);
+ }
+ </style>
+ <out-of-band></out-of-band>
+ </template>
+
+ <template id="oob-other-parent">
+ <style>
+ out-of-band {
+ --foo: rgb(255, 0, 0);
+ }
+ </style>
+ </template>
+
+ <oob-parent></oob-parent>
+ <oob-other-parent></oob-other-parent>
+
+ <template id="x-sample-dynamic">
+ <style>
+ .target {
+ background-color: rgb(255, 0, 0);
+ }
+ </style>
+ <h2></h2>
+ <p>here .target elements are red</p>
+ <div class="target">I'm red</div>
+ <template id="renderer">
+ <div class="target"></div>
+ </template>
+ </template>
+
+ <template id="x-container-dynamic">
+ <style>
+ .target {
+ background-color: rgb(123, 123, 123);
+ }
+ </style>
+ <h1>x-container</h1>
+ <p>here .target elements are gray</p>
+ <div class="target">I'm gray</div>
+ <slot></slot>
+ </template>
+
+ <x-container-dynamic></x-container-dynamic>
+
+ <template id="css-build" css-build="shady">
+ <style>:host{@apply --fake;}</style>
+ <div class="style-scope css-build"></div>
+ </template>
+
+ <template id="css-build-comment"><!--css-build:shady-->
+ <style>:host{@apply --fake;}</style>
+ <div class="style-scope css-build-comment"></div>
+ </template>
+
+ <script>
+ suite('Dynamic Scoping', () => {
+ function stamp(parent, host) {
+ let template = host.shadowRoot.querySelector('template#renderer')
+ let el = template.content.cloneNode(true).querySelector('div.target');
+ el.textContent = `stamped by ${host.id}`;
+ parent.appendChild(el);
+ return el;
+ }
+ test('DOM is scoped correctly when stamped from an element into document', (done) => {
+ let inBody = document.querySelector('x-sample#inBody');
+ let inContainer = document.querySelector('x-sample#inContainer');
+ makeElement('x-sample', function() {
+ this.shadowRoot.querySelector('h2').textContent = `${this.id}`;
+ });
+ makeElement('x-container');
+ setTimeout(() => {
+ let body = stamp(document.body, inBody);
+ let container = stamp(document.querySelector('x-container').shadowRoot, inContainer);
+ requestAnimationFrame(() => {
+ assert.equal(getComputedStyle(body).backgroundColor, 'rgb(0, 255, 0)');
+ assert.equal(getComputedStyle(container).backgroundColor, 'rgb(123, 123, 123)')
+ done();
+ });
+ }, 300);
+ });
+ test('DOM is scoped correctly when created dynamically inside a scoped container', (done) => {
+ makeElement('x-dynamic', function() {
+ let div = this.shadowRoot.querySelector('#container');
+ let newDiv = div.cloneNode(true);
+ let span = document.createElement('span');
+ span.textContent = 'created dynamically';
+ newDiv.appendChild(span);
+ this.shadowRoot.appendChild(newDiv);
+ requestAnimationFrame(() => {
+ assert.equal(getComputedStyle(span).backgroundColor, 'rgb(123, 123, 123)');
+ done();
+ })
+ });
+ });
+ test('moving a custom element between scopes recalculates correctly', function(done) {
+ makeElement('out-of-band');
+ makeElement('oob-parent');
+ makeElement('oob-other-parent');
+ let parent = document.querySelector('oob-parent');
+ let newParent = document.querySelector('oob-other-parent');
+ let oob = parent.shadowRoot.querySelector('out-of-band');
+ let shadowDiv = oob.shadowRoot.querySelector('div');
+ newParent.shadowRoot.appendChild(oob);
+ requestAnimationFrame(() => {
+ assert.equal(getComputedStyle(shadowDiv).getPropertyValue('color').trim(), 'rgb(255, 0, 0)');
+ done();
+ });
+ })
+ function makeDynamicElement(name, connectedCallback) {
+ let template = document.querySelector(`template#${name}`);
+ if (template && window.ShadyCSS) {
+ window.ShadyCSS.prepareTemplate(template, name);
+ }
+ window.customElements.define(name, class extends window.HTMLElement {
+ constructor() {
+ super();
+ if (template && !this.shadowRoot) {
+ this.attachShadow({mode: 'open'});
+ this.shadowRoot.appendChild(document.importNode(template.content, true));
+ }
+ }
+ connectedCallback() {
+ window.ShadyCSS && window.ShadyCSS.styleElement(this);
+ if (connectedCallback) {
+ connectedCallback.call(this);
+ }
+ }
+ });
+ }
+ test('Nested DOM is scoped correctly when created dynamically inside a dynamic container', function(done) {
+ if (!window.customElements.polyfillWrapFlushCallback && window.ShadyDOM && window.ShadyDOM.inUse) {
+ /*
+ * This test is flaky if running with native custom elements and polyfill shadowdom,
+ * as the shadowdom polyfill may render inside of the constructor and create children,
+ * which is not allowed in the CE spec.
+ */
+ this.skip();
+ }
+ makeDynamicElement('x-container-dynamic');
+ makeDynamicElement('x-sample-dynamic');
+ const dynamicDiv = document.createElement('div');
+ dynamicDiv.classList.add('target');
+ dynamicDiv.innerText = 'I was created dynamically';
+ const dynamicSample = document.createElement('x-sample-dynamic');
+ const dynamicContainer = document.createElement('x-container-dynamic');
+ dynamicSample.shadowRoot.appendChild(dynamicDiv);
+ dynamicContainer.shadowRoot.appendChild(dynamicSample);
+ document.querySelector('x-container-dynamic').shadowRoot.appendChild(dynamicContainer);
+ requestAnimationFrame(() => {
+ dynamicSample.shadowRoot.querySelectorAll('div.target').forEach((target) =>
+ assert.equal(getComputedStyle(target).backgroundColor,'rgb(255, 0, 0)'));
+ done();
+ });
+ });
+
+ test('templates marked with "css-build" will be left alone', function() {
+ makeElement('css-build');
+ const template = document.querySelector('template#css-build');
+ const div = template.content.querySelector('div');
+ const divClasses = Array.from(div.classList);
+ assert.includeMembers(divClasses, ['style-scope', 'css-build']);
+ const style = template.content.querySelector('style');
+ if (style) {
+ assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
+ }
+ });
+
+ test('templates with css-build comments will be left alone', function() {
+ const template = document.querySelector('template#css-build-comment');
+ const buildComment = template.content.firstChild;
+ assert.instanceOf(buildComment, Comment, 'first child of template content should be a Comment');
+ makeElement('css-build-comment');
+ const div = template.content.querySelector('div');
+ const divClasses = Array.from(div.classList);
+ assert.includeMembers(divClasses, ['style-scope', 'css-build-comment']);
+ const style = template.content.querySelector('style');
+ if (style) {
+ assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
+ }
+ assert.equal(buildComment.parentNode, null, 'build commment should have been removed');
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/html-imports/custom-style-import.html b/catapult/third_party/polymer/components/shadycss/tests/html-imports/custom-style-import.html
new file mode 100644
index 00000000..3f9991e8
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/html-imports/custom-style-import.html
@@ -0,0 +1,16 @@
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<custom-style>
+ <style>
+ html {
+ --color: rgb(0, 0, 255);
+ }
+ </style>
+</custom-style> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/lazy-init.html b/catapult/third_party/polymer/components/shadycss/tests/lazy-init.html
new file mode 100644
index 00000000..42273b32
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/lazy-init.html
@@ -0,0 +1,157 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<script>WCT = { waitFor(cb) { addEventListener('DOMContentLoaded', cb) } };</script>
+<script src="test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../scoping-shim.min.js"></script>
+<script src="../apply-shim.min.js"></script>
+<script src="../custom-style-interface.min.js"></script>
+<template id="eager-host">
+ <style>
+ :host {
+ display: block;
+ height: 100px;
+ background-color: blue;
+ }
+
+ :host > late-client {
+ --foo: rgb(255, 0, 0);
+ }
+ </style>
+ <late-client></late-client>
+</template>
+<template id="late-client">
+ <style>
+ :host {
+ display: block;
+ color: var(--foo);
+ }
+
+ div {
+ border: 2px solid rgb(0, 255, 0);
+ border-color: var(--foo);
+ }
+ </style>
+ <div>Hello!</div>
+</template>
+
+<template id="x-parent">
+ <style>
+ :host {
+ --property: 10px solid black;
+ }
+ </style>
+ <x-child></x-child>
+</template>
+<template id="x-child">
+ <style>
+ div {
+ border: var(--property);
+ }
+ </style>
+ <div></div>
+</template>
+
+<script>
+ class LateClient extends HTMLElement {
+ constructor() {
+ super();
+ this.initialized = false;
+ this.attachShadow({mode: 'open'});
+ }
+ init() {
+ if (this.initialized) {
+ return;
+ }
+ this.initialized = true;
+ const template = document.querySelector(`template#${this.localName}`);
+ if (!template.initialized) {
+ template.initialized = true;
+ window.ShadyCSS.prepareTemplate(template, this.localName);
+ }
+ this.shadowRoot.appendChild(template.content.cloneNode(true));
+ window.ShadyCSS.styleElement(this);
+ }
+ connectedCallback() {
+ if (this.initialized) {
+ window.ShadyCSS.styleElement(this);
+ }
+ }
+ }
+
+ class EagerHost extends HTMLElement {
+ constructor() {
+ super();
+ this.template = document.querySelector(`template#${this.localName}`);
+ if (!this.template.initialized) {
+ this.template.initialized = true;
+ window.ShadyCSS.prepareTemplate(this.template, this.localName);
+ }
+ }
+ connectedCallback() {
+ window.ShadyCSS.styleElement(this);
+ if (this.template && !this.shadowRoot) {
+ this.attachShadow({mode: 'open'});
+ this.shadowRoot.appendChild(this.template.content.cloneNode(true));
+ }
+ }
+ }
+
+ class StampBeforeStyle extends HTMLElement {
+ constructor() {
+ super();
+ this.template = document.querySelector(`template#${this.localName}`);
+ if (!this.template.initialized) {
+ this.template.initialized = true;
+ window.ShadyCSS.prepareTemplate(this.template, this.localName);
+ }
+ }
+ connectedCallback() {
+ if (this.template && !this.shadowRoot) {
+ this.attachShadow({ mode: 'open' });
+ this.shadowRoot.appendChild(this.template.content.cloneNode(true));
+ }
+ window.ShadyCSS.styleElement(this);
+ }
+ }
+
+ suite('Lazy Initialization', function() {
+ test('Late child element is eventually correct', function() {
+ customElements.define('late-client', class extends LateClient{});
+ customElements.define('eager-host', class extends EagerHost{});
+ const host = document.createElement('eager-host');
+ document.body.appendChild(host);
+ window.ShadyCSS.styleDocument();
+ const inner = host.shadowRoot.querySelector('late-client');
+ if (inner.init) {
+ inner.init();
+ }
+ const div = inner.shadowRoot.querySelector('div');
+ assert.equal(getComputedStyle(div).getPropertyValue('border-color').trim(), 'rgb(255, 0, 0)');
+ });
+
+ test('Custom Property Shim can force unprepared parent to evaluate', function() {
+ customElements.define('x-child', class extends StampBeforeStyle {});
+ customElements.define('x-parent', class extends StampBeforeStyle {});
+ const host = document.createElement('x-parent');
+ document.body.appendChild(host);
+ const inner = host.shadowRoot.querySelector('x-child');
+ const div = inner.shadowRoot.querySelector('div');
+ assert.equal(getComputedStyle(div).getPropertyValue('border-top-width').trim(), '10px');
+ });
+ });
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/media-query.html b/catapult/third_party/polymer/components/shadycss/tests/media-query.html
new file mode 100644
index 00000000..6cf20ef1
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/media-query.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<html>
+
+<head>
+
+ <meta charset="utf-8">
+ <script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script>
+ if (customElements.polyfillWrapFlushCallback) {
+ // delay definition of custom-style until after template polyfill loads
+ customElements.polyfillWrapFlushCallback(function (cb) {
+ HTMLImports.whenReady(cb);
+ });
+ }
+ </script>
+ <script src="module/generated/make-element.js"></script>
+ <title>Media Querty</title>
+
+</head>
+
+<body>
+ <template id="simple-element">
+ <style>
+ :host {
+ height: 100px;
+ width: 100px;
+ display: block;
+ background-color: var(--color, rgb(0, 0, 0));
+ }
+ @media (min-width: 1px) and (max-width: 1px) {
+ :host {
+ --color: rgb(128, 128, 128);
+ }
+ }
+ </style>
+ </template>
+ <script>
+ suite('Media Query correctness', function() {
+ makeElement('simple-element');
+ test('test against whole @media rule', function() {
+ var el = document.createElement('simple-element');
+ document.body.appendChild(el);
+ let bg = getComputedStyle(el).getPropertyValue('background-color').trim();
+ assert.equal(bg, 'rgb(0, 0, 0)', 'background-color did not match');
+ });
+ });
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/mixin-fallbacks.html b/catapult/third_party/polymer/components/shadycss/tests/mixin-fallbacks.html
new file mode 100644
index 00000000..746caa30
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/mixin-fallbacks.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2018 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
+-->
+<script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+</script>
+<script src="./test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../scoping-shim.min.js"></script>
+<script src="../apply-shim.min.js"></script>
+<script src="../custom-style-interface.min.js"></script>
+<script src="module/generated/make-element.js"></script>
+
+<template id="mixin-element">
+ <style>
+ :host {
+ background-color: rgb(0, 0, 255);
+ height: 100px;
+ display: block;
+ }
+ :host {
+ @apply --mixin;
+ }
+ </style>
+</template>
+
+<template id="outer-element">
+ <style>
+ :host > * {
+ --mixin: {
+ background-color: rgb(255, 0, 0);
+ }
+ }
+ </style>
+ <mixin-element></mixin-element>
+</template>
+
+<script>
+ suite('Mixin Fallbacks', function() {
+ suiteSetup(function() {
+ makeElement('mixin-element');
+ makeElement('outer-element');
+ });
+
+ test('outer-element sets mixin color', function() {
+ const el = document.createElement('outer-element');
+ document.body.appendChild(el);
+ const inner = el.shadowRoot.querySelector('mixin-element');
+ const color = getComputedStyle(inner).getPropertyValue('background-color').trim();
+ assert.equal(color, 'rgb(255, 0, 0)');
+ });
+ test('mixin-element by itself falls back correctly', function() {
+ const mixinOnly = document.createElement('mixin-element');
+ document.body.appendChild(mixinOnly);
+ const color = getComputedStyle(mixinOnly).getPropertyValue('background-color').trim();
+ assert.equal(color, 'rgb(0, 0, 255)');
+ });
+ });
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/mixin-ordering.html b/catapult/third_party/polymer/components/shadycss/tests/mixin-ordering.html
new file mode 100644
index 00000000..2191b25e
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/mixin-ordering.html
@@ -0,0 +1,143 @@
+<!doctype html>
+<head>
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/make-element.js"></script>
+</head>
+<body>
+ <div>
+ <x-item-a>item A</x-item-a>
+ <x-item-b>item B</x-item-b>
+ </div>
+ <x-menu>
+ </x-menu>
+ <x-menu-group>
+ </x-menu-group>
+
+ <template id="x-item-a">
+ <style>
+ :host {
+ display:block;
+ background: rgb(255, 255, 255);
+ @apply --item-mixin;
+ }
+ </style>
+ <slot></slot>
+ </template>
+
+ <template id="x-menu">
+ <style>
+ :host {
+ display:block;
+ border:1px solid black;
+ margin:2px;
+ --item-mixin:{background:rgb(0, 0, 255);};
+ }
+ </style>
+ <x-item-a>menu item A</x-item-a>
+ <x-item-b>menu item B</x-item-b>
+ </template>
+
+ <template id="x-group">
+ <style>
+ :host{
+ display:block;
+ --item-mixin:{background:rgb(255, 0, 0);};
+ }
+ </style>
+ <x-item-a>group item A</x-item-a>
+ <x-item-b>group item B</x-item-b>
+ </template>
+
+ <template id="x-menu-group">
+ <style>
+ :host {
+ display:block;
+ border:1px solid black;
+ margin:2px;
+ --item-mixin:{background:rgb(0, 0, 255);};
+ }
+ </style>
+ <x-group></x-group>
+ </template>
+
+ <template id="x-item-b">
+ <style>
+ :host {
+ display:block;
+ background: rgb(255, 255, 255);
+ @apply --item-mixin;
+ }
+ </style>
+ <slot></slot>
+ </template>
+
+ <template id="x-dynamic">
+ <style>
+ :host {
+ display: block;
+ background: rgb(255, 255, 255);
+ @apply --mixin;
+ }
+ </style>
+ <span>dynamic item</span>
+ </template>
+
+ <template id="x-dynamic-container">
+ <style>
+ :host {
+ --mixin: {
+ background-color: rgb(123, 123, 123);
+ };
+ }
+ </style>
+ <x-dynamic></x-dynamic>
+ </template>
+
+ <script>
+ suite('Mixin Ordering', function() {
+ suiteSetup(function() {
+ makeElement('x-item-a');
+ makeElement('x-menu');
+ makeElement('x-group');
+ makeElement('x-menu-group');
+ makeElement('x-item-b');
+ });
+ test('mixins are re-evaluated with element upgrade', function() {
+ function checkBg(node) {
+ var itemA = node.querySelector('x-item-a');
+ var itemB = node.querySelector('x-item-b');
+ var itemA_BG = getComputedStyle(itemA)['background-color'].trim();
+ var itemB_BG = getComputedStyle(itemB)['background-color'].trim();
+ assert.equal(itemA_BG, itemB_BG, 'x-item-a and x-item-b should have the same background color');
+ }
+ checkBg(document.querySelector('div'));
+ checkBg(document.querySelector('x-menu').shadowRoot);
+ checkBg(document.querySelector('x-menu-group').shadowRoot.querySelector('x-group').shadowRoot);
+ });
+ test('dynamically updates', function() {
+ makeElement('x-dynamic');
+ makeElement('x-dynamic-container');
+ var container = document.createElement('x-dynamic-container');
+ document.body.appendChild(container);
+ if (window.ShadyDOM) {
+ ShadyDOM.flush();
+ }
+ assert.equal(getComputedStyle(container.shadowRoot.querySelector('x-dynamic'))['background-color'].trim(), 'rgb(123, 123, 123)');
+ });
+ });
+ </script>
+</body>
+
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/css-parse.js b/catapult/third_party/polymer/components/shadycss/tests/module/css-parse.js
new file mode 100644
index 00000000..386ff297
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/css-parse.js
@@ -0,0 +1,17 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import * as CssParse from '../../src/css-parse'
+
+window['CssParse'] = CssParse;
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/custom-style-element.js b/catapult/third_party/polymer/components/shadycss/tests/module/custom-style-element.js
new file mode 100644
index 00000000..b306989c
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/custom-style-element.js
@@ -0,0 +1,15 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import '../../examples/custom-style-element'
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/make-element.js b/catapult/third_party/polymer/components/shadycss/tests/module/make-element.js
new file mode 100644
index 00000000..45eb3b3e
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/make-element.js
@@ -0,0 +1,35 @@
+/**
+@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
+*/
+
+/*
+A simple webcomponents helper
+*/
+'use strict';
+
+window.makeElement = (name, connectedCallback) => {
+ let template = document.querySelector(`template#${name}`);
+ if (template && window.ShadyCSS) {
+ window.ShadyCSS.prepareTemplate(template, name);
+ }
+ window.customElements.define(name, class extends window.HTMLElement {
+ connectedCallback() {
+ window.ShadyCSS && window.ShadyCSS.styleElement(this);
+ if (!this.shadowRoot) {
+ this.attachShadow({mode: 'open'});
+ if (template) {
+ this.shadowRoot.appendChild(template.content.cloneNode(true));
+ }
+ }
+ if (connectedCallback) {
+ connectedCallback.call(this);
+ }
+ }
+ });
+};
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/style-cache.js b/catapult/third_party/polymer/components/shadycss/tests/module/style-cache.js
new file mode 100644
index 00000000..cb5a46cf
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/style-cache.js
@@ -0,0 +1,16 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import StyleCache from '../../src/style-cache'
+window['StyleCache'] = StyleCache;
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/style-info.js b/catapult/third_party/polymer/components/shadycss/tests/module/style-info.js
new file mode 100644
index 00000000..31e31843
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/style-info.js
@@ -0,0 +1,16 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import StyleInfo from '../../src/style-info'
+window['StyleInfo'] = StyleInfo;
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/style-placeholder.js b/catapult/third_party/polymer/components/shadycss/tests/module/style-placeholder.js
new file mode 100644
index 00000000..0c01f4b0
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/style-placeholder.js
@@ -0,0 +1,15 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import '../../src/style-placeholder'
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/style-properties.js b/catapult/third_party/polymer/components/shadycss/tests/module/style-properties.js
new file mode 100644
index 00000000..054c4e73
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/style-properties.js
@@ -0,0 +1,16 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import StyleProperties from '../../src/style-properties'
+window['StyleProperties'] = StyleProperties;
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/style-settings.js b/catapult/third_party/polymer/components/shadycss/tests/module/style-settings.js
new file mode 100644
index 00000000..b73830b7
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/style-settings.js
@@ -0,0 +1,16 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import * as StyleSettings from '../../src/style-settings'
+window['StyleSettings'] = StyleSettings;
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/style-transformer.js b/catapult/third_party/polymer/components/shadycss/tests/module/style-transformer.js
new file mode 100644
index 00000000..77a4097a
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/style-transformer.js
@@ -0,0 +1,16 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import StyleTransformer from '../../src/style-transformer'
+window['StyleTransformer'] = StyleTransformer;
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/style-util.js b/catapult/third_party/polymer/components/shadycss/tests/module/style-util.js
new file mode 100644
index 00000000..6b59a18c
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/style-util.js
@@ -0,0 +1,16 @@
+/**
+@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
+*/
+
+/*
+A simple shim to generate a testable module
+*/
+
+import * as StyleUtil from '../../src/style-util'
+window['StyleUtil'] = StyleUtil;
diff --git a/catapult/third_party/polymer/components/shadycss/tests/module/svg-in-shadow.js b/catapult/third_party/polymer/components/shadycss/tests/module/svg-in-shadow.js
new file mode 100644
index 00000000..502cd702
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/module/svg-in-shadow.js
@@ -0,0 +1,43 @@
+/**
+@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
+*/
+
+'use strict';
+
+const ShadyCSS = window.ShadyCSS;
+
+window.registerSVGElement = () => {
+ const LOCAL_NAME = 'svg-in-shadow';
+ const TEMPLATE = document.querySelector(`template#${LOCAL_NAME}`);
+ ShadyCSS.prepareTemplate(TEMPLATE, LOCAL_NAME);
+
+ class SVGInShadow extends window.HTMLElement {
+ connectedCallback() {
+ ShadyCSS.styleElement(this);
+ this.attachShadow({mode: 'open'});
+ this.shadowRoot.appendChild(document.importNode(TEMPLATE.content, true));
+ }
+
+ get svg() {
+ return this.shadowRoot.querySelector('svg');
+ }
+
+ addCircle() {
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
+ const x = 10 + Math.floor(80 * Math.random());
+ const y = 10 + Math.floor(80 * Math.random());
+ circle.setAttribute('cx', String(x));
+ circle.setAttribute('cy', String(y));
+ circle.setAttribute('r', '10');
+ this.svg.appendChild(circle);
+ return circle;
+ }
+ }
+ window.customElements.define(LOCAL_NAME, SVGInShadow);
+}; \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-late.html b/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-late.html
new file mode 100644
index 00000000..7011498d
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-late.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../../node_modules/@webcomponents/template/template.js"></script>
+<script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../../custom-style-interface.min.js"></script>
+<script src="../module/generated/make-element.js"></script>
+<script src="../module/generated/custom-style-element.js"></script>
+<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+
+<template id="late">
+ <custom-style class="late-style">
+ <style>
+ html {
+ --late: 2px solid red;
+ }
+ </style>
+ </custom-style>
+</template>
+
+<template id="x-late">
+ <style>
+ :host {
+ display: block;
+ border: var(--late);
+ }
+ </style>
+ <div>late</div>
+</template>
+
+<template id="x-host">
+ <style>
+ :host {
+ display: block;
+ padding: 4px;
+ }
+ </style>
+ <x-late></x-late>
+</template>
+
+<x-host></x-host>
+
+<script>
+suite('Async custom-style', function() {
+ suiteSetup(function() {
+ makeElement('x-host');
+ });
+ test('late custom-style updates elements', function(done) {
+ var lateTemplate = document.querySelector('template#late');
+ var host = document.querySelector('x-host');
+ var inner = host.shadowRoot.querySelector('x-late');
+ requestAnimationFrame(function() {
+ document.body.appendChild(document.importNode(lateTemplate.content, true));
+ makeElement('x-late');
+ requestAnimationFrame(function() {
+ assert.equal(getComputedStyle(inner).borderTopWidth.trim(), '2px');
+ done();
+ });
+ });
+ })
+})
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-only.html b/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-only.html
new file mode 100644
index 00000000..59cf219b
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style-only.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+
+<script src=".././test-flags.js"></script>
+<script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../../node_modules/@webcomponents/template/template.js"></script>
+<script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+</script>
+<script src="../../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script>
+ if (window.customElements && customElements.polyfillWrapFlushCallback) {
+ // delay definition of custom-style until after template polyfill loads
+ customElements.polyfillWrapFlushCallback(function (cb) {
+ HTMLImports.whenReady(cb);
+ });
+ }
+</script>
+<script src="../../scoping-shim.min.js"></script>
+<script src="../../custom-style-interface.min.js"></script>
+<script src="../module/generated/make-element.js"></script>
+<script src="../module/generated/custom-style-element.js"></script>
+<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+
+<custom-style>
+ <style>
+ #target {
+ display: block;
+ border: var(--late);
+ }
+ </style>
+</custom-style>
+
+<template id="late">
+ <custom-style class="late-style">
+ <style>
+ html {
+ --late: 2px solid red;
+ }
+ </style>
+ </custom-style>
+</template>
+
+<div id="target"></div>
+
+<script>
+suite('custom-style only', function() {
+ var host = document.querySelector('#target');
+ test('custom-style by itself works as expected', function() {
+ assert.equal(getComputedStyle(host).getPropertyValue('display').trim(), 'block');
+ });
+ test('late custom-style updates styling', function(done) {
+ var lateTemplate = document.querySelector('template#late');
+ document.body.appendChild(document.importNode(lateTemplate.content, true));
+ // two rAF to wait for after custom-style-interface's batching
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ assert.equal(getComputedStyle(host).borderTopWidth.trim(), '2px');
+ done();
+ });
+ });
+ })
+})
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style.html b/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style.html
new file mode 100644
index 00000000..0794a41a
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-applyshim/custom-style.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../../node_modules/@webcomponents/template/template.js"></script>
+<script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../../custom-style-interface.min.js"></script>
+<script src="../module/generated/make-element.js"></script>
+<script src="../module/generated/custom-style-element.js"></script>
+
+<custom-style>
+ <style>
+ html {
+ --foo: rgb(123, 123, 123);
+ }
+ </style>
+</custom-style>
+
+<template id="x-inner">
+ <style>
+ :host {
+ display: block;
+ height: 100px;
+ width: 100px;
+ border: 4px solid blue;
+ background-color: var(--foo);
+ }
+ </style>
+</template>
+
+<template id="x-outer">
+ <style>
+ :host {
+ display: block;
+ }
+ </style>
+ <x-inner></x-inner>
+</template>
+
+<x-outer id="target"></x-outer>
+
+<script>
+ suite('Custom Style upgrades', function() {
+ suiteSetup(function() {
+ makeElement('x-inner');
+ makeElement('x-outer');
+ });
+ test('custom-style applies to deeply nested elements', function() {
+ var target = document.querySelector('#target');
+ var inner = target.shadowRoot.querySelector('x-inner');
+ assert.equal(getComputedStyle(inner).backgroundColor, 'rgb(123, 123, 123)');
+ });
+ });
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/apply-shim.html b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/apply-shim.html
new file mode 100644
index 00000000..bac2b38c
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/apply-shim.html
@@ -0,0 +1,309 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<html>
+<head>
+
+ <meta charset="utf-8">
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../../apply-shim.min.js"></script>
+ <script src="../../custom-style-interface.min.js"></script>
+ <script src="../module/generated/make-element.js"></script>
+ <script src="../module/generated/custom-style-element.js"></script>
+ <script src="../module/generated/style-util.js"></script>
+ <title>Apply Shim</title>
+
+</head>
+<body>
+ <template id="basic">
+ <style>
+ :host {
+ --mixin: {
+ border: 2px solid black;
+ };
+ }
+ div {
+ @apply --mixin;
+ }
+ </style>
+ </template>
+
+ <template id="defaults">
+ <style>
+ :host {
+ --mixin: {
+ border: 2px solid black;
+ }
+ }
+ div {
+ border: 1px dotted orange;
+ @apply --mixin;
+ }
+ span {
+ border: inherit;
+ @apply --mixin;
+ }
+ span {
+ border: initial;
+ @apply --mixin;
+ }
+ </style>
+ </template>
+
+ <template id="override">
+ <style>
+ :host {
+ --override: {
+ padding: 2px;
+ };
+ }
+ :host([override]) {
+ --override: {
+ border: 2px solid black;
+ };
+ }
+ div {
+ @apply --override;
+ }
+ </style>
+ </template>
+
+ <template id="override-with-property">
+ <style>
+ :root {
+ --prop-mixin: {
+ border: 2px solid black;
+ };
+ }
+ x-foo {
+ --prop-mixin: blue;
+ color: var(--prop-mixin);
+ }
+ div {
+ @apply --prop-mixin;
+ }
+ </style>
+ </template>
+
+ <template id="define-with-var">
+ <style>
+ :root {
+ --mixin-var: {
+ border: 2px solid black;
+ };
+ }
+ div {
+ --mixin-var2: var(--mixin-var);
+ }
+ span {
+ --mixin-var: 20px;
+ --variable: var(--mixin-var);
+ }
+ </style>
+ </template>
+
+ <template id="x-element">
+ <style>
+ :host {
+ @apply --my-mixin;
+ }
+ </style>
+ </template>
+
+ <template id="x-element2">
+ <custom-style>
+ <style>
+ html {
+ --my-mixin: {
+ border: 2px solid black;
+ };
+ }
+ </style>
+ </custom-style>
+ </template>
+
+ <template id="css-build" css-build="shadow">
+ <style>:host{@apply --fake;}</style>
+ </template>
+
+ <template id="css-build-comment"><!--css-build:shadow-->
+ <style>:host{@apply --fake;}</style>
+ </template>
+
+ <script>
+ suite('Apply Shim', function() {
+ function copy(name) {
+ var template = document.querySelector('template#' + name);
+ return template.content.cloneNode(true);
+ }
+
+ function prep(templateName, elementName) {
+ var style = copy(templateName).querySelector('style');
+ var ast = window.ShadyCSS.ApplyShim.transformStyle(style, elementName);
+ return {style: style, ast: ast};
+ }
+
+ suite('Basic', function() {
+ var style, ast;
+ suiteSetup(function() {
+ var info = prep('basic');
+ style = info.style;
+ ast = info.ast;
+ style.textContent = window.StyleUtil.toCssText(ast);
+ });
+
+ test('style is transformed', function() {
+ var orig = copy('basic').querySelector('style');
+ assert.notEqual(style.textContent, orig.textContent);
+ });
+
+ test('mixin became custom properties', function() {
+ var definition = ast.rules[0];
+ var application = ast.rules[1];
+ assert.match(definition.cssText, /--mixin_-_border:\s*2px solid black/);
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border\)/);
+ });
+ });
+ suite('Defaults', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('defaults');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('properties defined before mixin are used as defaults', function() {
+ var application = ast.rules[1];
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*1px dotted orange\)/);
+ });
+
+ test('inherit and initial default values are preserved', function () {
+ var application = ast.rules[2];
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*inherit\)/);
+ application = ast.rules[3];
+ assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*initial\)/);
+ });
+ });
+
+ suite('override', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('override');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('mixin redefinition sets unused properties to initial', function() {
+ var def1 = ast.rules[0];
+ assert.match(def1.cssText, /--override_-_padding:\s*2px/);
+ var def2 = ast.rules[1];
+ assert.match(def2.cssText, /--override_-_padding:\s*initial/);
+ assert.match(def2.cssText, /--override_-_border:\s*2px solid black/);
+ });
+
+ test('mixin application includes all values', function() {
+ var application = ast.rules[2];
+ assert.match(application.cssText, /padding:\s*var\(--override_-_padding\)/);
+ assert.match(application.cssText, /border:\s*var\(--override_-_border\)/);
+ });
+ });
+
+ suite('override with property', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('override-with-property');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('mixin definition defers to property definition', function() {
+ var def = ast.rules[1];
+ assert.notMatch(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
+ });
+
+ test('mixin can still be used by other parts of the page', function() {
+ var def = ast.rules[2];
+ assert.match(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
+ });
+ });
+
+ suite('define with var()', function() {
+ var style, ast; // eslint-disable-line no-unused-vars
+ suiteSetup(function() {
+ var info = prep('define-with-var');
+ style = info.style;
+ ast = info.ast;
+ });
+
+ test('mixin-var2 is defined with mixin-var\'s values', function() {
+ var def = ast.rules[1];
+ assert.match(def.cssText, /--mixin-var2_-_border:\s*var\(--mixin-var_-_border\)/);
+ });
+
+ test('var usage of mixin is not removed, preserving override functionality', function() {
+ var def = ast.rules[2];
+ assert.match(def.cssText, /--variable:\s*var\(--mixin-var\)/);
+ });
+ });
+
+ suite('invalidation on new definitions', function() {
+ var style, ast, element;
+ suiteSetup(function() {
+ makeElement('x-element');
+ element = document.createElement('x-element');
+ document.body.appendChild(element);
+ style = element.shadowRoot ? element.shadowRoot.querySelector('style') : document.head.querySelector('style[scope=x-element]');
+ });
+
+ test('element initially has no definition', function() {
+ var ast = window.StyleUtil.rulesForStyle(style);
+ assert.equal(ast.rules[0].cssText, ';');
+ });
+
+ test('Revalidating Apply Shim on element template fills in properties', function() {
+ var nodes = copy('x-element2');
+ document.body.appendChild(nodes);
+ window.ShadyCSS.styleDocument();
+ var ast = window.StyleUtil.rulesForStyle(style);
+ assert.match(ast.rules[0].cssText, /border:\s*var\(--my-mixin_-_border\)/);
+ });
+ });
+
+ test('templates with "css-build" will not be processed by ApplyShim', function() {
+ makeElement('css-build');
+ const template = document.querySelector('template#css-build');
+ assert.equal(template._styleAst, undefined);
+ const style = template.content.querySelector('style');
+ assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
+ });
+
+ test('templates with "css-build" comment will not be processed by ApplyShim', function () {
+ const template = document.querySelector('template#css-build-comment');
+ const buildComment = template.content.firstChild;
+ assert.instanceOf(buildComment, Comment, 'first node in template content should be a Comment');
+ makeElement('css-build-comment');
+ assert.equal(template._styleAst, undefined);
+ const style = template.content.querySelector('style');
+ assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
+ assert.equal(buildComment.parentNode, null, 'build comment should be removed');
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/complicated-mixin-ordering.html b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/complicated-mixin-ordering.html
new file mode 100644
index 00000000..7fb84257
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/complicated-mixin-ordering.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<html>
+
+<head>
+
+ <meta charset="utf-8">
+ <script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+ </script>
+ <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../../apply-shim.min.js"></script>
+ <script src="../../custom-style-interface.min.js"></script>
+ <script src="../module/generated/make-element.js"></script>
+ <script src="../module/generated/custom-style-element.js"></script>
+ <script src="../module/generated/style-util.js"></script>
+ <title>Complicated Order</title>
+
+</head>
+
+<body>
+ <template id="child-element">
+ <style>
+ p {
+ @apply --my-mixin;
+ }
+ </style>
+ <p>I'm a DOM element. This is my local DOM!</p>
+ </template>
+
+ <template id="container-element">
+ <style>
+ child-element {
+ --my-mixin: {
+ background-color: rgb(255, 0, 0);
+ }
+ }
+ </style>
+ <child-element></child-element>
+ </template>
+
+ <template id="other-container-element">
+ <style>
+ child-element {
+ --my-mixin: {
+ font-size: 40px;
+ background-color: rgb(0, 255, 0);
+ }
+ }
+ </style>
+ <child-element></child-element>
+ </template>
+
+ <container-element></container-element>
+ <other-container-element></other-container-element>
+
+ <script>
+ suite('Complicated Order', () => {
+ function assertComputed(node, property, expectedValue, msg) {
+ assert.equal(getComputedStyle(node).getPropertyValue(property).trim(), expectedValue, msg);
+ }
+ suiteSetup(() => {
+ makeElement('child-element');
+ makeElement('container-element');
+ makeElement('other-container-element');
+ });
+ test('complicated ordering works as expected', () => {
+ let initialFontSize = getComputedStyle(document.head).getPropertyValue('font-size').trim();
+ let con = document.querySelector('container-element');
+ let oth = document.querySelector('other-container-element');
+ assertComputed(con.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'background-color', 'rgb(255, 0, 0)');
+ assertComputed(con.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'font-size', initialFontSize);
+ assertComputed(oth.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'background-color', 'rgb(0, 255, 0)');
+ assertComputed(oth.shadowRoot.querySelector('child-element').shadowRoot.querySelector('p'), 'font-size', '40px');
+ con.parentNode.removeChild(con);
+ oth.parentNode.removeChild(oth);
+ })
+ })
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-late.html b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-late.html
new file mode 100644
index 00000000..c2a548f3
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-late.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../../node_modules/@webcomponents/template/template.js"></script>
+<script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../../apply-shim.min.js"></script>
+<script src="../../custom-style-interface.min.js"></script>
+<script src="../module/generated/make-element.js"></script>
+<script src="../module/generated/custom-style-element.js"></script>
+<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+
+<template id="late">
+ <custom-style class="late-style">
+ <style>
+ html {
+ --late: {
+ border: 2px solid red;
+ };
+ }
+ </style>
+ </custom-style>
+</template>
+
+<template id="x-late">
+ <style>
+ :host {
+ display: block;
+ @apply --late;
+ }
+ </style>
+ <div>late</div>
+</template>
+
+<template id="x-host">
+ <style>
+ :host {
+ display: block;
+ padding: 4px;
+ }
+ </style>
+ <x-late></x-late>
+</template>
+
+<x-host></x-host>
+
+<script>
+suite('Async custom-style', function() {
+ suiteSetup(function() {
+ makeElement('x-host');
+ });
+ test('late custom-style updates elements', function(done) {
+ var lateTemplate = document.querySelector('template#late');
+ var host = document.querySelector('x-host');
+ var inner = host.shadowRoot.querySelector('x-late');
+ requestAnimationFrame(function() {
+ document.body.appendChild(document.importNode(lateTemplate.content, true));
+ makeElement('x-late');
+ requestAnimationFrame(function() {
+ assert.equal(getComputedStyle(inner).borderTopWidth.trim(), '2px');
+ done();
+ });
+ });
+ })
+})
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-only.html b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-only.html
new file mode 100644
index 00000000..28b59eed
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style-only.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script src=".././test-flags.js"></script>
+<script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../../node_modules/@webcomponents/template/template.js"></script>
+<script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+</script>
+<script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../../apply-shim.min.js"></script>
+<script>
+ if (window.customElements && customElements.polyfillWrapFlushCallback) {
+ // delay definition of custom-style until after template polyfill loads
+ customElements.polyfillWrapFlushCallback(function (cb) {
+ HTMLImports.whenReady(cb);
+ });
+ }
+</script>
+<script src="../../custom-style-interface.min.js"></script>
+<script src="../module/generated/make-element.js"></script>
+<script src="../module/generated/custom-style-element.js"></script>
+<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+
+
+<custom-style>
+ <style>
+ #target {
+ display: block;
+ @apply --late;
+ }
+ </style>
+</custom-style>
+
+<template id="late">
+ <custom-style class="late-style">
+ <style>
+ html {
+ --late: {
+ border: 2px solid red;
+ };
+ }
+ </style>
+ </custom-style>
+</template>
+
+<div id="target"></div>
+
+<script>
+suite('custom-style only', function() {
+ var host = document.querySelector('#target');
+ test('custom-style by itself works as expected', function() {
+ assert.equal(getComputedStyle(host).getPropertyValue('display').trim(), 'block');
+ });
+ test('late custom-style updates styling', function(done) {
+ var lateTemplate = document.querySelector('template#late');
+ document.body.appendChild(document.importNode(lateTemplate.content, true));
+ // two rAF to wait for after custom-style-interface's batching
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ assert.equal(getComputedStyle(host).borderTopWidth.trim(), '2px');
+ done();
+ });
+ });
+ })
+})
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style.html b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style.html
new file mode 100644
index 00000000..8f8dfc7e
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/custom-style.html
@@ -0,0 +1,80 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../../node_modules/@webcomponents/template/template.js"></script>
+<script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../../apply-shim.min.js"></script>
+<script src="../../custom-style-interface.min.js"></script>
+<script src="../module/generated/make-element.js"></script>
+<script src="../module/generated/custom-style-element.js"></script>
+
+<custom-style>
+ <style>
+ html {
+ --foo: rgb(123, 123, 123);
+ --bar: {
+ border: 2px solid red;
+ }
+ }
+ </style>
+</custom-style>
+
+<template id="x-inner">
+ <style>
+ :host {
+ display: block;
+ height: 100px;
+ width: 100px;
+ border: 4px solid blue;
+ background-color: var(--foo);
+ @apply --bar;
+ }
+ </style>
+</template>
+
+<template id="x-outer">
+ <style>
+ :host {
+ display: block;
+ @apply --bar;
+ }
+ </style>
+ <x-inner></x-inner>
+</template>
+
+<x-outer id="target"></x-outer>
+
+<script>
+ suite('Custom Style upgrades', function() {
+ suiteSetup(function() {
+ makeElement('x-inner');
+ makeElement('x-outer');
+ });
+ test('custom-style applies to deeply nested elements', function() {
+ var target = document.querySelector('#target');
+ var inner = target.shadowRoot.querySelector('x-inner');
+ assert.equal(getComputedStyle(inner).backgroundColor, 'rgb(123, 123, 123)');
+ });
+ test('custom-style applied mixins update', function() {
+ var target = document.querySelector('#target');
+ var inner = target.shadowRoot.querySelector('x-inner');
+ assert.equal(getComputedStyle(target).borderTopWidth.trim(), '2px');
+ assert.equal(getComputedStyle(inner).borderTopWidth.trim(), '2px');
+ });
+ });
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/mixin-ordering.html b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/mixin-ordering.html
new file mode 100644
index 00000000..8914f026
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/no-scopingshim/mixin-ordering.html
@@ -0,0 +1,141 @@
+<!doctype html>
+<head>
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../../apply-shim.min.js"></script>
+ <script src="../../custom-style-interface.min.js"></script>
+ <script src="../module/generated/make-element.js"></script>
+</head>
+<body>
+ <div>
+ <x-item-a>item A</x-item-a>
+ <x-item-b>item B</x-item-b>
+ </div>
+ <x-menu>
+ </x-menu>
+ <x-menu-group>
+ </x-menu-group>
+
+ <template id="x-item-a">
+ <style>
+ :host {
+ display:block;
+ background: rgb(255, 255, 255);
+ @apply --item-mixin;
+ }
+ </style>
+ <slot></slot>
+ </template>
+
+ <template id="x-menu">
+ <style>
+ :host {
+ display:block;
+ border:1px solid black;
+ margin:2px;
+ --item-mixin:{background:rgb(0, 0, 255);};
+ }
+ </style>
+ <x-item-a>menu item A</x-item-a>
+ <x-item-b>menu item B</x-item-b>
+ </template>
+
+ <template id="x-group">
+ <style>
+ :host{
+ display:block;
+ --item-mixin:{background:rgb(255, 0, 0);};
+ }
+ </style>
+ <x-item-a>group item A</x-item-a>
+ <x-item-b>group item B</x-item-b>
+ </template>
+
+ <template id="x-menu-group">
+ <style>
+ :host {
+ display:block;
+ border:1px solid black;
+ margin:2px;
+ --item-mixin:{background:rgb(0, 0, 255);};
+ }
+ </style>
+ <x-group></x-group>
+ </template>
+
+ <template id="x-item-b">
+ <style>
+ :host {
+ display:block;
+ background: rgb(255, 255, 255);
+ @apply --item-mixin;
+ }
+ </style>
+ <slot></slot>
+ </template>
+
+ <template id="x-dynamic">
+ <style>
+ :host {
+ display: block;
+ background: rgb(255, 255, 255);
+ @apply --mixin;
+ }
+ </style>
+ <span>dynamic item</span>
+ </template>
+
+ <template id="x-dynamic-container">
+ <style>
+ :host {
+ --mixin: {
+ background-color: rgb(123, 123, 123);
+ };
+ }
+ </style>
+ <x-dynamic></x-dynamic>
+ </template>
+
+ <script>
+ suite('Mixin Ordering', function() {
+ suiteSetup(function() {
+ makeElement('x-item-a');
+ makeElement('x-menu');
+ makeElement('x-group');
+ makeElement('x-menu-group');
+ makeElement('x-item-b');
+ });
+ test('mixins are re-evaluated with element upgrade', function() {
+ function checkBg(node) {
+ var itemA = node.querySelector('x-item-a');
+ var itemB = node.querySelector('x-item-b');
+ var itemA_BG = getComputedStyle(itemA)['background-color'].trim();
+ var itemB_BG = getComputedStyle(itemB)['background-color'].trim();
+ assert.equal(itemA_BG, itemB_BG, 'x-item-a and x-item-b should have the same background color');
+ }
+ checkBg(document.querySelector('div'));
+ checkBg(document.querySelector('x-menu').shadowRoot);
+ checkBg(document.querySelector('x-menu-group').shadowRoot.querySelector('x-group').shadowRoot);
+ });
+ test('dynamically updates', function() {
+ makeElement('x-dynamic');
+ makeElement('x-dynamic-container');
+ var container = document.createElement('x-dynamic-container');
+ document.body.appendChild(container);
+ if (window.ShadyDOM) {
+ ShadyDOM.flush();
+ }
+ assert.equal(getComputedStyle(container.shadowRoot.querySelector('x-dynamic'))['background-color'].trim(), 'rgb(123, 123, 123)');
+ });
+ });
+ </script>
+</body>
+
diff --git a/catapult/third_party/polymer/components/shadycss/tests/ordering.html b/catapult/third_party/polymer/components/shadycss/tests/ordering.html
new file mode 100644
index 00000000..392897c4
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/ordering.html
@@ -0,0 +1,173 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<script>WCT = {waitFor(cb){addEventListener('DOMContentLoaded', cb)}};</script>
+<script src="test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="module/generated/make-element.js"></script>
+
+<custom-style>
+ <style>
+ html {
+ --coloring: {
+ color: rgb(0, 0, 255);
+ background-color: rgb(255, 0, 0);
+ }
+ }
+ </style>
+</custom-style>
+
+<custom-style>
+ <style>
+ html {
+ --border: 2px solid black;
+ }
+ </style>
+</custom-style>
+
+<template id="x-inner">
+ <style>
+ :host {
+ @apply --inner;
+ @apply --coloring;
+ }
+ </style>
+<slot></slot>
+</template>
+
+<template id="x-element">
+ <style>
+ :host {
+ display: block;
+ border: var(--border);
+ @apply --coloring;
+ }
+ x-inner {
+ --inner: {
+ border: 10px solid black;
+ }
+ }
+ </style>
+ <div>What color?</div>
+ <x-inner>Inner</x-inner>
+</template>
+
+<template id="x-early">
+ <style>
+ :host {
+ display: block;
+ background: rgb(123, 123, 123);
+ color: rgb(255, 165, 0);
+ border: var(--border, 10px dotted blue);
+ @apply --coloring;
+ }
+ </style>
+ <div>Early!</div>
+</template>
+
+<x-early></x-early>
+
+<script>
+ function loadScript(src) {
+ console.log(`loading ${src}`);
+ let script = document.createElement('script')
+ script.src = src;
+ let p = new Promise((resolve, reject) => {
+ script.onload = () => {console.log(`loaded ${src}`); resolve()};
+ script.onerror = () => {console.error(`error ${src}`); reject()};
+ });
+ document.head.appendChild(script);
+ return p;
+ }
+
+ function registerEarly() {
+ makeElement('x-early');
+ }
+
+ function loadScopingShim() {
+ return loadScript('../scoping-shim.min.js');
+ }
+
+ function loadCustomStyle() {
+ return loadScript('../custom-style-interface.min.js').then(() => {
+ return loadScript('module/generated/custom-style-element.js')
+ });
+ }
+
+ function loadApplyShim() {
+ return loadScript('../apply-shim.min.js');
+ }
+
+ function register() {
+ makeElement('x-inner');
+ makeElement('x-element');
+ }
+
+ function assertComputed(element, property, expected) {
+ let value = (getComputedStyle(element).getPropertyValue(property) || '').trim();
+ assert.equal(expected, value, `${property} on ${element.localName} incorrect`);
+ }
+
+ function chain(arr) {
+ let out = Promise.resolve();
+ for (let i = 0; i < arr.length; i++) {
+ out = out.then(arr[i]);
+ }
+ return out;
+ }
+
+ let orderSteps = {
+ scoping: loadScopingShim,
+ early: registerEarly,
+ apply: loadApplyShim,
+ custom: loadCustomStyle,
+ };
+
+ /**
+ * Support the following permutations of loading via url query params:
+ *
+ * Apply Shim, CustomStyle
+ * Scoping Shim, Apply Shim, Custom Style
+ * Apply Shim, Element, CustomStyle
+ * Scoping Shim, Element, Apply Shim, Custom Style
+ * Scoping Shim, Apply Shim, Element, Custom Style
+ */
+
+ suite('Dynamic ordering of components', () => {
+ let flags = window.WebComponents.flags;
+ let order = decodeURIComponent(flags.order || 'scoping,apply,custom').split(',');
+ let steps = chain(order.map(o => orderSteps[o]));
+ let otherFlags = `${flags.ce ? 'ce' : ''} ${flags.shadydom ? 'shadydom' : ''} ${flags.shimcssproperties ? 'shimcssproperties' : ''}`;
+ let needsScopingShim = window.ShadyDOM && window.ShadyDOM.inUse || flags.shimcssproperties;
+
+ test(`${order.join(', ')} with ${otherFlags}`, function() {
+ console.log(order, flags);
+ if (order.indexOf('scoping') === -1 && needsScopingShim) {
+ this.skip();
+ }
+ return steps.then(
+ register
+ ).then(() => {
+ let el = document.createElement('x-element');
+ document.body.appendChild(el);
+ assertComputed(el, 'background-color', 'rgb(255, 0, 0)');
+ assertComputed(el, 'border-top-width', '2px');
+ assertComputed(el.shadowRoot.querySelector('div'), 'color', 'rgb(0, 0, 255)');
+ assertComputed(el.shadowRoot.querySelector('x-inner'), 'border-top-width', '10px');
+ });
+ })
+ })
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/placeholder-ordering.html b/catapult/third_party/polymer/components/shadycss/tests/placeholder-ordering.html
new file mode 100644
index 00000000..33eecdbb
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/placeholder-ordering.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="module/generated/make-element.js"></script>
+</head>
+<body>
+ <template id="x-a">
+ <style>
+ :host {
+ display: block;
+ }
+ </style>
+ </template>
+ <template id="x-b">
+ <style>
+ :host {
+ display: block;
+ }
+ </style>
+ </template>
+ <template id="x-c">
+ <style>
+ :host {
+ display: block;
+ }
+ </style>
+ </template>
+ <script>
+ suite('placeholder ordering', function() {
+ test.skip('placeholders are in order', function() {
+ makeElement('x-a');
+ makeElement('x-b');
+ var el = document.createElement('x-a');
+ document.body.appendChild(el);
+ makeElement('x-c');
+ var styles = Array.from(document.querySelectorAll('style[scope]')).map(function(style) {
+ return style.getAttribute('scope');
+ });
+ assert.deepEqual(styles, ['x-a', 'x-b', 'x-c'], 'styles are not in order');
+ })
+ });
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/runner.html b/catapult/third_party/polymer/components/shadycss/tests/runner.html
new file mode 100644
index 00000000..5eaf3784
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/runner.html
@@ -0,0 +1,113 @@
+<!doctype html>
+<!--
+ @license
+ Copyright (c) 2014 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
+-->
+<title>ScopingShim Tests</title>
+<meta charset="utf-8">
+
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+
+<script>
+(function(){
+ var suites = [
+ 'css-parse.html',
+ 'apply-shim.html',
+ 'async-loading.html',
+ 'placeholder-ordering.html',
+ 'scoping.html',
+ 'mixin-ordering.html',
+ 'svg.html',
+ 'style-transformer.html',
+ 'custom-style.html',
+ 'custom-style-late.html',
+ 'custom-style-only.html',
+ 'complicated-mixin-ordering.html',
+ 'dynamic-scoping.html',
+ 'settings.html',
+ 'chrome-devtools.html',
+ 'workarounds.html',
+ 'deferred-apply.html',
+ 'no-applyshim/custom-style-only.html',
+ 'wc-1.html',
+ 'scoping-api.html',
+ 'mixin-fallbacks.html'
+ ];
+
+ // http://eddmann.com/posts/cartesian-product-in-javascript/
+ function flatten(arr) { return [].concat.apply([], arr) }
+ function product(sets) {
+ return sets.reduce(function(acc, set) {
+ return flatten(acc.map(function(x) {
+ return set.map(function(y) { return x.concat(y); });
+ }));
+ }, [[]]);
+ }
+ function combinations(suites, flags) {
+ return product(flags).reduce(function(list, f) {
+ f = f.filter(function(i) { return i; }).join('&');
+ return list.concat(suites.map(function(s) { return s + (f ? '?' + f : '') }))
+ }, []);
+ }
+
+ function addUrlOption(previous, next) {
+ previous = previous || '';
+ next = next || '';
+ return previous + (previous ? '&' : '') + next;
+ }
+
+ // test shadowdom/custom elements polyfills together
+ // prefering both if possible.
+ var matrix = [''];
+ var webcomponents = '';
+ if (window.customElements) {
+ webcomponents = 'wc-register=true';
+ }
+ // if native is available, make sure to test polyfill
+ if (Element.prototype.attachShadow && document.documentElement.getRootNode) {
+ webcomponents = addUrlOption(webcomponents, 'wc-shadydom=true');
+ }
+ // ce + sd becomes a single test iteration.
+ if (webcomponents) {
+ matrix.push(webcomponents);
+ }
+ // economize testing by testing css shimming
+ // only against 1 environment (native or polyfill).
+ if (window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)')) {
+ var last = matrix[matrix.length-1];
+ matrix.push(addUrlOption(last, 'wc-shimcssproperties=true'));
+ }
+ suites = combinations(suites, [matrix]);
+
+ var orderingScenarios = [
+ 'wc-order=apply,custom',
+ 'wc-order=scoping,apply,custom',
+ 'wc-order=apply,early,custom',
+ 'wc-order=scoping,early,apply,custom',
+ 'wc-order=scoping,apply,early,custom'
+ ];
+
+ suites = suites.concat(combinations(['ordering.html'], [matrix, orderingScenarios]));
+
+ if (matrix.length > 2) {
+ suites = suites.concat([
+ 'no-scopingshim/apply-shim.html',
+ 'no-scopingshim/mixin-ordering.html',
+ 'no-scopingshim/custom-style.html',
+ 'no-scopingshim/custom-style-late.html',
+ 'no-scopingshim/complicated-mixin-ordering.html',
+ 'no-scopingshim/custom-style-only.html',
+ 'no-applyshim/custom-style.html',
+ 'no-applyshim/custom-style-late.html'
+ ]);
+ }
+
+ console.log(suites);
+ WCT.loadSuites(suites);
+})();
+</script>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/scoping-api.html b/catapult/third_party/polymer/components/shadycss/tests/scoping-api.html
new file mode 100644
index 00000000..d537ed7c
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/scoping-api.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2018 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
+-->
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <script>
+ WCT = { waitFor(cb) { window.HTMLImports.whenReady(cb) } }
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script>
+ window.ShadyDOM = {force: true}
+ </script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script>
+ // disable document watcher
+ window.ShadyDOM.handlesDynamicScoping = true;
+ </script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/make-element.js"></script>
+</head>
+<body>
+ <template id="sync-element">
+ <style>
+ div {
+ background: rgb(255, 0, 0);
+ border: 10px solid black;
+ }
+ </style>
+ <div id="inner">Test</div>
+ </template>
+ <div id="arena"></div>
+ <script>
+ function assertComputedStyle(node, expectedValue, property = 'border-top-width') {
+ const actualValue = getComputedStyle(node).getPropertyValue(property).trim();
+ assert.equal(actualValue, expectedValue, `${property} does not have the expected value`);
+ }
+ suite('Synchronous Scoping API', function() {
+ const arena = document.querySelector('#arena');
+ const csfn = (node) => {
+ return window.ShadyCSS.ScopingShim.currentScopeForNode(node);
+ };
+ const sfn = (node) => {
+ return window.ShadyCSS.ScopingShim.scopeForNode(node);
+ };
+ const scopeNode = (node, scope) => {
+ window.ShadyCSS.ScopingShim.scopeNode(node, scope);
+ }
+ const unscopeNode = (node, scope) => {
+ window.ShadyCSS.ScopingShim.unscopeNode(node, scope);
+ }
+ let el;
+
+ suiteSetup(function() {
+ makeElement('sync-element');
+ });
+
+ setup(function() {
+ el = document.createElement('sync-element');
+ arena.appendChild(el);
+ });
+
+ teardown(function() {
+ arena.innerHTML = '';
+ });
+
+ test('mutation observer is disabled', function(done) {
+ const inner = el.shadowRoot.querySelector('#inner');
+ arena.appendChild(inner);
+ setTimeout(() => {
+ assertComputedStyle(inner, 'rgb(255, 0, 0)', 'background-color');
+ done();
+ }, 100);
+ });
+
+ test('currentScopeForNode', function() {
+ assert.equal(csfn(el), '', 'sync-scoping should be document scope');
+ const inner = el.shadowRoot.querySelector('#inner');
+ assert.equal(csfn(inner), 'sync-element', 'inner div should have sync-element scope');
+ const disconnected = document.createElement('sync-element');
+ assert.equal(csfn(disconnected), '', 'disconnected element should have a blank scope')
+ const dynamic = document.createElement('div');
+ el.shadowRoot.appendChild(dynamic);
+ assert.equal(csfn(dynamic), '', 'dynamically appended node will not be scoped yet');
+ });
+
+ test('scopeForNode', function() {
+ assert.equal(sfn(el), '', 'sync-scoping should be document scope');
+ const inner = el.shadowRoot.querySelector('#inner');
+ assert.equal(sfn(inner), 'sync-element', 'inner div should have sync-element scope');
+ const disconnected = document.createElement('sync-element');
+ assert.equal(sfn(disconnected), '', 'disconnected element should have a blank scope');
+ const dynamic = document.createElement('div');
+ el.shadowRoot.appendChild(dynamic);
+ assert.equal(sfn(dynamic), 'sync-element', 'dynamically created node should have sync-element scope');
+ });
+
+ test('scopeNode', function() {
+ const div = document.createElement('div');
+ el.shadowRoot.appendChild(div);
+ scopeNode(div, sfn(div));
+ assertComputedStyle(div, '10px');
+ assertComputedStyle(div, 'rgb(255, 0, 0)', 'background-color');
+ });
+
+ test('unscopeNode', function() {
+ const inner = el.shadowRoot.querySelector('#inner');
+ arena.appendChild(inner);
+ unscopeNode(inner, csfn(inner));
+ assertComputedStyle(inner, '0px');
+ });
+ });
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/scoping.html b/catapult/third_party/polymer/components/shadycss/tests/scoping.html
new file mode 100644
index 00000000..eaab8e04
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/scoping.html
@@ -0,0 +1,1058 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/make-element.js"></script>
+
+ <custom-style>
+ <style>
+ div#priority {
+ border: 1px solid black;
+ }
+ </style>
+ </custom-style>
+</head>
+<body>
+
+<template id="x-gchild">
+ <style>
+ </style>
+ <div id="target">x-gchild</div>
+</template>
+
+<template id="x-child">
+ <div id="simple">simple</div>
+ <div id="complex1" class="scoped">complex1</div>
+ <div id="complex2" selected>complex2</div>
+ <div id="media">media</div>
+ <div id="shadow" class="shadowTarget">shadowTarget</div>
+ <div id="deep" class="deepTarget">deepTarget</div>
+ <x-gchild id="gchild1"></x-gchild>
+ <x-gchild id="gchild2" class="wide"></x-gchild>
+</template>
+
+<template id="x-child2">
+ <style>
+ :host(.wide) #target{
+ border: none;
+ }
+ </style>
+ <div id="target">x-child2</div>
+</template>
+
+<template id="x-scope-class">
+ <div id="scope">Trivial</div>
+</template>
+
+<template id="x-scoped">
+ <style>
+ :host {
+ display: block;
+ border: 1px solid orange;
+ --keyframes100: 100px;
+ }
+
+ :host(.wide) {
+ border-width: 2px;
+ }
+
+ :host(.wide)::after {
+ content: '-content-';
+ };
+
+ #keyframes2.special {
+ --keyframes100: 200px;
+ }
+
+ #simple {
+ border: 3px solid orange;
+ }
+
+ .scoped, [selected] {
+ border: 4px solid pink;
+ }
+
+ @media(max-width: 10000px) {
+ .media {
+ border: 5px solid brown;
+ }
+ }
+
+ .container ::slotted(*) {
+ border: 6px solid navy;
+ }
+
+ #priority {
+ border: 9px solid orange;
+ }
+
+ .container1 > ::slotted([slot=content1]) {
+ border: 13px solid navy;
+ }
+
+ .container2 > ::slotted([slot=content2]) {
+ border: 14px solid navy;
+ }
+
+ .computed {
+ border: 15px solid orange;
+ }
+
+ .computeda {
+ border: 20px solid orange;
+ }
+
+ #child {
+ border: 16px solid tomato;
+ display: block;
+ }
+
+ svg {
+ margin-top: 20px;
+ }
+
+ #circle {
+ fill: seagreen;
+ stroke-width: 1px;
+ stroke: tomato;
+ }
+ </style>
+ <slot name="blank"></slot>
+ <div id="simple">simple</div>
+ <div id="complex1" class="scoped">complex1</div>
+ <div id="complex2" selected>complex2</div>
+ <div id="media" class="media">media</div>
+ <div class="container1">
+ <slot name="content1"></slot>
+ </div>
+ <div class="container2">
+ <slot name="content2"></slot>
+ </div>
+ <div class="container">
+ <slot></slot>
+ </div>
+ <x-child id="child"></x-child>
+ <div id="priority">priority</div>
+ <x-child2 class="wide" id="child2"></x-child2>
+ <div id="computed">Computed</div>
+ <svg height="25" width="25">
+ <circle id="circle" cx="12" cy="12" r="10"></circle>
+ </svg>
+ <x-scope-class id="scopeClass"></x-scope-class>
+ <x-keyframes id="keyframes"></x-keyframes>
+ <x-keyframes id="keyframes2"></x-keyframes>
+</template>
+
+<template id="x-slotted">
+ <style>
+ ::slotted(.auto-content) {
+ border: 2px solid orange;
+ }
+ .bar, ::slotted(.complex-child) {
+ border: 6px solid navy;
+ }
+ #container ::slotted(*) {
+ border: 8px solid green;
+ }
+ </style>
+ <slot></slot>
+ <div id="container">
+ <slot name="container"></slot>
+ </div>
+</template>
+
+<template id="dynamic">
+ <div class="added">
+ Added
+ <div class="sub-added">
+ Sub-added
+ </div>
+ </div>
+ </div>
+</template>
+
+<template id="x-dynamic-scope">
+ <style>
+ .added {
+ border: 17px solid beige;
+ }
+ .sub-added {
+ border: 18px solid #fafafa;
+ }
+ </style>
+ <div id="container"></div>
+</template>
+
+<template id="x-keyframes">
+ <style>
+ :host {
+ display: block;
+ position: relative;
+ border: 10px solid blue;
+ left: 0px;
+ /* Prefix required by Safari <= 8 */
+ -webkit-animation-duration: 0.3s;
+ animation-duration: 0.3s;
+ -webkit-animation-fill-mode: forwards;
+ animation-fill-mode: forwards;
+ }
+
+ :host([animated]) {
+ /* Prefix required by Safari <= 8 */
+ -webkit-animation-name: x-keyframes-animation;
+ animation-name: x-keyframes-animation;
+ }
+
+ /* Prefix required by Safari <= 8 */
+ @-webkit-keyframes x-keyframes-animation {
+ 0% {
+ left: var(--keyframes0, 0px);
+ }
+
+ 100% {
+ left: var(--keyframes100, 10px);
+ }
+ }
+ @keyframes x-keyframes-animation {
+ 0% {
+ left: var(--keyframes0, 0px);
+ }
+
+ 100% {
+ left: var(--keyframes100, 10px);
+ }
+ }
+ </style>
+ x-keyframes
+</template>
+
+<template id="x-attr-selector">
+ <style>
+ #foo1 ~ #bar1 {
+ border: 2px solid red;
+ }
+
+ #foo1 ~ #bar1 ~ #foo2[attr~=foo2] ~ #bar2[attr~=bar2] {
+ border: 4px solid red;
+ }
+
+ #foo1 ~ #bar1 ~ #foo2[attr~=foo2] ~ #bar2[attr~=bar2] ~ #foo3[attr~=foo3][a~=a] ~ #bar3[attr~=bar3][a~=a] {
+ border: 6px solid red;
+ }
+ </style>
+ <div id="foo1"></div>
+ <div id="bar1">bar1</div>
+ <div id="foo2" attr="foo2"></div>
+ <div id="bar2" attr="bar2">bar2</div>
+ <div id="foo3" attr="foo3" a="a"></div>
+ <div id="bar3" attr="bar3" a="a">bar3</div>
+</template>
+
+<template id="x-adjacent-sibling">
+ <style>
+ div {
+ border: 20px solid black;
+ }
+ #foo2 + #foo1 {
+ border: 2px solid black;
+ }
+ #foo1 + #foo2 {
+ border: 4px solid black;
+ }
+ #foo2 + #foo3 {
+ border: 6px solid black;
+ }
+ </style>
+ <div id="foo1"></div>
+ <div id="foo2"></div>
+ <div id="foo3"></div>
+</template>
+
+<template id="svg">
+ <svg class="svg" viewBox="0 0 24 24">
+ <circle id="circle" r="12" cx="12" cy="12" />
+ </svg>
+</template>
+
+<template id="x-dynamic-svg">
+ <style>
+ .svg {
+ height: 24px;
+ width: 24px;
+ }
+ #circle {
+ fill: red;
+ fill-opacity: 0.5;
+ }
+ </style>
+ <div id="container"></div>
+</template>
+
+<template id="x-specificity">
+ <style>
+ :host {
+ border-top: 1px solid red;
+ }
+ :host(.bar) {
+ border-top: 2px solid red;
+ }
+ </style>
+ <slot></slot>
+</template>
+
+<template id="self-test">
+ <style>
+ :host {
+ --border: 10px solid rgb(123, 123, 123);
+ }
+
+ a {
+ border: var(--border);
+ }
+ </style>
+ <a>I should be red.</a>
+</template>
+
+<template id="nth-plus-one">
+ <style>
+ .foo.bar {
+ color: rgb(255, 0, 0);
+ }
+ div:nth-child(n+1) {
+ color: rgb(0, 255, 0);
+ }
+ </style>
+ <div>1</div>
+ <div class="foo bar">2</div>
+</template>
+
+<template id="shady-unscoped">
+ <style shady-unscoped>
+ .unscoped {
+ color: rgb(255, 0, 0);
+ }
+ </style>
+ <div class="unscoped"></div>
+</template>
+
+<template id="shady-unscoped-2">
+ <style shady-unscoped>
+ .unscoped {
+ color: rgb(255, 0, 0);
+ }
+ </style>
+ <span class="unscoped"></span>
+</template>
+
+<template id="unscoped-apply-user">
+ <style>
+ div {
+ @apply --unscoped-foo;
+ }
+ </style>
+ <div></div>
+</template>
+
+<template id="unscoped-apply">
+ <style shady-unscoped>
+ html, :host > * {
+ --unscoped-foo: {border: 10px solid black};
+ }
+ </style>
+ <unscoped-apply-user></unscoped-apply-user>
+</template>
+
+<template id="any-selector">
+ <style>
+ :-webkit-any(div, span) {
+ color: rgb(123, 123, 123);
+ }
+ :-moz-any(div, span) {
+ color: rgb(123, 123, 123);
+ }
+ </style>
+ <div>a</div>
+ <span>b</span>
+</template>
+
+<template id="scoped-keyframes">
+ <style>
+ :host {
+ --time: 0.1s;
+ }
+
+ div {
+ /* prefix for older chrome and safari */
+ -webkit-animation-duration: var(--time);
+ animation-duration: var(--time);
+ -webkit-animation-fill-mode: forwards;
+ animation-fill-mode: forwards;
+ border: 0px solid black;
+ }
+
+ :host([animate]) div {
+ /* prefix for older chrome and safari */
+ -webkit-animation-name: border-grow;
+ animation-name: border-grow;
+ }
+
+ /* prefix for older chrome and safari */
+ @-webkit-keyframes border {}
+ @-webkit-keyframes border-grow {
+ to {
+ border-top-width: 10px;
+ }
+ }
+ @keyframes border {}
+ @keyframes border-grow {
+ to {
+ border-top-width: 10px;
+ }
+ }
+ </style>
+
+ <div id="target">Hello world</div>
+</template>
+
+<template id="nested-templates">
+ <style>
+ * {
+ opacity: 0.5;
+ }
+ </style>
+ <div id="a"></div>
+ <template id="t1">
+ <div id="b">
+ <div id="c">
+ <template id="t2">
+ <div id="d"></div>
+ </template>
+ </div>
+ </div>
+ </template>
+ <svg>
+ <template id="t3">
+ <g id="g">
+ <circle id="circle"></circle>
+ </g>
+ </template>
+ </svg>
+</template>
+
+<template id="bad-mixin">
+ <style>
+ :host(.nomatch) {
+ --div-border: {
+ border: 2px solid black;
+ }
+ }
+ div {
+ @apply --div-border;
+ }
+ </style>
+ <div></div>
+</template>
+
+<template id="x-parent-skip">
+ <style>
+ :host {
+ --foo: 10px solid black;
+ }
+ </style>
+ <x-skip></x-skip>
+</template>
+
+<template id="x-skip">
+ <x-child-skip></x-child-skip>
+</template>
+
+<template id="x-child-skip">
+ <style>
+ div {
+ border: var(--foo);
+ }
+ </style>
+ <div></div>
+</template>
+
+<script>
+(function() {
+ function assertComputed(element, value, property, pseudo) {
+ var computed = getComputedStyle(element, pseudo);
+ property = property || 'border-top-width';
+ if (Array.isArray(value)) {
+ assert.oneOf(computed[property], value, 'computed style incorrect for ' + property);
+ } else {
+ assert.equal(computed[property], value, 'computed style incorrect for ' + property);
+ }
+ }
+
+ function findNode(desc) {
+ var parts = desc.split('.');
+ var root = document;
+ var node;
+ for (var i=0, p; i < parts.length; i++) {
+ p = parts[i];
+ if (p == '$') {
+ root = node.shadowRoot;
+ } else {
+ node = root.querySelector('#' + p);
+ }
+ }
+ return node;
+ }
+
+ function flush() {
+ if (window.ShadyDOM) {
+ window.ShadyDOM.flush();
+ }
+ window.ShadyCSS.ScopingShim.flush();
+ }
+
+ suite('scoped-styling', function() {
+
+ suiteSetup(function() {
+ makeElement('x-gchild');
+ makeElement('x-child', function() {
+ this.classList.add('nug');
+ });
+ makeElement('x-child2');
+ makeElement('x-scope-class');
+ makeElement('x-scoped');
+ makeElement('x-slotted');
+ (function() {
+ var dynamic = document.querySelector('template#dynamic');
+
+ makeElement('x-dynamic-scope',
+ function() {
+ // simulate 3rd party action by using normal dom to add to element.
+ var dom = document.importNode(dynamic.content, true);
+ this.shadowRoot.querySelector('#container').appendChild(dom);
+ });
+ })();
+ makeElement('x-keyframes');
+ makeElement('x-attr-selector');
+ (function() {
+ var template = document.querySelector('template#svg');
+
+ makeElement('x-dynamic-svg', function() {
+ var dom = document.importNode(template.content, true);
+ this.shadowRoot.querySelector('#container').appendChild(dom);
+ });
+ })();
+ makeElement('x-specificity');
+ makeElement('nested-templates');
+ });
+
+ var el;
+ setup(function() {
+ el = document.createElement('x-scoped');
+ el.id = 'el';
+ document.body.appendChild(el);
+ flush();
+ });
+
+ teardown(function() {
+ document.body.removeChild(el);
+ });
+
+ test(':host', function() {
+ assertComputed(el, '1px');
+ assertComputed(el, ['', 'none'], 'content', '::after');
+ });
+
+ test(':host(...)', function() {
+ var el2 = document.createElement('x-scoped');
+ el2.classList.add('wide');
+ document.body.appendChild(el2);
+ flush();
+ assertComputed(el2, '2px');
+ assertComputed(el2, ['"-content-"', '-content-'], 'content', '::after');
+ document.body.removeChild(el2);
+ });
+
+ test('scoped selectors, simple and complex', function() {
+ assertComputed(findNode('el.$.simple'), '3px');
+ assertComputed(findNode('el.$.complex1'), '4px');
+ assertComputed(findNode('el.$.complex2'), '4px');
+ });
+
+ test('media query scoped selectors', function() {
+ assertComputed(findNode('el.$.media'), '5px');
+ });
+
+ test('upper bound encapsulation', function() {
+ var d = document.createElement('div');
+ d.classList.add('scoped');
+ document.body.appendChild(d);
+ assertComputed(d, '0px');
+ document.body.removeChild(d);
+ });
+
+ test('lower bound encapsulation', function() {
+ assertComputed(findNode('el.$.child.$.simple'), '0px');
+ assertComputed(findNode('el.$.child.$.complex1'), '0px');
+ assertComputed(findNode('el.$.child.$.complex2'), '0px');
+ assertComputed(findNode('el.$.child.$.media'), '0px');
+ });
+
+ test('nested templates', function() {
+ var el = document.createElement('nested-templates');
+ document.body.appendChild(el);
+ // Append nested template content. Note the <template> in <svg> is not
+ // an HTML template with .content at this point; it is just an unknown
+ // SVGElement so we don't have to stamp it
+ var t1 = el.shadowRoot.querySelector('#t1');
+ el.shadowRoot.appendChild(t1.content.cloneNode(true));
+ var t2 = el.shadowRoot.querySelector('#t2');
+ el.shadowRoot.appendChild(t2.content.cloneNode(true));
+ // Everything should now have 'opacity: 0.5'
+ var els = Array.from(el.shadowRoot.querySelectorAll('[id]'));
+ assert.deepEqual(els.map(e => e.getAttribute('id')), ['a', 't1', 't3', 'g', 'circle', 'b', 'c', 't2', 'd']);
+ els.forEach(e => {
+ assert.equal(getComputedStyle(e).opacity, '0.5', `Element with id "${e.id}" does not have the correct opacity`);
+ });
+ document.body.removeChild(el);
+ });
+
+ });
+
+ suite('slotted', function() {
+
+ test('::slotted selectors', function() {
+ var el = document.createElement('x-scoped');
+ document.body.appendChild(el);
+ var content1 = document.createElement('div');
+ content1.slot = 'content1';
+ var content2 = document.createElement('div');
+ content2.slot = 'content2';
+ var content = document.createElement('div');
+ content.className = 'content';
+ el.appendChild(content1);
+ el.appendChild(content2);
+ el.appendChild(content);
+ flush();
+
+ assertComputed(content, '6px');
+ assertComputed(content1, '13px');
+ assertComputed(content2, '14px');
+ document.body.removeChild(el);
+ });
+
+ test('auto ::slotted selector', function() {
+ var x = document.createElement('x-slotted');
+ var d1 = document.createElement('div');
+ d1.classList.add('auto-content');
+ d1.textContent = 'auto-content';
+ document.body.appendChild(x);
+ x.appendChild(d1);
+ flush();
+ assertComputed(d1, '2px');
+ document.body.removeChild(x);
+ });
+
+ test('::slotted + child in complex selector', function() {
+ var x = document.createElement('x-slotted');
+ var d1 = document.createElement('div');
+ d1.classList.add('complex-child');
+ d1.textContent = 'complex-child';
+ document.body.appendChild(x);
+ x.appendChild(d1);
+ flush();
+ assertComputed(d1, '6px');
+ document.body.removeChild(x);
+ });
+
+ test('::slotted + named slot', function() {
+ var x = document.createElement('x-slotted');
+ var d1 = document.createElement('div');
+ d1.setAttribute('slot', 'container')
+ d1.textContent = 'named slot child';
+ document.body.appendChild(x);
+ x.appendChild(d1);
+ flush();
+ assertComputed(d1, '8px');
+ document.body.removeChild(x);
+ });
+
+ });
+
+ suite('dynamic changes', function() {
+
+ test('elements dynamically added/removed from root', function() {
+ var el = document.createElement('x-scoped');
+ document.body.appendChild(el);
+ flush();
+ var d = document.createElement('div');
+ d.classList.add('scoped');
+ d.textContent = 'Dynamically... Scoped!';
+ el.shadowRoot.appendChild(d);
+ flush();
+ assertComputed(d, '4px');
+ document.body.appendChild(d);
+ flush();
+ assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when added to other root');
+ assert.notInclude(d.className, el.is, 'scoping class not removed when added to other root');
+ el.shadowRoot.appendChild(d);
+ flush();
+ assertComputed(d, '4px');
+ el.shadowRoot.removeChild(d);
+ flush();
+ assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when removed from root');
+ assert.notInclude(d.className, el.is, 'scoping class not removed when removed from root');
+ el.shadowRoot.appendChild(d);
+ flush();
+ assertComputed(d, '4px');
+ document.body.removeChild(el);
+ });
+
+ test('elements dynamically added/removed from host', function() {
+ var el = document.createElement('x-scoped');
+ document.body.appendChild(el);
+ var d = document.createElement('div');
+ d.classList.add('scoped');
+ d.slot = 'blank';
+ d.textContent = 'Dynamically... unScoped!';
+ el.appendChild(d);
+ flush();
+ assertComputed(d, '0px');
+ el.removeChild(d);
+ flush();
+ assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when added to other root');
+ assert.notInclude(d.className, el.is, 'scoping class not removed when added to other root');
+ el.appendChild(d);
+ flush();
+ assertComputed(d, '0px');
+ el.removeChild(d);
+ flush();
+ assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when removed from root');
+ assert.notInclude(d.className, el.is, 'scoping class not removed when removed from root');
+ el.appendChild(d);
+ flush();
+ assertComputed(d, '0px');
+ document.body.removeChild(el);
+ });
+
+ test('element subtree added via dom api', function() {
+ var el = document.createElement('x-dynamic-scope');
+ document.body.appendChild(el);
+ flush();
+ var container = el.shadowRoot.querySelector('#container');
+ var a = container.querySelector('.added');
+ assertComputed(a, '17px');
+ var b = container.querySelector('.sub-added');
+ assertComputed(b, '18px');
+ document.body.removeChild(el);
+ });
+
+ test('changes to class attribute', function() {
+ var el = document.createElement('x-scoped');
+ el.id = 'el'
+ document.body.appendChild(el);
+ flush();
+ var d = findNode('el.$.computed');
+ assertComputed(d, '0px');
+ d.setAttribute('class', 'computed');
+ assertComputed(d, '15px');
+ d.setAttribute('class', '', 'empty class attr does not remove class');
+ assertComputed(d, '0px');
+ d.setAttribute('class', 'computed ', 'class attr with space does not apply');
+ assertComputed(d, '15px');
+ document.body.removeChild(el);
+ });
+
+ });
+
+ suite('misc', function() {
+
+ var el;
+ setup(function() {
+ el = document.createElement('x-scoped');
+ el.id = 'el';
+ document.body.appendChild(el);
+ flush();
+ });
+
+ teardown(function() {
+ document.body.removeChild(el);
+ });
+
+ test('keyframes change scope', function(done) {
+ var xKeyframes = findNode('el.$.keyframes');
+ // Edge 16 does not support CSS Custom Properties in keyframes
+ if (window.ShadyCSS.nativeCss && navigator.userAgent.match(/Edge/)) {
+ this.skip();
+ }
+ var onAnimationEnd = function() {
+ xKeyframes.removeEventListener('animationend', onAnimationEnd);
+ xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd);
+ assertComputed(xKeyframes, '100px', 'left');
+
+ xKeyframes = findNode('el.$.keyframes2');
+
+ onAnimationEnd = function() {
+ xKeyframes.removeEventListener('animationend', onAnimationEnd);
+ xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd);
+ assertComputed(xKeyframes, '200px', 'left');
+ done();
+ };
+
+ xKeyframes.addEventListener('animationend', onAnimationEnd);
+ xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd);
+
+ xKeyframes.classList.add('special');
+ xKeyframes.setAttribute('animated', '');
+ window.ShadyCSS.ScopingShim.styleElement(xKeyframes);
+ };
+ xKeyframes.addEventListener('animationend', onAnimationEnd);
+ xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd);
+ xKeyframes.setAttribute('animated', '');
+ assertComputed(xKeyframes, '0px', 'left');
+ });
+
+ test('keyframe names are transformed correctly', function(done) {
+ makeElement('scoped-keyframes');
+ var e = document.createElement('scoped-keyframes');
+ document.body.appendChild(e);
+ flush();
+ var target = e.shadowRoot.querySelector('#target');
+ var onAnimationEnd = function() {
+ assertComputed(target, '10px');
+ target.removeEventListener('animationend', onAnimationEnd);
+ target.removeEventListener('webkitAnimationEnd', onAnimationEnd);
+ document.body.removeChild(e);
+ done();
+ };
+ target.addEventListener('animationend', onAnimationEnd);
+ target.addEventListener('webkitAnimationEnd', onAnimationEnd);
+ e.setAttribute('animate', '');
+ assertComputed(target, '0px');
+ });
+
+ test('attribute inclusive selector and general sibling selectors', function() {
+ var x = document.createElement('x-attr-selector');
+ x.id = 'x';
+ document.body.appendChild(x);
+ flush();
+ assertComputed(findNode('x.$.bar1'), '2px');
+ assertComputed(findNode('x.$.bar2'), '4px');
+ assertComputed(findNode('x.$.bar3'), '6px');
+ document.body.removeChild(x);
+ });
+
+ test('adjacent sibling selectors', function() {
+ makeElement('x-adjacent-sibling');
+ var x = document.createElement('x-adjacent-sibling');
+ x.id = 'x';
+ document.body.appendChild(x);
+ flush();
+ assertComputed(findNode('x.$.foo1'), '20px');
+ assertComputed(findNode('x.$.foo2'), '4px');
+ assertComputed(findNode('x.$.foo3'), '6px');
+ document.body.removeChild(x);
+ })
+
+ test('svg classes are dynamically scoped correctly', function() {
+ var x = document.createElement('x-dynamic-svg');
+ x.id = 'x';
+ document.body.appendChild(x);
+ flush();
+ var container = findNode('x.$.container');
+ var svg = container.querySelector('.svg');
+ var computed = getComputedStyle(svg);
+ assert.equal(computed.height, '24px');
+ assert.equal(computed.width, '24px');
+ var circle = container.querySelector('#circle');
+ computed = getComputedStyle(circle);
+ assert.equal(computed['fill-opacity'], '0.5');
+ document.body.removeChild(x);
+ });
+
+ test(':host selectors always lowest priority', function() {
+ var priority = findNode('el.$.priority');
+ assertComputed(priority, '9px');
+ el.setAttribute('class', 'wide');
+ assertComputed(priority, '9px');
+ });
+
+ test('svg elements properly scoped', function() {
+ if (window.ShadyCSS.nativeShadow) {
+ this.skip();
+ }
+ var circle = findNode('el.$.circle');
+ var classes = (circle.getAttribute('class') || '').split(/\s+/);
+ assert.include(classes, 'x-scoped');
+ assert.include(classes, 'style-scope');
+ assert.notInclude(classes, 'null');
+ assertComputed(circle, '1px', 'strokeWidth');
+ });
+
+ test('set attribute class has style scoping selectors', function() {
+ if (window.ShadyCSS.nativeShadow) {
+ this.skip();
+ }
+ var s = findNode('el.$.scopeClass');
+ var scope = findNode('el.$.scopeClass.$.scope');
+ assert.isTrue(s.classList.contains('style-scope'));
+ assert.isTrue(s.classList.contains('x-scoped'));
+ s.setAttribute('class', 'foo');
+ assert.isTrue(s.classList.contains('foo'));
+ assert.isTrue(s.classList.contains('style-scope'));
+ assert.isTrue(s.classList.contains('x-scoped'));
+ //
+ assert.isTrue(scope.classList.contains('style-scope'));
+ assert.isTrue(scope.classList.contains('x-scope-class'));
+ scope.setAttribute('class', 'foo');
+ assert.isTrue(scope.classList.contains('foo'));
+ assert.isTrue(scope.classList.contains('style-scope'));
+ assert.isTrue(scope.classList.contains('x-scope-class'));
+ });
+
+ test('specificity of :host selector with class', function() {
+ var e1 = document.createElement('x-specificity');
+ document.body.appendChild(e1);
+ flush();
+ assertComputed(e1, '1px');
+ document.body.removeChild(e1);
+ var e2 = document.createElement('x-specificity');
+ e2.setAttribute('class', 'bar');
+ document.body.appendChild(e2);
+ flush();
+ assertComputed(e2, '2px');
+ document.body.removeChild(e2);
+ });
+
+ test('self-use is supported', function() {
+ makeElement('self-test');
+ var e = document.createElement('self-test');
+ document.body.appendChild(e);
+ flush();
+ assertComputed(e.shadowRoot.querySelector('a'), '10px');
+ document.body.removeChild(e);
+ });
+
+ test('nth-child selectors work correctly with plusses', function() {
+ makeElement('nth-plus-one');
+ var e = document.createElement('nth-plus-one');
+ document.body.appendChild(e);
+ flush();
+ assertComputed(e.shadowRoot.querySelector('.foo'), 'rgb(255, 0, 0)', 'color');
+ document.body.removeChild(e);
+ });
+
+ test(':-webkit-any and :-moz-any selectors are supported', function() {
+ if (navigator.userAgent.match(/Trident|Edge/)) {
+ this.skip();
+ }
+ makeElement('any-selector');
+ var e = document.createElement('any-selector');
+ document.body.appendChild(e);
+ flush();
+ assertComputed(e.shadowRoot.querySelector('div'), 'rgb(123, 123, 123)', 'color');
+ assertComputed(e.shadowRoot.querySelector('span'), 'rgb(123, 123, 123)', 'color');
+ document.body.removeChild(e);
+ });
+
+ test(':host() sets mixin definitions correctly', function() {
+ makeElement('bad-mixin');
+ var e = document.createElement('bad-mixin');
+ document.body.appendChild(e);
+ flush();
+ assertComputed(e.shadowRoot.querySelector('div'), '0px');
+ document.body.removeChild(e);
+ });
+
+ test('trees with elements missing styles render correctly', function() {
+ makeElement('x-parent-skip');
+ makeElement('x-skip');
+ makeElement('x-child-skip');
+ const p = document.createElement('x-parent-skip');
+ document.body.appendChild(p);
+ flush();
+ const inner = p.shadowRoot.querySelector('x-skip').shadowRoot.querySelector('x-child-skip').shadowRoot.querySelector('div');
+ assertComputed(inner, '10px');
+ document.body.removeChild(p);
+ });
+
+ test('trees with elements missing templates render correctly', function() {
+ makeElement('no-shadow');
+ const p = document.createElement('x-parent-skip');
+ const n = document.createElement('no-shadow');
+ const c = document.createElement('x-child-skip');
+ document.body.appendChild(p);
+ p.shadowRoot.appendChild(n);
+ n.shadowRoot.appendChild(c);
+ flush();
+ const inner = c.shadowRoot.querySelector('div');
+ assertComputed(inner, '10px');
+ document.body.removeChild(p);
+ })
+
+ });
+
+ suite('unscoping', function() {
+ suiteSetup(function() {
+ makeElement('shady-unscoped');
+ });
+ test('styles with "shady-unscoped" attr work in Shady and Shadow', function() {
+ var el = document.createElement('shady-unscoped');
+ document.body.appendChild(el);
+ flush();
+ var div = el.shadowRoot.querySelector('div');
+ assertComputed(div, 'rgb(255, 0, 0)', 'color');
+ document.body.removeChild(el);
+ });
+ test('styles with "shady-unscoped" attr deduplicate', function(){
+ if (window.ShadyCSS.nativeShadow) {
+ this.skip();
+ }
+ makeElement('shady-unscoped-2');
+ var el1 = document.createElement('shady-unscoped');
+ var el2 = document.createElement('shady-unscoped-2');
+ document.body.appendChild(el1);
+ document.body.appendChild(el2);
+ flush();
+ assert.equal(document.querySelectorAll('style[shady-unscoped]').length, 1);
+ document.body.removeChild(el1);
+ document.body.removeChild(el2);
+ });
+ test('@apply does not work in shady-unscoped', function() {
+ makeElement('unscoped-apply-user');
+ makeElement('unscoped-apply');
+ var el = document.createElement('unscoped-apply');
+ document.body.appendChild(el);
+ flush();
+ var inner = el.shadowRoot.querySelector('unscoped-apply-user');
+ var target = inner.shadowRoot.querySelector('div');
+ assertComputed(target, '0px');
+ document.body.removeChild(el);
+ });
+ });
+
+})();
+</script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/settings.html b/catapult/third_party/polymer/components/shadycss/tests/settings.html
new file mode 100644
index 00000000..ccf773e6
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/settings.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<script src="./test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script>
+ suite('Settings', () => {
+ window.ShadyCSS = { cssBuild: 'shady' }
+ let origCss;
+ let origShady;
+ let origCssBuild;
+ test(`settings remain correct no matter the order of components loaded ${JSON.stringify(window.WebComponents.flags)}`, (done) => {
+ let script = document.createElement('script');
+ script.src = '../custom-style-interface.min.js';
+ script.onerror = (err) => done(err);
+ script.onload = () => {
+ origCss = window.ShadyCSS.nativeCss;
+ origShady = window.ShadyCSS.nativeShadow;
+ origCssBuild = window.ShadyCSS.cssBuild;
+ assert.notEqual(origCss, undefined, 'nativeCss should be defined');
+ assert.notEqual(origShady, undefined, 'nativeShadow should be defined');
+ assert.equal(origCssBuild, 'shady', 'cssBuild should be defined');
+ let script = document.createElement('script');
+ script.src = '../apply-shim.min.js';
+ script.onerror = (err) => done(err);
+ script.onload = () => {
+ assert.equal(origCss, window.ShadyCSS.nativeCss);
+ assert.equal(origShady, window.ShadyCSS.nativeShadow);
+ assert.equal(origCssBuild, window.ShadyCSS.cssBuild);
+ let script = document.createElement('script');
+ script.src = '../scoping-shim.min.js';
+ script.onerrer = (err) => done(err);
+ script.onload = () => {
+ assert.equal(origCss, window.ShadyCSS.nativeCss);
+ assert.equal(origShady, window.ShadyCSS.nativeShadow);
+ assert.equal(origCssBuild, window.ShadyCSS.cssBuild);
+ done();
+ };
+ document.head.appendChild(script);
+ }
+ document.head.appendChild(script);
+ }
+ document.head.appendChild(script);
+ });
+ test('Native CSS Custom Properties disabled if ShadyDOM is in use', () => {
+ if (!window.ShadyDOM || !window.ShadyDOM.inUse) {
+ assert.isTrue(window.ShadyCSS.nativeCss, 'nativeCss should be enabled if not using ShadyDOM');
+ }
+ })
+ });
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/style-transformer.html b/catapult/third_party/polymer/components/shadycss/tests/style-transformer.html
new file mode 100644
index 00000000..cf457f3d
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/style-transformer.html
@@ -0,0 +1,299 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script>
+WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+</script>
+<script src="./test-flags.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+<script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+<script src="../node_modules/@webcomponents/template/template.js"></script>
+<script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+<script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+<script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+<script src="../scoping-shim.min.js"></script>
+<script src="module/generated/style-transformer.js"></script>
+<title>Style Transformer</title>
+
+<template id="host">
+ <style>
+ :host {
+ color: blue;
+ }
+ :host([red]) {
+ color: red;
+ }
+ :host > * {
+ color: green;
+ }
+ :host(.bar) :-webkit-any(.foo, .bar) {
+ color: purple;
+ };
+ :-webkit-any([baz], [zot="foo"]) :matches(foo, bar) > :-moz-any(:not(:defined), :unresolved) {
+ color: rgb(123, 123, 123);
+ }
+ :matches(a, b) {
+ color: black;
+ }
+ </style>
+</template>
+
+<template id="slotted">
+ <style>
+ div::slotted(.foo) {
+ color: gray;
+ }
+ ::slotted(.bar) {
+ color: lightgray;
+ }
+ :host > ::slotted(*:nth-of-type(2n - 1)) {
+ color: red;
+ }
+ </style>
+</template>
+
+<template id="dir">
+ <style>
+ div:dir(rtl) {
+ color: blue;
+ }
+ :host(:dir(rtl)) {
+ color: blue;
+ }
+ </style>
+</template>
+
+<template id="custom-style">
+ <style>
+ :root {
+ color: black;
+ }
+ </style>
+</template>
+
+<template id="shared-style">
+ <style>
+ html, :host {
+ color: gray;
+ }
+ </style>
+</template>
+
+<template id="attribute-style">
+ <style>
+ [foo="foo:bar"] {
+ background-color: blue;
+ }
+ </style>
+</template>
+
+<template id="nested-attribute-style">
+ <style>
+ [foo="foo:bar"] [foo="foo:bar"] {
+ background-color: blue;
+ }
+ </style>
+</template>
+
+<template id="prepended-attribute-style">
+ <style>
+ foo[foo="foo:bar"] {
+ background-color: blue;
+ }
+ </style>
+</template>
+
+<script>
+function processTemplate(templateName, elementName) {
+ var template = document.querySelector('template#' + templateName);
+ window.ShadyCSS.prepareTemplate(template, elementName);
+ return template._styleAst;
+}
+suite('Style Transformer', function() {
+ setup(function() {
+ if (window.ShadyCSS.nativeShadow) {
+ this.skip();
+ }
+ })
+ suite(':host transforms', function() {
+ var ast;
+ suiteSetup(function() {
+ ast = processTemplate('host', 'x-foo');
+ });
+
+ test(':host{}', function() {
+ assert.equal(ast.rules[0].selector, 'x-foo');
+ });
+
+ test(':host([red]){}', function() {
+ assert.equal(ast.rules[1].selector, 'x-foo[red]');
+ });
+
+ test(':host > *{}', function() {
+ assert.equal(ast.rules[2].selector, 'x-foo > *.x-foo');
+ });
+
+ test(':host() :-webkit-any()', function() {
+ assert.equal(ast.rules[3].selector, 'x-foo.bar :-webkit-any(.foo, .bar).x-foo');
+ });
+
+ test('lots of :matches()', function() {
+ assert.equal(ast.rules[4].selector, ':-webkit-any([baz], [zot="foo"]).x-foo :matches(foo, bar).x-foo > :-moz-any(:not(:defined), :unresolved).x-foo');
+ });
+
+ test('only match', function() {
+ assert.equal(ast.rules[5].selector, ':matches(a, b).x-foo');
+ });
+ });
+
+ suite('::slotted transforms', function() {
+ var ast;
+ suiteSetup(function() {
+ ast = processTemplate('slotted', 'x-slot');
+ });
+
+ test('div::slotted(.foo)', function() {
+ assert.equal(ast.rules[0].selector, 'div.x-slot > .foo');
+ });
+
+ test('::slotted(.bar)', function() {
+ assert.equal(ast.rules[1].selector, 'x-slot > .bar');
+ });
+
+ test(':host > ::slotted(*:nth-of-type(2n - 1))', function() {
+ assert.equal(ast.rules[2].selector, 'x-slot > *:nth-of-type(2n-1)');
+ });
+ });
+
+ suite('dir transforms', function() {
+ var ast;
+ suiteSetup(function() {
+ ast = processTemplate('dir', 'x-dir');
+ });
+
+ test('div:dir(rtl)', function() {
+ assert.equal(ast.rules[0].selector, '[dir="rtl"] div.x-dir, div.x-dir[dir="rtl"]');
+ });
+
+ test('host(:dir(rtl))', function() {
+ assert.equal(ast.rules[1].selector, '[dir="rtl"] x-dir, x-dir[dir="rtl"]');
+ });
+
+ });
+
+ suite('custom-style transforms', function() {
+ var rule;
+ setup(function() {
+ var template = document.querySelector('template#custom-style');
+ var style = template.content.querySelector('style').cloneNode(true);
+ var ast = window.ShadyCSS.ScopingShim.getStyleAst(style);
+ rule = ast.rules[0];
+ });
+
+ test('native ShadowDOM', function() {
+ window.StyleTransformer.normalizeRootSelector(rule);
+ assert.equal(rule.selector, 'html');
+ });
+
+ test('ShadyDOM', function() {
+ window.StyleTransformer.documentRule(rule);
+ assert.equal(rule.selector, 'html:not(.style-scope)');
+ });
+ });
+
+ suite('shared style (light or shadow dom) transforms', function() {
+ let rule;
+
+ setup(function() {
+ const template = document.querySelector('template#shared-style');
+ const style = template.content.querySelector('style').cloneNode(true);
+ const ast = window.ShadyCSS.ScopingShim.getStyleAst(style);
+ rule = ast.rules[0];
+ });
+
+ test('native ShadowDOM', function() {
+ window.StyleTransformer.normalizeRootSelector(rule);
+ assert.equal(rule.selector, 'html, :host');
+ });
+
+ test('ShadyDOM', function() {
+ window.StyleTransformer.documentRule(rule);
+ assert.equal(rule.selector, 'html:not(.style-scope)');
+ });
+ });
+
+ suite('attribute selectors', function() {
+
+ suite('simple', function() {
+ let rule;
+
+ setup(function() {
+ const template = document.querySelector('template#attribute-style');
+ const style = template.content.querySelector('style').cloneNode(true);
+ const ast = window.ShadyCSS.ScopingShim.getStyleAst(style);
+ rule = ast.rules[0];
+ });
+
+ test('native ShadowDOM', function() {
+ window.StyleTransformer.normalizeRootSelector(rule);
+ assert.equal(rule.selector, '[foo="foo:bar"]');
+ });
+
+ test('ShadyDOM', function() {
+ window.StyleTransformer.documentRule(rule);
+ assert.equal(rule.selector, ':not(.style-scope)[foo="foo:bar"]');
+ });
+ });
+
+ suite('nested', function() {
+ let rule;
+
+ setup(function() {
+ const template = document.querySelector('template#nested-attribute-style');
+ const style = template.content.querySelector('style').cloneNode(true);
+ const ast = window.ShadyCSS.ScopingShim.getStyleAst(style);
+ rule = ast.rules[0];
+ });
+
+ test('native ShadowDOM', function() {
+ window.StyleTransformer.normalizeRootSelector(rule);
+ assert.equal(rule.selector, '[foo="foo:bar"] [foo="foo:bar"]');
+ });
+
+ test('ShadyDOM', function() {
+ window.StyleTransformer.documentRule(rule);
+ assert.equal(rule.selector, ':not(.style-scope)[foo="foo:bar"] :not(.style-scope)[foo="foo:bar"]');
+ });
+ });
+
+ suite('prepended', function() {
+ let rule;
+
+ setup(function() {
+ const template = document.querySelector('template#prepended-attribute-style');
+ const style = template.content.querySelector('style').cloneNode(true);
+ const ast = window.ShadyCSS.ScopingShim.getStyleAst(style);
+ rule = ast.rules[0];
+ });
+
+ test('native ShadowDOM', function() {
+ window.StyleTransformer.normalizeRootSelector(rule);
+ assert.equal(rule.selector, 'foo[foo="foo:bar"]');
+ });
+
+ test('ShadyDOM', function() {
+ window.StyleTransformer.documentRule(rule);
+ assert.equal(rule.selector, 'foo:not(.style-scope)[foo="foo:bar"]');
+ });
+ });
+ });
+});
+</script>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/svg.html b/catapult/third_party/polymer/components/shadycss/tests/svg.html
new file mode 100644
index 00000000..a613f467
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/svg.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>SVG</title>
+ <script>
+ window.ShadyDOM = {force: true};
+ window.ShadyCSS = {shimshadow: true};
+ </script>
+ <script>
+ WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/svg-in-shadow.js"></script>
+</head>
+<body>
+ <template id="svg-in-shadow">
+ <style>
+ :host {
+ display: inline-block;
+ background-color: #ccc;
+ }
+
+ .test-class {
+ border: 3px solid blue;
+ }
+
+ circle {
+ fill: blue;
+ }
+ </style>
+ <svg
+ xmlns="http://www.w3.org/2000/svg" version="1.1"
+ width="100px" height="100px" viewBox="0 0 100 100"
+ class="test-class"
+ ></svg>
+ </template>
+
+ <script>
+ suite('SVG', function() {
+ var STYLE_SCOPE_CLASS = 'style-scope';
+
+ suiteSetup(function() {
+ window.registerSVGElement();
+ });
+
+ function flush() {
+ if (window.ShadyDOM) {
+ window.ShadyDOM.flush();
+ }
+ window.ShadyCSS.ScopingShim.flush();
+ }
+
+ test('SVG elements within a style scope should have style scoping classes.', function() {
+ var elementWithSVG = document.createElement('svg-in-shadow');
+ // Force upgrade.
+ document.body.appendChild(elementWithSVG);
+ flush();
+ var svg = elementWithSVG.svg;
+ // The svg element should have a style scope.
+ assert(svg.getAttribute('class').indexOf(STYLE_SCOPE_CLASS) > -1);
+ var circle = elementWithSVG.addCircle();
+ flush();
+ // The circle should also have a style scope.
+ assert(circle.getAttribute('class').indexOf(STYLE_SCOPE_CLASS) > -1);
+ // Clean up.
+ document.body.removeChild(elementWithSVG);
+ });
+
+ test('SVG elements removed from style scopes should have scoping classes removed.', function() {
+ var elementWithSVG = document.createElement('svg-in-shadow');
+ // Force upgrade.
+ document.body.appendChild(elementWithSVG);
+ flush();
+ // Get references to test elements.
+ var svg = elementWithSVG.svg;
+ var circle = elementWithSVG.addCircle();
+ flush();
+ // Move the SVG element out of the shadow root.
+ svg.parentNode.removeChild(svg);
+ document.body.appendChild(svg);
+ flush();
+ // The svg element should keep the class that was not involved in style scoping.
+ assert.equal(svg.getAttribute('class').trim(), 'test-class');
+ // The svg element and circle should not have style scope classes.
+ if (svg.hasAttribute('class')) {
+ assert(svg.getAttribute('class').indexOf(STYLE_SCOPE_CLASS) === -1);
+ }
+ if (circle.hasAttribute('class')) {
+ assert(circle.getAttribute('class').indexOf(STYLE_SCOPE_CLASS) === -1);
+ }
+ // Clean up.
+ document.body.removeChild(elementWithSVG);
+ document.body.removeChild(svg);
+ });
+ });
+ </script>
+</body>
+</html>
diff --git a/catapult/third_party/polymer/components/shadycss/tests/test-flags.js b/catapult/third_party/polymer/components/shadycss/tests/test-flags.js
new file mode 100644
index 00000000..a734044f
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/test-flags.js
@@ -0,0 +1,54 @@
+/**
+ * @license
+ * Copyright (c) 2017 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
+ */
+
+(function () {
+
+ 'use strict';
+
+ // Establish scope.
+ window['WebComponents'] = window['WebComponents'] || { 'flags': {} };
+
+ var flagMatcher = /wc-(.+)/;
+
+ // Flags. Convert url arguments to flags
+ var flags = {};
+ if (!flags['noOpts']) {
+ // from url
+ location.search.slice(1).split('&').forEach(function (option) {
+ var parts = option.split('=');
+ var match;
+ if (parts[0] && (match = parts[0].match(flagMatcher))) {
+ flags[match[1]] = parts[1] || true;
+ }
+ });
+ }
+
+ // exports
+ window['WebComponents']['flags'] = flags;
+ var forceShady = flags['shadydom'];
+ if (forceShady) {
+ window['ShadyDOM'] = window['ShadyDOM'] || {};
+ window['ShadyDOM']['force'] = forceShady;
+ }
+
+ var forceCE = flags['register'] || flags['ce'];
+ if (forceCE && window['customElements']) {
+ window['customElements']['forcePolyfill'] = forceCE;
+ }
+
+ var forceShimCss = flags['shimcssproperties'];
+ if (forceShimCss) {
+ window['ShadyCSS'] = window['ShadyCSS'] || {};
+ window['ShadyCSS']['shimcssproperties'] = true;
+ }
+
+ window['WebComponents']['ready'] = true;
+
+})(); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/wc-1.html b/catapult/third_party/polymer/components/shadycss/tests/wc-1.html
new file mode 100644
index 00000000..fbe410bd
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/wc-1.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<!--
+@license
+Copyright (c) 2014 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
+-->
+<script src="./test-flags.js"></script>
+<script src="https://unpkg.com/@webcomponents/webcomponentsjs@1/webcomponents-lite.js"></script>
+<script src="../custom-style-interface.min.js"></script>
+<script src="../node_modules/wct-browser-legacy/browser.js"></script>
+<script src="module/generated/make-element.js"></script>
+<script src="module/generated/custom-style-element.js"></script>
+<template id="my-element">
+ <style>
+ :host {
+ display: block;
+ --foo: rgb(255, 0, 0);
+ }
+ div {
+ color: var(--foo);
+ }
+ </style>
+ <div>Hi!</div>
+</template>
+<script>
+ suite('WC v1 compat', function() {
+ suiteSetup(function() {
+ makeElement('my-element');
+ });
+ test('element renders correctly', function() {
+ const el = document.createElement('my-element');
+ document.body.appendChild(el);
+ const color = getComputedStyle(el.shadowRoot.querySelector('div')).getPropertyValue('color').trim();
+ assert.equal(color, 'rgb(255, 0, 0)');
+ document.body.removeChild(el);
+ });
+ });
+</script> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/tests/workarounds.html b/catapult/third_party/polymer/components/shadycss/tests/workarounds.html
new file mode 100644
index 00000000..5b67af94
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/tests/workarounds.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (c) 2017 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
+-->
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+ <script>
+ WCT = { waitFor: function (cb) { HTMLImports.whenReady(cb) } }
+ </script>
+ <script src="./test-flags.js"></script>
+ <script src="../node_modules/wct-browser-legacy/browser.js"></script>
+ <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
+ <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
+ <script src="../node_modules/@webcomponents/template/template.js"></script>
+ <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
+ <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
+ <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
+ <script src="../scoping-shim.min.js"></script>
+ <script src="../apply-shim.min.js"></script>
+ <script src="../custom-style-interface.min.js"></script>
+ <script src="module/generated/make-element.js"></script>
+</head>
+<body>
+ <template id="x-bug">
+ <style>
+ :host {
+ --bg: rgb(255, 0, 0);
+ }
+ div::after {
+ content: 'test';
+ background-color: var(--bg);
+ }
+ </style>
+ <div></div>
+ </template>
+ <script>
+ suite('Workarounds', function() {
+ test('Edge 15', function() {
+ makeElement('x-bug');
+ let el = document.createElement('x-bug');
+ document.body.appendChild(el);
+ let div = el.shadowRoot.querySelector('div');
+ assert.notEqual(getComputedStyle(div).getPropertyValue('background-color').trim(), 'rgb(255, 0, 0)');
+ })
+ });
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/shadycss/wct.conf.json b/catapult/third_party/polymer/components/shadycss/wct.conf.json
new file mode 100644
index 00000000..8e6ad2bc
--- /dev/null
+++ b/catapult/third_party/polymer/components/shadycss/wct.conf.json
@@ -0,0 +1,14 @@
+{
+ "suites": ["tests/runner.html"],
+ "npm": true,
+ "plugins": {
+ "local": {
+ "browserOptions": {
+ "chrome": [
+ "disable-gpu",
+ "no-sandbox"
+ ]
+ }
+ }
+ }
+}
diff --git a/catapult/third_party/polymer/components/web-animations-js/.bower.json b/catapult/third_party/polymer/components/web-animations-js/.bower.json
new file mode 100644
index 00000000..1a699734
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/.bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "web-animations-js",
+ "description": "JavaScript implementation of the Web Animations API",
+ "homepage": "https://github.com/web-animations/web-animations-js",
+ "main": "web-animations.min.js",
+ "moduleType": [
+ "globals"
+ ],
+ "keywords": [
+ "animations",
+ "polyfill"
+ ],
+ "license": "Apache-2.0",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "templates",
+ "test",
+ "Gruntfile.js",
+ "package.json",
+ "target-config.js",
+ "target-loader.js",
+ "web-animations.dev.html",
+ "web-animations.dev.js",
+ "web-animations-next.dev.html",
+ "web-animations-next.dev.js",
+ "web-animations-next-lite.dev.html",
+ "web-animations-next-lite.dev.js"
+ ],
+ "version": "2.3.1",
+ "_release": "2.3.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "2.3.1",
+ "commit": "a2dddad1c210c8abe5bc1c5a5e126e9946d44252"
+ },
+ "_source": "https://github.com/web-animations/web-animations-js.git",
+ "_target": "^2.2.0",
+ "_originalSource": "web-animations/web-animations-js"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/web-animations-js/CONTRIBUTING.md b/catapult/third_party/polymer/components/web-animations-js/CONTRIBUTING.md
new file mode 100644
index 00000000..15142a7e
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/CONTRIBUTING.md
@@ -0,0 +1,123 @@
+## Developer instructions
+
+### Setup
+
+1. Fork web-animations/web-animations-js.
+1. `git clone git@github.com:$GITHUB_USER/web-animations-js.git`
+1. `git submodule update --init --recursive` (Necessary for running tests.)
+1. Install [node](https://nodejs.org/en/) and make sure `npm` is in your $PATH
+1. Run `npm install` in the respository to pull in development dependencies.
+1. Run `npm install -g grunt-cli` to get the build tools for the command line.
+
+### Contributing
+
+Note that development should occur against the `dev` branch, not `master`. This
+is the default target for pull requests.
+
+1. In your fork of web-animations-js, `git checkout dev` or create a new branch whose parent is dev.
+1. Run `grunt` to build the polyfill.
+1. Run `grunt test` to run polyfill and web-platform-tests tests.
+1. Commit changes to your fork.
+1. Create a pull request from your fork of web-animations-js to
+ [web-animations/web-animations-js/dev](https://github.com/web-animations/web-animations-js/tree/dev).
+1. Ensure that you've signed the [Google Contributor License Agreement](https://cla.developers.google.com/clas).
+
+
+## Debugging tests
+
+You can run the tests in an interactive mode with `grunt debug`. This starts the
+Karma server once for each polyfill target for each test framework.
+Navigate to `http://localhost:9876/debug.html` to open the test runner in your
+browser of choice, all test results appear in the Javascript console.
+Test failures can be accessed via `window.failures` and `window.formattedFailures`
+once the tests have completed.
+
+The polyfill target and tests can be specified as arguments to the `debug` task.
+Example: `grunt debug:web-animations-next:test/web-platform-tests/web-animations/animation/pause.html`
+Multiple test files may be listed with comma separation. Specifying files will output their URL in the command line.
+Example: `http://localhost:9876/base/test/web-platform-tests/web-animations/animation/pause.html`
+
+
+## Design notes
+
+[Design diagrams](https://drive.google.com/folderview?id=0B9rpPoIDv3vTNlZxOVp6a2tNa1E&usp=sharing)
+
+
+## Publishing a release
+
+1. Determine the version number for the release
+
+ * Increment the first number and reset others to 0 when there are large breaking changes
+ * Increment the second number and reset the third to 0 when there are significant new, but backwards compatible features
+ * Otherwise, increment the third number
+
+1. Add versioned release notes to `History.md`, for example:
+
+ ### 3.13.37 — *November 1, 2001*
+
+ * Fixed a bug where nothing worked
+
+ Use the following to generate a summary of commits, but edit the list to contain only
+ relevant information.
+
+ git log --first-parent `git describe --tags --abbrev=0 master`..dev --pretty=format:" * %s"
+
+1. Specify the new version inside `package.json` (for NPM), for example:
+
+ ```js
+ "version": "3.13.37",
+ ```
+
+1. Build the polyfill with `npm install && grunt` then update `docs/experimental.md`'s Build Target Comparison with the current gzipped sizes.
+
+1. Commit the above changes to web-animations-js/dev and merge to
+ web-animations-js/master.
+
+ ```sh
+ git checkout master
+ git merge dev --no-edit --quiet
+ ```
+
+1. Build and commit minified JavaScript files.
+
+ ```sh
+ npm install
+ grunt
+ # Optional "grunt test" to make sure everything still passes.
+ git add -f *.min.js{,.map}
+ git rm .gitignore
+ git commit -m 'Add build artifacts from '`cat .git/refs/heads/dev`
+ git push HEAD:refs/heads/master
+ ```
+
+1. Draft a [new release](https://github.com/web-animations/web-animations-js/releases) at the
+ commit pushed to web-animations-js in step #4. Copy the release notes from `History.md`
+ added in step #2.
+
+1. Once you've pushed to web-animations-js, run `npm publish` from that checked-out folder
+
+ To do this, you'll need to be a collaborator [on the NPM project](https://www.npmjs.com/package/web-animations-js), or have a collaborator help you.
+
+1. If there are any breaking changes to the API in this release you must notify web-animations-changes@googlegroups.com.
+
+ Only owners of the group may post to it so you may need to request ownership or ask someone to post it for you.
+
+## Testing architecture
+
+This is an overview of what happens when `grunt test` is run.
+
+1. Polyfill tests written in mocha and chai are run.
+ 1. grunt creates a karma config with mocha and chai adapters.
+ 1. grunt adds the test/js files as includes to the karma config.
+ 1. grunt starts the karma server with the config and waits for the result.
+ 1. The mocha adaptor runs the included tests and reports the results to karma.
+ 1. karma outputs results to the console and returns the final pass/fail result to grunt.
+1. web-platform-tests/web-animations tests written in testtharness.js are run.
+ 1. grunt creates a karma config with karma-testharness-adaptor.js included.
+ 1. grunt adds the web-platform-tests/web-animations files to the custom testharnessTests config in the karma config.
+ 1. grunt adds failure expectations to the custom testharnessTests config in the karma config.
+ 1. grunt starts the karma server with the config and waits for the result.
+ 1. The testharness.js adaptor runs the included tests (ignoring expected failures) and reports the results to karma.
+ 1. karma outputs results to the console and returns the final pass/fail result to grunt.
+1. grunt exits successfully if both test runs passed.
+
diff --git a/catapult/third_party/polymer/components/web-animations-js/COPYING b/catapult/third_party/polymer/components/web-animations-js/COPYING
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/catapult/third_party/polymer/components/web-animations-js/History.md b/catapult/third_party/polymer/components/web-animations-js/History.md
new file mode 100644
index 00000000..6deef415
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/History.md
@@ -0,0 +1,265 @@
+### 2.3.1 - *July 20 2017*
+
+ * Fix [https://github.com/web-animations/web-animations-js/issues/157](missing web-animations.min.js issue)
+
+### 2.3.0 - *July 20 2017*
+
+ * [Support IE/Edge SVG transforms.](https://github.com/web-animations/web-animations-js/pull/148)
+
+ * [Parse and evaluate calc expressions without eval.](https://github.com/web-animations/web-animations-js/pull/151)
+
+### 2.2.5 - *April 17 2017*
+
+ * Removed erroneously added *.gz files from release.
+
+### 2.2.4 - *April 14 2017*
+
+ * [Reverted adding 'use strict' to source files.](https://github.com/web-animations/web-animations-next/pull/124)
+
+### 2.2.3 - *April 13 2017*
+
+ * [Added HTML import targets.](https://github.com/web-animations/web-animations-js/pull/94)
+
+ * Added support for animating SVG related properties:
+ * [fill](https://github.com/web-animations/web-animations-next/pull/484)
+ * [floodColor](https://github.com/web-animations/web-animations-next/pull/484)
+ * [lightingColor](https://github.com/web-animations/web-animations-next/pull/484)
+ * [stopColor](https://github.com/web-animations/web-animations-next/pull/484)
+ * [strokeDashoffset](https://github.com/web-animations/web-animations-js/pull/96)
+ * [strokeDasharray](https://github.com/web-animations/web-animations-js/pull/120)
+
+ * [Fixed crash when animating non-invertable matrices.](https://github.com/web-animations/web-animations-js/pull/121)
+
+ * [Fixed source maps for Bower installs.](https://github.com/web-animations/web-animations-js/pull/93)
+
+ * [Updated README.md documentation structure.](https://github.com/web-animations/web-animations-next/pull/489)
+
+ * [Added 'use strict' to source files.](https://github.com/web-animations/web-animations-next/pull/488)
+
+### 2.2.2 - *August 3 2016*
+
+ * [Fixed handling of keyframes with overlapping offsets.](https://github.com/web-animations/web-animations-next/pull/470)
+
+ * [Throw TypeError on invalid keyframe input.](https://github.com/web-animations/web-animations-next/pull/471)
+
+ * [Fixed display and other animation properties being animated.](https://github.com/web-animations/web-animations-next/pull/474)
+
+ * [Throw InvalidStateError when calling play() on reversed infinite animation.](https://github.com/web-animations/web-animations-next/pull/475)
+
+ * [Fixed infinite loop in cubic-bezier timing function.](https://github.com/web-animations/web-animations-next/pull/476)
+
+ * [Fixed idle animations not becoming paused when seeked.](https://github.com/web-animations/web-animations-next/pull/479)
+
+ * [Fixed pause() not rewinding idl animations.](https://github.com/web-animations/web-animations-next/pull/480)
+
+ * [Extended cubic-bezier timing function domain from [0, 1] to (-Infinity, Infinity).](https://github.com/web-animations/web-animations-next/pull/481)
+
+ * [Fixed timing model to handle corner cases involving Infinity and 0 correctly.](https://github.com/web-animations/web-animations-next/pull/482)
+
+ * [Fixed source files missing from npm package.](https://github.com/web-animations/web-animations-next/pull/483)
+
+ * [Improved performance of starting and updating individual animations.](https://github.com/web-animations/web-animations-next/pull/485)
+
+### 2.2.1 - *April 28 2016*
+ * [Deprecated invalid timing inputs](https://github.com/web-animations/web-animations-next/pull/437) as they will soon throw [TypeErrors](https://github.com/web-animations/web-animations-next/pull/426) in native browsers.
+
+ For example, this is deprecated and will eventually throw a TypeError:
+
+ element.animate([], {
+ duration: -1,
+ iterationStart: -1,
+ iterations: -1,
+ easing: 'garbage string',
+ });
+
+ * [Fixed polyfill crash in browsers based on Chromium 36 to 46.](https://github.com/web-animations/web-animations-next/pull/434)
+
+ * [Increased cubic-bezier accuracy.](https://github.com/web-animations/web-animations-next/pull/428)
+
+ * [Added support for grad and turn units for angles.](https://github.com/web-animations/web-animations-next/pull/427)
+
+### 2.2.0 - *April 6 2016*
+ * Deprecated the use of hyphens in property names.
+
+ For example, this is deprecated:
+
+ element.animate([{'font-size': '0px'}, {'font-size': '10px'}]);
+
+ and this should be used instead:
+
+ element.animate([{fontSize: '0px'}, {fontSize: '10px'}]);
+
+ * Added arbitrary easing capitalisation.
+
+ * Added "id" effect option. (http://w3c.github.io/web-animations/#dom-keyframeanimationoptions-id)
+
+ * Added "oncancel" event handler.
+
+ * Added value list keyframe syntax.
+
+ As as alternative to:
+
+ element.animate([{color: 'red'}, {color: 'green'}, {color: 'blue'}]);
+
+ you can now use:
+
+ element.animate({color: ['red', 'green', 'blue']});
+
+ * Fixed easing TypeError in FireFox Nightly when using groups.
+
+ * Fixed delayed animation updates on Safari and Firefox
+
+ * Fixed infinite recursion when setting onfinish to null.
+
+### 2.1.4 - *December 1 2015*
+ * Use `Date.now()` instead of `performace.now()` for mobile Safari.
+
+### 2.1.3 - *October 12 2015*
+ * Removed web-animations.min.js.gz
+
+### 2.1.2 - *July 8 2015*
+ * Fix a bug where onfinish was being called for GroupEffects before they were finished.
+
+### 2.1.1 - *July 1 2015*
+ * Add Animation.timeline getter
+ * Add AnimationEffect.parent getter
+ * Make AnimationEffectTiming (returned by AnimationEffect.timing) attributes mutable
+ * Expose the Animation constructor
+ * Change custom effects from AnimationEffects to onsample functions. Custom effects should now be created by setting the onsample attribute of a KeyframeEffect.
+
+ For example, this is deprecated:
+
+ var myEffect = new KeyframeEffect(
+ element,
+ function(timeFraction, target, effect) {
+ target.style.opacity = timeFraction;
+ },
+ 1000);
+ var myAnimation = document.timeline.play(myEffect);
+
+ and this should be used insead:
+
+ var myEffect = new KeyframeEffect(element, [], 1000);
+ effect.onsample = function(timeFraction, effect, animation) {
+ effect.target.style.opacity = timeFraction;
+ };
+ var myAnimation = document.timeline.play(myEffect);
+
+### 2.1.0 - *June 15 2015*
+ * Fix bug affecting GroupEffects with infinite iteration children
+ * Add GroupEffect.firstChild and GroupEffect.lastChild
+ * Add initial values for most CSS properties
+ * Allow `timeline.play()` to be called with no arguments
+ * Add AnimationEffect.clone
+ * Add GroupEffect.append and GroupEffect.prepend
+ * Add AnimationEffect.remove
+ * Add Animation.ready and Animation.finished promises
+
+### 2.0.0 - *April 5 2015*
+
+ * Improve behavior of group Animation playback rate.
+ * Rename Animation to KeyframeEffect.
+ * Rename AnimationSequence to SequenceEffect.
+ * Rename AnimationGroup to GroupEffect.
+ * Rename AnimationPlayer to Animation.
+ * Remove KeyframeEffect.effect and add KeyframeEffect.getFrames.
+ * Rename Animation.source to Animation.effect.
+ * Rename Timeline.getAnimationPlayers to Timeline.getAnimations.
+ * Rename Element.getAnimationPlayers to Element.getAnimations.
+
+### 1.0.7 - *March 10 2015*
+
+ * Improve performance of constructing groups and sequences.
+ * Remove support for animating zoom.
+ * Add bower file.
+
+### 1.0.6 - *February 5 2015*
+
+ * Implement playbackRate setter for group players.
+ * Fix pausing a group player before its first tick.
+ * Fix cancelling a group player before its first tick.
+ * Fix excess CPU use on idle pages where custom effects and groups were used.
+ * Suppress AnimationTiming.playbackRate deprecation warning for cases where AnimationTiming.playbackRate == 1.
+
+### 1.0.5 - *January 6 2015*
+
+ * Fix loading the polyfill in an SVG document
+ * Fix a problem where groups didn't take effect in their first frame
+ * Don't rely on performance.now
+
+### 1.0.4 - *December 8 2014*
+
+ * Fix a critical bug where deprecation logic wasn't being loaded
+ when `web-animations-next` and `web-animations-next-lite` were
+ executed on top of a native `element.animate`.
+
+### 1.0.3 - *December 4 2014*
+
+ * Fix a critical bug on iOS 7 and Safari <= 6. Due to limitations,
+ inline style patching is not supported on these platforms.
+
+### 1.0.2 - *November 28 2014*
+
+ * Deprecated `AnimationTiming.playbackRate`.
+
+ For example, this is no longer supported:
+
+ var player = element.animate(
+ keyframes,
+ {duration: 1000, playbackRate: 2});
+
+ Use `AnimationPlayer.playbackRate` instead:
+
+ var player = element.animate(
+ keyframes,
+ {duration: 1000});
+ player.playbackRate = 2;
+
+ If you have any feedback on this change, please start a discussion
+ on the public-fx mailing list:
+ http://lists.w3.org/Archives/Public/public-fx/
+
+ Or file an issue against the specification on GitHub:
+ https://github.com/w3c/web-animations/issues/new
+
+### 1.0.1 - *November 26 2014*
+
+ * Players should be constructed in idle state
+ * `play()` and `reverse()` should not force a start times
+ * Add `requestAnimationFrame` ids and `cancelAnimationFrame`
+
+### 1.0.0 — *November 21 2014*
+
+ The web-animations-js hackers are pleased to announce the release of
+ a new codebase for the Web Animations Polyfill:
+ https://github.com/web-animations/web-animations-js
+
+ The previous polyfill has been moved to:
+ https://github.com/web-animations/web-animations-js-legacy
+
+ The new codebase is focused on code-size -- our smallest target is
+ now only 33kb or 11kb after gzip.
+
+ We've implemented native fallback. If the target browser provides
+ Web Animations features natively, the Polyfill will use them.
+
+ We now provide three different build targets:
+
+ `web-animations.min.js` - Tracks the Web Animations features that
+ are supported natively in browsers. Today that means Element.animate
+ and Playback Control in Chrome. If you’re not sure what features you
+ will need, start with this.
+
+ `web-animations-next.min.js` - All of web-animations.min.js plus
+ features that are still undergoing discussion or have yet to be
+ implemented natively.
+
+ `web-animations-next-lite.min.js` - A cut down version of
+ web-animations-next, removes several lesser used property handlers
+ and some of the larger and less used features such as matrix
+ interpolation/decomposition.
+
+ Not all features of the previous polyfill have been ported to the
+ new codebase; most notably mutation of Animations and Groups and
+ Additive Animations are not yet supported. These features are still
+ important and will be implemented in the coming weeks.
diff --git a/catapult/third_party/polymer/components/web-animations-js/README.md b/catapult/third_party/polymer/components/web-animations-js/README.md
new file mode 100644
index 00000000..e3410b61
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/README.md
@@ -0,0 +1,80 @@
+
+What is Web Animations?
+-----------------------
+
+A new JavaScript API for driving animated content on the web. By unifying
+the animation features of SVG and CSS, Web Animations unlocks features
+previously only usable declaratively, and exposes powerful, high-performance
+animation capabilities to developers.
+
+What is in this repository?
+---------------------------
+
+A JavaScript implementation of the Web Animations API that provides Web
+Animation features in browsers that do not support it natively. The polyfill
+falls back to the native implementation when one is available.
+
+Quick start
+-----------
+
+Here's a simple example of an animation that fades and scales a `<div>`.
+[Try it as a live demo.](http://jsbin.com/yageyezabo/edit?html,js,output)
+
+```html
+<!-- Include the polyfill -->
+<script src="web-animations.min.js"></script>
+
+<!-- Set up a target to animate -->
+<div class="pulse" style="width: 150px;">Hello world!</div>
+
+<!-- Animate! -->
+<script>
+ var elem = document.querySelector('.pulse');
+ var animation = elem.animate({
+ opacity: [0.5, 1],
+ transform: ['scale(0.5)', 'scale(1)'],
+ }, {
+ direction: 'alternate',
+ duration: 500,
+ iterations: Infinity,
+ });
+</script>
+```
+
+Documentation
+-------------
+
+* [Codelab tutorial](https://github.com/web-animations/web-animations-codelabs)
+* [Examples of usage](/docs/examples.md)
+* [Live demos](https://web-animations.github.io/web-animations-demos)
+* [MDN reference](https://developer.mozilla.org/en-US/docs/Web/API/Element/animate)
+* [W3C specification](http://w3c.github.io/web-animations/)
+
+We love feedback!
+-----------------
+
+* For feedback on the API and the specification:
+ * File an issue on GitHub: <https://github.com/w3c/web-animations/issues/new>
+ * Alternatively, send an email to <public-fx@w3.org> with subject line
+"[web-animations] ... message topic ..."
+([archives](http://lists.w3.org/Archives/Public/public-fx/)).
+
+* For issues with the polyfill, report them on GitHub:
+<https://github.com/web-animations/web-animations-js/issues/new>.
+
+Keep up-to-date
+---------------
+
+Breaking polyfill changes will be announced on this low-volume mailing list:
+[web-animations-changes@googlegroups.com](https://groups.google.com/forum/#!forum/web-animations-changes).
+
+More info
+---------
+
+* [Technical details about the polyfill](/docs/support.md)
+ * [Browser support](/docs/support.md#browser-support)
+ * [Fallback to native](/docs/support.md#native-fallback)
+ * [Feature list](/docs/support.md#features)
+ * [Feature deprecation and removal processes](/docs/support.md#process-for-breaking-changes)
+* To test experimental API features, try one of the
+ [experimental targets](/docs/experimental.md)
diff --git a/catapult/third_party/polymer/components/web-animations-js/bower.json b/catapult/third_party/polymer/components/web-animations-js/bower.json
new file mode 100644
index 00000000..afa0f9e1
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/bower.json
@@ -0,0 +1,30 @@
+{
+ "name": "web-animations-js",
+ "description": "JavaScript implementation of the Web Animations API",
+ "homepage": "https://github.com/web-animations/web-animations-js",
+ "main": "web-animations.min.js",
+ "moduleType": [
+ "globals"
+ ],
+ "keywords": [
+ "animations",
+ "polyfill"
+ ],
+ "license": "Apache-2.0",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "templates",
+ "test",
+ "Gruntfile.js",
+ "package.json",
+ "target-config.js",
+ "target-loader.js",
+ "web-animations.dev.html",
+ "web-animations.dev.js",
+ "web-animations-next.dev.html",
+ "web-animations-next.dev.js",
+ "web-animations-next-lite.dev.html",
+ "web-animations-next-lite.dev.js"
+ ]
+}
diff --git a/catapult/third_party/polymer/components/web-animations-js/docs/examples.md b/catapult/third_party/polymer/components/web-animations-js/docs/examples.md
new file mode 100644
index 00000000..7957a29c
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/docs/examples.md
@@ -0,0 +1,241 @@
+#Examples of using Web Animations
+
+Property indexed keyframes syntax
+---------------------------------
+- Each CSS property specifies its keyframe values as a list, different properties may have differently sized lists.
+- The `easing` property applies its timing function to all keyframes.
+
+[**Live demo**](http://jsbin.com/qiyeriruru/edit?js,output)
+```javascript
+element.animate({
+ transform: [
+ 'scaleY(0.5)',
+ 'scaleX(0.5)',
+ 'scaleY(0.5)',
+ ],
+ background: [
+ 'red',
+ 'blue',
+ 'orange',
+ 'red',
+ ],
+ easing: 'ease-in-out',
+}, {
+ duration: 2000,
+ iterations: Infinity,
+});
+```
+
+Keyframe list syntax
+--------------------
+- Keyframes can be specified as a list of multiple CSS property values.
+- Individual keyframes can be given specific offsets and easings.
+- Not all properties need to be specified in every keyframe.
+- Offsets are implicitly distributed if not specified.
+
+[**Live demo**](http://jsbin.com/yajatoyere/edit?js,output)
+```javascript
+element.animate([
+ {
+ background: 'red',
+ transform: 'none',
+ easing: 'ease-out',
+ },
+ {
+ offset: 0.1,
+ transform: 'translateY(100px)',
+ easing: 'ease-in-out',
+ },
+ {
+ offset: 0.2,
+ transform: 'translate(100px, 100px)',
+ easing: 'ease-in-out',
+ },
+ {
+ offset: 0.4,
+ transform: 'translateX(100px)',
+ easing: 'ease-out',
+ },
+ {
+ background: 'blue',
+ transform: 'none',
+ },
+], {
+ duration: 4000,
+ iterations: Infinity,
+});
+```
+
+Timing parameters
+-----------------
+- Web Animations inherits many of its timing parameters from CSS Animations.
+- See the [specification](http://w3c.github.io/web-animations/#animationeffecttimingreadonly) for details on each parameter.
+
+[**Live demo**](http://jsbin.com/dabehipiyo/edit?js,output)
+```javascript
+element.animate({
+ transform: ['none', 'translateX(100px)'],
+ background: ['green', 'lime'],
+}, {
+ // Apply effect during delay.
+ fill: 'backwards',
+
+ // Delay starting by 500ms.
+ delay: 500,
+
+ // Iterations last for 2000ms.
+ duration: 2000,
+
+ // Start at 25% through an iteration.
+ iterationStart: 0.25,
+
+ // Run for 2 iterations.
+ iterations: 2,
+
+ // Play every second iteration backwards.
+ direction: 'alternate',
+
+ // Stop animating 500ms earlier.
+ endDelay: -500,
+
+ // The timing function to use with each iteration.
+ easing: 'ease-in-out',
+});
+```
+
+Playback controls
+-----------------
+- element.animate() returns an Animation object with basic playback controls.
+- See the [specification](http://w3c.github.io/web-animations/#the-animation-interface) for details on each method.
+
+[**Live demo**](http://jsbin.com/kutaqoxejo/edit?js,output)
+```javascript
+var animation = element.animate({
+ transform: ['none', 'translateX(200px)'],
+ background: ['red', 'orange'],
+}, {
+ duration: 4000,
+ fill: 'both',
+});
+animation.play();
+animation.reverse();
+animation.pause();
+animation.currentTime = 2000;
+animation.playbackRate += 0.25;
+animation.playbackRate -= 0.25;
+animation.finish();
+animation.cancel();
+```
+
+Transitioning states with element.animate()
+-------------------------------------------
+- This is an example of how to animate from one state to another using Web Animations.
+
+[**Live demo**](http://jsbin.com/musufiwule/edit?js,output)
+```javascript
+var isOpen = false;
+var openHeight = '100px';
+var closedHeight = '0px';
+var duration = 300;
+
+button.addEventListener('click', function() {
+ // Prevent clicks while we transition states.
+ button.disabled = true;
+ button.textContent = '...';
+
+ // Determine where we're animation from/to.
+ var fromHeight = isOpen ? openHeight : closedHeight;
+ var toHeight = isOpen ? closedHeight : openHeight;
+
+ // Start an animation transitioning from our current state to the final state.
+ var animation = element.animate({ height: [fromHeight, toHeight] }, duration);
+
+ // Update the button once the animation finishes.
+ animation.onfinish = function() {
+ isOpen = !isOpen;
+ button.textContent = isOpen ? 'Close' : 'Open';
+ button.disabled = false;
+ };
+
+ // Put our element in the final state.
+ // Inline styles are overridden by active animations.
+ // When the above animation finishes it will stop applying and
+ // the element's style will fall back onto this inline style value.
+ element.style.setProperty('height', toHeight);
+});
+```
+
+Generating animations
+---------------------
+- The Javascript API allows for procedurally generating a diverse range of interesting animations.
+
+[**Live demo**](http://jsbin.com/xolacasiyu/edit?js,output)
+```html
+<!DOCTYPE html>
+<script src="https://rawgit.com/web-animations/web-animations-js/master/web-animations.min.js"></script>
+
+<style>
+#perspective {
+ margin-left: 100px;
+ width: 300px;
+ height: 300px;
+ perspective: 600px;
+}
+
+#container {
+ width: 300px;
+ height: 300px;
+ line-height: 0;
+ transform-style: preserve-3d;
+}
+
+.box {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: black;
+}
+</style>
+
+<div id="perspective">
+ <div id="container"></div>
+</div>
+
+<script>
+container.animate({
+ transform: [
+ 'rotateX(70deg) rotateZ(0deg)',
+ 'rotateX(70deg) rotateZ(360deg)',
+ ],
+}, {
+ duration: 20000,
+ iterations: Infinity,
+});
+
+for (var y = -7; y <= 7; y++) {
+ for (var x = -7; x <= 7; x++) {
+ var box = createBox();
+ box.animate({
+ transform: [
+ 'translateZ(0px)',
+ 'translateZ(20px)',
+ ],
+ opacity: [1, 0],
+ }, {
+ delay: (x*x + y*y) * 20,
+ duration: 2000,
+ iterations: Infinity,
+ direction: 'alternate',
+ easing: 'ease-in',
+ });
+ }
+}
+
+function createBox() {
+ var box = document.createElement('div');
+ box.className = 'box';
+ container.appendChild(box);
+ return box;
+}
+</script>
+```
diff --git a/catapult/third_party/polymer/components/web-animations-js/docs/experimental.md b/catapult/third_party/polymer/components/web-animations-js/docs/experimental.md
new file mode 100644
index 00000000..e3f22898
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/docs/experimental.md
@@ -0,0 +1,58 @@
+
+Experimental build targets
+--------------------------
+
+Most people should use the basic polyfill in `web-animations.min.js`. This
+tracks the Web Animations features that are supported natively in browsers.
+However, we also provide two additional build targets that contain experimental
+features.
+
+### web-animations-next.min.js
+
+Contains all of web-animations.min.js plus features that are still undergoing
+discussion or have yet to be implemented natively.
+
+### web-animations-next-lite.min.js
+
+A cut down version of web-animations-next, it removes several lesser used
+property handlers and some of the larger and less used features such as matrix
+interpolation/decomposition.
+
+Build target comparison
+-----------------------
+
+| | web-animations | web-animations-next | web-animations-next-lite |
+|------------------------|:--------------:|:-------------------:|:------------------------:|
+|Size (gzipped) | 15KB | 19KB | 15KB |
+|Element.animate | ✔ | ✔ | ✔ |
+|Timing input (easings, duration, fillMode, etc.) for animation effects| ✔ | ✔ | ✔ |
+|Playback control | ✔ | ✔ | ✔ |
+|Support for animating lengths, transforms and opacity| ✔ | ✔ | ✔ |
+|Support for animating other CSS properties| ✔ | ✔ | 🚫 |
+|Matrix fallback for transform animations | ✔ | ✔ | 🚫 |
+|KeyframeEffect constructor | 🚫 | ✔ | ✔ |
+|Simple GroupEffects & SequenceEffects | 🚫 | ✔ | ✔ |
+|Custom Effects | 🚫 | ✔ | ✔ |
+|Timing input (easings, duration, fillMode, etc.) for groups</div>| 🚫 | 🚫\* | 🚫 |
+|Additive animation | 🚫\* | 🚫\* | 🚫 |
+|Motion path | 🚫\* | 🚫\* | 🚫 |
+|Modifiable keyframe effect timing| 🚫 | 🚫\* | 🚫\* |
+|Modifiable group timing | 🚫 | 🚫\* | 🚫\* |
+|Usable inline style\*\* | ✔ | ✔ | 🚫 |
+
+\* support is planned for these features.
+\*\* see inline style caveat below.
+
+Caveat: Inline style
+--------------------
+
+Inline style modification is the mechanism used by the polyfill to animate
+properties. Both web-animations and web-animations-next incorporate a module
+that emulates a vanilla inline style object, so that style modification from
+JavaScript can still work in the presence of animations. However, to keep the
+size of web-animations-next-lite as small as possible, the style emulation
+module is not included. When using this version of the polyfill, JavaScript
+inline style modification will be overwritten by animations.
+Due to browser constraints inline style modification is not supported on iOS 7
+or Safari 6 (or earlier versions).
+
diff --git a/catapult/third_party/polymer/components/web-animations-js/docs/support.md b/catapult/third_party/polymer/components/web-animations-js/docs/support.md
new file mode 100644
index 00000000..ae07af76
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/docs/support.md
@@ -0,0 +1,69 @@
+
+Getting the polyfill
+--------------------
+
+There are three ways to get a copy of the polyfill:
+
+1. Download and use the `web-animations.min.js` file directly from this repository
+1. Using npm: Add [`web-animations-js`](https://www.npmjs.com/package/web-animations-js) to your `package.json`
+1. Using Bower: Add `web-animations/web-animations-js` to your `bower.json`
+
+Browser support
+---------------
+
+The polyfill is supported on modern versions of all major browsers, including:
+
+* Chrome 55+
+* Firefox 27+
+* IE10+ (including Edge)
+* Safari (iOS) 7.1+
+* Safari (Mac) 9+
+
+Native fallback
+---------------
+
+When the polyfill runs on a browser that implements `Element.animate()` and
+`Animation` playback control, it will detect and use the underlying native
+features for better performance.
+
+Features
+--------
+
+The `web-animations.min.js` polyfill target tracks the Web Animations features
+that are supported natively in browsers. These include:
+
+* Element.animate()
+* Timing input (easings, duration, fillMode, etc.) for animation effects
+* Playback control (play, pause, reverse, currentTime, cancel, onfinish)
+* Support for animating CSS properties
+
+Caveat: Prefix handling
+-----------------------
+
+The polyfill will automatically detect the correctly prefixed name to use when
+writing animated properties back to the platform. Where possible, the polyfill
+will only accept unprefixed versions of experimental features. For example:
+
+```js
+element.animate({transform: ['none', 'translateX(100px)']}, 1000);
+```
+
+will work in all browsers that implement a conforming version of transform, but
+
+```js
+element.animate({webkitTransform: ['none', 'translateX(100px)']}, 1000);
+```
+
+will not work anywhere.
+
+Process for breaking changes
+----------------------------
+
+When we make a potentially breaking change to the polyfill's API
+surface (like a rename) we will, where possible, continue supporting the
+old version, deprecated, for three months, and ensure that there are
+console warnings to indicate that a change is pending. After three
+months, the old version of the API surface (e.g. the old version of a
+function name) will be removed. *If you see deprecation warnings, you
+can't avoid them by not updating*.
+
diff --git a/catapult/third_party/polymer/components/web-animations-js/externs/README.md b/catapult/third_party/polymer/components/web-animations-js/externs/README.md
new file mode 100644
index 00000000..3f16bf65
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/externs/README.md
@@ -0,0 +1,10 @@
+
+Closure Compiler Externs for Web Animations
+-------------------------------------------
+
+This folder contains externs for using the Web Animations API with the Closure
+Compiler. These externs aren't strictly part of the polyfill, as they can be
+used for either the native or polyfilled versions.
+
+web-animations-next requires that you also include web-animations.
+
diff --git a/catapult/third_party/polymer/components/web-animations-js/externs/web-animations-next.js b/catapult/third_party/polymer/components/web-animations-js/externs/web-animations-next.js
new file mode 100644
index 00000000..743a3ba5
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/externs/web-animations-next.js
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * @fileoverview Basic externs for the Web Animations API (Level 2 / Groups).
+ * This is not intended to be exhaustive, and requires the base externs from
+ * web-animations.js.
+ * @externs
+ */
+
+
+/**
+ * @interface
+ */
+var AnimationEffectReadOnly = function() {};
+
+/** @type {!AnimationEffectTiming} */
+AnimationEffectReadOnly.prototype.timing;
+
+
+/**
+ * @param {Element} target
+ * @param {!Array<!Object>} frames
+ * @param {(number|AnimationEffectTimingProperties)=} opt_options
+ * @constructor
+ * @implements {AnimationEffectReadOnly}
+ */
+var KeyframeEffect = function(target, frames, opt_options) {};
+
+/**
+ * @return {!Array<!Object>}
+ */
+KeyframeEffect.prototype.getFrames = function() {};
+
+/** @type {!AnimationEffectTiming} */
+KeyframeEffect.prototype.timing;
+
+/** @type {Element} */
+KeyframeEffect.prototype.target;
+
+/** @type {?function(number, !KeyframeEffect, !Animation)} */
+KeyframeEffect.prototype.onsample;
+
+
+/**
+ * @param {!Array<!AnimationEffectReadOnly>} children
+ * @param {AnimationEffectTimingProperties=} opt_timing
+ * @constructor
+ * @implements {AnimationEffectReadOnly}
+ */
+var SequenceEffect = function(children, opt_timing) {};
+
+/** @type {!AnimationEffectTiming} */
+SequenceEffect.prototype.timing;
+
+/** @type {!Array<!AnimationEffectReadOnly>} */
+SequenceEffect.prototype.children;
+
+
+/**
+ * @param {!Array<!AnimationEffectReadOnly>} children
+ * @param {AnimationEffectTimingProperties=} opt_timing
+ * @constructor
+ * @implements {AnimationEffectReadOnly}
+ */
+var GroupEffect = function(children, opt_timing) {};
+
+/** @type {!AnimationEffectTiming} */
+GroupEffect.prototype.timing;
+
+/** @type {!Array<!AnimationEffectReadOnly>} */
+GroupEffect.prototype.children;
+
+
+/**
+ * @interface
+ */
+var AnimationTimeline = function() {};
+
+/** @type {?number} */
+AnimationTimeline.prototype.currentTime;
+
+/**
+ * @param {!AnimationEffectReadOnly} effect
+ * @return {!Animation}
+ */
+AnimationTimeline.prototype.play = function(effect) {};
+
+/**
+ * @interface
+ * @extends {AnimationTimeline}
+ */
+var DocumentTimeline = function() {};
+
+/** @type {AnimationEffectReadOnly|undefined} */
+Animation.prototype.effect;
+
+/** @type {!DocumentTimeline} */
+Document.prototype.timeline;
diff --git a/catapult/third_party/polymer/components/web-animations-js/externs/web-animations.js b/catapult/third_party/polymer/components/web-animations-js/externs/web-animations.js
new file mode 100644
index 00000000..7059b400
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/externs/web-animations.js
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * @fileoverview Basic externs for the Web Animations API. This is not
+ * nessecarily exhaustive. For more information, see the spec-
+ * https://w3c.github.io/web-animations
+ * @externs
+ */
+
+
+/**
+ * @param {!Array<!Object>} frames
+ * @param {(number|AnimationEffectTimingProperties)=} opt_options
+ * @return {!Animation}
+ */
+Element.prototype.animate = function(frames, opt_options) {};
+
+
+/**
+ * @interface
+ * @extends {EventTarget}
+ */
+var Animation = function() {};
+
+/**
+ * @return {undefined}
+ */
+Animation.prototype.cancel = function() {};
+
+/**
+ * @return {undefined}
+ */
+Animation.prototype.finish = function() {};
+
+/**
+ * @return {undefined}
+ */
+Animation.prototype.reverse = function() {};
+
+/**
+ * @return {undefined}
+ */
+Animation.prototype.pause = function() {};
+
+/**
+ * @return {undefined}
+ */
+Animation.prototype.play = function() {};
+
+/** @type {number} */
+Animation.prototype.startTime;
+
+/** @type {number} */
+Animation.prototype.currentTime;
+
+/** @type {number} */
+Animation.prototype.playbackRate;
+
+/** @type {string} */
+Animation.prototype.playState;
+
+/** @type {?function(!Event)} */
+Animation.prototype.oncancel;
+
+/** @type {?function(!Event)} */
+Animation.prototype.onfinish;
+
+
+/**
+ * @typedef {{
+ * delay: (number|undefined),
+ * endDelay: (number|undefined),
+ * fillMode: (string|undefined),
+ * iterationStart: (number|undefined),
+ * iterations: (number|undefined),
+ * duration: (number|string|undefined),
+ * direction: (string|undefined),
+ * easing: (string|undefined)
+ * }}
+ */
+var AnimationEffectTimingProperties;
+
+
+/**
+ * @interface
+ */
+var AnimationEffectTiming = function() {};
+
+/** @type {number} */
+AnimationEffectTiming.prototype.delay;
+
+/** @type {number} */
+AnimationEffectTiming.prototype.endDelay;
+
+/** @type {string} */
+AnimationEffectTiming.prototype.fillMode;
+
+/** @type {number} */
+AnimationEffectTiming.prototype.iterationStart;
+
+/** @type {number} */
+AnimationEffectTiming.prototype.iterations;
+
+/** @type {number|string} */
+AnimationEffectTiming.prototype.duration;
+
+/** @type {string} */
+AnimationEffectTiming.prototype.direction;
+
+/** @type {string} */
+AnimationEffectTiming.prototype.easing;
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/animation.js b/catapult/third_party/polymer/components/web-animations-js/src/animation.js
new file mode 100644
index 00000000..e9f64826
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/animation.js
@@ -0,0 +1,279 @@
+// Copyright 2014 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.
+
+(function(shared, scope, testing) {
+
+ shared.sequenceNumber = 0;
+
+ var AnimationEvent = function(target, currentTime, timelineTime) {
+ this.target = target;
+ this.currentTime = currentTime;
+ this.timelineTime = timelineTime;
+
+ this.type = 'finish';
+ this.bubbles = false;
+ this.cancelable = false;
+ this.currentTarget = target;
+ this.defaultPrevented = false;
+ this.eventPhase = Event.AT_TARGET;
+ this.timeStamp = Date.now();
+ };
+
+ scope.Animation = function(effect) {
+ this.id = '';
+ if (effect && effect._id) {
+ this.id = effect._id;
+ }
+ this._sequenceNumber = shared.sequenceNumber++;
+ this._currentTime = 0;
+ this._startTime = null;
+ this._paused = false;
+ this._playbackRate = 1;
+ this._inTimeline = true;
+ this._finishedFlag = true;
+ this.onfinish = null;
+ this._finishHandlers = [];
+ this._effect = effect;
+ this._inEffect = this._effect._update(0);
+ this._idle = true;
+ this._currentTimePending = false;
+ };
+
+ scope.Animation.prototype = {
+ _ensureAlive: function() {
+ // If an animation is playing backwards and is not fill backwards/both
+ // then it should go out of effect when it reaches the start of its
+ // active interval (currentTime == 0).
+ if (this.playbackRate < 0 && this.currentTime === 0) {
+ this._inEffect = this._effect._update(-1);
+ } else {
+ this._inEffect = this._effect._update(this.currentTime);
+ }
+ if (!this._inTimeline && (this._inEffect || !this._finishedFlag)) {
+ this._inTimeline = true;
+ scope.timeline._animations.push(this);
+ }
+ },
+ _tickCurrentTime: function(newTime, ignoreLimit) {
+ if (newTime != this._currentTime) {
+ this._currentTime = newTime;
+ if (this._isFinished && !ignoreLimit)
+ this._currentTime = this._playbackRate > 0 ? this._totalDuration : 0;
+ this._ensureAlive();
+ }
+ },
+ get currentTime() {
+ if (this._idle || this._currentTimePending)
+ return null;
+ return this._currentTime;
+ },
+ set currentTime(newTime) {
+ newTime = +newTime;
+ if (isNaN(newTime))
+ return;
+ scope.restart();
+ if (!this._paused && this._startTime != null) {
+ this._startTime = this._timeline.currentTime - newTime / this._playbackRate;
+ }
+ this._currentTimePending = false;
+ if (this._currentTime == newTime)
+ return;
+ if (this._idle) {
+ this._idle = false;
+ this._paused = true;
+ }
+ this._tickCurrentTime(newTime, true);
+ scope.applyDirtiedAnimation(this);
+ },
+ get startTime() {
+ return this._startTime;
+ },
+ set startTime(newTime) {
+ newTime = +newTime;
+ if (isNaN(newTime))
+ return;
+ if (this._paused || this._idle)
+ return;
+ this._startTime = newTime;
+ this._tickCurrentTime((this._timeline.currentTime - this._startTime) * this.playbackRate);
+ scope.applyDirtiedAnimation(this);
+ },
+ get playbackRate() {
+ return this._playbackRate;
+ },
+ set playbackRate(value) {
+ if (value == this._playbackRate) {
+ return;
+ }
+ var oldCurrentTime = this.currentTime;
+ this._playbackRate = value;
+ this._startTime = null;
+ if (this.playState != 'paused' && this.playState != 'idle') {
+ this._finishedFlag = false;
+ this._idle = false;
+ this._ensureAlive();
+ scope.applyDirtiedAnimation(this);
+ }
+ if (oldCurrentTime != null) {
+ this.currentTime = oldCurrentTime;
+ }
+ },
+ get _isFinished() {
+ return !this._idle && (this._playbackRate > 0 && this._currentTime >= this._totalDuration ||
+ this._playbackRate < 0 && this._currentTime <= 0);
+ },
+ get _totalDuration() { return this._effect._totalDuration; },
+ get playState() {
+ if (this._idle)
+ return 'idle';
+ if ((this._startTime == null && !this._paused && this.playbackRate != 0) || this._currentTimePending)
+ return 'pending';
+ if (this._paused)
+ return 'paused';
+ if (this._isFinished)
+ return 'finished';
+ return 'running';
+ },
+ _rewind: function() {
+ if (this._playbackRate >= 0) {
+ this._currentTime = 0;
+ } else if (this._totalDuration < Infinity) {
+ this._currentTime = this._totalDuration;
+ } else {
+ throw new DOMException(
+ 'Unable to rewind negative playback rate animation with infinite duration',
+ 'InvalidStateError');
+ }
+ },
+ play: function() {
+ this._paused = false;
+ if (this._isFinished || this._idle) {
+ this._rewind();
+ this._startTime = null;
+ }
+ this._finishedFlag = false;
+ this._idle = false;
+ this._ensureAlive();
+ scope.applyDirtiedAnimation(this);
+ },
+ pause: function() {
+ if (!this._isFinished && !this._paused && !this._idle) {
+ this._currentTimePending = true;
+ } else if (this._idle) {
+ this._rewind();
+ this._idle = false;
+ }
+ this._startTime = null;
+ this._paused = true;
+ },
+ finish: function() {
+ if (this._idle)
+ return;
+ this.currentTime = this._playbackRate > 0 ? this._totalDuration : 0;
+ this._startTime = this._totalDuration - this.currentTime;
+ this._currentTimePending = false;
+ scope.applyDirtiedAnimation(this);
+ },
+ cancel: function() {
+ if (!this._inEffect)
+ return;
+ this._inEffect = false;
+ this._idle = true;
+ this._paused = false;
+ this._isFinished = true;
+ this._finishedFlag = true;
+ this._currentTime = 0;
+ this._startTime = null;
+ this._effect._update(null);
+ // effects are invalid after cancellation as the animation state
+ // needs to un-apply.
+ scope.applyDirtiedAnimation(this);
+ },
+ reverse: function() {
+ this.playbackRate *= -1;
+ this.play();
+ },
+ addEventListener: function(type, handler) {
+ if (typeof handler == 'function' && type == 'finish')
+ this._finishHandlers.push(handler);
+ },
+ removeEventListener: function(type, handler) {
+ if (type != 'finish')
+ return;
+ var index = this._finishHandlers.indexOf(handler);
+ if (index >= 0)
+ this._finishHandlers.splice(index, 1);
+ },
+ _fireEvents: function(baseTime) {
+ if (this._isFinished) {
+ if (!this._finishedFlag) {
+ var event = new AnimationEvent(this, this._currentTime, baseTime);
+ var handlers = this._finishHandlers.concat(this.onfinish ? [this.onfinish] : []);
+ setTimeout(function() {
+ handlers.forEach(function(handler) {
+ handler.call(event.target, event);
+ });
+ }, 0);
+ this._finishedFlag = true;
+ }
+ } else {
+ this._finishedFlag = false;
+ }
+ },
+ _tick: function(timelineTime, isAnimationFrame) {
+ if (!this._idle && !this._paused) {
+ if (this._startTime == null) {
+ if (isAnimationFrame) {
+ this.startTime = timelineTime - this._currentTime / this.playbackRate;
+ }
+ } else if (!this._isFinished) {
+ this._tickCurrentTime((timelineTime - this._startTime) * this.playbackRate);
+ }
+ }
+
+ if (isAnimationFrame) {
+ this._currentTimePending = false;
+ this._fireEvents(timelineTime);
+ }
+ },
+ get _needsTick() {
+ return (this.playState in {'pending': 1, 'running': 1}) || !this._finishedFlag;
+ },
+ _targetAnimations: function() {
+ var target = this._effect._target;
+ if (!target._activeAnimations) {
+ target._activeAnimations = [];
+ }
+ return target._activeAnimations;
+ },
+ _markTarget: function() {
+ var animations = this._targetAnimations();
+ if (animations.indexOf(this) === -1) {
+ animations.push(this);
+ }
+ },
+ _unmarkTarget: function() {
+ var animations = this._targetAnimations();
+ var index = animations.indexOf(this);
+ if (index !== -1) {
+ animations.splice(index, 1);
+ }
+ },
+ };
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.webAnimations1Animation = scope.Animation;
+ }
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/apply-preserving-inline-style.js b/catapult/third_party/polymer/components/web-animations-js/src/apply-preserving-inline-style.js
new file mode 100644
index 00000000..4002a2f5
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/apply-preserving-inline-style.js
@@ -0,0 +1,239 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ var SVG_TRANSFORM_PROP = '_webAnimationsUpdateSvgTransformAttr';
+
+ /**
+ * IE/Edge do not support `transform` styles for SVG elements. Instead,
+ * `transform` attribute can be animated with some restrictions.
+ * See https://connect.microsoft.com/IE/feedback/details/811744/ie11-bug-with-implementation-of-css-transforms-in-svg,
+ * https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/1173754/,
+ * https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101242/, etc.
+ * The same problem is exhibited by pre-Chrome Android browsers (ICS).
+ * Unfortunately, there's no easy way to feature-detect it.
+ */
+ function updateSvgTransformAttr(window, element) {
+ if (!element.namespaceURI || element.namespaceURI.indexOf('/svg') == -1) {
+ return false;
+ }
+ if (!(SVG_TRANSFORM_PROP in window)) {
+ window[SVG_TRANSFORM_PROP] =
+ /Trident|MSIE|IEMobile|Edge|Android 4/i.test(window.navigator.userAgent);
+ }
+ return window[SVG_TRANSFORM_PROP];
+ }
+
+ var styleAttributes = {
+ cssText: 1,
+ length: 1,
+ parentRule: 1,
+ };
+
+ var styleMethods = {
+ getPropertyCSSValue: 1,
+ getPropertyPriority: 1,
+ getPropertyValue: 1,
+ item: 1,
+ removeProperty: 1,
+ setProperty: 1,
+ };
+
+ var styleMutatingMethods = {
+ removeProperty: 1,
+ setProperty: 1,
+ };
+
+ function configureProperty(object, property, descriptor) {
+ descriptor.enumerable = true;
+ descriptor.configurable = true;
+ Object.defineProperty(object, property, descriptor);
+ }
+
+ function AnimatedCSSStyleDeclaration(element) {
+ WEB_ANIMATIONS_TESTING && console.assert(!(element.style instanceof AnimatedCSSStyleDeclaration),
+ 'Element must not already have an animated style attached.');
+
+ this._element = element;
+ // Stores the inline style of the element on its behalf while the
+ // polyfill uses the element's inline style to simulate web animations.
+ // This is needed to fake regular inline style CSSOM access on the element.
+ this._surrogateStyle = document.createElementNS('http://www.w3.org/1999/xhtml', 'div').style;
+ this._style = element.style;
+ this._length = 0;
+ this._isAnimatedProperty = {};
+ this._updateSvgTransformAttr = updateSvgTransformAttr(window, element);
+ this._savedTransformAttr = null;
+
+ // Copy the inline style contents over to the surrogate.
+ for (var i = 0; i < this._style.length; i++) {
+ var property = this._style[i];
+ this._surrogateStyle[property] = this._style[property];
+ }
+ this._updateIndices();
+ }
+
+ AnimatedCSSStyleDeclaration.prototype = {
+ get cssText() {
+ return this._surrogateStyle.cssText;
+ },
+ set cssText(text) {
+ var isAffectedProperty = {};
+ for (var i = 0; i < this._surrogateStyle.length; i++) {
+ isAffectedProperty[this._surrogateStyle[i]] = true;
+ }
+ this._surrogateStyle.cssText = text;
+ this._updateIndices();
+ for (var i = 0; i < this._surrogateStyle.length; i++) {
+ isAffectedProperty[this._surrogateStyle[i]] = true;
+ }
+ for (var property in isAffectedProperty) {
+ if (!this._isAnimatedProperty[property]) {
+ this._style.setProperty(property, this._surrogateStyle.getPropertyValue(property));
+ }
+ }
+ },
+ get length() {
+ return this._surrogateStyle.length;
+ },
+ get parentRule() {
+ return this._style.parentRule;
+ },
+ // Mirror the indexed getters and setters of the surrogate style.
+ _updateIndices: function() {
+ while (this._length < this._surrogateStyle.length) {
+ Object.defineProperty(this, this._length, {
+ configurable: true,
+ enumerable: false,
+ get: (function(index) {
+ return function() { return this._surrogateStyle[index]; };
+ })(this._length)
+ });
+ this._length++;
+ }
+ while (this._length > this._surrogateStyle.length) {
+ this._length--;
+ Object.defineProperty(this, this._length, {
+ configurable: true,
+ enumerable: false,
+ value: undefined
+ });
+ }
+ },
+ _set: function(property, value) {
+ this._style[property] = value;
+ this._isAnimatedProperty[property] = true;
+ if (this._updateSvgTransformAttr &&
+ scope.unprefixedPropertyName(property) == 'transform') {
+ // On IE/Edge, also set SVG element's `transform` attribute to 2d
+ // matrix of the transform. The `transform` style does not work, but
+ // `transform` attribute can be used instead.
+ // Notice, if the platform indeed supports SVG/CSS transforms the CSS
+ // declaration is supposed to override the attribute.
+ if (this._savedTransformAttr == null) {
+ this._savedTransformAttr = this._element.getAttribute('transform');
+ }
+ this._element.setAttribute('transform', scope.transformToSvgMatrix(value));
+ }
+ },
+ _clear: function(property) {
+ this._style[property] = this._surrogateStyle[property];
+ if (this._updateSvgTransformAttr &&
+ scope.unprefixedPropertyName(property) == 'transform') {
+ if (this._savedTransformAttr) {
+ this._element.setAttribute('transform', this._savedTransformAttr);
+ } else {
+ this._element.removeAttribute('transform');
+ }
+ this._savedTransformAttr = null;
+ }
+ delete this._isAnimatedProperty[property];
+ },
+ };
+
+ // Wrap the style methods.
+ for (var method in styleMethods) {
+ AnimatedCSSStyleDeclaration.prototype[method] = (function(method, modifiesStyle) {
+ return function() {
+ var result = this._surrogateStyle[method].apply(this._surrogateStyle, arguments);
+ if (modifiesStyle) {
+ if (!this._isAnimatedProperty[arguments[0]])
+ this._style[method].apply(this._style, arguments);
+ this._updateIndices();
+ }
+ return result;
+ }
+ })(method, method in styleMutatingMethods);
+ }
+
+ // Wrap the style.cssProperty getters and setters.
+ for (var property in document.documentElement.style) {
+ if (property in styleAttributes || property in styleMethods) {
+ continue;
+ }
+ (function(property) {
+ configureProperty(AnimatedCSSStyleDeclaration.prototype, property, {
+ get: function() {
+ return this._surrogateStyle[property];
+ },
+ set: function(value) {
+ this._surrogateStyle[property] = value;
+ this._updateIndices();
+ if (!this._isAnimatedProperty[property])
+ this._style[property] = value;
+ }
+ });
+ })(property);
+ }
+
+ function ensureStyleIsPatched(element) {
+ if (element._webAnimationsPatchedStyle)
+ return;
+
+ var animatedStyle = new AnimatedCSSStyleDeclaration(element);
+ try {
+ configureProperty(element, 'style', { get: function() { return animatedStyle; } });
+ } catch (_) {
+ // iOS and older versions of Safari (pre v7) do not support overriding an element's
+ // style object. Animations will clobber any inline styles as a result.
+ element.style._set = function(property, value) {
+ element.style[property] = value;
+ };
+ element.style._clear = function(property) {
+ element.style[property] = '';
+ };
+ }
+
+ // We must keep a handle on the patched style to prevent it from getting GC'd.
+ element._webAnimationsPatchedStyle = element.style;
+ }
+
+ scope.apply = function(element, property, value) {
+ ensureStyleIsPatched(element);
+ element.style._set(scope.propertyName(property), value);
+ };
+
+ scope.clear = function(element, property) {
+ if (element._webAnimationsPatchedStyle) {
+ element.style._clear(scope.propertyName(property));
+ }
+ };
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.ensureStyleIsPatched = ensureStyleIsPatched;
+ testing.updateSvgTransformAttr = updateSvgTransformAttr;
+ }
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/apply.js b/catapult/third_party/polymer/components/web-animations-js/src/apply.js
new file mode 100644
index 00000000..3200f967
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/apply.js
@@ -0,0 +1,25 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ scope.apply = function(element, property, value) {
+ element.style[scope.propertyName(property)] = value;
+ };
+
+ scope.clear = function(element, property) {
+ element.style[scope.propertyName(property)] = '';
+ };
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/box-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/box-handler.js
new file mode 100644
index 00000000..3399263a
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/box-handler.js
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+ function consumeLengthPercentOrAuto(string) {
+ return scope.consumeLengthOrPercent(string) || scope.consumeToken(/^auto/, string);
+ }
+ function parseBox(string) {
+ var result = scope.consumeList([
+ scope.ignore(scope.consumeToken.bind(null, /^rect/)),
+ scope.ignore(scope.consumeToken.bind(null, /^\(/)),
+ scope.consumeRepeated.bind(null, consumeLengthPercentOrAuto, /^,/),
+ scope.ignore(scope.consumeToken.bind(null, /^\)/)),
+ ], string);
+ if (result && result[0].length == 4) {
+ return result[0];
+ }
+ }
+
+ function mergeComponent(left, right) {
+ if (left == 'auto' || right == 'auto') {
+ return [true, false, function(t) {
+ var result = t ? left : right;
+ if (result == 'auto') {
+ return 'auto';
+ }
+ // FIXME: There's probably a better way to turn a dimension back into a string.
+ var merged = scope.mergeDimensions(result, result);
+ return merged[2](merged[0]);
+ }];
+ }
+ return scope.mergeDimensions(left, right);
+ }
+
+ function wrap(result) {
+ return 'rect(' + result + ')';
+ }
+
+ var mergeBoxes = scope.mergeWrappedNestedRepeated.bind(null, wrap, mergeComponent, ', ');
+
+ scope.parseBox = parseBox;
+ scope.mergeBoxes = mergeBoxes;
+
+ scope.addPropertiesHandler(parseBox, mergeBoxes, ['clip']);
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/color-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/color-handler.js
new file mode 100644
index 00000000..3a05bff0
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/color-handler.js
@@ -0,0 +1,63 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
+ canvas.width = canvas.height = 1;
+ var context = canvas.getContext('2d');
+
+ function parseColor(string) {
+ string = string.trim();
+ // The context ignores invalid colors
+ context.fillStyle = '#000';
+ context.fillStyle = string;
+ var contextSerializedFillStyle = context.fillStyle;
+ context.fillStyle = '#fff';
+ context.fillStyle = string;
+ if (contextSerializedFillStyle != context.fillStyle)
+ return;
+ context.fillRect(0, 0, 1, 1);
+ var pixelColor = context.getImageData(0, 0, 1, 1).data;
+ context.clearRect(0, 0, 1, 1);
+ var alpha = pixelColor[3] / 255;
+ return [pixelColor[0] * alpha, pixelColor[1] * alpha, pixelColor[2] * alpha, alpha];
+ }
+
+ function mergeColors(left, right) {
+ return [left, right, function(x) {
+ function clamp(v) {
+ return Math.max(0, Math.min(255, v));
+ }
+ if (x[3]) {
+ for (var i = 0; i < 3; i++)
+ x[i] = Math.round(clamp(x[i] / x[3]));
+ }
+ x[3] = scope.numberToString(scope.clamp(0, 1, x[3]));
+ return 'rgba(' + x.join(',') + ')';
+ }];
+ }
+
+ scope.addPropertiesHandler(parseColor, mergeColors,
+ ['background-color', 'border-bottom-color', 'border-left-color', 'border-right-color',
+ 'border-top-color', 'color', 'fill', 'flood-color', 'lighting-color',
+ 'outline-color', 'stop-color', 'stroke', 'text-decoration-color']);
+ scope.consumeColor = scope.consumeParenthesised.bind(null, parseColor);
+ scope.mergeColors = mergeColors;
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.parseColor = parseColor;
+ }
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/deprecation.js b/catapult/third_party/polymer/components/web-animations-js/src/deprecation.js
new file mode 100644
index 00000000..6bdb4a22
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/deprecation.js
@@ -0,0 +1,47 @@
+// Copyright 2014 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.
+
+(function(shared) {
+
+ var silenced = {};
+
+ shared.isDeprecated = function(feature, date, advice, plural) {
+ if (WEB_ANIMATIONS_TESTING) {
+ return true;
+ }
+
+ var auxVerb = plural ? 'are' : 'is';
+ var today = new Date();
+ var expiry = new Date(date);
+ expiry.setMonth(expiry.getMonth() + 3); // 3 months grace period
+
+ if (today < expiry) {
+ if (!(feature in silenced)) {
+ console.warn('Web Animations: ' + feature + ' ' + auxVerb + ' deprecated and will stop working on ' + expiry.toDateString() + '. ' + advice);
+ }
+ silenced[feature] = true;
+ return false;
+ } else {
+ return true;
+ }
+ };
+
+ shared.deprecated = function(feature, date, advice, plural) {
+ var auxVerb = plural ? 'are' : 'is';
+ if (shared.isDeprecated(feature, date, advice, plural)) {
+ throw new Error(feature + ' ' + auxVerb + ' no longer supported. ' + advice);
+ }
+ };
+
+})(webAnimationsShared);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/dev.js b/catapult/third_party/polymer/components/web-animations-js/src/dev.js
new file mode 100644
index 00000000..a5e225c0
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/dev.js
@@ -0,0 +1,16 @@
+// Copyright 2014 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.
+
+var WEB_ANIMATIONS_TESTING = false;
+var webAnimationsTesting = null;
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/dimension-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/dimension-handler.js
new file mode 100644
index 00000000..9e487f97
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/dimension-handler.js
@@ -0,0 +1,240 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ // Evaluates a calc expression.
+ // https://drafts.csswg.org/css-values-3/#calc-notation
+ function calculate(expression) {
+ // In calc expressions, white space is required on both sides of the
+ // + and - operators. https://drafts.csswg.org/css-values-3/#calc-notation
+ // Thus any + or - immediately adjacent to . or 0..9 is part of the number,
+ // e.g. -1.23e+45
+ // This regular expression matches ( ) * / + - and numbers.
+ var tokenRegularExpression = /([\+\-\w\.]+|[\(\)\*\/])/g;
+ var currentToken;
+ function consume() {
+ var matchResult = tokenRegularExpression.exec(expression);
+ if (matchResult)
+ currentToken = matchResult[0];
+ else
+ currentToken = undefined;
+ }
+ consume(); // Read the initial token.
+
+ function calcNumber() {
+ // https://drafts.csswg.org/css-values-3/#number-value
+ var result = Number(currentToken);
+ consume();
+ return result;
+ }
+
+ function calcValue() {
+ // <calc-value> = <number> | <dimension> | <percentage> | ( <calc-sum> )
+ if (currentToken !== '(')
+ return calcNumber();
+ consume();
+ var result = calcSum();
+ if (currentToken !== ')')
+ return NaN;
+ consume();
+ return result;
+ }
+
+ function calcProduct() {
+ // <calc-product> = <calc-value> [ '*' <calc-value> | '/' <calc-number-value> ]*
+ var left = calcValue();
+ while (currentToken === '*' || currentToken === '/') {
+ var operator = currentToken;
+ consume();
+ var right = calcValue();
+ if (operator === '*')
+ left *= right;
+ else
+ left /= right;
+ }
+ return left;
+ }
+
+ function calcSum() {
+ // <calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]*
+ var left = calcProduct();
+ while (currentToken === '+' || currentToken === '-') {
+ var operator = currentToken;
+ consume();
+ var right = calcProduct();
+ if (operator === '+')
+ left += right;
+ else
+ left -= right;
+ }
+ return left;
+ }
+
+ // <calc()> = calc( <calc-sum> )
+ return calcSum();
+ }
+
+ function parseDimension(unitRegExp, string) {
+ string = string.trim().toLowerCase();
+
+ if (string == '0' && 'px'.search(unitRegExp) >= 0)
+ return {px: 0};
+
+ // If we have parenthesis, we're a calc and need to start with 'calc'.
+ if (!/^[^(]*$|^calc/.test(string))
+ return;
+ string = string.replace(/calc\(/g, '(');
+
+ // We tag units by prefixing them with 'U' (note that we are already
+ // lowercase) to prevent problems with types which are substrings of
+ // each other (although prefixes may be problematic!)
+ var matchedUnits = {};
+ string = string.replace(unitRegExp, function(match) {
+ matchedUnits[match] = null;
+ return 'U' + match;
+ });
+ var taggedUnitRegExp = 'U(' + unitRegExp.source + ')';
+
+ // Validating input is simply applying as many reductions as we can.
+ var typeCheck = string.replace(/[-+]?(\d*\.)?\d+([Ee][-+]?\d+)?/g, 'N')
+ .replace(new RegExp('N' + taggedUnitRegExp, 'g'), 'D')
+ .replace(/\s[+-]\s/g, 'O')
+ .replace(/\s/g, '');
+ var reductions = [/N\*(D)/g, /(N|D)[*/]N/g, /(N|D)O\1/g, /\((N|D)\)/g];
+ var i = 0;
+ while (i < reductions.length) {
+ if (reductions[i].test(typeCheck)) {
+ typeCheck = typeCheck.replace(reductions[i], '$1');
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ if (typeCheck != 'D')
+ return;
+
+ for (var unit in matchedUnits) {
+ var result = calculate(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0'));
+ if (!isFinite(result))
+ return;
+ matchedUnits[unit] = result;
+ }
+ return matchedUnits;
+ }
+
+ function mergeDimensionsNonNegative(left, right) {
+ return mergeDimensions(left, right, true);
+ }
+
+ function mergeDimensions(left, right, nonNegative) {
+ var units = [], unit;
+ for (unit in left)
+ units.push(unit);
+ for (unit in right) {
+ if (units.indexOf(unit) < 0)
+ units.push(unit);
+ }
+
+ left = units.map(function(unit) { return left[unit] || 0; });
+ right = units.map(function(unit) { return right[unit] || 0; });
+ return [left, right, function(values) {
+ var result = values.map(function(value, i) {
+ if (values.length == 1 && nonNegative) {
+ value = Math.max(value, 0);
+ }
+ // Scientific notation (e.g. 1e2) is not yet widely supported by browser vendors.
+ return scope.numberToString(value) + units[i];
+ }).join(' + ');
+ return values.length > 1 ? 'calc(' + result + ')' : result;
+ }];
+ }
+
+ var lengthUnits = 'px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc';
+ var parseLength = parseDimension.bind(null, new RegExp(lengthUnits, 'g'));
+ var parseLengthOrPercent = parseDimension.bind(null, new RegExp(lengthUnits + '|%', 'g'));
+ var parseAngle = parseDimension.bind(null, /deg|rad|grad|turn/g);
+
+ scope.parseLength = parseLength;
+ scope.parseLengthOrPercent = parseLengthOrPercent;
+ scope.consumeLengthOrPercent = scope.consumeParenthesised.bind(null, parseLengthOrPercent);
+ scope.parseAngle = parseAngle;
+ scope.mergeDimensions = mergeDimensions;
+
+ var consumeLength = scope.consumeParenthesised.bind(null, parseLength);
+ var consumeSizePair = scope.consumeRepeated.bind(undefined, consumeLength, /^/);
+ var consumeSizePairList = scope.consumeRepeated.bind(undefined, consumeSizePair, /^,/);
+ scope.consumeSizePairList = consumeSizePairList;
+
+ var parseSizePairList = function(input) {
+ var result = consumeSizePairList(input);
+ if (result && result[1] == '') {
+ return result[0];
+ }
+ };
+
+ var mergeNonNegativeSizePair = scope.mergeNestedRepeated.bind(undefined, mergeDimensionsNonNegative, ' ');
+ var mergeNonNegativeSizePairList = scope.mergeNestedRepeated.bind(undefined, mergeNonNegativeSizePair, ',');
+ scope.mergeNonNegativeSizePair = mergeNonNegativeSizePair;
+
+ scope.addPropertiesHandler(parseSizePairList, mergeNonNegativeSizePairList, [
+ 'background-size'
+ ]);
+
+ scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensionsNonNegative, [
+ 'border-bottom-width',
+ 'border-image-width',
+ 'border-left-width',
+ 'border-right-width',
+ 'border-top-width',
+ 'flex-basis',
+ 'font-size',
+ 'height',
+ 'line-height',
+ 'max-height',
+ 'max-width',
+ 'outline-width',
+ 'width',
+ ]);
+
+ scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensions, [
+ 'border-bottom-left-radius',
+ 'border-bottom-right-radius',
+ 'border-top-left-radius',
+ 'border-top-right-radius',
+ 'bottom',
+ 'left',
+ 'letter-spacing',
+ 'margin-bottom',
+ 'margin-left',
+ 'margin-right',
+ 'margin-top',
+ 'min-height',
+ 'min-width',
+ 'outline-offset',
+ 'padding-bottom',
+ 'padding-left',
+ 'padding-right',
+ 'padding-top',
+ 'perspective',
+ 'right',
+ 'shape-margin',
+ 'stroke-dashoffset',
+ 'text-indent',
+ 'top',
+ 'vertical-align',
+ 'word-spacing',
+ ]);
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/effect-callback.js b/catapult/third_party/polymer/components/web-animations-js/src/effect-callback.js
new file mode 100644
index 00000000..3051a593
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/effect-callback.js
@@ -0,0 +1,98 @@
+// Copyright 2014 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.
+(function(shared, scope, testing) {
+
+ var nullTarget = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+
+ var sequenceNumber = 0;
+ scope.bindAnimationForCustomEffect = function(animation) {
+ var target = animation.effect.target;
+ var effectFunction;
+ var isKeyframeEffect = typeof animation.effect.getFrames() == 'function';
+ if (isKeyframeEffect) {
+ effectFunction = animation.effect.getFrames();
+ } else {
+ effectFunction = animation.effect._onsample;
+ }
+ var timing = animation.effect.timing;
+ var last = null;
+ timing = shared.normalizeTimingInput(timing);
+ var callback = function() {
+ var t = callback._animation ? callback._animation.currentTime : null;
+ if (t !== null) {
+ t = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), t, timing);
+ if (isNaN(t))
+ t = null;
+ }
+ // FIXME: There are actually more conditions under which the effectFunction
+ // should be called.
+ if (t !== last) {
+ if (isKeyframeEffect) {
+ effectFunction(t, target, animation.effect);
+ } else {
+ effectFunction(t, animation.effect, animation.effect._animation);
+ }
+ }
+ last = t;
+ };
+
+ callback._animation = animation;
+ callback._registered = false;
+ callback._sequenceNumber = sequenceNumber++;
+ animation._callback = callback;
+ register(callback);
+ };
+
+ var callbacks = [];
+ var ticking = false;
+ function register(callback) {
+ if (callback._registered)
+ return;
+ callback._registered = true;
+ callbacks.push(callback);
+ if (!ticking) {
+ ticking = true;
+ requestAnimationFrame(tick);
+ }
+ }
+
+ function tick(t) {
+ var updating = callbacks;
+ callbacks = [];
+ updating.sort(function(left, right) {
+ return left._sequenceNumber - right._sequenceNumber;
+ });
+ updating = updating.filter(function(callback) {
+ callback();
+ var playState = callback._animation ? callback._animation.playState : 'idle';
+ if (playState != 'running' && playState != 'pending')
+ callback._registered = false;
+ return callback._registered;
+ });
+ callbacks.push.apply(callbacks, updating);
+
+ if (callbacks.length) {
+ ticking = true;
+ requestAnimationFrame(tick);
+ } else {
+ ticking = false;
+ }
+ }
+
+ scope.Animation.prototype._register = function() {
+ if (this._callback)
+ register(this._callback);
+ };
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/element-animatable.js b/catapult/third_party/polymer/components/web-animations-js/src/element-animatable.js
new file mode 100644
index 00000000..765edf06
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/element-animatable.js
@@ -0,0 +1,23 @@
+// Copyright 2014 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.
+
+(function(scope) {
+ window.Element.prototype.animate = function(effectInput, options) {
+ var id = '';
+ if (options && options.id) {
+ id = options.id;
+ }
+ return scope.timeline._play(scope.KeyframeEffect(this, effectInput, options, id));
+ };
+})(webAnimations1);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/font-weight-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/font-weight-handler.js
new file mode 100644
index 00000000..4760486a
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/font-weight-handler.js
@@ -0,0 +1,42 @@
+// Copyright 2014 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.
+
+(function(scope) {
+ function parse(string) {
+ var out = Number(string);
+ if (isNaN(out) || out < 100 || out > 900 || out % 100 !== 0) {
+ return;
+ }
+ return out;
+ }
+
+ function toCss(value) {
+ value = Math.round(value / 100) * 100;
+ value = scope.clamp(100, 900, value);
+ if (value === 400) {
+ return 'normal';
+ }
+ if (value === 700) {
+ return 'bold';
+ }
+ return String(value);
+ }
+
+ function merge(left, right) {
+ return [left, right, toCss];
+ }
+
+ scope.addPropertiesHandler(parse, merge, ['font-weight']);
+
+})(webAnimations1);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/group-constructors.js b/catapult/third_party/polymer/components/web-animations-js/src/group-constructors.js
new file mode 100644
index 00000000..e5c0a102
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/group-constructors.js
@@ -0,0 +1,204 @@
+// Copyright 2014 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.
+
+(function(shared, scope, testing) {
+
+ function groupChildDuration(node) {
+ return node._timing.delay + node.activeDuration + node._timing.endDelay;
+ }
+
+ function constructor(children, timingInput, id) {
+ this._id = id;
+ this._parent = null;
+ this.children = children || [];
+ this._reparent(this.children);
+ timingInput = shared.numericTimingToObject(timingInput);
+ this._timingInput = shared.cloneTimingInput(timingInput);
+ this._timing = shared.normalizeTimingInput(timingInput, true);
+ this.timing = shared.makeTiming(timingInput, true, this);
+ this.timing._effect = this;
+
+ if (this._timing.duration === 'auto') {
+ this._timing.duration = this.activeDuration;
+ }
+ }
+
+ window.SequenceEffect = function() {
+ constructor.apply(this, arguments);
+ };
+
+ window.GroupEffect = function() {
+ constructor.apply(this, arguments);
+ };
+
+ constructor.prototype = {
+ _isAncestor: function(effect) {
+ var a = this;
+ while (a !== null) {
+ if (a == effect)
+ return true;
+ a = a._parent;
+ }
+ return false;
+ },
+ _rebuild: function() {
+ // Re-calculate durations for ancestors with specified duration 'auto'.
+ var node = this;
+ while (node) {
+ if (node.timing.duration === 'auto') {
+ node._timing.duration = node.activeDuration;
+ }
+ node = node._parent;
+ }
+ if (this._animation) {
+ this._animation._rebuildUnderlyingAnimation();
+ }
+ },
+ _reparent: function(newChildren) {
+ scope.removeMulti(newChildren);
+ for (var i = 0; i < newChildren.length; i++) {
+ newChildren[i]._parent = this;
+ }
+ },
+ _putChild: function(args, isAppend) {
+ var message = isAppend ? 'Cannot append an ancestor or self' : 'Cannot prepend an ancestor or self';
+ for (var i = 0; i < args.length; i++) {
+ if (this._isAncestor(args[i])) {
+ throw {
+ type: DOMException.HIERARCHY_REQUEST_ERR,
+ name: 'HierarchyRequestError',
+ message: message
+ };
+ }
+ }
+ var oldParents = [];
+ for (var i = 0; i < args.length; i++) {
+ isAppend ? this.children.push(args[i]) : this.children.unshift(args[i]);
+ }
+ this._reparent(args);
+ this._rebuild();
+ },
+ append: function() {
+ this._putChild(arguments, true);
+ },
+ prepend: function() {
+ this._putChild(arguments, false);
+ },
+ get parent() {
+ return this._parent;
+ },
+ get firstChild() {
+ return this.children.length ? this.children[0] : null;
+ },
+ get lastChild() {
+ return this.children.length ? this.children[this.children.length - 1] : null;
+ },
+ clone: function() {
+ var clonedTiming = shared.cloneTimingInput(this._timingInput);
+ var clonedChildren = [];
+ for (var i = 0; i < this.children.length; i++) {
+ clonedChildren.push(this.children[i].clone());
+ }
+ return (this instanceof GroupEffect) ?
+ new GroupEffect(clonedChildren, clonedTiming) :
+ new SequenceEffect(clonedChildren, clonedTiming);
+ },
+ remove: function() {
+ scope.removeMulti([this]);
+ }
+ };
+
+ window.SequenceEffect.prototype = Object.create(constructor.prototype);
+ Object.defineProperty(
+ window.SequenceEffect.prototype,
+ 'activeDuration',
+ {
+ get: function() {
+ var total = 0;
+ this.children.forEach(function(child) {
+ total += groupChildDuration(child);
+ });
+ return Math.max(total, 0);
+ }
+ });
+
+ window.GroupEffect.prototype = Object.create(constructor.prototype);
+ Object.defineProperty(
+ window.GroupEffect.prototype,
+ 'activeDuration',
+ {
+ get: function() {
+ var max = 0;
+ this.children.forEach(function(child) {
+ max = Math.max(max, groupChildDuration(child));
+ });
+ return max;
+ }
+ });
+
+ scope.newUnderlyingAnimationForGroup = function(group) {
+ var underlyingAnimation;
+ var timing = null;
+ var ticker = function(tf) {
+ var animation = underlyingAnimation._wrapper;
+ if (!animation) {
+ return;
+ }
+ if (animation.playState == 'pending') {
+ return;
+ }
+ if (!animation.effect) {
+ return;
+ }
+ if (tf == null) {
+ animation._removeChildAnimations();
+ return;
+ }
+
+ // If the group has a negative playback rate and is not fill backwards/both, then it should go
+ // out of effect when it reaches the start of its active interval (tf == 0). If it is fill
+ // backwards/both then it should stay in effect. calculateIterationProgress will return 0 in the
+ // backwards-filling case, and null otherwise.
+ if (tf == 0 && animation.playbackRate < 0) {
+ if (!timing) {
+ timing = shared.normalizeTimingInput(animation.effect.timing);
+ }
+ tf = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), -1, timing);
+ if (isNaN(tf) || tf == null) {
+ animation._forEachChild(function(child) {
+ child.currentTime = -1;
+ });
+ animation._removeChildAnimations();
+ return;
+ }
+ }
+ };
+
+ var underlyingEffect = new KeyframeEffect(null, [], group._timing, group._id);
+ underlyingEffect.onsample = ticker;
+ underlyingAnimation = scope.timeline._play(underlyingEffect);
+ return underlyingAnimation;
+ };
+
+ scope.bindAnimationForGroup = function(animation) {
+ animation._animation._wrapper = animation;
+ animation._isGroup = true;
+ scope.awaitStartTime(animation);
+ animation._constructChildAnimations();
+ animation._setExternalAnimation(animation);
+ };
+
+ scope.groupChildDuration = groupChildDuration;
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/handler-utils.js b/catapult/third_party/polymer/components/web-animations-js/src/handler-utils.js
new file mode 100644
index 00000000..d9f05e17
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/handler-utils.js
@@ -0,0 +1,177 @@
+// Copyright 2014 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.
+
+(function(scope) {
+
+ // consume* functions return a 2 value array of [parsed-data, '' or not-yet consumed input]
+
+ // Regex should be anchored with /^
+ function consumeToken(regex, string) {
+ var result = regex.exec(string);
+ if (result) {
+ result = regex.ignoreCase ? result[0].toLowerCase() : result[0];
+ return [result, string.substr(result.length)];
+ }
+ }
+
+ function consumeTrimmed(consumer, string) {
+ string = string.replace(/^\s*/, '');
+ var result = consumer(string);
+ if (result) {
+ return [result[0], result[1].replace(/^\s*/, '')];
+ }
+ }
+
+ function consumeRepeated(consumer, separator, string) {
+ consumer = consumeTrimmed.bind(null, consumer);
+ var list = [];
+ while (true) {
+ var result = consumer(string);
+ if (!result) {
+ return [list, string];
+ }
+ list.push(result[0]);
+ string = result[1];
+ result = consumeToken(separator, string);
+ if (!result || result[1] == '') {
+ return [list, string];
+ }
+ string = result[1];
+ }
+ }
+
+ // Consumes a token or expression with balanced parentheses
+ function consumeParenthesised(parser, string) {
+ var nesting = 0;
+ for (var n = 0; n < string.length; n++) {
+ if (/\s|,/.test(string[n]) && nesting == 0) {
+ break;
+ } else if (string[n] == '(') {
+ nesting++;
+ } else if (string[n] == ')') {
+ nesting--;
+ if (nesting == 0)
+ n++;
+ if (nesting <= 0)
+ break;
+ }
+ }
+ var parsed = parser(string.substr(0, n));
+ return parsed == undefined ? undefined : [parsed, string.substr(n)];
+ }
+
+ function lcm(a, b) {
+ var c = a;
+ var d = b;
+ while (c && d)
+ c > d ? c %= d : d %= c;
+ c = (a * b) / (c + d);
+ return c;
+ }
+
+ function ignore(value) {
+ return function(input) {
+ var result = value(input);
+ if (result)
+ result[0] = undefined;
+ return result;
+ }
+ }
+
+ function optional(value, defaultValue) {
+ return function(input) {
+ var result = value(input);
+ if (result)
+ return result;
+ return [defaultValue, input];
+ }
+ }
+
+ function consumeList(list, input) {
+ var output = [];
+ for (var i = 0; i < list.length; i++) {
+ var result = scope.consumeTrimmed(list[i], input);
+ if (!result || result[0] == '')
+ return;
+ if (result[0] !== undefined)
+ output.push(result[0]);
+ input = result[1];
+ }
+ if (input == '') {
+ return output;
+ }
+ }
+
+ function mergeWrappedNestedRepeated(wrap, nestedMerge, separator, left, right) {
+ var matchingLeft = [];
+ var matchingRight = [];
+ var reconsititution = [];
+ var length = lcm(left.length, right.length);
+ for (var i = 0; i < length; i++) {
+ var thing = nestedMerge(left[i % left.length], right[i % right.length]);
+ if (!thing) {
+ return;
+ }
+ matchingLeft.push(thing[0]);
+ matchingRight.push(thing[1]);
+ reconsititution.push(thing[2]);
+ }
+ return [matchingLeft, matchingRight, function(positions) {
+ var result = positions.map(function(position, i) {
+ return reconsititution[i](position);
+ }).join(separator);
+ return wrap ? wrap(result) : result;
+ }];
+ }
+
+ function mergeList(left, right, list) {
+ var lefts = [];
+ var rights = [];
+ var functions = [];
+ var j = 0;
+ for (var i = 0; i < list.length; i++) {
+ if (typeof list[i] == 'function') {
+ var result = list[i](left[j], right[j++]);
+ lefts.push(result[0]);
+ rights.push(result[1]);
+ functions.push(result[2]);
+ } else {
+ (function(pos) {
+ lefts.push(false);
+ rights.push(false);
+ functions.push(function() { return list[pos]; });
+ })(i);
+ }
+ }
+ return [lefts, rights, function(results) {
+ var result = '';
+ for (var i = 0; i < results.length; i++) {
+ result += functions[i](results[i]);
+ }
+ return result;
+ }];
+ }
+
+ scope.consumeToken = consumeToken;
+ scope.consumeTrimmed = consumeTrimmed;
+ scope.consumeRepeated = consumeRepeated;
+ scope.consumeParenthesised = consumeParenthesised;
+ scope.ignore = ignore;
+ scope.optional = optional;
+ scope.consumeList = consumeList;
+ scope.mergeNestedRepeated = mergeWrappedNestedRepeated.bind(null, null);
+ scope.mergeWrappedNestedRepeated = mergeWrappedNestedRepeated;
+ scope.mergeList = mergeList;
+
+})(webAnimations1);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/interpolation.js b/catapult/third_party/polymer/components/web-animations-js/src/interpolation.js
new file mode 100644
index 00000000..ba63ed35
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/interpolation.js
@@ -0,0 +1,49 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ function interpolate(from, to, f) {
+ if ((typeof from == 'number') && (typeof to == 'number')) {
+ return from * (1 - f) + to * f;
+ }
+ if ((typeof from == 'boolean') && (typeof to == 'boolean')) {
+ return f < 0.5 ? from : to;
+ }
+
+ WEB_ANIMATIONS_TESTING && console.assert(
+ Array.isArray(from) && Array.isArray(to),
+ 'If interpolation arguments are not numbers or bools they must be arrays');
+
+ if (from.length == to.length) {
+ var r = [];
+ for (var i = 0; i < from.length; i++) {
+ r.push(interpolate(from[i], to[i], f));
+ }
+ return r;
+ }
+ throw 'Mismatched interpolation arguments ' + from + ':' + to;
+ }
+
+ scope.Interpolation = function(from, to, convertToString) {
+ return function(f) {
+ return convertToString(interpolate(from, to, f));
+ }
+ };
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.interpolate = interpolate;
+ }
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect-constructor.js b/catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect-constructor.js
new file mode 100644
index 00000000..3d0f1bd6
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect-constructor.js
@@ -0,0 +1,183 @@
+// Copyright 2014 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.
+
+(function(shared, scope, testing) {
+
+ var disassociate = function(effect) {
+ effect._animation = undefined;
+ if (effect instanceof window.SequenceEffect || effect instanceof window.GroupEffect) {
+ for (var i = 0; i < effect.children.length; i++) {
+ disassociate(effect.children[i]);
+ }
+ }
+ };
+
+ scope.removeMulti = function(effects) {
+ var oldParents = [];
+ for (var i = 0; i < effects.length; i++) {
+ var effect = effects[i];
+ if (effect._parent) {
+ if (oldParents.indexOf(effect._parent) == -1) {
+ oldParents.push(effect._parent);
+ }
+ effect._parent.children.splice(effect._parent.children.indexOf(effect), 1);
+ effect._parent = null;
+ disassociate(effect);
+ } else if (effect._animation && (effect._animation.effect == effect)) {
+ effect._animation.cancel();
+ effect._animation.effect = new KeyframeEffect(null, []);
+ if (effect._animation._callback) {
+ effect._animation._callback._animation = null;
+ }
+ effect._animation._rebuildUnderlyingAnimation();
+ disassociate(effect);
+ }
+ }
+ for (i = 0; i < oldParents.length; i++) {
+ oldParents[i]._rebuild();
+ }
+ };
+
+ function KeyframeList(effectInput) {
+ this._frames = shared.normalizeKeyframes(effectInput);
+ }
+
+ scope.KeyframeEffect = function(target, effectInput, timingInput, id) {
+ this.target = target;
+ this._parent = null;
+
+ timingInput = shared.numericTimingToObject(timingInput);
+ this._timingInput = shared.cloneTimingInput(timingInput);
+ this._timing = shared.normalizeTimingInput(timingInput);
+
+ this.timing = shared.makeTiming(timingInput, false, this);
+ this.timing._effect = this;
+ if (typeof effectInput == 'function') {
+ shared.deprecated('Custom KeyframeEffect', '2015-06-22', 'Use KeyframeEffect.onsample instead.');
+ this._normalizedKeyframes = effectInput;
+ } else {
+ this._normalizedKeyframes = new KeyframeList(effectInput);
+ }
+ this._keyframes = effectInput;
+ this.activeDuration = shared.calculateActiveDuration(this._timing);
+ this._id = id;
+ return this;
+ };
+
+ scope.KeyframeEffect.prototype = {
+ getFrames: function() {
+ if (typeof this._normalizedKeyframes == 'function')
+ return this._normalizedKeyframes;
+ return this._normalizedKeyframes._frames;
+ },
+ set onsample(callback) {
+ if (typeof this.getFrames() == 'function') {
+ throw new Error('Setting onsample on custom effect KeyframeEffect is not supported.');
+ }
+ this._onsample = callback;
+ if (this._animation) {
+ this._animation._rebuildUnderlyingAnimation();
+ }
+ },
+ get parent() {
+ return this._parent;
+ },
+ clone: function() {
+ if (typeof this.getFrames() == 'function') {
+ throw new Error('Cloning custom effects is not supported.');
+ }
+ var clone = new KeyframeEffect(this.target, [], shared.cloneTimingInput(this._timingInput), this._id);
+ clone._normalizedKeyframes = this._normalizedKeyframes;
+ clone._keyframes = this._keyframes;
+ return clone;
+ },
+ remove: function() {
+ scope.removeMulti([this]);
+ }
+ };
+
+ var originalElementAnimate = Element.prototype.animate;
+ Element.prototype.animate = function(effectInput, options) {
+ var id = '';
+ if (options && options.id) {
+ id = options.id;
+ }
+ return scope.timeline._play(new scope.KeyframeEffect(this, effectInput, options, id));
+ };
+
+ var nullTarget = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ scope.newUnderlyingAnimationForKeyframeEffect = function(keyframeEffect) {
+ if (keyframeEffect) {
+ var target = keyframeEffect.target || nullTarget;
+ var keyframes = keyframeEffect._keyframes;
+ if (typeof keyframes == 'function') {
+ keyframes = [];
+ }
+ var options = keyframeEffect._timingInput;
+ options.id = keyframeEffect._id;
+ } else {
+ var target = nullTarget;
+ var keyframes = [];
+ var options = 0;
+ }
+ return originalElementAnimate.apply(target, [keyframes, options]);
+ };
+
+ // TODO: Remove this once we remove support for custom KeyframeEffects.
+ scope.bindAnimationForKeyframeEffect = function(animation) {
+ if (animation.effect && typeof animation.effect._normalizedKeyframes == 'function') {
+ scope.bindAnimationForCustomEffect(animation);
+ }
+ };
+
+ var pendingGroups = [];
+ scope.awaitStartTime = function(groupAnimation) {
+ if (groupAnimation.startTime !== null || !groupAnimation._isGroup)
+ return;
+ if (pendingGroups.length == 0) {
+ requestAnimationFrame(updatePendingGroups);
+ }
+ pendingGroups.push(groupAnimation);
+ };
+ function updatePendingGroups() {
+ var updated = false;
+ while (pendingGroups.length) {
+ var group = pendingGroups.shift();
+ group._updateChildren();
+ updated = true;
+ }
+ return updated;
+ }
+ var originalGetComputedStyle = window.getComputedStyle;
+ Object.defineProperty(window, 'getComputedStyle', {
+ configurable: true,
+ enumerable: true,
+ value: function() {
+ scope.timeline._updateAnimationsPromises();
+ var result = originalGetComputedStyle.apply(this, arguments);
+ if (updatePendingGroups())
+ result = originalGetComputedStyle.apply(this, arguments);
+ scope.timeline._updateAnimationsPromises();
+ return result;
+ },
+ });
+
+ window.KeyframeEffect = scope.KeyframeEffect;
+ window.Element.prototype.getAnimations = function() {
+ return document.timeline.getAnimations().filter(function(animation) {
+ return animation.effect !== null && animation.effect.target == this;
+ }.bind(this));
+ };
+
+}(webAnimationsShared, webAnimationsNext, webAnimationsTesting));
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect.js b/catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect.js
new file mode 100644
index 00000000..7775d585
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/keyframe-effect.js
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+(function(shared, scope, testing) {
+
+ function EffectTime(timing) {
+ var timeFraction = 0;
+ var activeDuration = shared.calculateActiveDuration(timing);
+ var effectTime = function(localTime) {
+ return shared.calculateIterationProgress(activeDuration, localTime, timing);
+ };
+ effectTime._totalDuration = timing.delay + activeDuration + timing.endDelay;
+ return effectTime;
+ }
+
+ scope.KeyframeEffect = function(target, effectInput, timingInput, id) {
+ var effectTime = EffectTime(shared.normalizeTimingInput(timingInput));
+ var interpolations = scope.convertEffectInput(effectInput);
+ var timeFraction;
+ var keyframeEffect = function() {
+ WEB_ANIMATIONS_TESTING && console.assert(typeof timeFraction !== 'undefined');
+ interpolations(target, timeFraction);
+ };
+ // Returns whether the keyframeEffect is in effect or not after the timing update.
+ keyframeEffect._update = function(localTime) {
+ timeFraction = effectTime(localTime);
+ return timeFraction !== null;
+ };
+ keyframeEffect._clear = function() {
+ interpolations(target, null);
+ };
+ keyframeEffect._hasSameTarget = function(otherTarget) {
+ return target === otherTarget;
+ };
+ keyframeEffect._target = target;
+ keyframeEffect._totalDuration = effectTime._totalDuration;
+ keyframeEffect._id = id;
+ return keyframeEffect;
+ };
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.webAnimations1KeyframeEffect = scope.KeyframeEffect;
+ testing.effectTime = EffectTime;
+ }
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/keyframe-interpolations.js b/catapult/third_party/polymer/components/web-animations-js/src/keyframe-interpolations.js
new file mode 100644
index 00000000..cab801ed
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/keyframe-interpolations.js
@@ -0,0 +1,123 @@
+// Copyright 2014 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.
+
+(function(shared, scope, testing) {
+
+ scope.convertEffectInput = function(effectInput) {
+ var keyframes = shared.normalizeKeyframes(effectInput);
+ var propertySpecificKeyframeGroups = makePropertySpecificKeyframeGroups(keyframes);
+ var interpolations = makeInterpolations(propertySpecificKeyframeGroups);
+ return function(target, fraction) {
+ if (fraction != null) {
+ interpolations.filter(function(interpolation) {
+ return fraction >= interpolation.applyFrom && fraction < interpolation.applyTo;
+ }).forEach(function(interpolation) {
+ var offsetFraction = fraction - interpolation.startOffset;
+ var localDuration = interpolation.endOffset - interpolation.startOffset;
+ var scaledLocalTime = localDuration == 0 ? 0 : interpolation.easingFunction(offsetFraction / localDuration);
+ scope.apply(target, interpolation.property, interpolation.interpolation(scaledLocalTime));
+ });
+ } else {
+ for (var property in propertySpecificKeyframeGroups)
+ if (property != 'offset' && property != 'easing' && property != 'composite')
+ scope.clear(target, property);
+ }
+ };
+ };
+
+
+ function makePropertySpecificKeyframeGroups(keyframes) {
+ var propertySpecificKeyframeGroups = {};
+
+ for (var i = 0; i < keyframes.length; i++) {
+ for (var member in keyframes[i]) {
+ if (member != 'offset' && member != 'easing' && member != 'composite') {
+ var propertySpecificKeyframe = {
+ offset: keyframes[i].offset,
+ easing: keyframes[i].easing,
+ value: keyframes[i][member]
+ };
+ propertySpecificKeyframeGroups[member] = propertySpecificKeyframeGroups[member] || [];
+ propertySpecificKeyframeGroups[member].push(propertySpecificKeyframe);
+ }
+ }
+ }
+
+ for (var groupName in propertySpecificKeyframeGroups) {
+ var group = propertySpecificKeyframeGroups[groupName];
+ if (group[0].offset != 0 || group[group.length - 1].offset != 1) {
+ throw {
+ type: DOMException.NOT_SUPPORTED_ERR,
+ name: 'NotSupportedError',
+ message: 'Partial keyframes are not supported'
+ };
+ }
+ }
+ return propertySpecificKeyframeGroups;
+ }
+
+
+ function makeInterpolations(propertySpecificKeyframeGroups) {
+ var interpolations = [];
+ for (var groupName in propertySpecificKeyframeGroups) {
+ var keyframes = propertySpecificKeyframeGroups[groupName];
+ for (var i = 0; i < keyframes.length - 1; i++) {
+ var startIndex = i;
+ var endIndex = i + 1;
+ var startOffset = keyframes[startIndex].offset;
+ var endOffset = keyframes[endIndex].offset;
+ var applyFrom = startOffset;
+ var applyTo = endOffset;
+
+ if (i == 0) {
+ applyFrom = -Infinity;
+ WEB_ANIMATIONS_TESTING && console.assert(startOffset == 0);
+ if (endOffset == 0) {
+ endIndex = startIndex;
+ }
+ }
+ if (i == keyframes.length - 2) {
+ applyTo = Infinity;
+ WEB_ANIMATIONS_TESTING && console.assert(endOffset == 1);
+ if (startOffset == 1) {
+ startIndex = endIndex;
+ }
+ }
+
+ interpolations.push({
+ applyFrom: applyFrom,
+ applyTo: applyTo,
+ startOffset: keyframes[startIndex].offset,
+ endOffset: keyframes[endIndex].offset,
+ easingFunction: shared.parseEasingFunction(keyframes[startIndex].easing),
+ property: groupName,
+ interpolation: scope.propertyInterpolation(groupName,
+ keyframes[startIndex].value,
+ keyframes[endIndex].value)
+ });
+ }
+ }
+ interpolations.sort(function(leftInterpolation, rightInterpolation) {
+ return leftInterpolation.startOffset - rightInterpolation.startOffset;
+ });
+ return interpolations;
+ }
+
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.makePropertySpecificKeyframeGroups = makePropertySpecificKeyframeGroups;
+ testing.makeInterpolations = makeInterpolations;
+ }
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/matrix-decomposition.js b/catapult/third_party/polymer/components/web-animations-js/src/matrix-decomposition.js
new file mode 100644
index 00000000..aa9cfe76
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/matrix-decomposition.js
@@ -0,0 +1,440 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+ var decomposeMatrix = (function() {
+ function determinant(m) {
+ return m[0][0] * m[1][1] * m[2][2] +
+ m[1][0] * m[2][1] * m[0][2] +
+ m[2][0] * m[0][1] * m[1][2] -
+ m[0][2] * m[1][1] * m[2][0] -
+ m[1][2] * m[2][1] * m[0][0] -
+ m[2][2] * m[0][1] * m[1][0];
+ }
+
+ // from Wikipedia:
+ //
+ // [A B]^-1 = [A^-1 + A^-1B(D - CA^-1B)^-1CA^-1 -A^-1B(D - CA^-1B)^-1]
+ // [C D] [-(D - CA^-1B)^-1CA^-1 (D - CA^-1B)^-1 ]
+ //
+ // Therefore
+ //
+ // [A [0]]^-1 = [A^-1 [0]]
+ // [C 1 ] [ -CA^-1 1 ]
+ function inverse(m) {
+ var iDet = 1 / determinant(m);
+ var a = m[0][0], b = m[0][1], c = m[0][2];
+ var d = m[1][0], e = m[1][1], f = m[1][2];
+ var g = m[2][0], h = m[2][1], k = m[2][2];
+ var Ainv = [
+ [(e * k - f * h) * iDet, (c * h - b * k) * iDet,
+ (b * f - c * e) * iDet, 0],
+ [(f * g - d * k) * iDet, (a * k - c * g) * iDet,
+ (c * d - a * f) * iDet, 0],
+ [(d * h - e * g) * iDet, (g * b - a * h) * iDet,
+ (a * e - b * d) * iDet, 0]
+ ];
+ var lastRow = [];
+ for (var i = 0; i < 3; i++) {
+ var val = 0;
+ for (var j = 0; j < 3; j++) {
+ val += m[3][j] * Ainv[j][i];
+ }
+ lastRow.push(val);
+ }
+ lastRow.push(1);
+ Ainv.push(lastRow);
+ return Ainv;
+ }
+
+ function transposeMatrix4(m) {
+ return [[m[0][0], m[1][0], m[2][0], m[3][0]],
+ [m[0][1], m[1][1], m[2][1], m[3][1]],
+ [m[0][2], m[1][2], m[2][2], m[3][2]],
+ [m[0][3], m[1][3], m[2][3], m[3][3]]];
+ }
+
+ function multVecMatrix(v, m) {
+ var result = [];
+ for (var i = 0; i < 4; i++) {
+ var val = 0;
+ for (var j = 0; j < 4; j++) {
+ val += v[j] * m[j][i];
+ }
+ result.push(val);
+ }
+ return result;
+ }
+
+ function normalize(v) {
+ var len = length(v);
+ return [v[0] / len, v[1] / len, v[2] / len];
+ }
+
+ function length(v) {
+ return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ }
+
+ function combine(v1, v2, v1s, v2s) {
+ return [v1s * v1[0] + v2s * v2[0], v1s * v1[1] + v2s * v2[1],
+ v1s * v1[2] + v2s * v2[2]];
+ }
+
+ function cross(v1, v2) {
+ return [v1[1] * v2[2] - v1[2] * v2[1],
+ v1[2] * v2[0] - v1[0] * v2[2],
+ v1[0] * v2[1] - v1[1] * v2[0]];
+ }
+
+ function decomposeMatrix(matrix) {
+ var m3d = [
+ matrix.slice(0, 4),
+ matrix.slice(4, 8),
+ matrix.slice(8, 12),
+ matrix.slice(12, 16)
+ ];
+
+ // skip normalization step as m3d[3][3] should always be 1
+ if (m3d[3][3] !== 1) {
+ return null;
+ }
+
+ var perspectiveMatrix = [];
+ for (var i = 0; i < 4; i++) {
+ perspectiveMatrix.push(m3d[i].slice());
+ }
+
+ for (var i = 0; i < 3; i++) {
+ perspectiveMatrix[i][3] = 0;
+ }
+
+ if (determinant(perspectiveMatrix) === 0) {
+ return null;
+ }
+
+ var rhs = [];
+
+ var perspective;
+ if (m3d[0][3] || m3d[1][3] || m3d[2][3]) {
+ rhs.push(m3d[0][3]);
+ rhs.push(m3d[1][3]);
+ rhs.push(m3d[2][3]);
+ rhs.push(m3d[3][3]);
+
+ var inversePerspectiveMatrix = inverse(perspectiveMatrix);
+ var transposedInversePerspectiveMatrix =
+ transposeMatrix4(inversePerspectiveMatrix);
+ perspective = multVecMatrix(rhs, transposedInversePerspectiveMatrix);
+ } else {
+ perspective = [0, 0, 0, 1];
+ }
+
+ var translate = m3d[3].slice(0, 3);
+
+ var row = [];
+ row.push(m3d[0].slice(0, 3));
+ var scale = [];
+ scale.push(length(row[0]));
+ row[0] = normalize(row[0]);
+
+ var skew = [];
+ row.push(m3d[1].slice(0, 3));
+ skew.push(dot(row[0], row[1]));
+ row[1] = combine(row[1], row[0], 1.0, -skew[0]);
+
+ scale.push(length(row[1]));
+ row[1] = normalize(row[1]);
+ skew[0] /= scale[1];
+
+ row.push(m3d[2].slice(0, 3));
+ skew.push(dot(row[0], row[2]));
+ row[2] = combine(row[2], row[0], 1.0, -skew[1]);
+ skew.push(dot(row[1], row[2]));
+ row[2] = combine(row[2], row[1], 1.0, -skew[2]);
+
+ scale.push(length(row[2]));
+ row[2] = normalize(row[2]);
+ skew[1] /= scale[2];
+ skew[2] /= scale[2];
+
+ var pdum3 = cross(row[1], row[2]);
+ if (dot(row[0], pdum3) < 0) {
+ for (var i = 0; i < 3; i++) {
+ scale[i] *= -1;
+ row[i][0] *= -1;
+ row[i][1] *= -1;
+ row[i][2] *= -1;
+ }
+ }
+
+ var t = row[0][0] + row[1][1] + row[2][2] + 1;
+ var s;
+ var quaternion;
+
+ if (t > 1e-4) {
+ s = 0.5 / Math.sqrt(t);
+ quaternion = [
+ (row[2][1] - row[1][2]) * s,
+ (row[0][2] - row[2][0]) * s,
+ (row[1][0] - row[0][1]) * s,
+ 0.25 / s
+ ];
+ } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
+ s = Math.sqrt(1 + row[0][0] - row[1][1] - row[2][2]) * 2.0;
+ quaternion = [
+ 0.25 * s,
+ (row[0][1] + row[1][0]) / s,
+ (row[0][2] + row[2][0]) / s,
+ (row[2][1] - row[1][2]) / s
+ ];
+ } else if (row[1][1] > row[2][2]) {
+ s = Math.sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0;
+ quaternion = [
+ (row[0][1] + row[1][0]) / s,
+ 0.25 * s,
+ (row[1][2] + row[2][1]) / s,
+ (row[0][2] - row[2][0]) / s
+ ];
+ } else {
+ s = Math.sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0;
+ quaternion = [
+ (row[0][2] + row[2][0]) / s,
+ (row[1][2] + row[2][1]) / s,
+ 0.25 * s,
+ (row[1][0] - row[0][1]) / s
+ ];
+ }
+
+ return [translate, scale, skew, quaternion, perspective];
+ }
+ return decomposeMatrix;
+ })();
+
+ function dot(v1, v2) {
+ var result = 0;
+ for (var i = 0; i < v1.length; i++) {
+ result += v1[i] * v2[i];
+ }
+ return result;
+ }
+
+ function multiplyMatrices(a, b) {
+ return [
+ a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3],
+ a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3],
+ a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3],
+ a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3],
+
+ a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7],
+ a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7],
+ a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7],
+ a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7],
+
+ a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11],
+ a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11],
+ a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11],
+ a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11],
+
+ a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15],
+ a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15],
+ a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15],
+ a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]
+ ];
+ }
+
+ function toRadians(arg) {
+ var rads = arg.rad || 0;
+ var degs = arg.deg || 0;
+ var grads = arg.grad || 0;
+ var turns = arg.turn || 0;
+ var angle = (degs / 360 + grads / 400 + turns) * (2 * Math.PI) + rads;
+ return angle;
+ }
+
+ function convertItemToMatrix(item) {
+ switch (item.t) {
+ case 'rotatex':
+ var angle = toRadians(item.d[0]);
+ return [1, 0, 0, 0,
+ 0, Math.cos(angle), Math.sin(angle), 0,
+ 0, -Math.sin(angle), Math.cos(angle), 0,
+ 0, 0, 0, 1];
+ case 'rotatey':
+ var angle = toRadians(item.d[0]);
+ return [Math.cos(angle), 0, -Math.sin(angle), 0,
+ 0, 1, 0, 0,
+ Math.sin(angle), 0, Math.cos(angle), 0,
+ 0, 0, 0, 1];
+ case 'rotate':
+ case 'rotatez':
+ var angle = toRadians(item.d[0]);
+ return [Math.cos(angle), Math.sin(angle), 0, 0,
+ -Math.sin(angle), Math.cos(angle), 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ case 'rotate3d':
+ var x = item.d[0];
+ var y = item.d[1];
+ var z = item.d[2];
+ var angle = toRadians(item.d[3]);
+
+ var sqrLength = x * x + y * y + z * z;
+ if (sqrLength === 0) {
+ x = 1;
+ y = 0;
+ z = 0;
+ } else if (sqrLength !== 1) {
+ var length = Math.sqrt(sqrLength);
+ x /= length;
+ y /= length;
+ z /= length;
+ }
+
+ var s = Math.sin(angle / 2);
+ var sc = s * Math.cos(angle / 2);
+ var sq = s * s;
+ return [
+ 1 - 2 * (y * y + z * z) * sq,
+ 2 * (x * y * sq + z * sc),
+ 2 * (x * z * sq - y * sc),
+ 0,
+
+ 2 * (x * y * sq - z * sc),
+ 1 - 2 * (x * x + z * z) * sq,
+ 2 * (y * z * sq + x * sc),
+ 0,
+
+ 2 * (x * z * sq + y * sc),
+ 2 * (y * z * sq - x * sc),
+ 1 - 2 * (x * x + y * y) * sq,
+ 0,
+
+ 0, 0, 0, 1
+ ];
+ case 'scale':
+ return [item.d[0], 0, 0, 0,
+ 0, item.d[1], 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ case 'scalex':
+ return [item.d[0], 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ case 'scaley':
+ return [1, 0, 0, 0,
+ 0, item.d[0], 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ case 'scalez':
+ return [1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, item.d[0], 0,
+ 0, 0, 0, 1];
+ case 'scale3d':
+ return [item.d[0], 0, 0, 0,
+ 0, item.d[1], 0, 0,
+ 0, 0, item.d[2], 0,
+ 0, 0, 0, 1];
+ case 'skew':
+ var xAngle = toRadians(item.d[0]);
+ var yAngle = toRadians(item.d[1]);
+ return [1, Math.tan(yAngle), 0, 0,
+ Math.tan(xAngle), 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ case 'skewx':
+ var angle = toRadians(item.d[0]);
+ return [1, 0, 0, 0,
+ Math.tan(angle), 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ case 'skewy':
+ var angle = toRadians(item.d[0]);
+ return [1, Math.tan(angle), 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ case 'translate':
+ var x = item.d[0].px || 0;
+ var y = item.d[1].px || 0;
+ return [1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ x, y, 0, 1];
+ case 'translatex':
+ var x = item.d[0].px || 0;
+ return [1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ x, 0, 0, 1];
+ case 'translatey':
+ var y = item.d[0].px || 0;
+ return [1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, y, 0, 1];
+ case 'translatez':
+ var z = item.d[0].px || 0;
+ return [1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, z, 1];
+ case 'translate3d':
+ var x = item.d[0].px || 0;
+ var y = item.d[1].px || 0;
+ var z = item.d[2].px || 0;
+ return [1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ x, y, z, 1];
+ case 'perspective':
+ var p = item.d[0].px ? (-1 / item.d[0].px) : 0;
+ return [
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, p,
+ 0, 0, 0, 1];
+ case 'matrix':
+ return [item.d[0], item.d[1], 0, 0,
+ item.d[2], item.d[3], 0, 0,
+ 0, 0, 1, 0,
+ item.d[4], item.d[5], 0, 1];
+ case 'matrix3d':
+ return item.d;
+ default:
+ WEB_ANIMATIONS_TESTING && console.assert(false, 'Transform item type ' + item.t +
+ ' conversion to matrix not yet implemented.');
+ }
+ }
+
+ function convertToMatrix(transformList) {
+ if (transformList.length === 0) {
+ return [1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1];
+ }
+ return transformList.map(convertItemToMatrix).reduce(multiplyMatrices);
+ }
+
+ function makeMatrixDecomposition(transformList) {
+ return [decomposeMatrix(convertToMatrix(transformList))];
+ }
+
+ scope.dot = dot;
+ scope.makeMatrixDecomposition = makeMatrixDecomposition;
+ scope.transformListToMatrix = convertToMatrix;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/matrix-interpolation.js b/catapult/third_party/polymer/components/web-animations-js/src/matrix-interpolation.js
new file mode 100644
index 00000000..9a35de3b
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/matrix-interpolation.js
@@ -0,0 +1,130 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+ var composeMatrix = (function() {
+ function multiply(a, b) {
+ var result = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
+ for (var i = 0; i < 4; i++) {
+ for (var j = 0; j < 4; j++) {
+ for (var k = 0; k < 4; k++) {
+ result[i][j] += b[i][k] * a[k][j];
+ }
+ }
+ }
+ return result;
+ }
+
+ function is2D(m) {
+ return (
+ m[0][2] == 0 &&
+ m[0][3] == 0 &&
+ m[1][2] == 0 &&
+ m[1][3] == 0 &&
+ m[2][0] == 0 &&
+ m[2][1] == 0 &&
+ m[2][2] == 1 &&
+ m[2][3] == 0 &&
+ m[3][2] == 0 &&
+ m[3][3] == 1);
+ }
+
+ function composeMatrix(translate, scale, skew, quat, perspective) {
+ var matrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
+
+ for (var i = 0; i < 4; i++) {
+ matrix[i][3] = perspective[i];
+ }
+
+ for (var i = 0; i < 3; i++) {
+ for (var j = 0; j < 3; j++) {
+ matrix[3][i] += translate[j] * matrix[j][i];
+ }
+ }
+
+ var x = quat[0], y = quat[1], z = quat[2], w = quat[3];
+
+ var rotMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
+
+ rotMatrix[0][0] = 1 - 2 * (y * y + z * z);
+ rotMatrix[0][1] = 2 * (x * y - z * w);
+ rotMatrix[0][2] = 2 * (x * z + y * w);
+ rotMatrix[1][0] = 2 * (x * y + z * w);
+ rotMatrix[1][1] = 1 - 2 * (x * x + z * z);
+ rotMatrix[1][2] = 2 * (y * z - x * w);
+ rotMatrix[2][0] = 2 * (x * z - y * w);
+ rotMatrix[2][1] = 2 * (y * z + x * w);
+ rotMatrix[2][2] = 1 - 2 * (x * x + y * y);
+
+ matrix = multiply(matrix, rotMatrix);
+
+ var temp = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
+ if (skew[2]) {
+ temp[2][1] = skew[2];
+ matrix = multiply(matrix, temp);
+ }
+
+ if (skew[1]) {
+ temp[2][1] = 0;
+ temp[2][0] = skew[0];
+ matrix = multiply(matrix, temp);
+ }
+
+ if (skew[0]) {
+ temp[2][0] = 0;
+ temp[1][0] = skew[0];
+ matrix = multiply(matrix, temp);
+ }
+
+ for (var i = 0; i < 3; i++) {
+ for (var j = 0; j < 3; j++) {
+ matrix[i][j] *= scale[i];
+ }
+ }
+
+ if (is2D(matrix)) {
+ return [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[3][0], matrix[3][1]];
+ }
+ return matrix[0].concat(matrix[1], matrix[2], matrix[3]);
+ }
+ return composeMatrix;
+ })();
+
+ function clamp(x, min, max) {
+ return Math.max(Math.min(x, max), min);
+ };
+
+ function quat(fromQ, toQ, f) {
+ var product = scope.dot(fromQ, toQ);
+ product = clamp(product, -1.0, 1.0);
+
+ var quat = [];
+ if (product === 1.0) {
+ quat = fromQ;
+ } else {
+ var theta = Math.acos(product);
+ var w = Math.sin(f * theta) * 1 / Math.sqrt(1 - product * product);
+
+ for (var i = 0; i < 4; i++) {
+ quat.push(fromQ[i] * (Math.cos(f * theta) - product * w) +
+ toQ[i] * w);
+ }
+ }
+ return quat;
+ }
+
+ scope.composeMatrix = composeMatrix;
+ scope.quat = quat;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/normalize-keyframes.js b/catapult/third_party/polymer/components/web-animations-js/src/normalize-keyframes.js
new file mode 100644
index 00000000..1fd02548
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/normalize-keyframes.js
@@ -0,0 +1,321 @@
+// Copyright 2014 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.
+
+(function(shared, testing) {
+ var shorthandToLonghand = {
+ background: [
+ 'backgroundImage',
+ 'backgroundPosition',
+ 'backgroundSize',
+ 'backgroundRepeat',
+ 'backgroundAttachment',
+ 'backgroundOrigin',
+ 'backgroundClip',
+ 'backgroundColor'
+ ],
+ border: [
+ 'borderTopColor',
+ 'borderTopStyle',
+ 'borderTopWidth',
+ 'borderRightColor',
+ 'borderRightStyle',
+ 'borderRightWidth',
+ 'borderBottomColor',
+ 'borderBottomStyle',
+ 'borderBottomWidth',
+ 'borderLeftColor',
+ 'borderLeftStyle',
+ 'borderLeftWidth'
+ ],
+ borderBottom: [
+ 'borderBottomWidth',
+ 'borderBottomStyle',
+ 'borderBottomColor'
+ ],
+ borderColor: [
+ 'borderTopColor',
+ 'borderRightColor',
+ 'borderBottomColor',
+ 'borderLeftColor'
+ ],
+ borderLeft: [
+ 'borderLeftWidth',
+ 'borderLeftStyle',
+ 'borderLeftColor'
+ ],
+ borderRadius: [
+ 'borderTopLeftRadius',
+ 'borderTopRightRadius',
+ 'borderBottomRightRadius',
+ 'borderBottomLeftRadius'
+ ],
+ borderRight: [
+ 'borderRightWidth',
+ 'borderRightStyle',
+ 'borderRightColor'
+ ],
+ borderTop: [
+ 'borderTopWidth',
+ 'borderTopStyle',
+ 'borderTopColor'
+ ],
+ borderWidth: [
+ 'borderTopWidth',
+ 'borderRightWidth',
+ 'borderBottomWidth',
+ 'borderLeftWidth'
+ ],
+ flex: [
+ 'flexGrow',
+ 'flexShrink',
+ 'flexBasis'
+ ],
+ font: [
+ 'fontFamily',
+ 'fontSize',
+ 'fontStyle',
+ 'fontVariant',
+ 'fontWeight',
+ 'lineHeight'
+ ],
+ margin: [
+ 'marginTop',
+ 'marginRight',
+ 'marginBottom',
+ 'marginLeft'
+ ],
+ outline: [
+ 'outlineColor',
+ 'outlineStyle',
+ 'outlineWidth'
+ ],
+ padding: [
+ 'paddingTop',
+ 'paddingRight',
+ 'paddingBottom',
+ 'paddingLeft'
+ ]
+ };
+
+ var shorthandExpanderElem = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+
+ var borderWidthAliases = {
+ thin: '1px',
+ medium: '3px',
+ thick: '5px'
+ };
+
+ var aliases = {
+ borderBottomWidth: borderWidthAliases,
+ borderLeftWidth: borderWidthAliases,
+ borderRightWidth: borderWidthAliases,
+ borderTopWidth: borderWidthAliases,
+ fontSize: {
+ 'xx-small': '60%',
+ 'x-small': '75%',
+ 'small': '89%',
+ 'medium': '100%',
+ 'large': '120%',
+ 'x-large': '150%',
+ 'xx-large': '200%'
+ },
+ fontWeight: {
+ normal: '400',
+ bold: '700'
+ },
+ outlineWidth: borderWidthAliases,
+ textShadow: {
+ none: '0px 0px 0px transparent'
+ },
+ boxShadow: {
+ none: '0px 0px 0px 0px transparent'
+ }
+ };
+
+ function antiAlias(property, value) {
+ if (property in aliases) {
+ return aliases[property][value] || value;
+ }
+ return value;
+ }
+
+ function isNotAnimatable(property) {
+ // https://w3c.github.io/web-animations/#concept-not-animatable
+ return property === 'display' || property.lastIndexOf('animation', 0) === 0 || property.lastIndexOf('transition', 0) === 0;
+ }
+
+ // This delegates parsing shorthand value syntax to the browser.
+ function expandShorthandAndAntiAlias(property, value, result) {
+ if (isNotAnimatable(property)) {
+ return;
+ }
+ var longProperties = shorthandToLonghand[property];
+ if (longProperties) {
+ shorthandExpanderElem.style[property] = value;
+ for (var i in longProperties) {
+ var longProperty = longProperties[i];
+ var longhandValue = shorthandExpanderElem.style[longProperty];
+ result[longProperty] = antiAlias(longProperty, longhandValue);
+ }
+ } else {
+ result[property] = antiAlias(property, value);
+ }
+ };
+
+ function convertToArrayForm(effectInput) {
+ var normalizedEffectInput = [];
+
+ for (var property in effectInput) {
+ if (property in ['easing', 'offset', 'composite']) {
+ continue;
+ }
+
+ var values = effectInput[property];
+ if (!Array.isArray(values)) {
+ values = [values];
+ }
+
+ var keyframe;
+ var numKeyframes = values.length;
+ for (var i = 0; i < numKeyframes; i++) {
+ keyframe = {};
+
+ if ('offset' in effectInput) {
+ keyframe.offset = effectInput.offset;
+ } else if (numKeyframes == 1) {
+ keyframe.offset = 1.0;
+ } else {
+ keyframe.offset = i / (numKeyframes - 1.0);
+ }
+
+ if ('easing' in effectInput) {
+ keyframe.easing = effectInput.easing;
+ }
+
+ if ('composite' in effectInput) {
+ keyframe.composite = effectInput.composite;
+ }
+
+ keyframe[property] = values[i];
+
+ normalizedEffectInput.push(keyframe);
+ }
+ }
+
+ normalizedEffectInput.sort(function(a, b) { return a.offset - b.offset; });
+ return normalizedEffectInput;
+ };
+
+ function normalizeKeyframes(effectInput) {
+ if (effectInput == null) {
+ return [];
+ }
+
+ if (window.Symbol && Symbol.iterator && Array.prototype.from && effectInput[Symbol.iterator]) {
+ // Handle custom iterables in most browsers by converting to an array
+ effectInput = Array.from(effectInput);
+ }
+
+ if (!Array.isArray(effectInput)) {
+ effectInput = convertToArrayForm(effectInput);
+ }
+
+ var keyframes = effectInput.map(function(originalKeyframe) {
+ var keyframe = {};
+ for (var member in originalKeyframe) {
+ var memberValue = originalKeyframe[member];
+ if (member == 'offset') {
+ if (memberValue != null) {
+ memberValue = Number(memberValue);
+ if (!isFinite(memberValue))
+ throw new TypeError('Keyframe offsets must be numbers.');
+ if (memberValue < 0 || memberValue > 1)
+ throw new TypeError('Keyframe offsets must be between 0 and 1.');
+ }
+ } else if (member == 'composite') {
+ if (memberValue == 'add' || memberValue == 'accumulate') {
+ throw {
+ type: DOMException.NOT_SUPPORTED_ERR,
+ name: 'NotSupportedError',
+ message: 'add compositing is not supported'
+ };
+ } else if (memberValue != 'replace') {
+ throw new TypeError('Invalid composite mode ' + memberValue + '.');
+ }
+ } else if (member == 'easing') {
+ memberValue = shared.normalizeEasing(memberValue);
+ } else {
+ memberValue = '' + memberValue;
+ }
+ expandShorthandAndAntiAlias(member, memberValue, keyframe);
+ }
+ if (keyframe.offset == undefined)
+ keyframe.offset = null;
+ if (keyframe.easing == undefined)
+ keyframe.easing = 'linear';
+ return keyframe;
+ });
+
+ var everyFrameHasOffset = true;
+ var looselySortedByOffset = true;
+ var previousOffset = -Infinity;
+ for (var i = 0; i < keyframes.length; i++) {
+ var offset = keyframes[i].offset;
+ if (offset != null) {
+ if (offset < previousOffset) {
+ throw new TypeError('Keyframes are not loosely sorted by offset. Sort or specify offsets.');
+ }
+ previousOffset = offset;
+ } else {
+ everyFrameHasOffset = false;
+ }
+ }
+
+ keyframes = keyframes.filter(function(keyframe) {
+ return keyframe.offset >= 0 && keyframe.offset <= 1;
+ });
+
+ function spaceKeyframes() {
+ var length = keyframes.length;
+ if (keyframes[length - 1].offset == null)
+ keyframes[length - 1].offset = 1;
+ if (length > 1 && keyframes[0].offset == null)
+ keyframes[0].offset = 0;
+
+ var previousIndex = 0;
+ var previousOffset = keyframes[0].offset;
+ for (var i = 1; i < length; i++) {
+ var offset = keyframes[i].offset;
+ if (offset != null) {
+ for (var j = 1; j < i - previousIndex; j++)
+ keyframes[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);
+ previousIndex = i;
+ previousOffset = offset;
+ }
+ }
+ }
+ if (!everyFrameHasOffset)
+ spaceKeyframes();
+
+ return keyframes;
+ }
+
+ shared.convertToArrayForm = convertToArrayForm;
+ shared.normalizeKeyframes = normalizeKeyframes;
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.normalizeKeyframes = normalizeKeyframes;
+ }
+
+})(webAnimationsShared, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/number-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/number-handler.js
new file mode 100644
index 00000000..7d0e7f9b
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/number-handler.js
@@ -0,0 +1,97 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ function numberToString(x) {
+ return x.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
+ }
+
+ function clamp(min, max, x) {
+ return Math.min(max, Math.max(min, x));
+ }
+
+ function parseNumber(string) {
+ if (/^\s*[-+]?(\d*\.)?\d+\s*$/.test(string))
+ return Number(string);
+ }
+
+ function mergeNumbers(left, right) {
+ return [left, right, numberToString];
+ }
+
+ // FIXME: This should probably go in it's own handler.
+ function mergeFlex(left, right) {
+ if (left == 0)
+ return;
+ return clampedMergeNumbers(0, Infinity)(left, right);
+ }
+
+ function mergePositiveIntegers(left, right) {
+ return [left, right, function(x) {
+ return Math.round(clamp(1, Infinity, x));
+ }];
+ }
+
+ function clampedMergeNumbers(min, max) {
+ return function(left, right) {
+ return [left, right, function(x) {
+ return numberToString(clamp(min, max, x));
+ }];
+ };
+ }
+
+ function parseNumberList(string) {
+ var items = string.trim().split(/\s*[\s,]\s*/);
+ if (items.length === 0) {
+ return;
+ }
+ var result = [];
+ for (var i = 0; i < items.length; i++) {
+ var number = parseNumber(items[i]);
+ if (number === undefined) {
+ return;
+ }
+ result.push(number);
+ }
+ return result;
+ }
+
+ function mergeNumberLists(left, right) {
+ if (left.length != right.length) {
+ return;
+ }
+ return [left, right, function(numberList) {
+ return numberList.map(numberToString).join(' ');
+ }];
+ }
+
+ function round(left, right) {
+ return [left, right, Math.round];
+ }
+
+ scope.clamp = clamp;
+ scope.addPropertiesHandler(parseNumberList, mergeNumberLists, ['stroke-dasharray']);
+ scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0, Infinity), ['border-image-width', 'line-height']);
+ scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0, 1), ['opacity', 'shape-image-threshold']);
+ scope.addPropertiesHandler(parseNumber, mergeFlex, ['flex-grow', 'flex-shrink']);
+ scope.addPropertiesHandler(parseNumber, mergePositiveIntegers, ['orphans', 'widows']);
+ scope.addPropertiesHandler(parseNumber, round, ['z-index']);
+
+ scope.parseNumber = parseNumber;
+ scope.parseNumberList = parseNumberList;
+ scope.mergeNumbers = mergeNumbers;
+ scope.numberToString = numberToString;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/position-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/position-handler.js
new file mode 100644
index 00000000..18cea438
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/position-handler.js
@@ -0,0 +1,117 @@
+// Copyright 2014 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.
+
+(function(scope) {
+
+ function negateDimension(dimension) {
+ var result = {};
+ for (var k in dimension) {
+ result[k] = -dimension[k];
+ }
+ return result;
+ }
+
+ function consumeOffset(string) {
+ return scope.consumeToken(/^(left|center|right|top|bottom)\b/i, string) || scope.consumeLengthOrPercent(string);
+ }
+
+ var offsetMap = {
+ left: {'%': 0},
+ center: {'%': 50},
+ right: {'%': 100},
+ top: {'%': 0},
+ bottom: {'%': 100},
+ };
+
+ function parseOrigin(slots, string) {
+ var result = scope.consumeRepeated(consumeOffset, /^/, string);
+ if (!result || result[1] != '') return;
+ var tokens = result[0];
+ tokens[0] = tokens[0] || 'center';
+ tokens[1] = tokens[1] || 'center';
+ if (slots == 3) {
+ tokens[2] = tokens[2] || {px: 0};
+ }
+ if (tokens.length != slots) {
+ return;
+ }
+ // Reorder so that the horizontal axis comes first.
+ if (/top|bottom/.test(tokens[0]) || /left|right/.test(tokens[1])) {
+ var tmp = tokens[0];
+ tokens[0] = tokens[1];
+ tokens[1] = tmp;
+ }
+ // Invalid if not horizontal then vertical.
+ if (!/left|right|center|Object/.test(tokens[0]))
+ return;
+ if (!/top|bottom|center|Object/.test(tokens[1]))
+ return;
+ return tokens.map(function(position) {
+ return typeof position == 'object' ? position : offsetMap[position];
+ });
+ }
+
+ var mergeOffsetList = scope.mergeNestedRepeated.bind(null, scope.mergeDimensions, ' ');
+ scope.addPropertiesHandler(parseOrigin.bind(null, 3), mergeOffsetList, ['transform-origin']);
+ scope.addPropertiesHandler(parseOrigin.bind(null, 2), mergeOffsetList, ['perspective-origin']);
+
+ function consumePosition(string) {
+ var result = scope.consumeRepeated(consumeOffset, /^/, string);
+ if (!result) {
+ return;
+ }
+
+ var tokens = result[0];
+ var out = [{'%': 50}, {'%': 50}];
+ var pos = 0;
+ var bottomOrRight = false;
+
+ for (var i = 0; i < tokens.length; i++) {
+ var token = tokens[i];
+ if (typeof token == 'string') {
+ bottomOrRight = /bottom|right/.test(token);
+ pos = {left: 0, right: 0, center: pos, top: 1, bottom: 1}[token];
+ out[pos] = offsetMap[token];
+ if (token == 'center') {
+ // Center doesn't accept a length offset.
+ pos++;
+ }
+ } else {
+ if (bottomOrRight) {
+ // If bottom or right we need to subtract the length from 100%
+ token = negateDimension(token);
+ token['%'] = (token['%'] || 0) + 100;
+ }
+ out[pos] = token;
+ pos++;
+ bottomOrRight = false;
+ }
+ }
+ return [out, result[1]];
+ }
+
+ function parsePositionList(string) {
+ var result = scope.consumeRepeated(consumePosition, /^,/, string);
+ if (result && result[1] == '') {
+ return result[0];
+ }
+ }
+
+ scope.consumePosition = consumePosition;
+ scope.mergeOffsetList = mergeOffsetList;
+
+ var mergePositionList = scope.mergeNestedRepeated.bind(null, mergeOffsetList, ', ');
+ scope.addPropertiesHandler(parsePositionList, mergePositionList, ['background-position', 'object-position']);
+
+})(webAnimations1);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/property-interpolation.js b/catapult/third_party/polymer/components/web-animations-js/src/property-interpolation.js
new file mode 100644
index 00000000..b7e594f3
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/property-interpolation.js
@@ -0,0 +1,127 @@
+// Copyright 2014 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.
+
+(function(shared, scope, testing) {
+
+ var propertyHandlers = {};
+
+ function toCamelCase(property) {
+ return property.replace(/-(.)/g, function(_, c) {
+ return c.toUpperCase();
+ });
+ }
+
+ function addPropertyHandler(parser, merger, property) {
+ propertyHandlers[property] = propertyHandlers[property] || [];
+ propertyHandlers[property].push([parser, merger]);
+ }
+ function addPropertiesHandler(parser, merger, properties) {
+ for (var i = 0; i < properties.length; i++) {
+ var property = properties[i];
+ WEB_ANIMATIONS_TESTING && console.assert(property.toLowerCase() === property);
+ addPropertyHandler(parser, merger, toCamelCase(property));
+ }
+ }
+ scope.addPropertiesHandler = addPropertiesHandler;
+
+ var initialValues = {
+ backgroundColor: 'transparent',
+ backgroundPosition: '0% 0%',
+ borderBottomColor: 'currentColor',
+ borderBottomLeftRadius: '0px',
+ borderBottomRightRadius: '0px',
+ borderBottomWidth: '3px',
+ borderLeftColor: 'currentColor',
+ borderLeftWidth: '3px',
+ borderRightColor: 'currentColor',
+ borderRightWidth: '3px',
+ // Spec says this should be 0 but in practise it is 2px.
+ borderSpacing: '2px',
+ borderTopColor: 'currentColor',
+ borderTopLeftRadius: '0px',
+ borderTopRightRadius: '0px',
+ borderTopWidth: '3px',
+ bottom: 'auto',
+ clip: 'rect(0px, 0px, 0px, 0px)',
+ color: 'black', // Depends on user agent.
+ fontSize: '100%',
+ fontWeight: '400',
+ height: 'auto',
+ left: 'auto',
+ letterSpacing: 'normal',
+ lineHeight: '120%',
+ marginBottom: '0px',
+ marginLeft: '0px',
+ marginRight: '0px',
+ marginTop: '0px',
+ maxHeight: 'none',
+ maxWidth: 'none',
+ minHeight: '0px',
+ minWidth: '0px',
+ opacity: '1.0',
+ outlineColor: 'invert',
+ outlineOffset: '0px',
+ outlineWidth: '3px',
+ paddingBottom: '0px',
+ paddingLeft: '0px',
+ paddingRight: '0px',
+ paddingTop: '0px',
+ right: 'auto',
+ strokeDasharray: 'none',
+ strokeDashoffset: '0px',
+ textIndent: '0px',
+ textShadow: '0px 0px 0px transparent',
+ top: 'auto',
+ transform: '',
+ verticalAlign: '0px',
+ visibility: 'visible',
+ width: 'auto',
+ wordSpacing: 'normal',
+ zIndex: 'auto'
+ };
+
+ function propertyInterpolation(property, left, right) {
+ var ucProperty = property;
+ if (/-/.test(property) && !shared.isDeprecated('Hyphenated property names', '2016-03-22', 'Use camelCase instead.', true)) {
+ ucProperty = toCamelCase(property);
+ }
+ if (left == 'initial' || right == 'initial') {
+ if (left == 'initial')
+ left = initialValues[ucProperty];
+ if (right == 'initial')
+ right = initialValues[ucProperty];
+ }
+ var handlers = left == right ? [] : propertyHandlers[ucProperty];
+ for (var i = 0; handlers && i < handlers.length; i++) {
+ var parsedLeft = handlers[i][0](left);
+ var parsedRight = handlers[i][0](right);
+ if (parsedLeft !== undefined && parsedRight !== undefined) {
+ var interpolationArgs = handlers[i][1](parsedLeft, parsedRight);
+ if (interpolationArgs) {
+ var interp = scope.Interpolation.apply(null, interpolationArgs);
+ return function(t) {
+ if (t == 0) return left;
+ if (t == 1) return right;
+ return interp(t);
+ };
+ }
+ }
+ }
+ return scope.Interpolation(false, true, function(bool) {
+ return bool ? right : left;
+ });
+ }
+ scope.propertyInterpolation = propertyInterpolation;
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/property-names.js b/catapult/third_party/polymer/components/web-animations-js/src/property-names.js
new file mode 100644
index 00000000..f106c066
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/property-names.js
@@ -0,0 +1,40 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ var prefixed = {};
+ var unprefixed = {};
+
+ function alias(name, aliases) {
+ aliases.concat([name]).forEach(function(candidate) {
+ if (candidate in document.documentElement.style) {
+ prefixed[name] = candidate;
+ }
+ unprefixed[candidate] = name;
+ });
+ }
+ alias('transform', ['webkitTransform', 'msTransform']);
+ alias('transformOrigin', ['webkitTransformOrigin']);
+ alias('perspective', ['webkitPerspective']);
+ alias('perspectiveOrigin', ['webkitPerspectiveOrigin']);
+
+ scope.propertyName = function(property) {
+ return prefixed[property] || property;
+ };
+ scope.unprefixedPropertyName = function(property) {
+ return unprefixed[property] || property;
+ };
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/scope.js b/catapult/third_party/polymer/components/web-animations-js/src/scope.js
new file mode 100644
index 00000000..c8248802
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/scope.js
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+var webAnimationsShared = {};
+var webAnimations1 = {};
+var webAnimationsNext = {};
+
+if (!WEB_ANIMATIONS_TESTING)
+ var webAnimationsTesting = null;
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/shadow-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/shadow-handler.js
new file mode 100644
index 00000000..3f8201d8
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/shadow-handler.js
@@ -0,0 +1,108 @@
+// Copyright 2014 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.
+
+(function(scope) {
+
+ function consumeShadow(string) {
+ var shadow = {
+ inset: false,
+ lengths: [],
+ color: null,
+ };
+ function consumePart(string) {
+ var result = scope.consumeToken(/^inset/i, string);
+ if (result) {
+ shadow.inset = true;
+ return result;
+ }
+ var result = scope.consumeLengthOrPercent(string);
+ if (result) {
+ shadow.lengths.push(result[0]);
+ return result;
+ }
+ var result = scope.consumeColor(string);
+ if (result) {
+ shadow.color = result[0];
+ return result;
+ }
+ }
+ var result = scope.consumeRepeated(consumePart, /^/, string);
+ if (result && result[0].length) {
+ return [shadow, result[1]];
+ }
+ }
+
+ function parseShadowList(string) {
+ var result = scope.consumeRepeated(consumeShadow, /^,/, string);
+ if (result && result[1] == '') {
+ return result[0];
+ }
+ }
+
+ function mergeShadow(left, right) {
+ while (left.lengths.length < Math.max(left.lengths.length, right.lengths.length))
+ left.lengths.push({px: 0});
+ while (right.lengths.length < Math.max(left.lengths.length, right.lengths.length))
+ right.lengths.push({px: 0});
+
+ if (left.inset != right.inset || !!left.color != !!right.color) {
+ return;
+ }
+ var lengthReconstitution = [];
+ var colorReconstitution;
+ var matchingLeft = [[], 0];
+ var matchingRight = [[], 0];
+ for (var i = 0; i < left.lengths.length; i++) {
+ var mergedDimensions = scope.mergeDimensions(left.lengths[i], right.lengths[i], i == 2);
+ matchingLeft[0].push(mergedDimensions[0]);
+ matchingRight[0].push(mergedDimensions[1]);
+ lengthReconstitution.push(mergedDimensions[2]);
+ }
+ if (left.color && right.color) {
+ var mergedColor = scope.mergeColors(left.color, right.color);
+ matchingLeft[1] = mergedColor[0];
+ matchingRight[1] = mergedColor[1];
+ colorReconstitution = mergedColor[2];
+ }
+ return [matchingLeft, matchingRight, function(value) {
+ var result = left.inset ? 'inset ' : ' ';
+ for (var i = 0; i < lengthReconstitution.length; i++) {
+ result += lengthReconstitution[i](value[0][i]) + ' ';
+ }
+ if (colorReconstitution) {
+ result += colorReconstitution(value[1]);
+ }
+ return result;
+ }];
+ }
+
+ function mergeNestedRepeatedShadow(nestedMerge, separator, left, right) {
+ var leftCopy = [];
+ var rightCopy = [];
+ function defaultShadow(inset) {
+ return {inset: inset, color: [0, 0, 0, 0], lengths: [{px: 0}, {px: 0}, {px: 0}, {px: 0}]};
+ }
+ for (var i = 0; i < left.length || i < right.length; i++) {
+ var l = left[i] || defaultShadow(right[i].inset);
+ var r = right[i] || defaultShadow(left[i].inset);
+ leftCopy.push(l);
+ rightCopy.push(r);
+ }
+ return scope.mergeNestedRepeated(nestedMerge, separator, leftCopy, rightCopy);
+ }
+
+ var mergeShadowList = mergeNestedRepeatedShadow.bind(null, mergeShadow, ', ');
+ scope.addPropertiesHandler(parseShadowList, mergeShadowList, ['box-shadow', 'text-shadow']);
+
+})(webAnimations1);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/shape-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/shape-handler.js
new file mode 100644
index 00000000..6bbf79fe
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/shape-handler.js
@@ -0,0 +1,85 @@
+// Copyright 2014 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.
+
+(function(scope) {
+
+ var consumeLengthOrPercent = scope.consumeParenthesised.bind(null, scope.parseLengthOrPercent);
+ var consumeLengthOrPercentPair = scope.consumeRepeated.bind(undefined, consumeLengthOrPercent, /^/);
+
+ var mergeSizePair = scope.mergeNestedRepeated.bind(undefined, scope.mergeDimensions, ' ');
+ var mergeSizePairList = scope.mergeNestedRepeated.bind(undefined, mergeSizePair, ',');
+
+ function parseShape(input) {
+ var circle = scope.consumeToken(/^circle/, input);
+ if (circle && circle[0]) {
+ return ['circle'].concat(scope.consumeList([
+ scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
+ consumeLengthOrPercent,
+ scope.ignore(scope.consumeToken.bind(undefined, /^at/)),
+ scope.consumePosition,
+ scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
+ ], circle[1]));
+ }
+ var ellipse = scope.consumeToken(/^ellipse/, input);
+ if (ellipse && ellipse[0]) {
+ return ['ellipse'].concat(scope.consumeList([
+ scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
+ consumeLengthOrPercentPair,
+ scope.ignore(scope.consumeToken.bind(undefined, /^at/)),
+ scope.consumePosition,
+ scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
+ ], ellipse[1]));
+ }
+ var polygon = scope.consumeToken(/^polygon/, input);
+ if (polygon && polygon[0]) {
+ return ['polygon'].concat(scope.consumeList([
+ scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
+ scope.optional(scope.consumeToken.bind(undefined, /^nonzero\s*,|^evenodd\s*,/), 'nonzero,'),
+ scope.consumeSizePairList,
+ scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
+ ], polygon[1]));
+ }
+ }
+
+ function mergeShapes(left, right) {
+ if (left[0] !== right[0])
+ return;
+ if (left[0] == 'circle') {
+ return scope.mergeList(left.slice(1), right.slice(1), [
+ 'circle(',
+ scope.mergeDimensions,
+ ' at ',
+ scope.mergeOffsetList,
+ ')']);
+ }
+ if (left[0] == 'ellipse') {
+ return scope.mergeList(left.slice(1), right.slice(1), [
+ 'ellipse(',
+ scope.mergeNonNegativeSizePair,
+ ' at ',
+ scope.mergeOffsetList,
+ ')']);
+ }
+ if (left[0] == 'polygon' && left[1] == right[1]) {
+ return scope.mergeList(left.slice(2), right.slice(2), [
+ 'polygon(',
+ left[1],
+ mergeSizePairList,
+ ')']);
+ }
+ }
+
+ scope.addPropertiesHandler(parseShape, mergeShapes, ['shape-outside']);
+
+})(webAnimations1);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/tick.js b/catapult/third_party/polymer/components/web-animations-js/src/tick.js
new file mode 100644
index 00000000..08b0a140
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/tick.js
@@ -0,0 +1,181 @@
+// Copyright 2014 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.
+
+
+(function(shared, scope, testing) {
+ var originalRequestAnimationFrame = window.requestAnimationFrame;
+ var rafCallbacks = [];
+ var rafId = 0;
+ window.requestAnimationFrame = function(f) {
+ var id = rafId++;
+ if (rafCallbacks.length == 0 && !WEB_ANIMATIONS_TESTING) {
+ originalRequestAnimationFrame(processRafCallbacks);
+ }
+ rafCallbacks.push([id, f]);
+ return id;
+ };
+
+ window.cancelAnimationFrame = function(id) {
+ rafCallbacks.forEach(function(entry) {
+ if (entry[0] == id) {
+ entry[1] = function() {};
+ }
+ });
+ };
+
+ function processRafCallbacks(t) {
+ var processing = rafCallbacks;
+ rafCallbacks = [];
+ if (t < timeline.currentTime)
+ t = timeline.currentTime;
+ timeline._animations.sort(compareAnimations);
+ timeline._animations = tick(t, true, timeline._animations)[0];
+ processing.forEach(function(entry) { entry[1](t); });
+ applyPendingEffects();
+ _now = undefined;
+ }
+
+ function compareAnimations(leftAnimation, rightAnimation) {
+ return leftAnimation._sequenceNumber - rightAnimation._sequenceNumber;
+ }
+
+ function InternalTimeline() {
+ this._animations = [];
+ // Android 4.3 browser has window.performance, but not window.performance.now
+ this.currentTime = window.performance && performance.now ? performance.now() : 0;
+ };
+
+ InternalTimeline.prototype = {
+ _play: function(effect) {
+ effect._timing = shared.normalizeTimingInput(effect.timing);
+ var animation = new scope.Animation(effect);
+ animation._idle = false;
+ animation._timeline = this;
+ this._animations.push(animation);
+ scope.restart();
+ scope.applyDirtiedAnimation(animation);
+ return animation;
+ }
+ };
+
+ var _now = undefined;
+
+ if (WEB_ANIMATIONS_TESTING) {
+ var now = function() { return timeline.currentTime; };
+ } else {
+ var now = function() {
+ if (_now == undefined)
+ _now = window.performance && performance.now ? performance.now() : Date.now();
+ return _now;
+ };
+ }
+
+ var ticking = false;
+ var hasRestartedThisFrame = false;
+
+ scope.restart = function() {
+ if (!ticking) {
+ ticking = true;
+ requestAnimationFrame(function() {});
+ hasRestartedThisFrame = true;
+ }
+ return hasRestartedThisFrame;
+ };
+
+ // RAF is supposed to be the last script to occur before frame rendering but not
+ // all browsers behave like this. This function is for synchonously updating an
+ // animation's effects whenever its state is mutated by script to work around
+ // incorrect script execution ordering by the browser.
+ scope.applyDirtiedAnimation = function(animation) {
+ if (inTick) {
+ return;
+ }
+ animation._markTarget();
+ var animations = animation._targetAnimations();
+ animations.sort(compareAnimations);
+ var inactiveAnimations = tick(scope.timeline.currentTime, false, animations.slice())[1];
+ inactiveAnimations.forEach(function(animation) {
+ var index = timeline._animations.indexOf(animation);
+ if (index !== -1) {
+ timeline._animations.splice(index, 1);
+ }
+ });
+ applyPendingEffects();
+ };
+
+ var pendingEffects = [];
+ function applyPendingEffects() {
+ pendingEffects.forEach(function(f) { f(); });
+ pendingEffects.length = 0;
+ }
+
+ var t60hz = 1000 / 60;
+
+ var inTick = false;
+ function tick(t, isAnimationFrame, updatingAnimations) {
+ inTick = true;
+ hasRestartedThisFrame = false;
+ var timeline = scope.timeline;
+
+ timeline.currentTime = t;
+ ticking = false;
+
+ var newPendingClears = [];
+ var newPendingEffects = [];
+ var activeAnimations = [];
+ var inactiveAnimations = [];
+ updatingAnimations.forEach(function(animation) {
+ animation._tick(t, isAnimationFrame);
+
+ if (!animation._inEffect) {
+ newPendingClears.push(animation._effect);
+ animation._unmarkTarget();
+ } else {
+ newPendingEffects.push(animation._effect);
+ animation._markTarget();
+ }
+
+ if (animation._needsTick)
+ ticking = true;
+
+ var alive = animation._inEffect || animation._needsTick;
+ animation._inTimeline = alive;
+ if (alive) {
+ activeAnimations.push(animation);
+ } else {
+ inactiveAnimations.push(animation);
+ }
+ });
+
+ // FIXME: Should remove dupliactes from pendingEffects.
+ pendingEffects.push.apply(pendingEffects, newPendingClears);
+ pendingEffects.push.apply(pendingEffects, newPendingEffects);
+
+ if (ticking)
+ requestAnimationFrame(function() {});
+
+ inTick = false;
+ return [activeAnimations, inactiveAnimations];
+ };
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.tick = function(t) { timeline.currentTime = t; processRafCallbacks(t); };
+ testing.isTicking = function() { return ticking; };
+ testing.setTicking = function(newVal) { ticking = newVal; };
+ }
+
+ var timeline = new InternalTimeline();
+ scope.timeline = timeline;
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/timeline.js b/catapult/third_party/polymer/components/web-animations-js/src/timeline.js
new file mode 100644
index 00000000..2b213b55
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/timeline.js
@@ -0,0 +1,101 @@
+// Copyright 2014 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.
+
+
+(function(shared, scope, testing) {
+ var originalRequestAnimationFrame = window.requestAnimationFrame;
+ window.requestAnimationFrame = function(f) {
+ return originalRequestAnimationFrame(function(x) {
+ scope.timeline._updateAnimationsPromises();
+ f(x);
+ scope.timeline._updateAnimationsPromises();
+ });
+ };
+
+ scope.AnimationTimeline = function() {
+ this._animations = [];
+ this.currentTime = undefined;
+ };
+
+ scope.AnimationTimeline.prototype = {
+ getAnimations: function() {
+ this._discardAnimations();
+ return this._animations.slice();
+ },
+ _updateAnimationsPromises: function() {
+ scope.animationsWithPromises = scope.animationsWithPromises.filter(function(animation) {
+ return animation._updatePromises();
+ });
+ },
+ _discardAnimations: function() {
+ this._updateAnimationsPromises();
+ this._animations = this._animations.filter(function(animation) {
+ return animation.playState != 'finished' && animation.playState != 'idle';
+ });
+ },
+ _play: function(effect) {
+ var animation = new scope.Animation(effect, this);
+ this._animations.push(animation);
+ scope.restartWebAnimationsNextTick();
+ // Use animation._animation.play() here, NOT animation.play().
+ //
+ // Timeline.play calls new scope.Animation(effect) which (indirectly) calls Timeline.play on
+ // effect's children, and Animation.play is also recursive. We only need to call play on each
+ // animation in the tree once.
+ animation._updatePromises();
+ animation._animation.play();
+ animation._updatePromises();
+ return animation;
+ },
+ play: function(effect) {
+ if (effect) {
+ effect.remove();
+ }
+ return this._play(effect);
+ }
+ };
+
+ var ticking = false;
+
+ scope.restartWebAnimationsNextTick = function() {
+ if (!ticking) {
+ ticking = true;
+ requestAnimationFrame(webAnimationsNextTick);
+ }
+ };
+
+ function webAnimationsNextTick(t) {
+ var timeline = scope.timeline;
+ timeline.currentTime = t;
+ timeline._discardAnimations();
+ if (timeline._animations.length == 0)
+ ticking = false;
+ else
+ requestAnimationFrame(webAnimationsNextTick);
+ }
+
+ var timeline = new scope.AnimationTimeline();
+ scope.timeline = timeline;
+
+ try {
+ Object.defineProperty(window.document, 'timeline', {
+ configurable: true,
+ get: function() { return timeline; }
+ });
+ } catch (e) { }
+ try {
+ window.document.timeline = timeline;
+ } catch (e) { }
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/timing-utilities.js b/catapult/third_party/polymer/components/web-animations-js/src/timing-utilities.js
new file mode 100644
index 00000000..2f16f3b1
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/timing-utilities.js
@@ -0,0 +1,420 @@
+// Copyright 2014 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.
+
+(function(shared, testing) {
+
+ var fills = 'backwards|forwards|both|none'.split('|');
+ var directions = 'reverse|alternate|alternate-reverse'.split('|');
+ var linear = function(x) { return x; };
+
+ function cloneTimingInput(timingInput) {
+ if (typeof timingInput == 'number') {
+ return timingInput;
+ }
+ var clone = {};
+ for (var m in timingInput) {
+ clone[m] = timingInput[m];
+ }
+ return clone;
+ }
+
+ function AnimationEffectTiming() {
+ this._delay = 0;
+ this._endDelay = 0;
+ this._fill = 'none';
+ this._iterationStart = 0;
+ this._iterations = 1;
+ this._duration = 0;
+ this._playbackRate = 1;
+ this._direction = 'normal';
+ this._easing = 'linear';
+ this._easingFunction = linear;
+ }
+
+ function isInvalidTimingDeprecated() {
+ return shared.isDeprecated('Invalid timing inputs', '2016-03-02', 'TypeError exceptions will be thrown instead.', true);
+ }
+
+ AnimationEffectTiming.prototype = {
+ _setMember: function(member, value) {
+ this['_' + member] = value;
+ if (this._effect) {
+ this._effect._timingInput[member] = value;
+ this._effect._timing = shared.normalizeTimingInput(this._effect._timingInput);
+ this._effect.activeDuration = shared.calculateActiveDuration(this._effect._timing);
+ if (this._effect._animation) {
+ this._effect._animation._rebuildUnderlyingAnimation();
+ }
+ }
+ },
+ get playbackRate() {
+ return this._playbackRate;
+ },
+ set delay(value) {
+ this._setMember('delay', value);
+ },
+ get delay() {
+ return this._delay;
+ },
+ set endDelay(value) {
+ this._setMember('endDelay', value);
+ },
+ get endDelay() {
+ return this._endDelay;
+ },
+ set fill(value) {
+ this._setMember('fill', value);
+ },
+ get fill() {
+ return this._fill;
+ },
+ set iterationStart(value) {
+ if ((isNaN(value) || value < 0) && isInvalidTimingDeprecated()) {
+ throw new TypeError('iterationStart must be a non-negative number, received: ' + timing.iterationStart);
+ }
+ this._setMember('iterationStart', value);
+ },
+ get iterationStart() {
+ return this._iterationStart;
+ },
+ set duration(value) {
+ if (value != 'auto' && (isNaN(value) || value < 0) && isInvalidTimingDeprecated()) {
+ throw new TypeError('duration must be non-negative or auto, received: ' + value);
+ }
+ this._setMember('duration', value);
+ },
+ get duration() {
+ return this._duration;
+ },
+ set direction(value) {
+ this._setMember('direction', value);
+ },
+ get direction() {
+ return this._direction;
+ },
+ set easing(value) {
+ this._easingFunction = parseEasingFunction(normalizeEasing(value));
+ this._setMember('easing', value);
+ },
+ get easing() {
+ return this._easing;
+ },
+ set iterations(value) {
+ if ((isNaN(value) || value < 0) && isInvalidTimingDeprecated()) {
+ throw new TypeError('iterations must be non-negative, received: ' + value);
+ }
+ this._setMember('iterations', value);
+ },
+ get iterations() {
+ return this._iterations;
+ }
+ };
+
+ function makeTiming(timingInput, forGroup, effect) {
+ var timing = new AnimationEffectTiming();
+ if (forGroup) {
+ timing.fill = 'both';
+ timing.duration = 'auto';
+ }
+ if (typeof timingInput == 'number' && !isNaN(timingInput)) {
+ timing.duration = timingInput;
+ } else if (timingInput !== undefined) {
+ Object.getOwnPropertyNames(timingInput).forEach(function(property) {
+ if (timingInput[property] != 'auto') {
+ if (typeof timing[property] == 'number' || property == 'duration') {
+ if (typeof timingInput[property] != 'number' || isNaN(timingInput[property])) {
+ return;
+ }
+ }
+ if ((property == 'fill') && (fills.indexOf(timingInput[property]) == -1)) {
+ return;
+ }
+ if ((property == 'direction') && (directions.indexOf(timingInput[property]) == -1)) {
+ return;
+ }
+ if (property == 'playbackRate' && timingInput[property] !== 1 && shared.isDeprecated('AnimationEffectTiming.playbackRate', '2014-11-28', 'Use Animation.playbackRate instead.')) {
+ return;
+ }
+ timing[property] = timingInput[property];
+ }
+ });
+ }
+ return timing;
+ }
+
+ function numericTimingToObject(timingInput) {
+ if (typeof timingInput == 'number') {
+ if (isNaN(timingInput)) {
+ timingInput = { duration: 0 };
+ } else {
+ timingInput = { duration: timingInput };
+ }
+ }
+ return timingInput;
+ }
+
+ function normalizeTimingInput(timingInput, forGroup) {
+ timingInput = shared.numericTimingToObject(timingInput);
+ return makeTiming(timingInput, forGroup);
+ }
+
+ function cubic(a, b, c, d) {
+ if (a < 0 || a > 1 || c < 0 || c > 1) {
+ return linear;
+ }
+ return function(x) {
+ if (x <= 0) {
+ var start_gradient = 0;
+ if (a > 0)
+ start_gradient = b / a;
+ else if (!b && c > 0)
+ start_gradient = d / c;
+ return start_gradient * x;
+ }
+ if (x >= 1) {
+ var end_gradient = 0;
+ if (c < 1)
+ end_gradient = (d - 1) / (c - 1);
+ else if (c == 1 && a < 1)
+ end_gradient = (b - 1) / (a - 1);
+ return 1 + end_gradient * (x - 1);
+ }
+
+ var start = 0, end = 1;
+ while (start < end) {
+ var mid = (start + end) / 2;
+ function f(a, b, m) { return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1 - m) * m * m + m * m * m};
+ var xEst = f(a, c, mid);
+ if (Math.abs(x - xEst) < 0.00001) {
+ return f(b, d, mid);
+ }
+ if (xEst < x) {
+ start = mid;
+ } else {
+ end = mid;
+ }
+ }
+ return f(b, d, mid);
+ }
+ }
+
+ var Start = 1;
+ var Middle = 0.5;
+ var End = 0;
+
+ function step(count, pos) {
+ return function(x) {
+ if (x >= 1) {
+ return 1;
+ }
+ var stepSize = 1 / count;
+ x += pos * stepSize;
+ return x - x % stepSize;
+ }
+ }
+
+ var presets = {
+ 'ease': cubic(0.25, 0.1, 0.25, 1),
+ 'ease-in': cubic(0.42, 0, 1, 1),
+ 'ease-out': cubic(0, 0, 0.58, 1),
+ 'ease-in-out': cubic(0.42, 0, 0.58, 1),
+ 'step-start': step(1, Start),
+ 'step-middle': step(1, Middle),
+ 'step-end': step(1, End)
+ };
+
+ var styleForCleaning = null;
+ var numberString = '\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*';
+ var cubicBezierRe = new RegExp('cubic-bezier\\(' + numberString + ',' + numberString + ',' + numberString + ',' + numberString + '\\)');
+ var stepRe = /steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/;
+
+ function normalizeEasing(easing) {
+ if (!styleForCleaning) {
+ styleForCleaning = document.createElement('div').style;
+ }
+ styleForCleaning.animationTimingFunction = '';
+ styleForCleaning.animationTimingFunction = easing;
+ var normalizedEasing = styleForCleaning.animationTimingFunction;
+ if (normalizedEasing == '' && isInvalidTimingDeprecated()) {
+ throw new TypeError(easing + ' is not a valid value for easing');
+ }
+ return normalizedEasing;
+ }
+
+ function parseEasingFunction(normalizedEasing) {
+ if (normalizedEasing == 'linear') {
+ return linear;
+ }
+ var cubicData = cubicBezierRe.exec(normalizedEasing);
+ if (cubicData) {
+ return cubic.apply(this, cubicData.slice(1).map(Number));
+ }
+ var stepData = stepRe.exec(normalizedEasing);
+ if (stepData) {
+ return step(Number(stepData[1]), {'start': Start, 'middle': Middle, 'end': End}[stepData[2]]);
+ }
+ var preset = presets[normalizedEasing];
+ if (preset) {
+ return preset;
+ }
+ // At this point none of our parse attempts succeeded; the easing is invalid.
+ // Fall back to linear in the interest of not crashing the page.
+ return linear;
+ }
+
+ function calculateActiveDuration(timing) {
+ return Math.abs(repeatedDuration(timing) / timing.playbackRate);
+ }
+
+ function repeatedDuration(timing) {
+ // https://w3c.github.io/web-animations/#calculating-the-active-duration
+ if (timing.duration === 0 || timing.iterations === 0) {
+ return 0;
+ }
+ return timing.duration * timing.iterations;
+ }
+
+ var PhaseNone = 0;
+ var PhaseBefore = 1;
+ var PhaseAfter = 2;
+ var PhaseActive = 3;
+
+ function calculatePhase(activeDuration, localTime, timing) {
+ // https://w3c.github.io/web-animations/#animation-effect-phases-and-states
+ if (localTime == null) {
+ return PhaseNone;
+ }
+
+ var endTime = timing.delay + activeDuration + timing.endDelay;
+ if (localTime < Math.min(timing.delay, endTime)) {
+ return PhaseBefore;
+ }
+ if (localTime >= Math.min(timing.delay + activeDuration, endTime)) {
+ return PhaseAfter;
+ }
+
+ return PhaseActive;
+ }
+
+ function calculateActiveTime(activeDuration, fillMode, localTime, phase, delay) {
+ // https://w3c.github.io/web-animations/#calculating-the-active-time
+ switch (phase) {
+ case PhaseBefore:
+ if (fillMode == 'backwards' || fillMode == 'both')
+ return 0;
+ return null;
+ case PhaseActive:
+ return localTime - delay;
+ case PhaseAfter:
+ if (fillMode == 'forwards' || fillMode == 'both')
+ return activeDuration;
+ return null;
+ case PhaseNone:
+ return null;
+ }
+ }
+
+ function calculateOverallProgress(iterationDuration, phase, iterations, activeTime, iterationStart) {
+ // https://w3c.github.io/web-animations/#calculating-the-overall-progress
+ var overallProgress = iterationStart;
+ if (iterationDuration === 0) {
+ if (phase !== PhaseBefore) {
+ overallProgress += iterations;
+ }
+ } else {
+ overallProgress += activeTime / iterationDuration;
+ }
+ return overallProgress;
+ }
+
+ function calculateSimpleIterationProgress(overallProgress, iterationStart, phase, iterations, activeTime, iterationDuration) {
+ // https://w3c.github.io/web-animations/#calculating-the-simple-iteration-progress
+
+ var simpleIterationProgress = (overallProgress === Infinity) ? iterationStart % 1 : overallProgress % 1;
+ if (simpleIterationProgress === 0 && phase === PhaseAfter && iterations !== 0 &&
+ (activeTime !== 0 || iterationDuration === 0)) {
+ simpleIterationProgress = 1;
+ }
+ return simpleIterationProgress;
+ }
+
+ function calculateCurrentIteration(phase, iterations, simpleIterationProgress, overallProgress) {
+ // https://w3c.github.io/web-animations/#calculating-the-current-iteration
+ if (phase === PhaseAfter && iterations === Infinity) {
+ return Infinity;
+ }
+ if (simpleIterationProgress === 1) {
+ return Math.floor(overallProgress) - 1;
+ }
+ return Math.floor(overallProgress);
+ }
+
+ function calculateDirectedProgress(playbackDirection, currentIteration, simpleIterationProgress) {
+ // https://w3c.github.io/web-animations/#calculating-the-directed-progress
+ var currentDirection = playbackDirection;
+ if (playbackDirection !== 'normal' && playbackDirection !== 'reverse') {
+ var d = currentIteration;
+ if (playbackDirection === 'alternate-reverse') {
+ d += 1;
+ }
+ currentDirection = 'normal';
+ if (d !== Infinity && d % 2 !== 0) {
+ currentDirection = 'reverse';
+ }
+ }
+ if (currentDirection === 'normal') {
+ return simpleIterationProgress;
+ }
+ return 1 - simpleIterationProgress;
+ }
+
+ function calculateIterationProgress(activeDuration, localTime, timing) {
+ var phase = calculatePhase(activeDuration, localTime, timing);
+ var activeTime = calculateActiveTime(activeDuration, timing.fill, localTime, phase, timing.delay);
+ if (activeTime === null)
+ return null;
+
+ var overallProgress = calculateOverallProgress(timing.duration, phase, timing.iterations, activeTime, timing.iterationStart);
+ var simpleIterationProgress = calculateSimpleIterationProgress(overallProgress, timing.iterationStart, phase, timing.iterations, activeTime, timing.duration);
+ var currentIteration = calculateCurrentIteration(phase, timing.iterations, simpleIterationProgress, overallProgress);
+ var directedProgress = calculateDirectedProgress(timing.direction, currentIteration, simpleIterationProgress);
+
+ // https://w3c.github.io/web-animations/#calculating-the-transformed-progress
+ // https://w3c.github.io/web-animations/#calculating-the-iteration-progress
+ return timing._easingFunction(directedProgress);
+ }
+
+ shared.cloneTimingInput = cloneTimingInput;
+ shared.makeTiming = makeTiming;
+ shared.numericTimingToObject = numericTimingToObject;
+ shared.normalizeTimingInput = normalizeTimingInput;
+ shared.calculateActiveDuration = calculateActiveDuration;
+ shared.calculateIterationProgress = calculateIterationProgress;
+ shared.calculatePhase = calculatePhase;
+ shared.normalizeEasing = normalizeEasing;
+ shared.parseEasingFunction = parseEasingFunction;
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.normalizeTimingInput = normalizeTimingInput;
+ testing.normalizeEasing = normalizeEasing;
+ testing.parseEasingFunction = parseEasingFunction;
+ testing.calculateActiveDuration = calculateActiveDuration;
+ testing.calculatePhase = calculatePhase;
+ testing.PhaseNone = PhaseNone;
+ testing.PhaseBefore = PhaseBefore;
+ testing.PhaseActive = PhaseActive;
+ testing.PhaseAfter = PhaseAfter;
+ }
+
+})(webAnimationsShared, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/transform-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/transform-handler.js
new file mode 100644
index 00000000..c6b4d934
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/transform-handler.js
@@ -0,0 +1,275 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ // This returns a function for converting transform functions to equivalent
+ // primitive functions, which will take an array of values from the
+ // derivative type and fill in the blanks (underscores) with them.
+ var _ = null;
+ function cast(pattern) {
+ return function(contents) {
+ var i = 0;
+ return pattern.map(function(x) { return x === _ ? contents[i++] : x; });
+ }
+ }
+
+ function id(x) { return x; }
+
+ var Opx = {px: 0};
+ var Odeg = {deg: 0};
+
+ // type: [argTypes, convertTo3D, convertTo2D]
+ // In the argument types string, lowercase characters represent optional arguments
+ var transformFunctions = {
+ matrix: ['NNNNNN', [_, _, 0, 0, _, _, 0, 0, 0, 0, 1, 0, _, _, 0, 1], id],
+ matrix3d: ['NNNNNNNNNNNNNNNN', id],
+ rotate: ['A'],
+ rotatex: ['A'],
+ rotatey: ['A'],
+ rotatez: ['A'],
+ rotate3d: ['NNNA'],
+ perspective: ['L'],
+ scale: ['Nn', cast([_, _, 1]), id],
+ scalex: ['N', cast([_, 1, 1]), cast([_, 1])],
+ scaley: ['N', cast([1, _, 1]), cast([1, _])],
+ scalez: ['N', cast([1, 1, _])],
+ scale3d: ['NNN', id],
+ skew: ['Aa', null, id],
+ skewx: ['A', null, cast([_, Odeg])],
+ skewy: ['A', null, cast([Odeg, _])],
+ translate: ['Tt', cast([_, _, Opx]), id],
+ translatex: ['T', cast([_, Opx, Opx]), cast([_, Opx])],
+ translatey: ['T', cast([Opx, _, Opx]), cast([Opx, _])],
+ translatez: ['L', cast([Opx, Opx, _])],
+ translate3d: ['TTL', id],
+ };
+
+ function parseTransform(string) {
+ string = string.toLowerCase().trim();
+ if (string == 'none')
+ return [];
+ // FIXME: Using a RegExp means calcs won't work here
+ var transformRegExp = /\s*(\w+)\(([^)]*)\)/g;
+ var result = [];
+ var match;
+ var prevLastIndex = 0;
+ while (match = transformRegExp.exec(string)) {
+ if (match.index != prevLastIndex)
+ return;
+ prevLastIndex = match.index + match[0].length;
+ var functionName = match[1];
+ var functionData = transformFunctions[functionName];
+ if (!functionData)
+ return;
+ var args = match[2].split(',');
+ var argTypes = functionData[0];
+ if (argTypes.length < args.length)
+ return;
+
+ var parsedArgs = [];
+ for (var i = 0; i < argTypes.length; i++) {
+ var arg = args[i];
+ var type = argTypes[i];
+ var parsedArg;
+ if (!arg)
+ parsedArg = ({a: Odeg,
+ n: parsedArgs[0],
+ t: Opx})[type];
+ else
+ parsedArg = ({A: function(s) { return s.trim() == '0' ? Odeg : scope.parseAngle(s); },
+ N: scope.parseNumber,
+ T: scope.parseLengthOrPercent,
+ L: scope.parseLength})[type.toUpperCase()](arg);
+ if (parsedArg === undefined)
+ return;
+ parsedArgs.push(parsedArg);
+ }
+ result.push({t: functionName, d: parsedArgs});
+
+ if (transformRegExp.lastIndex == string.length)
+ return result;
+ }
+ };
+
+ function numberToLongString(x) {
+ return x.toFixed(6).replace('.000000', '');
+ }
+
+ function mergeMatrices(left, right) {
+ if (left.decompositionPair !== right) {
+ left.decompositionPair = right;
+ var leftArgs = scope.makeMatrixDecomposition(left);
+ }
+ if (right.decompositionPair !== left) {
+ right.decompositionPair = left;
+ var rightArgs = scope.makeMatrixDecomposition(right);
+ }
+ if (leftArgs[0] == null || rightArgs[0] == null)
+ return [[false], [true], function(x) { return x ? right[0].d : left[0].d; }];
+ leftArgs[0].push(0);
+ rightArgs[0].push(1);
+ return [
+ leftArgs,
+ rightArgs,
+ function(list) {
+ var quat = scope.quat(leftArgs[0][3], rightArgs[0][3], list[5]);
+ var mat = scope.composeMatrix(list[0], list[1], list[2], quat, list[4]);
+ var stringifiedArgs = mat.map(numberToLongString).join(',');
+ return stringifiedArgs;
+ }
+ ];
+ }
+
+ function typeTo2D(type) {
+ return type.replace(/[xy]/, '');
+ }
+
+ function typeTo3D(type) {
+ return type.replace(/(x|y|z|3d)?$/, '3d');
+ }
+
+ function mergeTransforms(left, right) {
+ var matrixModulesLoaded = scope.makeMatrixDecomposition && true;
+
+ var flipResults = false;
+ if (!left.length || !right.length) {
+ if (!left.length) {
+ flipResults = true;
+ left = right;
+ right = [];
+ }
+ for (var i = 0; i < left.length; i++) {
+ var type = left[i].t;
+ var args = left[i].d;
+ var defaultValue = type.substr(0, 5) == 'scale' ? 1 : 0;
+ right.push({t: type, d: args.map(function(arg) {
+ if (typeof arg == 'number')
+ return defaultValue;
+ var result = {};
+ for (var unit in arg)
+ result[unit] = defaultValue;
+ return result;
+ })});
+ }
+ }
+
+ var isMatrixOrPerspective = function(lt, rt) {
+ return ((lt == 'perspective') && (rt == 'perspective')) ||
+ ((lt == 'matrix' || lt == 'matrix3d') && (rt == 'matrix' || rt == 'matrix3d'));
+ };
+ var leftResult = [];
+ var rightResult = [];
+ var types = [];
+
+ if (left.length != right.length) {
+ if (!matrixModulesLoaded)
+ return;
+ var merged = mergeMatrices(left, right);
+ leftResult = [merged[0]];
+ rightResult = [merged[1]];
+ types = [['matrix', [merged[2]]]];
+ } else {
+ for (var i = 0; i < left.length; i++) {
+ var leftType = left[i].t;
+ var rightType = right[i].t;
+ var leftArgs = left[i].d;
+ var rightArgs = right[i].d;
+
+ var leftFunctionData = transformFunctions[leftType];
+ var rightFunctionData = transformFunctions[rightType];
+
+ var type;
+ if (isMatrixOrPerspective(leftType, rightType)) {
+ if (!matrixModulesLoaded)
+ return;
+ var merged = mergeMatrices([left[i]], [right[i]]);
+ leftResult.push(merged[0]);
+ rightResult.push(merged[1]);
+ types.push(['matrix', [merged[2]]]);
+ continue;
+ } else if (leftType == rightType) {
+ type = leftType;
+ } else if (leftFunctionData[2] && rightFunctionData[2] && typeTo2D(leftType) == typeTo2D(rightType)) {
+ type = typeTo2D(leftType);
+ leftArgs = leftFunctionData[2](leftArgs);
+ rightArgs = rightFunctionData[2](rightArgs);
+ } else if (leftFunctionData[1] && rightFunctionData[1] && typeTo3D(leftType) == typeTo3D(rightType)) {
+ type = typeTo3D(leftType);
+ leftArgs = leftFunctionData[1](leftArgs);
+ rightArgs = rightFunctionData[1](rightArgs);
+ } else {
+ if (!matrixModulesLoaded)
+ return;
+ var merged = mergeMatrices(left, right);
+ leftResult = [merged[0]];
+ rightResult = [merged[1]];
+ types = [['matrix', [merged[2]]]];
+ break;
+ }
+
+ var leftArgsCopy = [];
+ var rightArgsCopy = [];
+ var stringConversions = [];
+ for (var j = 0; j < leftArgs.length; j++) {
+ var merge = typeof leftArgs[j] == 'number' ? scope.mergeNumbers : scope.mergeDimensions;
+ var merged = merge(leftArgs[j], rightArgs[j]);
+ leftArgsCopy[j] = merged[0];
+ rightArgsCopy[j] = merged[1];
+ stringConversions.push(merged[2]);
+ }
+ leftResult.push(leftArgsCopy);
+ rightResult.push(rightArgsCopy);
+ types.push([type, stringConversions]);
+ }
+ }
+
+ if (flipResults) {
+ var tmp = leftResult;
+ leftResult = rightResult;
+ rightResult = tmp;
+ }
+
+ return [leftResult, rightResult, function(list) {
+ return list.map(function(args, i) {
+ var stringifiedArgs = args.map(function(arg, j) {
+ return types[i][1][j](arg);
+ }).join(',');
+ if (types[i][0] == 'matrix' && stringifiedArgs.split(',').length == 16)
+ types[i][0] = 'matrix3d';
+ return types[i][0] + '(' + stringifiedArgs + ')';
+
+ }).join(' ');
+ }];
+ }
+
+ scope.addPropertiesHandler(parseTransform, mergeTransforms, ['transform']);
+
+ scope.transformToSvgMatrix = function(string) {
+ // matrix(<a> <b> <c> <d> <e> <f>)
+ var mat = scope.transformListToMatrix(parseTransform(string));
+ return 'matrix(' +
+ numberToLongString(mat[0]) + ' ' + // <a>
+ numberToLongString(mat[1]) + ' ' + // <b>
+ numberToLongString(mat[4]) + ' ' + // <c>
+ numberToLongString(mat[5]) + ' ' + // <d>
+ numberToLongString(mat[12]) + ' ' + // <e>
+ numberToLongString(mat[13]) + // <f>
+ ')';
+ };
+
+ if (WEB_ANIMATIONS_TESTING)
+ testing.parseTransform = parseTransform;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/visibility-handler.js b/catapult/third_party/polymer/components/web-animations-js/src/visibility-handler.js
new file mode 100644
index 00000000..53f29535
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/visibility-handler.js
@@ -0,0 +1,29 @@
+// Copyright 2014 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.
+
+(function(scope, testing) {
+
+ function merge(left, right) {
+ if (left != 'visible' && right != 'visible') return;
+ return [0, 1, function(x) {
+ if (x <= 0) return left;
+ if (x >= 1) return right;
+ return 'visible';
+ }];
+ }
+
+ scope.addPropertiesHandler(String, merge, ['visibility']);
+
+})(webAnimations1);
+
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-cancel-events.js b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-cancel-events.js
new file mode 100644
index 00000000..3905496f
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-cancel-events.js
@@ -0,0 +1,83 @@
+// 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.
+
+(function() {
+
+ if (document.createElement('div').animate([]).oncancel !== undefined) {
+ return;
+ }
+
+ if (WEB_ANIMATIONS_TESTING) {
+ var now = function() { return webAnimations1.timeline.currentTime; };
+ } else if (window.performance && performance.now) {
+ var now = function() { return performance.now(); };
+ } else {
+ var now = function() { return Date.now(); };
+ }
+
+ var AnimationCancelEvent = function(target, currentTime, timelineTime) {
+ this.target = target;
+ this.currentTime = currentTime;
+ this.timelineTime = timelineTime;
+
+ this.type = 'cancel';
+ this.bubbles = false;
+ this.cancelable = false;
+ this.currentTarget = target;
+ this.defaultPrevented = false;
+ this.eventPhase = Event.AT_TARGET;
+ this.timeStamp = Date.now();
+ };
+
+ var originalElementAnimate = window.Element.prototype.animate;
+ window.Element.prototype.animate = function(effectInput, options) {
+ var animation = originalElementAnimate.call(this, effectInput, options);
+
+ animation._cancelHandlers = [];
+ animation.oncancel = null;
+
+ var originalCancel = animation.cancel;
+ animation.cancel = function() {
+ originalCancel.call(this);
+ var event = new AnimationCancelEvent(this, null, now());
+ var handlers = this._cancelHandlers.concat(this.oncancel ? [this.oncancel] : []);
+ setTimeout(function() {
+ handlers.forEach(function(handler) {
+ handler.call(event.target, event);
+ });
+ }, 0);
+ };
+
+ var originalAddEventListener = animation.addEventListener;
+ animation.addEventListener = function(type, handler) {
+ if (typeof handler == 'function' && type == 'cancel')
+ this._cancelHandlers.push(handler);
+ else
+ originalAddEventListener.call(this, type, handler);
+ };
+
+ var originalRemoveEventListener = animation.removeEventListener;
+ animation.removeEventListener = function(type, handler) {
+ if (type == 'cancel') {
+ var index = this._cancelHandlers.indexOf(handler);
+ if (index >= 0)
+ this._cancelHandlers.splice(index, 1);
+ } else {
+ originalRemoveEventListener.call(this, type, handler);
+ }
+ };
+
+ return animation;
+ };
+})();
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-object-form-keyframes.js b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-object-form-keyframes.js
new file mode 100644
index 00000000..c1b6ecdc
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-bonus-object-form-keyframes.js
@@ -0,0 +1,63 @@
+// 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.
+
+(function(shared) {
+ // If the polyfill is being loaded in a context where Element.animate is
+ // supported but object-form syntax is not, then creating an animation
+ // using the new syntax will either have no effect or will throw an exception.
+ // In either case, we want to proceed to load this part of the polyfill.
+ //
+ // The test animation uses an opacity other than the one the element already
+ // has, and doesn't need to change during the animation for the test to work.
+ // After the test, the element's opacity will be left how we found it:
+ // - If the animation is not created, the test will leave the element's
+ // opacity untouched at originalOpacity.
+ // - If the animation is created, it will be cancelled, and leave the
+ // element's opacity at originalOpacity.
+ // - If the animation is somehow created and runs without being cancelled,
+ // when it finishes after 1ms, it will cease to have any effect (because
+ // fill is not specified), and opacity will again be left at originalOpacity.
+ var element = document.documentElement;
+ var animation = null;
+ var animated = false;
+ try {
+ var originalOpacity = getComputedStyle(element).getPropertyValue('opacity');
+ var testOpacity = originalOpacity == '0' ? '1' : '0';
+ animation = element.animate({'opacity': [testOpacity, testOpacity]},
+ {duration: 1});
+ animation.currentTime = 0;
+ animated = getComputedStyle(element).getPropertyValue('opacity') == testOpacity;
+ } catch (error) {
+ } finally {
+ if (animation)
+ animation.cancel();
+ }
+ if (animated) {
+ return;
+ }
+
+ var originalElementAnimate = window.Element.prototype.animate;
+ window.Element.prototype.animate = function(effectInput, options) {
+ if (window.Symbol && Symbol.iterator && Array.prototype.from && effectInput[Symbol.iterator]) {
+ // Handle custom iterables in most browsers by converting to an array
+ effectInput = Array.from(effectInput);
+ }
+
+ if (!Array.isArray(effectInput) && effectInput !== null) {
+ effectInput = shared.convertToArrayForm(effectInput);
+ }
+
+ return originalElementAnimate.call(this, effectInput, options);
+ };
+})(webAnimationsShared);
diff --git a/catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js
new file mode 100644
index 00000000..698532cd
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/src/web-animations-next-animation.js
@@ -0,0 +1,383 @@
+// Copyright 2014 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.
+
+(function(shared, scope, testing) {
+ scope.animationsWithPromises = [];
+
+ scope.Animation = function(effect, timeline) {
+ this.id = '';
+ if (effect && effect._id) {
+ this.id = effect._id;
+ }
+ this.effect = effect;
+ if (effect) {
+ effect._animation = this;
+ }
+ if (!timeline) {
+ throw new Error('Animation with null timeline is not supported');
+ }
+ this._timeline = timeline;
+ this._sequenceNumber = shared.sequenceNumber++;
+ this._holdTime = 0;
+ this._paused = false;
+ this._isGroup = false;
+ this._animation = null;
+ this._childAnimations = [];
+ this._callback = null;
+ this._oldPlayState = 'idle';
+ this._rebuildUnderlyingAnimation();
+ // Animations are constructed in the idle state.
+ this._animation.cancel();
+ this._updatePromises();
+ };
+
+ scope.Animation.prototype = {
+ _updatePromises: function() {
+ var oldPlayState = this._oldPlayState;
+ var newPlayState = this.playState;
+ if (this._readyPromise && newPlayState !== oldPlayState) {
+ if (newPlayState == 'idle') {
+ this._rejectReadyPromise();
+ this._readyPromise = undefined;
+ } else if (oldPlayState == 'pending') {
+ this._resolveReadyPromise();
+ } else if (newPlayState == 'pending') {
+ this._readyPromise = undefined;
+ }
+ }
+ if (this._finishedPromise && newPlayState !== oldPlayState) {
+ if (newPlayState == 'idle') {
+ this._rejectFinishedPromise();
+ this._finishedPromise = undefined;
+ } else if (newPlayState == 'finished') {
+ this._resolveFinishedPromise();
+ } else if (oldPlayState == 'finished') {
+ this._finishedPromise = undefined;
+ }
+ }
+ this._oldPlayState = this.playState;
+ return (this._readyPromise || this._finishedPromise);
+ },
+ _rebuildUnderlyingAnimation: function() {
+ this._updatePromises();
+ var oldPlaybackRate;
+ var oldPaused;
+ var oldStartTime;
+ var oldCurrentTime;
+ var hadUnderlying = this._animation ? true : false;
+ if (hadUnderlying) {
+ oldPlaybackRate = this.playbackRate;
+ oldPaused = this._paused;
+ oldStartTime = this.startTime;
+ oldCurrentTime = this.currentTime;
+ this._animation.cancel();
+ this._animation._wrapper = null;
+ this._animation = null;
+ }
+
+ if (!this.effect || this.effect instanceof window.KeyframeEffect) {
+ this._animation = scope.newUnderlyingAnimationForKeyframeEffect(this.effect);
+ scope.bindAnimationForKeyframeEffect(this);
+ }
+ if (this.effect instanceof window.SequenceEffect || this.effect instanceof window.GroupEffect) {
+ this._animation = scope.newUnderlyingAnimationForGroup(this.effect);
+ scope.bindAnimationForGroup(this);
+ }
+ if (this.effect && this.effect._onsample) {
+ scope.bindAnimationForCustomEffect(this);
+ }
+ if (hadUnderlying) {
+ if (oldPlaybackRate != 1) {
+ this.playbackRate = oldPlaybackRate;
+ }
+ if (oldStartTime !== null) {
+ this.startTime = oldStartTime;
+ } else if (oldCurrentTime !== null) {
+ this.currentTime = oldCurrentTime;
+ } else if (this._holdTime !== null) {
+ this.currentTime = this._holdTime;
+ }
+ if (oldPaused) {
+ this.pause();
+ }
+ }
+ this._updatePromises();
+ },
+ _updateChildren: function() {
+ if (!this.effect || this.playState == 'idle')
+ return;
+
+ var offset = this.effect._timing.delay;
+ this._childAnimations.forEach(function(childAnimation) {
+ this._arrangeChildren(childAnimation, offset);
+ if (this.effect instanceof window.SequenceEffect)
+ offset += scope.groupChildDuration(childAnimation.effect);
+ }.bind(this));
+ },
+ _setExternalAnimation: function(animation) {
+ if (!this.effect || !this._isGroup)
+ return;
+ for (var i = 0; i < this.effect.children.length; i++) {
+ this.effect.children[i]._animation = animation;
+ this._childAnimations[i]._setExternalAnimation(animation);
+ }
+ },
+ _constructChildAnimations: function() {
+ if (!this.effect || !this._isGroup)
+ return;
+ var offset = this.effect._timing.delay;
+ this._removeChildAnimations();
+ this.effect.children.forEach(function(child) {
+ var childAnimation = scope.timeline._play(child);
+ this._childAnimations.push(childAnimation);
+ childAnimation.playbackRate = this.playbackRate;
+ if (this._paused)
+ childAnimation.pause();
+ child._animation = this.effect._animation;
+
+ this._arrangeChildren(childAnimation, offset);
+
+ if (this.effect instanceof window.SequenceEffect)
+ offset += scope.groupChildDuration(child);
+ }.bind(this));
+ },
+ _arrangeChildren: function(childAnimation, offset) {
+ if (this.startTime === null) {
+ childAnimation.currentTime = this.currentTime - offset / this.playbackRate;
+ } else if (childAnimation.startTime !== this.startTime + offset / this.playbackRate) {
+ childAnimation.startTime = this.startTime + offset / this.playbackRate;
+ }
+ },
+ get timeline() {
+ return this._timeline;
+ },
+ get playState() {
+ return this._animation ? this._animation.playState : 'idle';
+ },
+ get finished() {
+ if (!window.Promise) {
+ console.warn('Animation Promises require JavaScript Promise constructor');
+ return null;
+ }
+ if (!this._finishedPromise) {
+ if (scope.animationsWithPromises.indexOf(this) == -1) {
+ scope.animationsWithPromises.push(this);
+ }
+ this._finishedPromise = new Promise(
+ function(resolve, reject) {
+ this._resolveFinishedPromise = function() {
+ resolve(this);
+ };
+ this._rejectFinishedPromise = function() {
+ reject({type: DOMException.ABORT_ERR, name: 'AbortError'});
+ };
+ }.bind(this));
+ if (this.playState == 'finished') {
+ this._resolveFinishedPromise();
+ }
+ }
+ return this._finishedPromise;
+ },
+ get ready() {
+ if (!window.Promise) {
+ console.warn('Animation Promises require JavaScript Promise constructor');
+ return null;
+ }
+ if (!this._readyPromise) {
+ if (scope.animationsWithPromises.indexOf(this) == -1) {
+ scope.animationsWithPromises.push(this);
+ }
+ this._readyPromise = new Promise(
+ function(resolve, reject) {
+ this._resolveReadyPromise = function() {
+ resolve(this);
+ };
+ this._rejectReadyPromise = function() {
+ reject({type: DOMException.ABORT_ERR, name: 'AbortError'});
+ };
+ }.bind(this));
+ if (this.playState !== 'pending') {
+ this._resolveReadyPromise();
+ }
+ }
+ return this._readyPromise;
+ },
+ get onfinish() {
+ return this._animation.onfinish;
+ },
+ set onfinish(v) {
+ if (typeof v == 'function') {
+ this._animation.onfinish = (function(e) {
+ e.target = this;
+ v.call(this, e);
+ }).bind(this);
+ } else {
+ this._animation.onfinish = v;
+ }
+ },
+ get oncancel() {
+ return this._animation.oncancel;
+ },
+ set oncancel(v) {
+ if (typeof v == 'function') {
+ this._animation.oncancel = (function(e) {
+ e.target = this;
+ v.call(this, e);
+ }).bind(this);
+ } else {
+ this._animation.oncancel = v;
+ }
+ },
+ get currentTime() {
+ this._updatePromises();
+ var currentTime = this._animation.currentTime;
+ this._updatePromises();
+ return currentTime;
+ },
+ set currentTime(v) {
+ this._updatePromises();
+ this._animation.currentTime = isFinite(v) ? v : Math.sign(v) * Number.MAX_VALUE;
+ this._register();
+ this._forEachChild(function(child, offset) {
+ child.currentTime = v - offset;
+ });
+ this._updatePromises();
+ },
+ get startTime() {
+ return this._animation.startTime;
+ },
+ set startTime(v) {
+ this._updatePromises();
+ this._animation.startTime = isFinite(v) ? v : Math.sign(v) * Number.MAX_VALUE;
+ this._register();
+ this._forEachChild(function(child, offset) {
+ child.startTime = v + offset;
+ });
+ this._updatePromises();
+ },
+ get playbackRate() {
+ return this._animation.playbackRate;
+ },
+ set playbackRate(value) {
+ this._updatePromises();
+ var oldCurrentTime = this.currentTime;
+ this._animation.playbackRate = value;
+ this._forEachChild(function(childAnimation) {
+ childAnimation.playbackRate = value;
+ });
+ if (oldCurrentTime !== null) {
+ this.currentTime = oldCurrentTime;
+ }
+ this._updatePromises();
+ },
+ play: function() {
+ this._updatePromises();
+ this._paused = false;
+ this._animation.play();
+ if (this._timeline._animations.indexOf(this) == -1) {
+ this._timeline._animations.push(this);
+ }
+ this._register();
+ scope.awaitStartTime(this);
+ this._forEachChild(function(child) {
+ var time = child.currentTime;
+ child.play();
+ child.currentTime = time;
+ });
+ this._updatePromises();
+ },
+ pause: function() {
+ this._updatePromises();
+ if (this.currentTime) {
+ this._holdTime = this.currentTime;
+ }
+ this._animation.pause();
+ this._register();
+ this._forEachChild(function(child) {
+ child.pause();
+ });
+ this._paused = true;
+ this._updatePromises();
+ },
+ finish: function() {
+ this._updatePromises();
+ this._animation.finish();
+ this._register();
+ this._updatePromises();
+ },
+ cancel: function() {
+ this._updatePromises();
+ this._animation.cancel();
+ this._register();
+ this._removeChildAnimations();
+ this._updatePromises();
+ },
+ reverse: function() {
+ this._updatePromises();
+ var oldCurrentTime = this.currentTime;
+ this._animation.reverse();
+ this._forEachChild(function(childAnimation) {
+ childAnimation.reverse();
+ });
+ if (oldCurrentTime !== null) {
+ this.currentTime = oldCurrentTime;
+ }
+ this._updatePromises();
+ },
+ addEventListener: function(type, handler) {
+ var wrapped = handler;
+ if (typeof handler == 'function') {
+ wrapped = (function(e) {
+ e.target = this;
+ handler.call(this, e);
+ }).bind(this);
+ handler._wrapper = wrapped;
+ }
+ this._animation.addEventListener(type, wrapped);
+ },
+ removeEventListener: function(type, handler) {
+ this._animation.removeEventListener(type, (handler && handler._wrapper) || handler);
+ },
+ _removeChildAnimations: function() {
+ while (this._childAnimations.length)
+ this._childAnimations.pop().cancel();
+ },
+ _forEachChild: function(f) {
+ var offset = 0;
+ if (this.effect.children && this._childAnimations.length < this.effect.children.length)
+ this._constructChildAnimations();
+ this._childAnimations.forEach(function(child) {
+ f.call(this, child, offset);
+ if (this.effect instanceof window.SequenceEffect)
+ offset += child.effect.activeDuration;
+ }.bind(this));
+
+ if (this.playState == 'pending')
+ return;
+ var timing = this.effect._timing;
+ var t = this.currentTime;
+ if (t !== null)
+ t = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), t, timing);
+ if (t == null || isNaN(t))
+ this._removeChildAnimations();
+ },
+ };
+
+ window.Animation = scope.Animation;
+
+ if (WEB_ANIMATIONS_TESTING) {
+ testing.webAnimationsNextAnimation = scope.Animation;
+ }
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.html b/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.html
new file mode 100644
index 00000000..3580355a
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.html
@@ -0,0 +1 @@
+<script src="./web-animations-next-lite.min.js"></script>
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js b/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js
new file mode 100644
index 00000000..965fcbb2
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js
@@ -0,0 +1,16 @@
+// Copyright 2014 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.
+
+!function(a,b){var c={},d={},e={};!function(a,b){function c(a){if("number"==typeof a)return a;var b={};for(var c in a)b[c]=a[c];return b}function d(){this._delay=0,this._endDelay=0,this._fill="none",this._iterationStart=0,this._iterations=1,this._duration=0,this._playbackRate=1,this._direction="normal",this._easing="linear",this._easingFunction=x}function e(){return a.isDeprecated("Invalid timing inputs","2016-03-02","TypeError exceptions will be thrown instead.",!0)}function f(b,c,e){var f=new d;return c&&(f.fill="both",f.duration="auto"),"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof f[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==v.indexOf(b[c]))return;if("direction"==c&&-1==w.indexOf(b[c]))return;if("playbackRate"==c&&1!==b[c]&&a.isDeprecated("AnimationEffectTiming.playbackRate","2014-11-28","Use Animation.playbackRate instead."))return;f[c]=b[c]}}):f.duration=b,f}function g(a){return"number"==typeof a&&(a=isNaN(a)?{duration:0}:{duration:a}),a}function h(b,c){return b=a.numericTimingToObject(b),f(b,c)}function i(a,b,c,d){return a<0||a>1||c<0||c>1?x:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}if(e<=0){var g=0;return a>0?g=b/a:!b&&c>0&&(g=d/c),g*e}if(e>=1){var h=0;return c<1?h=(d-1)/(c-1):1==c&&a<1&&(h=(b-1)/(a-1)),1+h*(e-1)}for(var i=0,j=1;i<j;){var k=(i+j)/2,l=f(a,c,k);if(Math.abs(e-l)<1e-5)return f(b,d,k);l<e?i=k:j=k}return f(b,d,k)}}function j(a,b){return function(c){if(c>=1)return 1;var d=1/a;return(c+=b*d)-c%d}}function k(a){C||(C=document.createElement("div").style),C.animationTimingFunction="",C.animationTimingFunction=a;var b=C.animationTimingFunction;if(""==b&&e())throw new TypeError(a+" is not a valid value for easing");return b}function l(a){if("linear"==a)return x;var b=E.exec(a);if(b)return i.apply(this,b.slice(1).map(Number));var c=F.exec(a);return c?j(Number(c[1]),{start:y,middle:z,end:A}[c[2]]):B[a]||x}function m(a){return Math.abs(n(a)/a.playbackRate)}function n(a){return 0===a.duration||0===a.iterations?0:a.duration*a.iterations}function o(a,b,c){if(null==b)return G;var d=c.delay+a+c.endDelay;return b<Math.min(c.delay,d)?H:b>=Math.min(c.delay+a,d)?I:J}function p(a,b,c,d,e){switch(d){case H:return"backwards"==b||"both"==b?0:null;case J:return c-e;case I:return"forwards"==b||"both"==b?a:null;case G:return null}}function q(a,b,c,d,e){var f=e;return 0===a?b!==H&&(f+=c):f+=d/a,f}function r(a,b,c,d,e,f){var g=a===1/0?b%1:a%1;return 0!==g||c!==I||0===d||0===e&&0!==f||(g=1),g}function s(a,b,c,d){return a===I&&b===1/0?1/0:1===c?Math.floor(d)-1:Math.floor(d)}function t(a,b,c){var d=a;if("normal"!==a&&"reverse"!==a){var e=b;"alternate-reverse"===a&&(e+=1),d="normal",e!==1/0&&e%2!=0&&(d="reverse")}return"normal"===d?c:1-c}function u(a,b,c){var d=o(a,b,c),e=p(a,c.fill,b,d,c.delay);if(null===e)return null;var f=q(c.duration,d,c.iterations,e,c.iterationStart),g=r(f,c.iterationStart,d,c.iterations,e,c.duration),h=s(d,c.iterations,g,f),i=t(c.direction,h,g);return c._easingFunction(i)}var v="backwards|forwards|both|none".split("|"),w="reverse|alternate|alternate-reverse".split("|"),x=function(a){return a};d.prototype={_setMember:function(b,c){this["_"+b]=c,this._effect&&(this._effect._timingInput[b]=c,this._effect._timing=a.normalizeTimingInput(this._effect._timingInput),this._effect.activeDuration=a.calculateActiveDuration(this._effect._timing),this._effect._animation&&this._effect._animation._rebuildUnderlyingAnimation())},get playbackRate(){return this._playbackRate},set delay(a){this._setMember("delay",a)},get delay(){return this._delay},set endDelay(a){this._setMember("endDelay",a)},get endDelay(){return this._endDelay},set fill(a){this._setMember("fill",a)},get fill(){return this._fill},set iterationStart(a){if((isNaN(a)||a<0)&&e())throw new TypeError("iterationStart must be a non-negative number, received: "+timing.iterationStart);this._setMember("iterationStart",a)},get iterationStart(){return this._iterationStart},set duration(a){if("auto"!=a&&(isNaN(a)||a<0)&&e())throw new TypeError("duration must be non-negative or auto, received: "+a);this._setMember("duration",a)},get duration(){return this._duration},set direction(a){this._setMember("direction",a)},get direction(){return this._direction},set easing(a){this._easingFunction=l(k(a)),this._setMember("easing",a)},get easing(){return this._easing},set iterations(a){if((isNaN(a)||a<0)&&e())throw new TypeError("iterations must be non-negative, received: "+a);this._setMember("iterations",a)},get iterations(){return this._iterations}};var y=1,z=.5,A=0,B={ease:i(.25,.1,.25,1),"ease-in":i(.42,0,1,1),"ease-out":i(0,0,.58,1),"ease-in-out":i(.42,0,.58,1),"step-start":j(1,y),"step-middle":j(1,z),"step-end":j(1,A)},C=null,D="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",E=new RegExp("cubic-bezier\\("+D+","+D+","+D+","+D+"\\)"),F=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,G=0,H=1,I=2,J=3;a.cloneTimingInput=c,a.makeTiming=f,a.numericTimingToObject=g,a.normalizeTimingInput=h,a.calculateActiveDuration=m,a.calculateIterationProgress=u,a.calculatePhase=o,a.normalizeEasing=k,a.parseEasingFunction=l}(c),function(a,b){function c(a,b){return a in k?k[a][b]||b:b}function d(a){return"display"===a||0===a.lastIndexOf("animation",0)||0===a.lastIndexOf("transition",0)}function e(a,b,e){if(!d(a)){var f=h[a];if(f){i.style[a]=b;for(var g in f){var j=f[g],k=i.style[j];e[j]=c(j,k)}}else e[a]=c(a,b)}}function f(a){var b=[];for(var c in a)if(!(c in["easing","offset","composite"])){var d=a[c];Array.isArray(d)||(d=[d]);for(var e,f=d.length,g=0;g<f;g++)e={},e.offset="offset"in a?a.offset:1==f?1:g/(f-1),"easing"in a&&(e.easing=a.easing),"composite"in a&&(e.composite=a.composite),e[c]=d[g],b.push(e)}return b.sort(function(a,b){return a.offset-b.offset}),b}function g(b){function c(){var a=d.length;null==d[a-1].offset&&(d[a-1].offset=1),a>1&&null==d[0].offset&&(d[0].offset=0);for(var b=0,c=d[0].offset,e=1;e<a;e++){var f=d[e].offset;if(null!=f){for(var g=1;g<e-b;g++)d[b+g].offset=c+(f-c)*g/(e-b);b=e,c=f}}}if(null==b)return[];window.Symbol&&Symbol.iterator&&Array.prototype.from&&b[Symbol.iterator]&&(b=Array.from(b)),Array.isArray(b)||(b=f(b));for(var d=b.map(function(b){var c={};for(var d in b){var f=b[d];if("offset"==d){if(null!=f){if(f=Number(f),!isFinite(f))throw new TypeError("Keyframe offsets must be numbers.");if(f<0||f>1)throw new TypeError("Keyframe offsets must be between 0 and 1.")}}else if("composite"==d){if("add"==f||"accumulate"==f)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"add compositing is not supported"};if("replace"!=f)throw new TypeError("Invalid composite mode "+f+".")}else f="easing"==d?a.normalizeEasing(f):""+f;e(d,f,c)}return void 0==c.offset&&(c.offset=null),void 0==c.easing&&(c.easing="linear"),c}),g=!0,h=-1/0,i=0;i<d.length;i++){var j=d[i].offset;if(null!=j){if(j<h)throw new TypeError("Keyframes are not loosely sorted by offset. Sort or specify offsets.");h=j}else g=!1}return d=d.filter(function(a){return a.offset>=0&&a.offset<=1}),g||c(),d}var h={background:["backgroundImage","backgroundPosition","backgroundSize","backgroundRepeat","backgroundAttachment","backgroundOrigin","backgroundClip","backgroundColor"],border:["borderTopColor","borderTopStyle","borderTopWidth","borderRightColor","borderRightStyle","borderRightWidth","borderBottomColor","borderBottomStyle","borderBottomWidth","borderLeftColor","borderLeftStyle","borderLeftWidth"],borderBottom:["borderBottomWidth","borderBottomStyle","borderBottomColor"],borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderLeft:["borderLeftWidth","borderLeftStyle","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderRight:["borderRightWidth","borderRightStyle","borderRightColor"],borderTop:["borderTopWidth","borderTopStyle","borderTopColor"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],flex:["flexGrow","flexShrink","flexBasis"],font:["fontFamily","fontSize","fontStyle","fontVariant","fontWeight","lineHeight"],margin:["marginTop","marginRight","marginBottom","marginLeft"],outline:["outlineColor","outlineStyle","outlineWidth"],padding:["paddingTop","paddingRight","paddingBottom","paddingLeft"]},i=document.createElementNS("http://www.w3.org/1999/xhtml","div"),j={thin:"1px",medium:"3px",thick:"5px"},k={borderBottomWidth:j,borderLeftWidth:j,borderRightWidth:j,borderTopWidth:j,fontSize:{"xx-small":"60%","x-small":"75%",small:"89%",medium:"100%",large:"120%","x-large":"150%","xx-large":"200%"},fontWeight:{normal:"400",bold:"700"},outlineWidth:j,textShadow:{none:"0px 0px 0px transparent"},boxShadow:{none:"0px 0px 0px 0px transparent"}};a.convertToArrayForm=f,a.normalizeKeyframes=g}(c),function(a){var b={};a.isDeprecated=function(a,c,d,e){var f=e?"are":"is",g=new Date,h=new Date(c);return h.setMonth(h.getMonth()+3),!(g<h&&(a in b||console.warn("Web Animations: "+a+" "+f+" deprecated and will stop working on "+h.toDateString()+". "+d),b[a]=!0,1))},a.deprecated=function(b,c,d,e){var f=e?"are":"is";if(a.isDeprecated(b,c,d,e))throw new Error(b+" "+f+" no longer supported. "+d)}}(c),function(){if(document.documentElement.animate){var a=document.documentElement.animate([],0),b=!0;if(a&&(b=!1,"play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(c){void 0===a[c]&&(b=!0)})),!b)return}!function(a,b,c){function d(a){for(var b={},c=0;c<a.length;c++)for(var d in a[c])if("offset"!=d&&"easing"!=d&&"composite"!=d){var e={offset:a[c].offset,easing:a[c].easing,value:a[c][d]};b[d]=b[d]||[],b[d].push(e)}for(var f in b){var g=b[f];if(0!=g[0].offset||1!=g[g.length-1].offset)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"Partial keyframes are not supported"}}return b}function e(c){var d=[];for(var e in c)for(var f=c[e],g=0;g<f.length-1;g++){var h=g,i=g+1,j=f[h].offset,k=f[i].offset,l=j,m=k;0==g&&(l=-1/0,0==k&&(i=h)),g==f.length-2&&(m=1/0,1==j&&(h=i)),d.push({applyFrom:l,applyTo:m,startOffset:f[h].offset,endOffset:f[i].offset,easingFunction:a.parseEasingFunction(f[h].easing),property:e,interpolation:b.propertyInterpolation(e,f[h].value,f[i].value)})}return d.sort(function(a,b){return a.startOffset-b.startOffset}),d}b.convertEffectInput=function(c){var f=a.normalizeKeyframes(c),g=d(f),h=e(g);return function(a,c){if(null!=c)h.filter(function(a){return c>=a.applyFrom&&c<a.applyTo}).forEach(function(d){var e=c-d.startOffset,f=d.endOffset-d.startOffset,g=0==f?0:d.easingFunction(e/f);b.apply(a,d.property,d.interpolation(g))});else for(var d in g)"offset"!=d&&"easing"!=d&&"composite"!=d&&b.clear(a,d)}}}(c,d),function(a,b,c){function d(a){return a.replace(/-(.)/g,function(a,b){return b.toUpperCase()})}function e(a,b,c){h[c]=h[c]||[],h[c].push([a,b])}function f(a,b,c){for(var f=0;f<c.length;f++){e(a,b,d(c[f]))}}function g(c,e,f){var g=c;/-/.test(c)&&!a.isDeprecated("Hyphenated property names","2016-03-22","Use camelCase instead.",!0)&&(g=d(c)),"initial"!=e&&"initial"!=f||("initial"==e&&(e=i[g]),"initial"==f&&(f=i[g]));for(var j=e==f?[]:h[g],k=0;j&&k<j.length;k++){var l=j[k][0](e),m=j[k][0](f);if(void 0!==l&&void 0!==m){var n=j[k][1](l,m);if(n){var o=b.Interpolation.apply(null,n);return function(a){return 0==a?e:1==a?f:o(a)}}}}return b.Interpolation(!1,!0,function(a){return a?f:e})}var h={};b.addPropertiesHandler=f;var i={backgroundColor:"transparent",backgroundPosition:"0% 0%",borderBottomColor:"currentColor",borderBottomLeftRadius:"0px",borderBottomRightRadius:"0px",borderBottomWidth:"3px",borderLeftColor:"currentColor",borderLeftWidth:"3px",borderRightColor:"currentColor",borderRightWidth:"3px",borderSpacing:"2px",borderTopColor:"currentColor",borderTopLeftRadius:"0px",borderTopRightRadius:"0px",borderTopWidth:"3px",bottom:"auto",clip:"rect(0px, 0px, 0px, 0px)",color:"black",fontSize:"100%",fontWeight:"400",height:"auto",left:"auto",letterSpacing:"normal",lineHeight:"120%",marginBottom:"0px",marginLeft:"0px",marginRight:"0px",marginTop:"0px",maxHeight:"none",maxWidth:"none",minHeight:"0px",minWidth:"0px",opacity:"1.0",outlineColor:"invert",outlineOffset:"0px",outlineWidth:"3px",paddingBottom:"0px",paddingLeft:"0px",paddingRight:"0px",paddingTop:"0px",right:"auto",strokeDasharray:"none",strokeDashoffset:"0px",textIndent:"0px",textShadow:"0px 0px 0px transparent",top:"auto",transform:"",verticalAlign:"0px",visibility:"visible",width:"auto",wordSpacing:"normal",zIndex:"auto"};b.propertyInterpolation=g}(c,d),function(a,b,c){function d(b){var c=a.calculateActiveDuration(b),d=function(d){return a.calculateIterationProgress(c,d,b)};return d._totalDuration=b.delay+c+b.endDelay,d}b.KeyframeEffect=function(c,e,f,g){var h,i=d(a.normalizeTimingInput(f)),j=b.convertEffectInput(e),k=function(){j(c,h)};return k._update=function(a){return null!==(h=i(a))},k._clear=function(){j(c,null)},k._hasSameTarget=function(a){return c===a},k._target=c,k._totalDuration=i._totalDuration,k._id=g,k}}(c,d),function(a,b){a.apply=function(b,c,d){b.style[a.propertyName(c)]=d},a.clear=function(b,c){b.style[a.propertyName(c)]=""}}(d),function(a){window.Element.prototype.animate=function(b,c){var d="";return c&&c.id&&(d=c.id),a.timeline._play(a.KeyframeEffect(this,b,c,d))}}(d),function(a,b){function c(a,b,d){if("number"==typeof a&&"number"==typeof b)return a*(1-d)+b*d;if("boolean"==typeof a&&"boolean"==typeof b)return d<.5?a:b;if(a.length==b.length){for(var e=[],f=0;f<a.length;f++)e.push(c(a[f],b[f],d));return e}throw"Mismatched interpolation arguments "+a+":"+b}a.Interpolation=function(a,b,d){return function(e){return d(c(a,b,e))}}}(d),function(a,b,c){a.sequenceNumber=0;var d=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="finish",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()};b.Animation=function(b){this.id="",b&&b._id&&(this.id=b._id),this._sequenceNumber=a.sequenceNumber++,this._currentTime=0,this._startTime=null,this._paused=!1,this._playbackRate=1,this._inTimeline=!0,this._finishedFlag=!0,this.onfinish=null,this._finishHandlers=[],this._effect=b,this._inEffect=this._effect._update(0),this._idle=!0,this._currentTimePending=!1},b.Animation.prototype={_ensureAlive:function(){this.playbackRate<0&&0===this.currentTime?this._inEffect=this._effect._update(-1):this._inEffect=this._effect._update(this.currentTime),this._inTimeline||!this._inEffect&&this._finishedFlag||(this._inTimeline=!0,b.timeline._animations.push(this))},_tickCurrentTime:function(a,b){a!=this._currentTime&&(this._currentTime=a,this._isFinished&&!b&&(this._currentTime=this._playbackRate>0?this._totalDuration:0),this._ensureAlive())},get currentTime(){return this._idle||this._currentTimePending?null:this._currentTime},set currentTime(a){a=+a,isNaN(a)||(b.restart(),this._paused||null==this._startTime||(this._startTime=this._timeline.currentTime-a/this._playbackRate),this._currentTimePending=!1,this._currentTime!=a&&(this._idle&&(this._idle=!1,this._paused=!0),this._tickCurrentTime(a,!0),b.applyDirtiedAnimation(this)))},get startTime(){return this._startTime},set startTime(a){a=+a,isNaN(a)||this._paused||this._idle||(this._startTime=a,this._tickCurrentTime((this._timeline.currentTime-this._startTime)*this.playbackRate),b.applyDirtiedAnimation(this))},get playbackRate(){return this._playbackRate},set playbackRate(a){if(a!=this._playbackRate){var c=this.currentTime;this._playbackRate=a,this._startTime=null,"paused"!=this.playState&&"idle"!=this.playState&&(this._finishedFlag=!1,this._idle=!1,this._ensureAlive(),b.applyDirtiedAnimation(this)),null!=c&&(this.currentTime=c)}},get _isFinished(){return!this._idle&&(this._playbackRate>0&&this._currentTime>=this._totalDuration||this._playbackRate<0&&this._currentTime<=0)},get _totalDuration(){return this._effect._totalDuration},get playState(){return this._idle?"idle":null==this._startTime&&!this._paused&&0!=this.playbackRate||this._currentTimePending?"pending":this._paused?"paused":this._isFinished?"finished":"running"},_rewind:function(){if(this._playbackRate>=0)this._currentTime=0;else{if(!(this._totalDuration<1/0))throw new DOMException("Unable to rewind negative playback rate animation with infinite duration","InvalidStateError");this._currentTime=this._totalDuration}},play:function(){this._paused=!1,(this._isFinished||this._idle)&&(this._rewind(),this._startTime=null),this._finishedFlag=!1,this._idle=!1,this._ensureAlive(),b.applyDirtiedAnimation(this)},pause:function(){this._isFinished||this._paused||this._idle?this._idle&&(this._rewind(),this._idle=!1):this._currentTimePending=!0,this._startTime=null,this._paused=!0},finish:function(){this._idle||(this.currentTime=this._playbackRate>0?this._totalDuration:0,this._startTime=this._totalDuration-this.currentTime,this._currentTimePending=!1,b.applyDirtiedAnimation(this))},cancel:function(){this._inEffect&&(this._inEffect=!1,this._idle=!0,this._paused=!1,this._isFinished=!0,this._finishedFlag=!0,this._currentTime=0,this._startTime=null,this._effect._update(null),b.applyDirtiedAnimation(this))},reverse:function(){this.playbackRate*=-1,this.play()},addEventListener:function(a,b){"function"==typeof b&&"finish"==a&&this._finishHandlers.push(b)},removeEventListener:function(a,b){if("finish"==a){var c=this._finishHandlers.indexOf(b);c>=0&&this._finishHandlers.splice(c,1)}},_fireEvents:function(a){if(this._isFinished){if(!this._finishedFlag){var b=new d(this,this._currentTime,a),c=this._finishHandlers.concat(this.onfinish?[this.onfinish]:[]);setTimeout(function(){c.forEach(function(a){a.call(b.target,b)})},0),this._finishedFlag=!0}}else this._finishedFlag=!1},_tick:function(a,b){this._idle||this._paused||(null==this._startTime?b&&(this.startTime=a-this._currentTime/this.playbackRate):this._isFinished||this._tickCurrentTime((a-this._startTime)*this.playbackRate)),b&&(this._currentTimePending=!1,this._fireEvents(a))},get _needsTick(){return this.playState in{pending:1,running:1}||!this._finishedFlag},_targetAnimations:function(){var a=this._effect._target;return a._activeAnimations||(a._activeAnimations=[]),a._activeAnimations},_markTarget:function(){var a=this._targetAnimations();-1===a.indexOf(this)&&a.push(this)},_unmarkTarget:function(){var a=this._targetAnimations(),b=a.indexOf(this);-1!==b&&a.splice(b,1)}}}(c,d),function(a,b,c){function d(a){var b=j;j=[],a<q.currentTime&&(a=q.currentTime),q._animations.sort(e),q._animations=h(a,!0,q._animations)[0],b.forEach(function(b){b[1](a)}),g(),l=void 0}function e(a,b){return a._sequenceNumber-b._sequenceNumber}function f(){this._animations=[],this.currentTime=window.performance&&performance.now?performance.now():0}function g(){o.forEach(function(a){a()}),o.length=0}function h(a,c,d){p=!0,n=!1,b.timeline.currentTime=a,m=!1;var e=[],f=[],g=[],h=[];return d.forEach(function(b){b._tick(a,c),b._inEffect?(f.push(b._effect),b._markTarget()):(e.push(b._effect),b._unmarkTarget()),b._needsTick&&(m=!0);var d=b._inEffect||b._needsTick;b._inTimeline=d,d?g.push(b):h.push(b)}),o.push.apply(o,e),o.push.apply(o,f),m&&requestAnimationFrame(function(){}),p=!1,[g,h]}var i=window.requestAnimationFrame,j=[],k=0;window.requestAnimationFrame=function(a){var b=k++;return 0==j.length&&i(d),j.push([b,a]),b},window.cancelAnimationFrame=function(a){j.forEach(function(b){b[0]==a&&(b[1]=function(){})})},f.prototype={_play:function(c){c._timing=a.normalizeTimingInput(c.timing);var d=new b.Animation(c);return d._idle=!1,d._timeline=this,this._animations.push(d),b.restart(),b.applyDirtiedAnimation(d),d}};var l=void 0,m=!1,n=!1;b.restart=function(){return m||(m=!0,requestAnimationFrame(function(){}),n=!0),n},b.applyDirtiedAnimation=function(a){if(!p){a._markTarget();var c=a._targetAnimations();c.sort(e),h(b.timeline.currentTime,!1,c.slice())[1].forEach(function(a){var b=q._animations.indexOf(a);-1!==b&&q._animations.splice(b,1)}),g()}};var o=[],p=!1,q=new f;b.timeline=q}(c,d),function(a){function b(a,b){var c=a.exec(b);if(c)return c=a.ignoreCase?c[0].toLowerCase():c[0],[c,b.substr(c.length)]}function c(a,b){b=b.replace(/^\s*/,"");var c=a(b);if(c)return[c[0],c[1].replace(/^\s*/,"")]}function d(a,d,e){a=c.bind(null,a);for(var f=[];;){var g=a(e);if(!g)return[f,e];if(f.push(g[0]),e=g[1],!(g=b(d,e))||""==g[1])return[f,e];e=g[1]}}function e(a,b){for(var c=0,d=0;d<b.length&&(!/\s|,/.test(b[d])||0!=c);d++)if("("==b[d])c++;else if(")"==b[d]&&(c--,0==c&&d++,c<=0))break;var e=a(b.substr(0,d));return void 0==e?void 0:[e,b.substr(d)]}function f(a,b){for(var c=a,d=b;c&&d;)c>d?c%=d:d%=c;return c=a*b/(c+d)}function g(a){return function(b){var c=a(b);return c&&(c[0]=void 0),c}}function h(a,b){return function(c){return a(c)||[b,c]}}function i(b,c){for(var d=[],e=0;e<b.length;e++){var f=a.consumeTrimmed(b[e],c);if(!f||""==f[0])return;void 0!==f[0]&&d.push(f[0]),c=f[1]}if(""==c)return d}function j(a,b,c,d,e){for(var g=[],h=[],i=[],j=f(d.length,e.length),k=0;k<j;k++){var l=b(d[k%d.length],e[k%e.length]);if(!l)return;g.push(l[0]),h.push(l[1]),i.push(l[2])}return[g,h,function(b){var d=b.map(function(a,b){return i[b](a)}).join(c);return a?a(d):d}]}function k(a,b,c){for(var d=[],e=[],f=[],g=0,h=0;h<c.length;h++)if("function"==typeof c[h]){var i=c[h](a[g],b[g++]);d.push(i[0]),e.push(i[1]),f.push(i[2])}else!function(a){d.push(!1),e.push(!1),f.push(function(){return c[a]})}(h);return[d,e,function(a){for(var b="",c=0;c<a.length;c++)b+=f[c](a[c]);return b}]}a.consumeToken=b,a.consumeTrimmed=c,a.consumeRepeated=d,a.consumeParenthesised=e,a.ignore=g,a.optional=h,a.consumeList=i,a.mergeNestedRepeated=j.bind(null,null),a.mergeWrappedNestedRepeated=j,a.mergeList=k}(d),function(a){function b(b){function c(b){var c=a.consumeToken(/^inset/i,b);if(c)return d.inset=!0,c;var c=a.consumeLengthOrPercent(b);if(c)return d.lengths.push(c[0]),c;var c=a.consumeColor(b);return c?(d.color=c[0],c):void 0}var d={inset:!1,lengths:[],color:null},e=a.consumeRepeated(c,/^/,b);if(e&&e[0].length)return[d,e[1]]}function c(c){var d=a.consumeRepeated(b,/^,/,c);if(d&&""==d[1])return d[0]}function d(b,c){for(;b.lengths.length<Math.max(b.lengths.length,c.lengths.length);)b.lengths.push({px:0});for(;c.lengths.length<Math.max(b.lengths.length,c.lengths.length);)c.lengths.push({px:0});if(b.inset==c.inset&&!!b.color==!!c.color){for(var d,e=[],f=[[],0],g=[[],0],h=0;h<b.lengths.length;h++){var i=a.mergeDimensions(b.lengths[h],c.lengths[h],2==h);f[0].push(i[0]),g[0].push(i[1]),e.push(i[2])}if(b.color&&c.color){var j=a.mergeColors(b.color,c.color);f[1]=j[0],g[1]=j[1],d=j[2]}return[f,g,function(a){for(var c=b.inset?"inset ":" ",f=0;f<e.length;f++)c+=e[f](a[0][f])+" ";return d&&(c+=d(a[1])),c}]}}function e(b,c,d,e){function f(a){return{inset:a,color:[0,0,0,0],lengths:[{px:0},{px:0},{px:0},{px:0}]}}for(var g=[],h=[],i=0;i<d.length||i<e.length;i++){var j=d[i]||f(e[i].inset),k=e[i]||f(d[i].inset);g.push(j),h.push(k)}return a.mergeNestedRepeated(b,c,g,h)}var f=e.bind(null,d,", ");a.addPropertiesHandler(c,f,["box-shadow","text-shadow"])}(d),function(a,b){function c(a){return a.toFixed(3).replace(/0+$/,"").replace(/\.$/,"")}function d(a,b,c){return Math.min(b,Math.max(a,c))}function e(a){if(/^\s*[-+]?(\d*\.)?\d+\s*$/.test(a))return Number(a)}function f(a,b){return[a,b,c]}function g(a,b){if(0!=a)return i(0,1/0)(a,b)}function h(a,b){return[a,b,function(a){return Math.round(d(1,1/0,a))}]}function i(a,b){return function(e,f){return[e,f,function(e){return c(d(a,b,e))}]}}function j(a){var b=a.trim().split(/\s*[\s,]\s*/);if(0!==b.length){for(var c=[],d=0;d<b.length;d++){var f=e(b[d]);if(void 0===f)return;c.push(f)}return c}}function k(a,b){if(a.length==b.length)return[a,b,function(a){return a.map(c).join(" ")}]}function l(a,b){return[a,b,Math.round]}a.clamp=d,a.addPropertiesHandler(j,k,["stroke-dasharray"]),a.addPropertiesHandler(e,i(0,1/0),["border-image-width","line-height"]),a.addPropertiesHandler(e,i(0,1),["opacity","shape-image-threshold"]),a.addPropertiesHandler(e,g,["flex-grow","flex-shrink"]),a.addPropertiesHandler(e,h,["orphans","widows"]),a.addPropertiesHandler(e,l,["z-index"]),a.parseNumber=e,a.parseNumberList=j,a.mergeNumbers=f,a.numberToString=c}(d),function(a,b){function c(a,b){if("visible"==a||"visible"==b)return[0,1,function(c){return c<=0?a:c>=1?b:"visible"}]}a.addPropertiesHandler(String,c,["visibility"])}(d),function(a,b){function c(a){a=a.trim(),f.fillStyle="#000",f.fillStyle=a;var b=f.fillStyle;if(f.fillStyle="#fff",f.fillStyle=a,b==f.fillStyle){f.fillRect(0,0,1,1);var c=f.getImageData(0,0,1,1).data;f.clearRect(0,0,1,1);var d=c[3]/255;return[c[0]*d,c[1]*d,c[2]*d,d]}}function d(b,c){return[b,c,function(b){function c(a){return Math.max(0,Math.min(255,a))}if(b[3])for(var d=0;d<3;d++)b[d]=Math.round(c(b[d]/b[3]));return b[3]=a.numberToString(a.clamp(0,1,b[3])),"rgba("+b.join(",")+")"}]}var e=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");e.width=e.height=1;var f=e.getContext("2d");a.addPropertiesHandler(c,d,["background-color","border-bottom-color","border-left-color","border-right-color","border-top-color","color","fill","flood-color","lighting-color","outline-color","stop-color","stroke","text-decoration-color"]),a.consumeColor=a.consumeParenthesised.bind(null,c),a.mergeColors=d}(d),function(a,b){function c(a){function b(){var b=h.exec(a);g=b?b[0]:void 0}function c(){var a=Number(g);return b(),a}function d(){if("("!==g)return c();b();var a=f();return")"!==g?NaN:(b(),a)}function e(){for(var a=d();"*"===g||"/"===g;){var c=g;b();var e=d();"*"===c?a*=e:a/=e}return a}function f(){for(var a=e();"+"===g||"-"===g;){var c=g;b();var d=e();"+"===c?a+=d:a-=d}return a}var g,h=/([\+\-\w\.]+|[\(\)\*\/])/g;return b(),f()}function d(a,b){if("0"==(b=b.trim().toLowerCase())&&"px".search(a)>=0)return{px:0};if(/^[^(]*$|^calc/.test(b)){b=b.replace(/calc\(/g,"(");var d={};b=b.replace(a,function(a){return d[a]=null,"U"+a});for(var e="U("+a.source+")",f=b.replace(/[-+]?(\d*\.)?\d+([Ee][-+]?\d+)?/g,"N").replace(new RegExp("N"+e,"g"),"D").replace(/\s[+-]\s/g,"O").replace(/\s/g,""),g=[/N\*(D)/g,/(N|D)[*\/]N/g,/(N|D)O\1/g,/\((N|D)\)/g],h=0;h<g.length;)g[h].test(f)?(f=f.replace(g[h],"$1"),h=0):h++;if("D"==f){for(var i in d){var j=c(b.replace(new RegExp("U"+i,"g"),"").replace(new RegExp(e,"g"),"*0"));if(!isFinite(j))return;d[i]=j}return d}}}function e(a,b){return f(a,b,!0)}function f(b,c,d){var e,f=[];for(e in b)f.push(e);for(e in c)f.indexOf(e)<0&&f.push(e);return b=f.map(function(a){return b[a]||0}),c=f.map(function(a){return c[a]||0}),[b,c,function(b){var c=b.map(function(c,e){return 1==b.length&&d&&(c=Math.max(c,0)),a.numberToString(c)+f[e]}).join(" + ");return b.length>1?"calc("+c+")":c}]}var g="px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc",h=d.bind(null,new RegExp(g,"g")),i=d.bind(null,new RegExp(g+"|%","g")),j=d.bind(null,/deg|rad|grad|turn/g);a.parseLength=h,a.parseLengthOrPercent=i,a.consumeLengthOrPercent=a.consumeParenthesised.bind(null,i),a.parseAngle=j,a.mergeDimensions=f;var k=a.consumeParenthesised.bind(null,h),l=a.consumeRepeated.bind(void 0,k,/^/),m=a.consumeRepeated.bind(void 0,l,/^,/);a.consumeSizePairList=m;var n=function(a){var b=m(a);if(b&&""==b[1])return b[0]},o=a.mergeNestedRepeated.bind(void 0,e," "),p=a.mergeNestedRepeated.bind(void 0,o,",");a.mergeNonNegativeSizePair=o,a.addPropertiesHandler(n,p,["background-size"]),a.addPropertiesHandler(i,e,["border-bottom-width","border-image-width","border-left-width","border-right-width","border-top-width","flex-basis","font-size","height","line-height","max-height","max-width","outline-width","width"]),a.addPropertiesHandler(i,f,["border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","bottom","left","letter-spacing","margin-bottom","margin-left","margin-right","margin-top","min-height","min-width","outline-offset","padding-bottom","padding-left","padding-right","padding-top","perspective","right","shape-margin","stroke-dashoffset","text-indent","top","vertical-align","word-spacing"])}(d),function(a,b){function c(b){return a.consumeLengthOrPercent(b)||a.consumeToken(/^auto/,b)}function d(b){var d=a.consumeList([a.ignore(a.consumeToken.bind(null,/^rect/)),a.ignore(a.consumeToken.bind(null,/^\(/)),a.consumeRepeated.bind(null,c,/^,/),a.ignore(a.consumeToken.bind(null,/^\)/))],b);if(d&&4==d[0].length)return d[0]}function e(b,c){return"auto"==b||"auto"==c?[!0,!1,function(d){var e=d?b:c;if("auto"==e)return"auto";var f=a.mergeDimensions(e,e);return f[2](f[0])}]:a.mergeDimensions(b,c)}function f(a){return"rect("+a+")"}var g=a.mergeWrappedNestedRepeated.bind(null,f,e,", ");a.parseBox=d,a.mergeBoxes=g,a.addPropertiesHandler(d,g,["clip"])}(d),function(a,b){function c(a){return function(b){var c=0;return a.map(function(a){return a===k?b[c++]:a})}}function d(a){return a}function e(b){if("none"==(b=b.toLowerCase().trim()))return[];for(var c,d=/\s*(\w+)\(([^)]*)\)/g,e=[],f=0;c=d.exec(b);){if(c.index!=f)return;f=c.index+c[0].length;var g=c[1],h=n[g];if(!h)return;var i=c[2].split(","),j=h[0];if(j.length<i.length)return;for(var k=[],o=0;o<j.length;o++){var p,q=i[o],r=j[o];if(void 0===(p=q?{A:function(b){return"0"==b.trim()?m:a.parseAngle(b)},N:a.parseNumber,T:a.parseLengthOrPercent,L:a.parseLength}[r.toUpperCase()](q):{a:m,n:k[0],t:l}[r]))return;k.push(p)}if(e.push({t:g,d:k}),d.lastIndex==b.length)return e}}function f(a){return a.toFixed(6).replace(".000000","")}function g(b,c){if(b.decompositionPair!==c){b.decompositionPair=c;var d=a.makeMatrixDecomposition(b)}if(c.decompositionPair!==b){c.decompositionPair=b;var e=a.makeMatrixDecomposition(c)}return null==d[0]||null==e[0]?[[!1],[!0],function(a){return a?c[0].d:b[0].d}]:(d[0].push(0),e[0].push(1),[d,e,function(b){var c=a.quat(d[0][3],e[0][3],b[5]);return a.composeMatrix(b[0],b[1],b[2],c,b[4]).map(f).join(",")}])}function h(a){return a.replace(/[xy]/,"")}function i(a){return a.replace(/(x|y|z|3d)?$/,"3d")}function j(b,c){var d=a.makeMatrixDecomposition&&!0,e=!1;if(!b.length||!c.length){b.length||(e=!0,b=c,c=[]);for(var f=0;f<b.length;f++){var j=b[f].t,k=b[f].d,l="scale"==j.substr(0,5)?1:0;c.push({t:j,d:k.map(function(a){if("number"==typeof a)return l;var b={};for(var c in a)b[c]=l;return b})})}}var m=function(a,b){return"perspective"==a&&"perspective"==b||("matrix"==a||"matrix3d"==a)&&("matrix"==b||"matrix3d"==b)},o=[],p=[],q=[];if(b.length!=c.length){if(!d)return;var r=g(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]]}else for(var f=0;f<b.length;f++){var j,s=b[f].t,t=c[f].t,u=b[f].d,v=c[f].d,w=n[s],x=n[t];if(m(s,t)){if(!d)return;var r=g([b[f]],[c[f]]);o.push(r[0]),p.push(r[1]),q.push(["matrix",[r[2]]])}else{if(s==t)j=s;else if(w[2]&&x[2]&&h(s)==h(t))j=h(s),u=w[2](u),v=x[2](v);else{if(!w[1]||!x[1]||i(s)!=i(t)){if(!d)return;var r=g(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]];break}j=i(s),u=w[1](u),v=x[1](v)}for(var y=[],z=[],A=[],B=0;B<u.length;B++){var C="number"==typeof u[B]?a.mergeNumbers:a.mergeDimensions,r=C(u[B],v[B]);y[B]=r[0],z[B]=r[1],A.push(r[2])}o.push(y),p.push(z),q.push([j,A])}}if(e){var D=o;o=p,p=D}return[o,p,function(a){return a.map(function(a,b){var c=a.map(function(a,c){return q[b][1][c](a)}).join(",");return"matrix"==q[b][0]&&16==c.split(",").length&&(q[b][0]="matrix3d"),q[b][0]+"("+c+")"}).join(" ")}]}var k=null,l={px:0},m={deg:0},n={matrix:["NNNNNN",[k,k,0,0,k,k,0,0,0,0,1,0,k,k,0,1],d],matrix3d:["NNNNNNNNNNNNNNNN",d],rotate:["A"],rotatex:["A"],rotatey:["A"],rotatez:["A"],rotate3d:["NNNA"],perspective:["L"],scale:["Nn",c([k,k,1]),d],scalex:["N",c([k,1,1]),c([k,1])],scaley:["N",c([1,k,1]),c([1,k])],scalez:["N",c([1,1,k])],scale3d:["NNN",d],skew:["Aa",null,d],skewx:["A",null,c([k,m])],skewy:["A",null,c([m,k])],translate:["Tt",c([k,k,l]),d],translatex:["T",c([k,l,l]),c([k,l])],translatey:["T",c([l,k,l]),c([l,k])],translatez:["L",c([l,l,k])],translate3d:["TTL",d]};a.addPropertiesHandler(e,j,["transform"]),a.transformToSvgMatrix=function(b){var c=a.transformListToMatrix(e(b));return"matrix("+f(c[0])+" "+f(c[1])+" "+f(c[4])+" "+f(c[5])+" "+f(c[12])+" "+f(c[13])+")"}}(d),function(a,b){function c(a,b){b.concat([a]).forEach(function(b){b in document.documentElement.style&&(d[a]=b),e[b]=a})}var d={},e={};c("transform",["webkitTransform","msTransform"]),c("transformOrigin",["webkitTransformOrigin"]),c("perspective",["webkitPerspective"]),c("perspectiveOrigin",["webkitPerspectiveOrigin"]),a.propertyName=function(a){return d[a]||a},a.unprefixedPropertyName=function(a){return e[a]||a}}(d)}(),function(){if(void 0===document.createElement("div").animate([]).oncancel){var a;if(window.performance&&performance.now)var a=function(){return performance.now()};else var a=function(){return Date.now()};var b=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="cancel",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()},c=window.Element.prototype.animate;window.Element.prototype.animate=function(d,e){var f=c.call(this,d,e);f._cancelHandlers=[],f.oncancel=null;var g=f.cancel;f.cancel=function(){g.call(this);var c=new b(this,null,a()),d=this._cancelHandlers.concat(this.oncancel?[this.oncancel]:[]);setTimeout(function(){d.forEach(function(a){a.call(c.target,c)})},0)};var h=f.addEventListener;f.addEventListener=function(a,b){"function"==typeof b&&"cancel"==a?this._cancelHandlers.push(b):h.call(this,a,b)};var i=f.removeEventListener;return f.removeEventListener=function(a,b){if("cancel"==a){var c=this._cancelHandlers.indexOf(b);c>=0&&this._cancelHandlers.splice(c,1)}else i.call(this,a,b)},f}}}(),function(a){var b=document.documentElement,c=null,d=!1;try{var e=getComputedStyle(b).getPropertyValue("opacity"),f="0"==e?"1":"0";c=b.animate({opacity:[f,f]},{duration:1}),c.currentTime=0,d=getComputedStyle(b).getPropertyValue("opacity")==f}catch(a){}finally{c&&c.cancel()}if(!d){var g=window.Element.prototype.animate;window.Element.prototype.animate=function(b,c){return window.Symbol&&Symbol.iterator&&Array.prototype.from&&b[Symbol.iterator]&&(b=Array.from(b)),Array.isArray(b)||null===b||(b=a.convertToArrayForm(b)),g.call(this,b,c)}}}(c),function(a,b,c){function d(a){var c=b.timeline;c.currentTime=a,c._discardAnimations(),0==c._animations.length?f=!1:requestAnimationFrame(d)}var e=window.requestAnimationFrame;window.requestAnimationFrame=function(a){return e(function(c){b.timeline._updateAnimationsPromises(),a(c),b.timeline._updateAnimationsPromises()})},b.AnimationTimeline=function(){this._animations=[],this.currentTime=void 0},b.AnimationTimeline.prototype={getAnimations:function(){return this._discardAnimations(),this._animations.slice()},_updateAnimationsPromises:function(){b.animationsWithPromises=b.animationsWithPromises.filter(function(a){return a._updatePromises()})},_discardAnimations:function(){this._updateAnimationsPromises(),this._animations=this._animations.filter(function(a){return"finished"!=a.playState&&"idle"!=a.playState})},_play:function(a){var c=new b.Animation(a,this);return this._animations.push(c),b.restartWebAnimationsNextTick(),c._updatePromises(),c._animation.play(),c._updatePromises(),c},play:function(a){return a&&a.remove(),this._play(a)}};var f=!1;b.restartWebAnimationsNextTick=function(){f||(f=!0,requestAnimationFrame(d))};var g=new b.AnimationTimeline;b.timeline=g;try{Object.defineProperty(window.document,"timeline",{configurable:!0,get:function(){return g}})}catch(a){}try{window.document.timeline=g}catch(a){}}(0,e),function(a,b,c){b.animationsWithPromises=[],b.Animation=function(b,c){if(this.id="",b&&b._id&&(this.id=b._id),this.effect=b,b&&(b._animation=this),!c)throw new Error("Animation with null timeline is not supported");this._timeline=c,this._sequenceNumber=a.sequenceNumber++,this._holdTime=0,this._paused=!1,this._isGroup=!1,this._animation=null,this._childAnimations=[],this._callback=null,this._oldPlayState="idle",this._rebuildUnderlyingAnimation(),this._animation.cancel(),this._updatePromises()},b.Animation.prototype={_updatePromises:function(){var a=this._oldPlayState,b=this.playState;return this._readyPromise&&b!==a&&("idle"==b?(this._rejectReadyPromise(),this._readyPromise=void 0):"pending"==a?this._resolveReadyPromise():"pending"==b&&(this._readyPromise=void 0)),this._finishedPromise&&b!==a&&("idle"==b?(this._rejectFinishedPromise(),this._finishedPromise=void 0):"finished"==b?this._resolveFinishedPromise():"finished"==a&&(this._finishedPromise=void 0)),this._oldPlayState=this.playState,this._readyPromise||this._finishedPromise},_rebuildUnderlyingAnimation:function(){this._updatePromises();var a,c,d,e,f=!!this._animation;f&&(a=this.playbackRate,c=this._paused,d=this.startTime,e=this.currentTime,this._animation.cancel(),this._animation._wrapper=null,this._animation=null),(!this.effect||this.effect instanceof window.KeyframeEffect)&&(this._animation=b.newUnderlyingAnimationForKeyframeEffect(this.effect),b.bindAnimationForKeyframeEffect(this)),(this.effect instanceof window.SequenceEffect||this.effect instanceof window.GroupEffect)&&(this._animation=b.newUnderlyingAnimationForGroup(this.effect),b.bindAnimationForGroup(this)),this.effect&&this.effect._onsample&&b.bindAnimationForCustomEffect(this),f&&(1!=a&&(this.playbackRate=a),null!==d?this.startTime=d:null!==e?this.currentTime=e:null!==this._holdTime&&(this.currentTime=this._holdTime),c&&this.pause()),this._updatePromises()},_updateChildren:function(){if(this.effect&&"idle"!=this.playState){var a=this.effect._timing.delay;this._childAnimations.forEach(function(c){this._arrangeChildren(c,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c.effect))}.bind(this))}},_setExternalAnimation:function(a){if(this.effect&&this._isGroup)for(var b=0;b<this.effect.children.length;b++)this.effect.children[b]._animation=a,this._childAnimations[b]._setExternalAnimation(a)},_constructChildAnimations:function(){if(this.effect&&this._isGroup){var a=this.effect._timing.delay;this._removeChildAnimations(),this.effect.children.forEach(function(c){var d=b.timeline._play(c);this._childAnimations.push(d),d.playbackRate=this.playbackRate,this._paused&&d.pause(),c._animation=this.effect._animation,this._arrangeChildren(d,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c))}.bind(this))}},_arrangeChildren:function(a,b){null===this.startTime?a.currentTime=this.currentTime-b/this.playbackRate:a.startTime!==this.startTime+b/this.playbackRate&&(a.startTime=this.startTime+b/this.playbackRate)},get timeline(){return this._timeline},get playState(){return this._animation?this._animation.playState:"idle"},get finished(){return window.Promise?(this._finishedPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._finishedPromise=new Promise(function(a,b){this._resolveFinishedPromise=function(){a(this)},this._rejectFinishedPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"finished"==this.playState&&this._resolveFinishedPromise()),this._finishedPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get ready(){return window.Promise?(this._readyPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._readyPromise=new Promise(function(a,b){this._resolveReadyPromise=function(){a(this)},this._rejectReadyPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"pending"!==this.playState&&this._resolveReadyPromise()),this._readyPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get onfinish(){return this._animation.onfinish},set onfinish(a){this._animation.onfinish="function"==typeof a?function(b){b.target=this,a.call(this,b)}.bind(this):a},get oncancel(){return this._animation.oncancel},set oncancel(a){this._animation.oncancel="function"==typeof a?function(b){b.target=this,a.call(this,b)}.bind(this):a},get currentTime(){this._updatePromises();var a=this._animation.currentTime;return this._updatePromises(),a},set currentTime(a){this._updatePromises(),this._animation.currentTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.currentTime=a-c}),this._updatePromises()},get startTime(){return this._animation.startTime},set startTime(a){this._updatePromises(),this._animation.startTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.startTime=a+c}),this._updatePromises()},get playbackRate(){return this._animation.playbackRate},set playbackRate(a){this._updatePromises();var b=this.currentTime;this._animation.playbackRate=a,this._forEachChild(function(b){b.playbackRate=a}),null!==b&&(this.currentTime=b),this._updatePromises()},play:function(){this._updatePromises(),this._paused=!1,this._animation.play(),-1==this._timeline._animations.indexOf(this)&&this._timeline._animations.push(this),this._register(),b.awaitStartTime(this),this._forEachChild(function(a){var b=a.currentTime;a.play(),a.currentTime=b}),this._updatePromises()},pause:function(){this._updatePromises(),this.currentTime&&(this._holdTime=this.currentTime),this._animation.pause(),this._register(),this._forEachChild(function(a){a.pause()}),this._paused=!0,this._updatePromises()},finish:function(){this._updatePromises(),this._animation.finish(),this._register(),this._updatePromises()},cancel:function(){this._updatePromises(),this._animation.cancel(),this._register(),this._removeChildAnimations(),this._updatePromises()},reverse:function(){this._updatePromises();var a=this.currentTime;this._animation.reverse(),this._forEachChild(function(a){a.reverse()}),null!==a&&(this.currentTime=a),this._updatePromises()},addEventListener:function(a,b){var c=b;"function"==typeof b&&(c=function(a){a.target=this,b.call(this,a)}.bind(this),b._wrapper=c),this._animation.addEventListener(a,c)},removeEventListener:function(a,b){this._animation.removeEventListener(a,b&&b._wrapper||b)},_removeChildAnimations:function(){for(;this._childAnimations.length;)this._childAnimations.pop().cancel()},_forEachChild:function(b){var c=0;if(this.effect.children&&this._childAnimations.length<this.effect.children.length&&this._constructChildAnimations(),this._childAnimations.forEach(function(a){b.call(this,a,c),this.effect instanceof window.SequenceEffect&&(c+=a.effect.activeDuration)}.bind(this)),"pending"!=this.playState){var d=this.effect._timing,e=this.currentTime;null!==e&&(e=a.calculateIterationProgress(a.calculateActiveDuration(d),e,d)),(null==e||isNaN(e))&&this._removeChildAnimations()}}},window.Animation=b.Animation}(c,e),function(a,b,c){function d(b){this._frames=a.normalizeKeyframes(b)}function e(){for(var a=!1;i.length;)i.shift()._updateChildren(),a=!0;return a}var f=function(a){if(a._animation=void 0,a instanceof window.SequenceEffect||a instanceof window.GroupEffect)for(var b=0;b<a.children.length;b++)f(a.children[b])};b.removeMulti=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];d._parent?(-1==b.indexOf(d._parent)&&b.push(d._parent),d._parent.children.splice(d._parent.children.indexOf(d),1),d._parent=null,f(d)):d._animation&&d._animation.effect==d&&(d._animation.cancel(),d._animation.effect=new KeyframeEffect(null,[]),d._animation._callback&&(d._animation._callback._animation=null),d._animation._rebuildUnderlyingAnimation(),f(d))}for(c=0;c<b.length;c++)b[c]._rebuild()},b.KeyframeEffect=function(b,c,e,f){return this.target=b,this._parent=null,e=a.numericTimingToObject(e),this._timingInput=a.cloneTimingInput(e),this._timing=a.normalizeTimingInput(e),this.timing=a.makeTiming(e,!1,this),this.timing._effect=this,"function"==typeof c?(a.deprecated("Custom KeyframeEffect","2015-06-22","Use KeyframeEffect.onsample instead."),this._normalizedKeyframes=c):this._normalizedKeyframes=new d(c),this._keyframes=c,this.activeDuration=a.calculateActiveDuration(this._timing),this._id=f,this},b.KeyframeEffect.prototype={getFrames:function(){return"function"==typeof this._normalizedKeyframes?this._normalizedKeyframes:this._normalizedKeyframes._frames},set onsample(a){if("function"==typeof this.getFrames())throw new Error("Setting onsample on custom effect KeyframeEffect is not supported.");this._onsample=a,this._animation&&this._animation._rebuildUnderlyingAnimation()},get parent(){return this._parent},clone:function(){if("function"==typeof this.getFrames())throw new Error("Cloning custom effects is not supported.");var b=new KeyframeEffect(this.target,[],a.cloneTimingInput(this._timingInput),this._id);return b._normalizedKeyframes=this._normalizedKeyframes,b._keyframes=this._keyframes,b},remove:function(){b.removeMulti([this])}};var g=Element.prototype.animate;Element.prototype.animate=function(a,c){var d="";return c&&c.id&&(d=c.id),b.timeline._play(new b.KeyframeEffect(this,a,c,d))};var h=document.createElementNS("http://www.w3.org/1999/xhtml","div");b.newUnderlyingAnimationForKeyframeEffect=function(a){if(a){var b=a.target||h,c=a._keyframes;"function"==typeof c&&(c=[]);var d=a._timingInput;d.id=a._id}else var b=h,c=[],d=0;return g.apply(b,[c,d])},b.bindAnimationForKeyframeEffect=function(a){a.effect&&"function"==typeof a.effect._normalizedKeyframes&&b.bindAnimationForCustomEffect(a)};var i=[];b.awaitStartTime=function(a){null===a.startTime&&a._isGroup&&(0==i.length&&requestAnimationFrame(e),i.push(a))};var j=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){b.timeline._updateAnimationsPromises();var a=j.apply(this,arguments);return e()&&(a=j.apply(this,arguments)),b.timeline._updateAnimationsPromises(),a}}),window.KeyframeEffect=b.KeyframeEffect,window.Element.prototype.getAnimations=function(){return document.timeline.getAnimations().filter(function(a){return null!==a.effect&&a.effect.target==this}.bind(this))}}(c,e),function(a,b,c){function d(a){a._registered||(a._registered=!0,g.push(a),h||(h=!0,requestAnimationFrame(e)))}function e(a){var b=g;g=[],b.sort(function(a,b){return a._sequenceNumber-b._sequenceNumber}),b=b.filter(function(a){a();var b=a._animation?a._animation.playState:"idle";return"running"!=b&&"pending"!=b&&(a._registered=!1),a._registered}),g.push.apply(g,b),g.length?(h=!0,requestAnimationFrame(e)):h=!1}var f=(document.createElementNS("http://www.w3.org/1999/xhtml","div"),0);b.bindAnimationForCustomEffect=function(b){var c,e=b.effect.target,g="function"==typeof b.effect.getFrames();c=g?b.effect.getFrames():b.effect._onsample;var h=b.effect.timing,i=null;h=a.normalizeTimingInput(h);var j=function(){var d=j._animation?j._animation.currentTime:null;null!==d&&(d=a.calculateIterationProgress(a.calculateActiveDuration(h),d,h),isNaN(d)&&(d=null)),d!==i&&(g?c(d,e,b.effect):c(d,b.effect,b.effect._animation)),i=d};j._animation=b,j._registered=!1,j._sequenceNumber=f++,b._callback=j,d(j)};var g=[],h=!1;b.Animation.prototype._register=function(){this._callback&&d(this._callback)}}(c,e),function(a,b,c){function d(a){return a._timing.delay+a.activeDuration+a._timing.endDelay}function e(b,c,d){this._id=d,this._parent=null,this.children=b||[],this._reparent(this.children),c=a.numericTimingToObject(c),this._timingInput=a.cloneTimingInput(c),this._timing=a.normalizeTimingInput(c,!0),this.timing=a.makeTiming(c,!0,this),this.timing._effect=this,"auto"===this._timing.duration&&(this._timing.duration=this.activeDuration)}window.SequenceEffect=function(){e.apply(this,arguments)},window.GroupEffect=function(){e.apply(this,arguments)},e.prototype={_isAncestor:function(a){for(var b=this;null!==b;){if(b==a)return!0;b=b._parent}return!1},_rebuild:function(){for(var a=this;a;)"auto"===a.timing.duration&&(a._timing.duration=a.activeDuration),a=a._parent;this._animation&&this._animation._rebuildUnderlyingAnimation()},_reparent:function(a){b.removeMulti(a);for(var c=0;c<a.length;c++)a[c]._parent=this},_putChild:function(a,b){for(var c=b?"Cannot append an ancestor or self":"Cannot prepend an ancestor or self",d=0;d<a.length;d++)if(this._isAncestor(a[d]))throw{type:DOMException.HIERARCHY_REQUEST_ERR,name:"HierarchyRequestError",message:c};for(var d=0;d<a.length;d++)b?this.children.push(a[d]):this.children.unshift(a[d]);this._reparent(a),this._rebuild()},append:function(){this._putChild(arguments,!0)},prepend:function(){this._putChild(arguments,!1)},get parent(){return this._parent},get firstChild(){return this.children.length?this.children[0]:null},get lastChild(){return this.children.length?this.children[this.children.length-1]:null},clone:function(){for(var b=a.cloneTimingInput(this._timingInput),c=[],d=0;d<this.children.length;d++)c.push(this.children[d].clone());return this instanceof GroupEffect?new GroupEffect(c,b):new SequenceEffect(c,b)},remove:function(){b.removeMulti([this])}},window.SequenceEffect.prototype=Object.create(e.prototype),Object.defineProperty(window.SequenceEffect.prototype,"activeDuration",{get:function(){var a=0;return this.children.forEach(function(b){a+=d(b)}),Math.max(a,0)}}),window.GroupEffect.prototype=Object.create(e.prototype),Object.defineProperty(window.GroupEffect.prototype,"activeDuration",{get:function(){var a=0;return this.children.forEach(function(b){a=Math.max(a,d(b))}),a}}),b.newUnderlyingAnimationForGroup=function(c){var d,e=null,f=function(b){var c=d._wrapper;if(c&&"pending"!=c.playState&&c.effect)return null==b?void c._removeChildAnimations():0==b&&c.playbackRate<0&&(e||(e=a.normalizeTimingInput(c.effect.timing)),b=a.calculateIterationProgress(a.calculateActiveDuration(e),-1,e),isNaN(b)||null==b)?(c._forEachChild(function(a){a.currentTime=-1}),void c._removeChildAnimations()):void 0},g=new KeyframeEffect(null,[],c._timing,c._id);return g.onsample=f,d=b.timeline._play(g)},b.bindAnimationForGroup=function(a){a._animation._wrapper=a,a._isGroup=!0,b.awaitStartTime(a),a._constructChildAnimations(),a._setExternalAnimation(a)},b.groupChildDuration=d}(c,e),b.true=a}({},function(){return this}());
+//# sourceMappingURL=web-animations-next-lite.min.js.map \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js.map b/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js.map
new file mode 100644
index 00000000..2234d482
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations-next-lite.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["src/scope.js","src/timing-utilities.js","src/normalize-keyframes.js","src/deprecation.js","src/web-animations-bonus-cancel-events.js","src/web-animations-bonus-object-form-keyframes.js","src/timeline.js","src/web-animations-next-animation.js","src/keyframe-effect-constructor.js","src/effect-callback.js","src/group-constructors.js"],"names":["webAnimationsShared","webAnimations1","webAnimationsNext","shared","testing","cloneTimingInput","timingInput","clone","m","AnimationEffectTiming","this","_delay","_endDelay","_fill","_iterationStart","_iterations","_duration","_playbackRate","_direction","_easing","_easingFunction","linear","isInvalidTimingDeprecated","isDeprecated","makeTiming","forGroup","effect","timing","fill","duration","isNaN","Object","getOwnPropertyNames","forEach","property","fills","indexOf","directions","numericTimingToObject","normalizeTimingInput","cubic","a","b","c","d","x","f","start_gradient","end_gradient","start","end","mid","xEst","Math","abs","step","count","pos","stepSize","normalizeEasing","easing","styleForCleaning","document","createElement","style","animationTimingFunction","normalizedEasing","TypeError","parseEasingFunction","cubicData","cubicBezierRe","exec","apply","slice","map","Number","stepData","stepRe","Start","middle","Middle","End","presets","calculateActiveDuration","repeatedDuration","playbackRate","iterations","calculatePhase","activeDuration","localTime","PhaseNone","endTime","delay","endDelay","min","PhaseBefore","PhaseAfter","PhaseActive","calculateActiveTime","fillMode","phase","calculateOverallProgress","iterationDuration","activeTime","iterationStart","overallProgress","calculateSimpleIterationProgress","simpleIterationProgress","Infinity","calculateCurrentIteration","floor","calculateDirectedProgress","playbackDirection","currentIteration","currentDirection","calculateIterationProgress","directedProgress","direction","split","prototype","_setMember","member","value","_effect","_timingInput","_timing","_animation","_rebuildUnderlyingAnimation","ease","ease-in","ease-out","ease-in-out","step-start","step-middle","step-end","numberString","RegExp","antiAlias","aliases","isNotAnimatable","lastIndexOf","expandShorthandAndAntiAlias","result","longProperties","shorthandToLonghand","shorthandExpanderElem","i","longProperty","longhandValue","convertToArrayForm","effectInput","normalizedEffectInput","values","Array","isArray","keyframe","numKeyframes","length","offset","composite","push","sort","normalizeKeyframes","spaceKeyframes","keyframes","previousIndex","previousOffset","j","window","Symbol","iterator","from","originalKeyframe","memberValue","isFinite","type","DOMException","NOT_SUPPORTED_ERR","name","message","everyFrameHasOffset","filter","background","border","borderBottom","borderColor","borderLeft","borderRadius","borderRight","borderTop","borderWidth","flex","font","margin","outline","padding","createElementNS","borderWidthAliases","thin","medium","thick","borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopWidth","fontSize","xx-small","x-small","small","large","x-large","xx-large","fontWeight","normal","bold","outlineWidth","textShadow","none","boxShadow","silenced","feature","date","advice","plural","auxVerb","today","Date","expiry","setMonth","getMonth","console","warn","toDateString","deprecated","Error","animate","oncancel","now","performance","AnimationCancelEvent","target","currentTime","timelineTime","bubbles","cancelable","currentTarget","defaultPrevented","eventPhase","Event","AT_TARGET","timeStamp","originalElementAnimate","Element","options","animation","call","_cancelHandlers","originalCancel","cancel","event","handlers","concat","setTimeout","handler","originalAddEventListener","addEventListener","originalRemoveEventListener","removeEventListener","index","splice","element","documentElement","animated","originalOpacity","getComputedStyle","getPropertyValue","testOpacity","opacity","error","scope","webAnimationsNextTick","t","timeline","_discardAnimations","_animations","ticking","requestAnimationFrame","originalRequestAnimationFrame","_updateAnimationsPromises","AnimationTimeline","getAnimations","animationsWithPromises","_updatePromises","playState","_play","Animation","restartWebAnimationsNextTick","play","remove","defineProperty","configurable","get","e","id","_id","_timeline","_sequenceNumber","sequenceNumber","_holdTime","_paused","_isGroup","_childAnimations","_callback","_oldPlayState","oldPlayState","newPlayState","_readyPromise","_rejectReadyPromise","_resolveReadyPromise","_finishedPromise","_rejectFinishedPromise","_resolveFinishedPromise","oldPlaybackRate","oldPaused","oldStartTime","oldCurrentTime","hadUnderlying","startTime","_wrapper","KeyframeEffect","newUnderlyingAnimationForKeyframeEffect","bindAnimationForKeyframeEffect","SequenceEffect","GroupEffect","newUnderlyingAnimationForGroup","bindAnimationForGroup","_onsample","bindAnimationForCustomEffect","pause","_updateChildren","childAnimation","_arrangeChildren","groupChildDuration","bind","_setExternalAnimation","children","_constructChildAnimations","_removeChildAnimations","child","finished","Promise","resolve","reject","ABORT_ERR","ready","onfinish","v","sign","MAX_VALUE","_register","_forEachChild","awaitStartTime","time","finish","reverse","wrapped","pop","KeyframeList","_frames","updatePendingGroups","updated","pendingGroups","shift","disassociate","removeMulti","effects","oldParents","_parent","_rebuild","_normalizedKeyframes","_keyframes","getFrames","onsample","callback","parent","nullTarget","keyframeEffect","groupAnimation","originalGetComputedStyle","enumerable","arguments","register","_registered","callbacks","tick","updating","left","right","effectFunction","isKeyframeEffect","last","node","constructor","_reparent","_isAncestor","newChildren","_putChild","args","isAppend","HIERARCHY_REQUEST_ERR","unshift","append","prepend","firstChild","lastChild","clonedTiming","clonedChildren","create","total","max","group","underlyingAnimation","ticker","tf","underlyingEffect","exports","webAnimationsTesting"],"mappings":";;;;;;;;;;;;;;CAcA,SAAIA,EAAAA,GAAJ,GAAIA,MACAC,KACAC,MCFJ,SAAUC,EAAQC,GAMhB,QAASC,GAAiBC,GACxB,GAA0B,gBAAfA,GACT,MAAOA,EAET,IAAIC,KACJ,KAAK,GAAIC,KAAKF,GACZC,EAAMC,GAAKF,EAAYE,EAEzB,OAAOD,GAGT,QAASE,KACPC,KAAKC,OAAS,EACdD,KAAKE,UAAY,EACjBF,KAAKG,MAAQ,OACbH,KAAKI,gBAAkB,EACvBJ,KAAKK,YAAc,EACnBL,KAAKM,UAAY,EACjBN,KAAKO,cAAgB,EACrBP,KAAKQ,WAAa,SAClBR,KAAKS,QAAU,SACfT,KAAKU,gBAAkBC,EAGzB,QAASC,KACP,MAAOnB,GAAOoB,aAAa,wBAAyB,aAAc,gDAAA,GA8EpE,QAASC,GAAWlB,EAAamB,EAAUC,GACzC,GAAIC,GAAS,GAAIlB,EA4BjB,OA3BIgB,KACFE,EAAOC,KAAO,OACdD,EAAOE,SAAW,QAEM,gBAAfvB,IAA4BwB,MAAMxB,OAAAA,KAElCA,GACTyB,OAAOC,oBAAoB1B,GAAa2B,QAAQ,SAASC,GACvD,GAA6B,QAAzB5B,EAAY4B,GAAqB,CACnC,IAA+B,gBAApBP,GAAOO,IAAqC,YAAZA,KACL,gBAAzB5B,GAAY4B,IAAyBJ,MAAMxB,EAAY4B,KAChE,MAGJ,IAAiB,QAAZA,IAAiE,GAAzCC,EAAMC,QAAQ9B,EAAY4B,IACrD,MAEF,IAAiB,aAAZA,IAA2E,GAA9CG,EAAWD,QAAQ9B,EAAY4B,IAC/D,MAEF,IAAgB,gBAAZA,GAAwD,IAA1B5B,EAAY4B,IAAmB/B,EAAOoB,aAAa,qCAAsC,aAAc,uCACvI,MAEFI,GAAOO,GAAY5B,EAAY4B,MAlBnCP,EAAOE,SAAWvB,EAsBbqB,EAGT,QAASW,GAAsBhC,GAQ7B,MAP0B,gBAAfA,KAEPA,EADEwB,MAAMxB,IACQuB,SAAU,IAEVA,SAAUvB,IAGvBA,EAGT,QAASiC,GAAqBjC,EAAamB,GAEzC,MADAnB,GAAcH,EAAOmC,sBAAsBhC,GACpCkB,EAAWlB,EAAamB,GAGjC,QAASe,GAAMC,EAAGC,EAAGC,EAAGC,GACtB,MAAIH,GAAI,GAAKA,EAAI,GAAKE,EAAI,GAAKA,EAAI,EAC1BtB,EAEF,SAASwB,GAqBZ,QAASC,GAAEL,EAAGC,EAAGlC,GAAK,MAAO,GAAIiC,GAAK,EAAIjC,IAAM,EAAIA,GAAKA,EAAI,EAAIkC,GAAK,EAAIlC,GAAKA,EAAIA,EAAIA,EAAIA,EAAIA,EApBjG,GAAIqC,GAAK,EAAG,CACV,GAAIE,GAAiB,CAKrB,OAJIN,GAAI,EACNM,EAAiBL,EAAID,GACbC,GAAKC,EAAI,IACjBI,EAAiBH,EAAID,GAChBI,EAAiBF,EAE1B,GAAIA,GAAK,EAAG,CACV,GAAIG,GAAe,CAKnB,OAJIL,GAAI,EACNK,GAAgBJ,EAAI,IAAMD,EAAI,GAClB,GAALA,GAAUF,EAAI,IACrBO,GAAgBN,EAAI,IAAMD,EAAI,IACzB,EAAIO,GAAgBH,EAAI,GAIjC,IADA,GAAII,GAAQ,EAAGC,EAAM,EACdD,EAAQC,GAAK,CAClB,GAAIC,IAAOF,EAAQC,GAAO,EAEtBE,EAAON,EAAEL,EAAGE,EAAGQ,EACnB,IAAIE,KAAKC,IAAIT,EAAIO,GAAQ,KACvB,MAAON,GAAEJ,EAAGE,EAAGO,EAEbC,GAAOP,EACTI,EAAQE,EAERD,EAAMC,EAGV,MAAOL,GAAEJ,EAAGE,EAAGO,IAQnB,QAASI,GAAKC,EAAOC,GACnB,MAAO,UAASZ,GACd,GAAIA,GAAK,EACP,MAAO,EAET,IAAIa,GAAW,EAAIF,CAEnB,QADAX,GAAKY,EAAMC,GACAb,EAAIa,GAmBnB,QAASC,GAAgBC,GAClBC,IACHA,EAAmBC,SAASC,cAAc,OAAOC,OAEnDH,EAAiBI,wBAA0B,GAC3CJ,EAAiBI,wBAA0BL,CAC3C,IAAIM,GAAmBL,EAAiBI,uBACxC,IAAwB,IAApBC,GAA0B5C,IAC5B,KAAM,IAAI6C,WAAUP,EAAS,mCAE/B,OAAOM,GAGT,QAASE,GAAoBF,GAC3B,GAAwB,UAApBA,EACF,MAAO7C,EAET,IAAIgD,GAAYC,EAAcC,KAAKL,EACnC,IAAIG,EACF,MAAO7B,GAAMgC,MAAM9D,KAAM2D,EAAUI,MAAM,GAAGC,IAAIC,QAElD,IAAIC,GAAWC,EAAON,KAAKL,EAC3B,OAAIU,GACKrB,EAAKoB,OAAOC,EAAS,KAAM3B,MAAS6B,EAAOC,OAAUC,EAAQ9B,IAAO+B,GAAKL,EAAS,KAE9EM,EAAQhB,IAMd7C,EAGT,QAAS8D,GAAwBxD,GAC/B,MAAO0B,MAAKC,IAAI8B,EAAiBzD,GAAUA,EAAO0D,cAGpD,QAASD,GAAiBzD,GAExB,MAAwB,KAApBA,EAAOE,UAAwC,IAAtBF,EAAO2D,WAC3B,EAEF3D,EAAOE,SAAWF,EAAO2D,WAQlC,QAASC,GAAeC,EAAgBC,EAAW9D,GAEjD,GAAiB,MAAb8D,EACF,MAAOC,EAGT,IAAIC,GAAUhE,EAAOiE,MAAQJ,EAAiB7D,EAAOkE,QACrD,OAAIJ,GAAYpC,KAAKyC,IAAInE,EAAOiE,MAAOD,GAC9BI,EAELN,GAAapC,KAAKyC,IAAInE,EAAOiE,MAAQJ,EAAgBG,GAChDK,EAGFC,EAGT,QAASC,GAAoBV,EAAgBW,EAAUV,EAAWW,EAAOR,GAEvE,OAAQQ,GACN,IAAKL,GACH,MAAgB,aAAZI,GAAuC,QAAZA,EACtB,EACF,IACT,KAAKF,GACH,MAAOR,GAAYG,CACrB,KAAKI,GACH,MAAgB,YAAZG,GAAsC,QAAZA,EACrBX,EACF,IACT,KAAKE,GACH,MAAO,OAIb,QAASW,GAAyBC,EAAmBF,EAAOd,EAAYiB,EAAYC,GAElF,GAAIC,GAAkBD,CAQtB,OAP0B,KAAtBF,EACEF,IAAUL,IACZU,GAAmBnB,GAGrBmB,GAAmBF,EAAaD,EAE3BG,EAGT,QAASC,GAAiCD,EAAiBD,EAAgBJ,EAAOd,EAAYiB,EAAYD,GAGxG,GAAIK,GAA2BF,IAAoBG,EAAAA,EAAYJ,EAAiB,EAAIC,EAAkB,CAKtG,OAJgC,KAA5BE,GAAiCP,IAAUJ,GAA6B,IAAfV,GACzC,IAAfiB,GAA0C,IAAtBD,IACvBK,EAA0B,GAErBA,EAGT,QAASE,GAA0BT,EAAOd,EAAYqB,EAAyBF,GAE7E,MAAIL,KAAUJ,GAAcV,IAAesB,EAAAA,EAClCA,EAAAA,EAEuB,IAA5BD,EACKtD,KAAKyD,MAAML,GAAmB,EAEhCpD,KAAKyD,MAAML,GAGpB,QAASM,GAA0BC,EAAmBC,EAAkBN,GAEtE,GAAIO,GAAmBF,CACvB,IAA0B,WAAtBA,GAAwD,YAAtBA,EAAiC,CACrE,GAAIpE,GAAIqE,CACkB,uBAAtBD,IACFpE,GAAK,GAEPsE,EAAmB,SACftE,IAAMgE,EAAAA,GAAYhE,EAAI,GAAM,IAC9BsE,EAAmB,WAGvB,MAAyB,WAArBA,EACKP,EAEF,EAAIA,EAGb,QAASQ,GAA2B3B,EAAgBC,EAAW9D,GAC7D,GAAIyE,GAAQb,EAAeC,EAAgBC,EAAW9D,GAClD4E,EAAaL,EAAoBV,EAAgB7D,EAAOC,KAAM6D,EAAWW,EAAOzE,EAAOiE,MAC3F,IAAmB,OAAfW,EACF,MAAO,KAET,IAAIE,GAAkBJ,EAAyB1E,EAAOE,SAAUuE,EAAOzE,EAAO2D,WAAYiB,EAAY5E,EAAO6E,gBACzGG,EAA0BD,EAAiCD,EAAiB9E,EAAO6E,eAAgBJ,EAAOzE,EAAO2D,WAAYiB,EAAY5E,EAAOE,UAChJoF,EAAmBJ,EAA0BT,EAAOzE,EAAO2D,WAAYqB,EAAyBF,GAChGW,EAAmBL,EAA0BpF,EAAO0F,UAAWJ,EAAkBN,EAIrF,OAAOhF,GAAOP,gBAAgBgG,GA1XhC,GAAIjF,GAAQ,+BAA+BmF,MAAM,KAC7CjF,EAAa,sCAAsCiF,MAAM,KACzDjG,EAAS,SAASwB,GAAK,MAAOA,GA8BlCpC,GAAsB8G,WACpBC,WAAY,SAASC,EAAQC,GAC3BhH,KAAK,IAAM+G,GAAUC,EACjBhH,KAAKiH,UACPjH,KAAKiH,QAAQC,aAAaH,GAAUC,EACpChH,KAAKiH,QAAQE,QAAU1H,EAAOoC,qBAAqB7B,KAAKiH,QAAQC,cAChElH,KAAKiH,QAAQnC,eAAiBrF,EAAOgF,wBAAwBzE,KAAKiH,QAAQE,SACtEnH,KAAKiH,QAAQG,YACfpH,KAAKiH,QAAQG,WAAWC,gCAI9B1C,mBACE,MAAO3E,MAAKO,eAEd2E,UAAU8B,GACRhH,KAAK8G,WAAW,QAASE,IAE3B9B,YACE,MAAOlF,MAAKC,QAEdkF,aAAa6B,GACXhH,KAAK8G,WAAW,WAAYE,IAE9B7B,eACE,MAAOnF,MAAKE,WAEdgB,SAAS8F,GACPhH,KAAK8G,WAAW,OAAQE,IAE1B9F,WACE,MAAOlB,MAAKG,OAEd2F,mBAAmBkB,GACjB,IAAK5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACjC,KAAM,IAAI6C,WAAU,2DAA6DxC,OAAO6E,eAE1F9F,MAAK8G,WAAW,iBAAkBE,IAEpClB,qBACE,MAAO9F,MAAKI,iBAEde,aAAa6F,GACX,GAAa,QAATA,IAAoB5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACpD,KAAM,IAAI6C,WAAU,oDAAsDuD,EAE5EhH,MAAK8G,WAAW,WAAYE,IAE9B7F,eACE,MAAOnB,MAAKM,WAEdqG,cAAcK,GACZhH,KAAK8G,WAAW,YAAaE,IAE/BL,gBACE,MAAO3G,MAAKQ,YAEd0C,WAAW8D,GACThH,KAAKU,gBAAkBgD,EAAoBT,EAAgB+D,IAC3DhH,KAAK8G,WAAW,SAAUE,IAE5B9D,aACE,MAAOlD,MAAKS,SAEdmE,eAAeoC,GACb,IAAK5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACjC,KAAM,IAAI6C,WAAU,8CAAgDuD,EAEtEhH,MAAK8G,WAAW,aAAcE,IAEhCpC,iBACE,MAAO5E,MAAKK,aA4FhB,IAAI+D,GAAQ,EACRE,EAAS,GACTC,EAAM,EAaNC,GACF8C,KAAQxF,EAAM,IAAM,GAAK,IAAM,GAC/ByF,UAAWzF,EAAM,IAAM,EAAG,EAAG,GAC7B0F,WAAY1F,EAAM,EAAG,EAAG,IAAM,GAC9B2F,cAAe3F,EAAM,IAAM,EAAG,IAAM,GACpC4F,aAAc7E,EAAK,EAAGuB,GACtBuD,cAAe9E,EAAK,EAAGyB,GACvBsD,WAAY/E,EAAK,EAAG0B,IAGlBpB,EAAmB,KACnB0E,EAAe,qCACfjE,EAAgB,GAAIkE,QAAO,kBAAoBD,EAAe,IAAMA,EAAe,IAAMA,EAAe,IAAMA,EAAe,OAC7H1D,EAAS,gDAgDTa,EAAY,EACZK,EAAc,EACdC,EAAa,EACbC,EAAc,CA2GlB9F,GAAOE,iBAAmBA,EAC1BF,EAAOqB,WAAaA,EACpBrB,EAAOmC,sBAAwBA,EAC/BnC,EAAOoC,qBAAuBA,EAC9BpC,EAAOgF,wBAA0BA,EACjChF,EAAOgH,2BAA6BA,EACpChH,EAAOoF,eAAiBA,EACxBpF,EAAOwD,gBAAkBA,EACzBxD,EAAOiE,oBAAsBA,GAc5BpE,GCrZH,SAAUG,EAAQC,GAmIhB,QAASqI,GAAUvG,EAAUwF,GAC3B,MAAIxF,KAAYwG,GACPA,EAAQxG,GAAUwF,IAAUA,EAE9BA,EAGT,QAASiB,GAAgBzG,GAEvB,MAAoB,YAAbA,GAAmE,IAAzCA,EAAS0G,YAAY,YAAa,IAAsD,IAA1C1G,EAAS0G,YAAY,aAAc,GAIpH,QAASC,GAA4B3G,EAAUwF,EAAOoB,GACpD,IAAIH,EAAgBzG,GAApB,CAGA,GAAI6G,GAAiBC,EAAoB9G,EACzC,IAAI6G,EAAgB,CAClBE,EAAsBjF,MAAM9B,GAAYwF,CACxC,KAAK,GAAIwB,KAAKH,GAAgB,CAC5B,GAAII,GAAeJ,EAAeG,GAC9BE,EAAgBH,EAAsBjF,MAAMmF,EAChDL,GAAOK,GAAgBV,EAAUU,EAAcC,QAGjDN,GAAO5G,GAAYuG,EAAUvG,EAAUwF,IAI3C,QAAS2B,GAAmBC,GAC1B,GAAIC,KAEJ,KAAK,GAAIrH,KAAYoH,GACnB,KAAIpH,KAAa,SAAU,SAAU,cAArC,CAIA,GAAIsH,GAASF,EAAYpH,EACpBuH,OAAMC,QAAQF,KACjBA,GAAUA,GAKZ,KAAK,GAFDG,GACAC,EAAeJ,EAAOK,OACjBX,EAAI,EAAGA,EAAIU,EAAcV,IAChCS,KAGEA,EAASG,OADP,UAAYR,GACIA,EAAYQ,OACL,GAAhBF,EACS,EAEAV,GAAKU,EAAe,GAGpC,UAAYN,KACdK,EAAS/F,OAAS0F,EAAY1F,QAG5B,aAAe0F,KACjBK,EAASI,UAAYT,EAAYS,WAGnCJ,EAASzH,GAAYsH,EAAON,GAE5BK,EAAsBS,KAAKL,GAK/B,MADAJ,GAAsBU,KAAK,SAASxH,EAAGC,GAAK,MAAOD,GAAEqH,OAASpH,EAAEoH,SACzDP,EAGT,QAASW,GAAmBZ,GAqE1B,QAASa,KACP,GAAIN,GAASO,EAAUP,MACa,OAAhCO,EAAUP,EAAS,GAAGC,SACxBM,EAAUP,EAAS,GAAGC,OAAS,GAC7BD,EAAS,GAA4B,MAAvBO,EAAU,GAAGN,SAC7BM,EAAU,GAAGN,OAAS,EAIxB,KAAK,GAFDO,GAAgB,EAChBC,EAAiBF,EAAU,GAAGN,OACzBZ,EAAI,EAAGA,EAAIW,EAAQX,IAAK,CAC/B,GAAIY,GAASM,EAAUlB,GAAGY,MAC1B,IAAc,MAAVA,EAAgB,CAClB,IAAK,GAAIS,GAAI,EAAGA,EAAIrB,EAAImB,EAAeE,IACrCH,EAAUC,EAAgBE,GAAGT,OAASQ,GAAkBR,EAASQ,GAAkBC,GAAKrB,EAAImB,EAC9FA,GAAgBnB,EAChBoB,EAAiBR,IAnFvB,GAAmB,MAAfR,EACF,QAGEkB,QAAOC,QAAUA,OAAOC,UAAYjB,MAAMlC,UAAUoD,MAAQrB,EAAYmB,OAAOC,YAEjFpB,EAAcG,MAAMkB,KAAKrB,IAGtBG,MAAMC,QAAQJ,KACjBA,EAAcD,EAAmBC,GA0CnC,KAAK,GAvCDc,GAAYd,EAAY5E,IAAI,SAASkG,GACvC,GAAIjB,KACJ,KAAK,GAAIlC,KAAUmD,GAAkB,CACnC,GAAIC,GAAcD,EAAiBnD,EACnC,IAAc,UAAVA,GACF,GAAmB,MAAfoD,EAAqB,CAEvB,GADAA,EAAclG,OAAOkG,IAChBC,SAASD,GACZ,KAAM,IAAI1G,WAAU,oCACtB,IAAI0G,EAAc,GAAKA,EAAc,EACnC,KAAM,IAAI1G,WAAU,kDAEnB,IAAc,aAAVsD,EAAuB,CAChC,GAAmB,OAAfoD,GAAuC,cAAfA,EAC1B,MACEE,KAAMC,aAAaC,kBACnBC,KAAM,oBACNC,QAAS,mCAEN,IAAmB,WAAfN,EACT,KAAM,IAAI1G,WAAU,0BAA4B0G,EAAc,SAGhEA,GADmB,UAAVpD,EACKtH,EAAOwD,gBAAgBkH,GAEvB,GAAKA,CAErBhC,GAA4BpB,EAAQoD,EAAalB,GAMnD,WAAA,IAJIA,EAASG,SACXH,EAASG,OAAS,UAAA,IAChBH,EAAS/F,SACX+F,EAAS/F,OAAS,UACb+F,IAGLyB,GAAAA,EAEAd,GAAAA,EAAAA,EACKpB,EAAI,EAAGA,EAAIkB,EAAUP,OAAQX,IAAK,CACzC,GAAIY,GAASM,EAAUlB,GAAGY,MAC1B,IAAc,MAAVA,EAAgB,CAClB,GAAIA,EAASQ,EACX,KAAM,IAAInG,WAAU,uEAEtBmG,GAAiBR,MAEjBsB,IAAAA,EA8BJ,MA1BAhB,GAAYA,EAAUiB,OAAO,SAAS1B,GACpC,MAAOA,GAASG,QAAU,GAAKH,EAASG,QAAU,IAsB/CsB,GACHjB,IAEKC,EAvST,GAAIpB,IACFsC,YACE,kBACA,qBACA,iBACA,mBACA,uBACA,mBACA,iBACA,mBAEFC,QACE,iBACA,iBACA,iBACA,mBACA,mBACA,mBACA,oBACA,oBACA,oBACA,kBACA,kBACA,mBAEFC,cACE,oBACA,oBACA,qBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,YACE,kBACA,kBACA,mBAEFC,cACE,sBACA,uBACA,0BACA,0BAEFC,aACE,mBACA,mBACA,oBAEFC,WACE,iBACA,iBACA,kBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,MACE,WACA,aACA,aAEFC,MACE,aACA,WACA,YACA,cACA,aACA,cAEFC,QACE,YACA,cACA,eACA,cAEFC,SACE,eACA,eACA,gBAEFC,SACE,aACA,eACA,gBACA,gBAIAlD,EAAwBnF,SAASsI,gBAAgB,+BAAgC,OAEjFC,GACFC,KAAM,MACNC,OAAQ,MACRC,MAAO,OAGL9D,GACF+D,kBAAmBJ,EACnBK,gBAAiBL,EACjBM,iBAAkBN,EAClBO,eAAgBP,EAChBQ,UACEC,WAAY,MACZC,UAAW,MACXC,MAAS,MACTT,OAAU,OACVU,MAAS,OACTC,UAAW,OACXC,WAAY,QAEdC,YACEC,OAAQ,MACRC,KAAM,OAERC,aAAclB,EACdmB,YACEC,KAAM,2BAERC,WACED,KAAM,+BA4KVtN,GAAOkJ,mBAAqBA,EAC5BlJ,EAAO+J,mBAAqBA,GAM3BlK,GClTH,SAAUG,GAER,GAAIwN,KAEJxN,GAAOoB,aAAe,SAASqM,EAASC,EAAMC,EAAQC,GAKpD,GAAIC,GAAUD,EAAS,MAAQ,KAC3BE,EAAQ,GAAIC,MACZC,EAAS,GAAID,MAAKL,EAGtB,OAFAM,GAAOC,SAASD,EAAOE,WAAa,KAEhCJ,EAAQE,IACJP,IAAWD,IACfW,QAAQC,KAAK,mBAAqBX,EAAU,IAAMI,EAAU,wCAA0CG,EAAOK,eAAiB,KAAOV,GAEvIH,EAASC,IAAAA,EAAW,KAOxBzN,EAAOsO,WAAa,SAASb,EAASC,EAAMC,EAAQC,GAClD,GAAIC,GAAUD,EAAS,MAAQ,IAC/B,IAAI5N,EAAOoB,aAAaqM,EAASC,EAAMC,EAAQC,GAC7C,KAAM,IAAIW,OAAMd,EAAU,IAAMI,EAAU,yBAA2BF,KAIxE9N,0stBChCH,WAEE,OAAA,KAAI8D,SAASC,cAAc,OAAO4K,YAAYC,SAA9C,CAKE,GAAIC,EACC,IAAIrE,OAAOsE,aAAeA,YAAYD,IAC3C,GAAIA,GAAM,WAAa,MAAOC,aAAYD,WAE1C,IAAIA,GAAM,WAAa,MAAOX,MAAKW,MAGrC,IAAIE,GAAuB,SAASC,EAAQC,EAAaC,GACvDxO,KAAKsO,OAASA,EACdtO,KAAKuO,YAAcA,EACnBvO,KAAKwO,aAAeA,EAEpBxO,KAAKqK,KAAO,SACZrK,KAAKyO,SAAAA,EACLzO,KAAK0O,YAAAA,EACL1O,KAAK2O,cAAgBL,EACrBtO,KAAK4O,kBAAAA,EACL5O,KAAK6O,WAAaC,MAAMC,UACxB/O,KAAKgP,UAAYxB,KAAKW,OAGpBc,EAAyBnF,OAAOoF,QAAQrI,UAAUoH,OACtDnE,QAAOoF,QAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GACvD,GAAIC,GAAYH,EAAuBI,KAAKrP,KAAM4I,EAAauG,EAE/DC,GAAUE,mBACVF,EAAUlB,SAAW,IAErB,IAAIqB,GAAiBH,EAAUI,MAC/BJ,GAAUI,OAAS,WACjBD,EAAeF,KAAKrP,KACpB,IAAIyP,GAAQ,GAAIpB,GAAqBrO,KAAM,KAAMmO,KAC7CuB,EAAW1P,KAAKsP,gBAAgBK,OAAO3P,KAAKkO,UAAYlO,KAAKkO,aACjE0B,YAAW,WACTF,EAASnO,QAAQ,SAASsO,GACxBA,EAAQR,KAAKI,EAAMnB,OAAQmB,MAE5B,GAGL,IAAIK,GAA2BV,EAAUW,gBACzCX,GAAUW,iBAAmB,SAAS1F,EAAMwF,GACpB,kBAAXA,IAAiC,UAARxF,EAClCrK,KAAKsP,gBAAgBhG,KAAKuG,GAE1BC,EAAyBT,KAAKrP,KAAMqK,EAAMwF,GAG9C,IAAIG,GAA8BZ,EAAUa,mBAW5C,OAVAb,GAAUa,oBAAsB,SAAS5F,EAAMwF,GAC7C,GAAY,UAARxF,EAAkB,CACpB,GAAI6F,GAAQlQ,KAAKsP,gBAAgB5N,QAAQmO,EACrCK,IAAS,GACXlQ,KAAKsP,gBAAgBa,OAAOD,EAAO,OAErCF,GAA4BX,KAAKrP,KAAMqK,EAAMwF,IAI1CT,OClEX,SAAU3P,GAgBR,GAAI2Q,GAAUhN,SAASiN,gBACnBjB,EAAY,KACZkB,GAAAA,CACJ,KACE,GAAIC,GAAkBC,iBAAiBJ,GAASK,iBAAiB,WAC7DC,EAAiC,KAAnBH,EAAyB,IAAM,GACjDnB,GAAYgB,EAAQnC,SAAS0C,SAAYD,EAAaA,KACjDvP,SAAU,IACfiO,EAAUb,YAAc,EACxB+B,EAAWE,iBAAiBJ,GAASK,iBAAiB,YAAcC,EACpE,MAAOE,IACP,QACIxB,GACFA,EAAUI,SAEd,IAAIc,EAAJ,CAIA,GAAIrB,GAAyBnF,OAAOoF,QAAQrI,UAAUoH,OACtDnE,QAAOoF,QAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GAUvD,MATIrF,QAAOC,QAAUA,OAAOC,UAAYjB,MAAMlC,UAAUoD,MAAQrB,EAAYmB,OAAOC,YAEjFpB,EAAcG,MAAMkB,KAAKrB,IAGtBG,MAAMC,QAAQJ,IAAgC,OAAhBA,IACjCA,EAAcnJ,EAAOkJ,mBAAmBC,IAGnCqG,EAAuBI,KAAKrP,KAAM4I,EAAauG,MAEvD7P,GC/CH,SAAUG,EAAQoR,EAAOnR,GA8DvB,QAASoR,GAAsBC,GAC7B,GAAIC,GAAWH,EAAMG,QACrBA,GAASzC,YAAcwC,EACvBC,EAASC,qBAC0B,GAA/BD,EAASE,YAAY/H,OACvBgI,GAAAA,EAEAC,sBAAsBN,GApE1B,GAAIO,GAAgCvH,OAAOsH,qBAC3CtH,QAAOsH,sBAAwB,SAAShP,GACtC,MAAOiP,GAA8B,SAASlP,GAC5C0O,EAAMG,SAASM,4BACflP,EAAED,GACF0O,EAAMG,SAASM,+BAInBT,EAAMU,kBAAoB,WACxBvR,KAAKkR,eACLlR,KAAKuO,gBAAAA,IAGPsC,EAAMU,kBAAkB1K,WACtB2K,cAAe,WAEb,MADAxR,MAAKiR,qBACEjR,KAAKkR,YAAYnN,SAE1BuN,0BAA2B,WACzBT,EAAMY,uBAAyBZ,EAAMY,uBAAuB9G,OAAO,SAASyE,GAC1E,MAAOA,GAAUsC,qBAGrBT,mBAAoB,WAClBjR,KAAKsR,4BACLtR,KAAKkR,YAAclR,KAAKkR,YAAYvG,OAAO,SAASyE,GAClD,MAA8B,YAAvBA,EAAUuC,WAAkD,QAAvBvC,EAAUuC,aAG1DC,MAAO,SAAS5Q,GACd,GAAIoO,GAAY,GAAIyB,GAAMgB,UAAU7Q,EAAQhB,KAW5C,OAVAA,MAAKkR,YAAY5H,KAAK8F,GACtByB,EAAMiB,+BAMN1C,EAAUsC,kBACVtC,EAAUhI,WAAW2K,OACrB3C,EAAUsC,kBACHtC,GAET2C,KAAM,SAAS/Q,GAIb,MAHIA,IACFA,EAAOgR,SAEFhS,KAAK4R,MAAM5Q,IAItB,IAAImQ,IAAAA,CAEJN,GAAMiB,6BAA+B,WAC9BX,IACHA,GAAAA,EACAC,sBAAsBN,IAc1B,IAAIE,GAAW,GAAIH,GAAMU,iBACzBV,GAAMG,SAAWA,CAEjB,KACE3P,OAAO4Q,eAAenI,OAAO1G,SAAU,YACrC8O,cAAAA,EACAC,IAAK,WAAa,MAAOnB,MAE3B,MAAOoB,IACT,IACEtI,OAAO1G,SAAS4N,SAAWA,EAC3B,MAAOoB,MAER9S,EAAqBE,GCtFxB,SAAUC,EAAQoR,EAAOnR,GACvBmR,EAAMY,0BAENZ,EAAMgB,UAAY,SAAS7Q,EAAQgQ,GASjC,GARAhR,KAAKqS,GAAK,GACNrR,GAAUA,EAAOsR,MACnBtS,KAAKqS,GAAKrR,EAAOsR,KAEnBtS,KAAKgB,OAASA,EACVA,IACFA,EAAOoG,WAAapH,OAEjBgR,EACH,KAAM,IAAIhD,OAAM,gDAElBhO,MAAKuS,UAAYvB,EACjBhR,KAAKwS,gBAAkB/S,EAAOgT,iBAC9BzS,KAAK0S,UAAY,EACjB1S,KAAK2S,SAAAA,EACL3S,KAAK4S,UAAAA,EACL5S,KAAKoH,WAAa,KAClBpH,KAAK6S,oBACL7S,KAAK8S,UAAY,KACjB9S,KAAK+S,cAAgB,OACrB/S,KAAKqH,8BAELrH,KAAKoH,WAAWoI,SAChBxP,KAAK0R,mBAGPb,EAAMgB,UAAUhL,WACd6K,gBAAiB,WACf,GAAIsB,GAAehT,KAAK+S,cACpBE,EAAejT,KAAK2R,SAsBxB,OArBI3R,MAAKkT,eAAiBD,IAAiBD,IACrB,QAAhBC,GACFjT,KAAKmT,sBACLnT,KAAKkT,kBAAAA,IACoB,WAAhBF,EACThT,KAAKoT,uBACoB,WAAhBH,IACTjT,KAAKkT,kBAAAA,KAGLlT,KAAKqT,kBAAoBJ,IAAiBD,IACxB,QAAhBC,GACFjT,KAAKsT,yBACLtT,KAAKqT,qBAAAA,IACoB,YAAhBJ,EACTjT,KAAKuT,0BACoB,YAAhBP,IACThT,KAAKqT,qBAAAA,KAGTrT,KAAK+S,cAAgB/S,KAAK2R,UAClB3R,KAAKkT,eAAiBlT,KAAKqT,kBAErChM,4BAA6B,WAC3BrH,KAAK0R,iBACL,IAAI8B,GACAC,EACAC,EACAC,EACAC,IAAgB5T,KAAKoH,UACrBwM,KACFJ,EAAkBxT,KAAK2E,aACvB8O,EAAYzT,KAAK2S,QACjBe,EAAe1T,KAAK6T,UACpBF,EAAiB3T,KAAKuO,YACtBvO,KAAKoH,WAAWoI,SAChBxP,KAAKoH,WAAW0M,SAAW,KAC3B9T,KAAKoH,WAAa,QAGfpH,KAAKgB,QAAUhB,KAAKgB,iBAAkB8I,QAAOiK,kBAChD/T,KAAKoH,WAAayJ,EAAMmD,wCAAwChU,KAAKgB,QACrE6P,EAAMoD,+BAA+BjU,QAEnCA,KAAKgB,iBAAkB8I,QAAOoK,gBAAkBlU,KAAKgB,iBAAkB8I,QAAOqK,eAChFnU,KAAKoH,WAAayJ,EAAMuD,+BAA+BpU,KAAKgB,QAC5D6P,EAAMwD,sBAAsBrU,OAE1BA,KAAKgB,QAAUhB,KAAKgB,OAAOsT,WAC7BzD,EAAM0D,6BAA6BvU,MAEjC4T,IACqB,GAAnBJ,IACFxT,KAAK2E,aAAe6O,GAED,OAAjBE,EACF1T,KAAK6T,UAAYH,EACW,OAAnBC,EACT3T,KAAKuO,YAAcoF,EACS,OAAnB3T,KAAK0S,YACd1S,KAAKuO,YAAcvO,KAAK0S,WAEtBe,GACFzT,KAAKwU,SAGTxU,KAAK0R,mBAEP+C,gBAAiB,WACf,GAAKzU,KAAKgB,QAA4B,QAAlBhB,KAAK2R,UAAzB,CAGA,GAAIvI,GAASpJ,KAAKgB,OAAOmG,QAAQjC,KACjClF,MAAK6S,iBAAiBtR,QAAQ,SAASmT,GACrC1U,KAAK2U,iBAAiBD,EAAgBtL,GAClCpJ,KAAKgB,iBAAkB8I,QAAOoK,iBAChC9K,GAAUyH,EAAM+D,mBAAmBF,EAAe1T,UACpD6T,KAAK7U,SAET8U,sBAAuB,SAAS1F,GAC9B,GAAKpP,KAAKgB,QAAWhB,KAAK4S,SAE1B,IAAK,GAAIpK,GAAI,EAAGA,EAAIxI,KAAKgB,OAAO+T,SAAS5L,OAAQX,IAC/CxI,KAAKgB,OAAO+T,SAASvM,GAAGpB,WAAagI,EACrCpP,KAAK6S,iBAAiBrK,GAAGsM,sBAAsB1F,IAGnD4F,0BAA2B,WACzB,GAAKhV,KAAKgB,QAAWhB,KAAK4S,SAA1B,CAEA,GAAIxJ,GAASpJ,KAAKgB,OAAOmG,QAAQjC,KACjClF,MAAKiV,yBACLjV,KAAKgB,OAAO+T,SAASxT,QAAQ,SAAS2T,GACpC,GAAIR,GAAiB7D,EAAMG,SAASY,MAAMsD,EAC1ClV,MAAK6S,iBAAiBvJ,KAAKoL,GAC3BA,EAAe/P,aAAe3E,KAAK2E,aAC/B3E,KAAK2S,SACP+B,EAAeF,QACjBU,EAAM9N,WAAapH,KAAKgB,OAAOoG,WAE/BpH,KAAK2U,iBAAiBD,EAAgBtL,GAElCpJ,KAAKgB,iBAAkB8I,QAAOoK,iBAChC9K,GAAUyH,EAAM+D,mBAAmBM,KACrCL,KAAK7U,SAET2U,iBAAkB,SAASD,EAAgBtL,GAClB,OAAnBpJ,KAAK6T,UACPa,EAAenG,YAAcvO,KAAKuO,YAAcnF,EAASpJ,KAAK2E,aACrD+P,EAAeb,YAAc7T,KAAK6T,UAAYzK,EAASpJ,KAAK2E,eACrE+P,EAAeb,UAAY7T,KAAK6T,UAAYzK,EAASpJ,KAAK2E,eAG9DqM,eACE,MAAOhR,MAAKuS,WAEdZ,gBACE,MAAO3R,MAAKoH,WAAapH,KAAKoH,WAAWuK,UAAY,QAEvDwD,eACE,MAAKrL,QAAOsL,SAIPpV,KAAKqT,oBAC2C,GAA/CxC,EAAMY,uBAAuB/P,QAAQ1B,OACvC6Q,EAAMY,uBAAuBnI,KAAKtJ,MAEpCA,KAAKqT,iBAAmB,GAAI+B,SACxB,SAASC,EAASC,GAChBtV,KAAKuT,wBAA0B,WAC7B8B,EAAQrV,OAEVA,KAAKsT,uBAAyB,WAC5BgC,GAAQjL,KAAMC,aAAaiL,UAAW/K,KAAM,iBAE9CqK,KAAK7U,OACW,YAAlBA,KAAK2R,WACP3R,KAAKuT,2BAGFvT,KAAKqT,mBApBVzF,QAAQC,KAAK,6DACN,OAqBX2H,YACE,MAAK1L,QAAOsL,SAIPpV,KAAKkT,iBAC2C,GAA/CrC,EAAMY,uBAAuB/P,QAAQ1B,OACvC6Q,EAAMY,uBAAuBnI,KAAKtJ,MAEpCA,KAAKkT,cAAgB,GAAIkC,SACrB,SAASC,EAASC,GAChBtV,KAAKoT,qBAAuB,WAC1BiC,EAAQrV,OAEVA,KAAKmT,oBAAsB,WACzBmC,GAAQjL,KAAMC,aAAaiL,UAAW/K,KAAM,iBAE9CqK,KAAK7U,OACY,YAAnBA,KAAK2R,WACP3R,KAAKoT,wBAGFpT,KAAKkT,gBApBVtF,QAAQC,KAAK,6DACN,OAqBX4H,eACE,MAAOzV,MAAKoH,WAAWqO,UAEzBA,aAAaC,GAET1V,KAAKoH,WAAWqO,SADF,kBAALC,GACkB,SAAUtD,GACnCA,EAAE9D,OAAStO,KACX0V,EAAErG,KAAKrP,KAAMoS,IACZyC,KAAK7U,MAEmB0V,GAG/BxH,eACE,MAAOlO,MAAKoH,WAAW8G,UAEzBA,aAAawH,GAET1V,KAAKoH,WAAW8G,SADF,kBAALwH,GACkB,SAAUtD,GACnCA,EAAE9D,OAAStO,KACX0V,EAAErG,KAAKrP,KAAMoS,IACZyC,KAAK7U,MAEmB0V,GAG/BnH,kBACEvO,KAAK0R,iBACL,IAAInD,GAAcvO,KAAKoH,WAAWmH,WAElC,OADAvO,MAAK0R,kBACEnD,GAETA,gBAAgBmH,GACd1V,KAAK0R,kBACL1R,KAAKoH,WAAWmH,YAAcnE,SAASsL,GAAKA,EAAI/S,KAAKgT,KAAKD,GAAKzR,OAAO2R,UACtE5V,KAAK6V,YACL7V,KAAK8V,cAAc,SAASZ,EAAO9L,GACjC8L,EAAM3G,YAAcmH,EAAItM,IAE1BpJ,KAAK0R,mBAEPmC,gBACE,MAAO7T,MAAKoH,WAAWyM,WAEzBA,cAAc6B,GACZ1V,KAAK0R,kBACL1R,KAAKoH,WAAWyM,UAAYzJ,SAASsL,GAAKA,EAAI/S,KAAKgT,KAAKD,GAAKzR,OAAO2R,UACpE5V,KAAK6V,YACL7V,KAAK8V,cAAc,SAASZ,EAAO9L,GACjC8L,EAAMrB,UAAY6B,EAAItM,IAExBpJ,KAAK0R,mBAEP/M,mBACE,MAAO3E,MAAKoH,WAAWzC,cAEzBA,iBAAiBqC,GACfhH,KAAK0R,iBACL,IAAIiC,GAAiB3T,KAAKuO,WAC1BvO,MAAKoH,WAAWzC,aAAeqC,EAC/BhH,KAAK8V,cAAc,SAASpB,GAC1BA,EAAe/P,aAAeqC,IAET,OAAnB2M,IACF3T,KAAKuO,YAAcoF,GAErB3T,KAAK0R,mBAEPK,KAAM,WACJ/R,KAAK0R,kBACL1R,KAAK2S,SAAAA,EACL3S,KAAKoH,WAAW2K,QACiC,GAA7C/R,KAAKuS,UAAUrB,YAAYxP,QAAQ1B,OACrCA,KAAKuS,UAAUrB,YAAY5H,KAAKtJ,MAElCA,KAAK6V,YACLhF,EAAMkF,eAAe/V,MACrBA,KAAK8V,cAAc,SAASZ,GAC1B,GAAIc,GAAOd,EAAM3G,WACjB2G,GAAMnD,OACNmD,EAAM3G,YAAcyH,IAEtBhW,KAAK0R,mBAEP8C,MAAO,WACLxU,KAAK0R,kBACD1R,KAAKuO,cACPvO,KAAK0S,UAAY1S,KAAKuO,aAExBvO,KAAKoH,WAAWoN,QAChBxU,KAAK6V,YACL7V,KAAK8V,cAAc,SAASZ,GAC1BA,EAAMV,UAERxU,KAAK2S,SAAAA,EACL3S,KAAK0R,mBAEPuE,OAAQ,WACNjW,KAAK0R,kBACL1R,KAAKoH,WAAW6O,SAChBjW,KAAK6V,YACL7V,KAAK0R,mBAEPlC,OAAQ,WACNxP,KAAK0R,kBACL1R,KAAKoH,WAAWoI,SAChBxP,KAAK6V,YACL7V,KAAKiV,yBACLjV,KAAK0R,mBAEPwE,QAAS,WACPlW,KAAK0R,iBACL,IAAIiC,GAAiB3T,KAAKuO,WAC1BvO,MAAKoH,WAAW8O,UAChBlW,KAAK8V,cAAc,SAASpB,GAC1BA,EAAewB,YAEM,OAAnBvC,IACF3T,KAAKuO,YAAcoF,GAErB3T,KAAK0R,mBAEP3B,iBAAkB,SAAS1F,EAAMwF,GAC/B,GAAIsG,GAAUtG,CACQ,mBAAXA,KACTsG,EAAU,SAAU/D,GAClBA,EAAE9D,OAAStO,KACX6P,EAAQR,KAAKrP,KAAMoS,IAClByC,KAAK7U,MACR6P,EAAQiE,SAAWqC,GAErBnW,KAAKoH,WAAW2I,iBAAiB1F,EAAM8L,IAEzClG,oBAAqB,SAAS5F,EAAMwF,GAClC7P,KAAKoH,WAAW6I,oBAAoB5F,EAAOwF,GAAWA,EAAQiE,UAAajE,IAE7EoF,uBAAwB,WACtB,KAAOjV,KAAK6S,iBAAiB1J,QAC3BnJ,KAAK6S,iBAAiBuD,MAAM5G,UAEhCsG,cAAe,SAAS1T,GACtB,GAAIgH,GAAS,CASb,IARIpJ,KAAKgB,OAAO+T,UAAY/U,KAAK6S,iBAAiB1J,OAASnJ,KAAKgB,OAAO+T,SAAS5L,QAC9EnJ,KAAKgV,4BACPhV,KAAK6S,iBAAiBtR,QAAQ,SAAS2T,GACrC9S,EAAEiN,KAAKrP,KAAMkV,EAAO9L,GAChBpJ,KAAKgB,iBAAkB8I,QAAOoK,iBAChC9K,GAAU8L,EAAMlU,OAAO8D,iBACzB+P,KAAK7U,OAEe,WAAlBA,KAAK2R,UAAT,CAEA,GAAI1Q,GAASjB,KAAKgB,OAAOmG,QACrB4J,EAAI/Q,KAAKuO,WACH,QAANwC,IACFA,EAAItR,EAAOgH,2BAA2BhH,EAAOgF,wBAAwBxD,GAAS8P,EAAG9P,KAC1E,MAAL8P,GAAa3P,MAAM2P,KACrB/Q,KAAKiV,4BAIXnL,OAAO+H,UAAYhB,EAAMgB,WAMxBvS,EAAqBE,GChXvB,SAASC,EAAQoR,EAAOnR,GAqCvB,QAAS2W,GAAazN,GACpB5I,KAAKsW,QAAU7W,EAAO+J,mBAAmBZ,GAoG3C,QAAS2N,KAEP,IADA,GAAIC,IAAAA,EACGC,EAActN,QACPsN,EAAcC,QACpBjC,kBACN+B,GAAAA,CAEF,OAAOA,GA/IT,GAAIG,GAAe,SAAS3V,GAE1B,GADAA,EAAOoG,eAAAA,GACHpG,YAAkB8I,QAAOoK,gBAAkBlT,YAAkB8I,QAAOqK,YACtE,IAAK,GAAI3L,GAAI,EAAGA,EAAIxH,EAAO+T,SAAS5L,OAAQX,IAC1CmO,EAAa3V,EAAO+T,SAASvM,IAKnCqI,GAAM+F,YAAc,SAASC,GAE3B,IAAK,GADDC,MACKtO,EAAI,EAAGA,EAAIqO,EAAQ1N,OAAQX,IAAK,CACvC,GAAIxH,GAAS6V,EAAQrO,EACjBxH,GAAO+V,UACkC,GAAvCD,EAAWpV,QAAQV,EAAO+V,UAC5BD,EAAWxN,KAAKtI,EAAO+V,SAEzB/V,EAAO+V,QAAQhC,SAAS5E,OAAOnP,EAAO+V,QAAQhC,SAASrT,QAAQV,GAAS,GACxEA,EAAO+V,QAAU,KACjBJ,EAAa3V,IACJA,EAAOoG,YAAepG,EAAOoG,WAAWpG,QAAUA,IAC3DA,EAAOoG,WAAWoI,SAClBxO,EAAOoG,WAAWpG,OAAS,GAAI+S,gBAAe,SAC1C/S,EAAOoG,WAAW0L,YACpB9R,EAAOoG,WAAW0L,UAAU1L,WAAa,MAE3CpG,EAAOoG,WAAWC,8BAClBsP,EAAa3V,IAGjB,IAAKwH,EAAI,EAAGA,EAAIsO,EAAW3N,OAAQX,IACjCsO,EAAWtO,GAAGwO,YAQlBnG,EAAMkD,eAAiB,SAASzF,EAAQ1F,EAAahJ,EAAayS,GAmBhE,MAlBArS,MAAKsO,OAASA,EACdtO,KAAK+W,QAAU,KAEfnX,EAAcH,EAAOmC,sBAAsBhC,GAC3CI,KAAKkH,aAAezH,EAAOE,iBAAiBC,GAC5CI,KAAKmH,QAAU1H,EAAOoC,qBAAqBjC,GAE3CI,KAAKiB,OAASxB,EAAOqB,WAAWlB,GAAAA,EAAoBI,MACpDA,KAAKiB,OAAOgG,QAAUjH,KACI,kBAAf4I,IACTnJ,EAAOsO,WAAW,wBAAyB,aAAc,wCACzD/N,KAAKiX,qBAAuBrO,GAE5B5I,KAAKiX,qBAAuB,GAAIZ,GAAazN,GAE/C5I,KAAKkX,WAAatO,EAClB5I,KAAK8E,eAAiBrF,EAAOgF,wBAAwBzE,KAAKmH,SAC1DnH,KAAKsS,IAAMD,EACJrS,MAGT6Q,EAAMkD,eAAelN,WACnBsQ,UAAW,WACT,MAAwC,kBAA7BnX,MAAKiX,qBACPjX,KAAKiX,qBACPjX,KAAKiX,qBAAqBX,SAEnCc,aAAaC,GACX,GAA+B,kBAApBrX,MAAKmX,YACd,KAAM,IAAInJ,OAAM,qEAElBhO,MAAKsU,UAAY+C,EACbrX,KAAKoH,YACPpH,KAAKoH,WAAWC,+BAGpBiQ,aACE,MAAOtX,MAAK+W,SAEdlX,MAAO,WACL,GAA+B,kBAApBG,MAAKmX,YACd,KAAM,IAAInJ,OAAM,2CAElB,IAAInO,GAAQ,GAAIkU,gBAAe/T,KAAKsO,UAAY7O,EAAOE,iBAAiBK,KAAKkH,cAAelH,KAAKsS,IAGjG,OAFAzS,GAAMoX,qBAAuBjX,KAAKiX,qBAClCpX,EAAMqX,WAAalX,KAAKkX,WACjBrX,GAETmS,OAAQ,WACNnB,EAAM+F,aAAa5W,QAIvB,IAAIiP,GAAyBC,QAAQrI,UAAUoH,OAC/CiB,SAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GAChD,GAAIkD,GAAK,EAIT,OAHIlD,IAAWA,EAAQkD,KACrBA,EAAKlD,EAAQkD,IAERxB,EAAMG,SAASY,MAAM,GAAIf,GAAMkD,eAAe/T,KAAM4I,EAAauG,EAASkD,IAGnF,IAAIkF,GAAanU,SAASsI,gBAAgB,+BAAgC,MAC1EmF,GAAMmD,wCAA0C,SAASwD,GACvD,GAAIA,EAAgB,CAClB,GAAIlJ,GAASkJ,EAAelJ,QAAUiJ,EAClC7N,EAAY8N,EAAeN,UACP,mBAAbxN,KACTA,KAEF,IAAIyF,GAAUqI,EAAetQ,YAC7BiI,GAAQkD,GAAKmF,EAAelF,QAE5B,IAAIhE,GAASiJ,EACT7N,KACAyF,EAAU,CAEhB,OAAOF,GAAuBnL,MAAMwK,GAAS5E,EAAWyF,KAI1D0B,EAAMoD,+BAAiC,SAAS7E,GAC1CA,EAAUpO,QAA0D,kBAAzCoO,GAAUpO,OAAOiW,sBAC9CpG,EAAM0D,6BAA6BnF,GAIvC,IAAIqH,KACJ5F,GAAMkF,eAAiB,SAAS0B,GACG,OAA7BA,EAAe5D,WAAuB4D,EAAe7E,WAE7B,GAAxB6D,EAActN,QAChBiI,sBAAsBmF,GAExBE,EAAcnN,KAAKmO,IAWrB,IAAIC,GAA2B5N,OAAO0G,gBACtCnP,QAAO4Q,eAAenI,OAAQ,oBAC5BoI,cAAAA,EACAyF,YAAAA,EACA3Q,MAAO,WACL6J,EAAMG,SAASM,2BACf,IAAIlJ,GAASsP,EAAyB5T,MAAM9D,KAAM4X,UAIlD,OAHIrB,OACFnO,EAASsP,EAAyB5T,MAAM9D,KAAM4X,YAChD/G,EAAMG,SAASM,4BACRlJ,KAIX0B,OAAOiK,eAAiBlD,EAAMkD,eAC9BjK,OAAOoF,QAAQrI,UAAU2K,cAAgB,WACvC,MAAOpO,UAAS4N,SAASQ,gBAAgB7G,OAAO,SAASyE,GACvD,MAA4B,QAArBA,EAAUpO,QAAmBoO,EAAUpO,OAAOsN,QAAUtO,MAC/D6U,KAAK7U,SAGTV,EAAqBE,GCzKvB,SAAUC,EAAQoR,EAAOnR,GA6CvB,QAASmY,GAASR,GACZA,EAASS,cAEbT,EAASS,aAAAA,EACTC,EAAUzO,KAAK+N,GACVlG,IACHA,GAAAA,EACAC,sBAAsB4G,KAI1B,QAASA,GAAKjH,GACZ,GAAIkH,GAAWF,CACfA,MACAE,EAAS1O,KAAK,SAAS2O,EAAMC,GAC3B,MAAOD,GAAK1F,gBAAkB2F,EAAM3F,kBAEtCyF,EAAWA,EAAStN,OAAO,SAAS0M,GAClCA,GACA,IAAI1F,GAAY0F,EAASjQ,WAAaiQ,EAASjQ,WAAWuK,UAAY,MAGtE,OAFiB,WAAbA,GAAuC,WAAbA,IAC5B0F,EAASS,aAAAA,GACJT,EAASS,cAElBC,EAAUzO,KAAKxF,MAAMiU,EAAWE,GAE5BF,EAAU5O,QACZgI,GAAAA,EACAC,sBAAsB4G,IAEtB7G,GAAAA,EAzEJ,GAEIsB,IAFarP,SAASsI,gBAAgB,+BAAgC,OAErD,EACrBmF,GAAM0D,6BAA+B,SAASnF,GAC5C,GACIgJ,GADA9J,EAASc,EAAUpO,OAAOsN,OAE1B+J,EAA0D,kBAAhCjJ,GAAUpO,OAAOmW,WAE7CiB,GADEC,EACejJ,EAAUpO,OAAOmW,YAEjB/H,EAAUpO,OAAOsT,SAEpC,IAAIrT,GAASmO,EAAUpO,OAAOC,OAC1BqX,EAAO,IACXrX,GAASxB,EAAOoC,qBAAqBZ,EACrC,IAAIoW,GAAW,WACb,GAAItG,GAAIsG,EAASjQ,WAAaiQ,EAASjQ,WAAWmH,YAAc,IACtD,QAANwC,IACFA,EAAItR,EAAOgH,2BAA2BhH,EAAOgF,wBAAwBxD,GAAS8P,EAAG9P,GAC7EG,MAAM2P,KACRA,EAAI,OAIJA,IAAMuH,IACJD,EACFD,EAAerH,EAAGzC,EAAQc,EAAUpO,QAEpCoX,EAAerH,EAAG3B,EAAUpO,OAAQoO,EAAUpO,OAAOoG,aAGzDkR,EAAOvH,EAGTsG,GAASjQ,WAAagI,EACtBiI,EAASS,aAAAA,EACTT,EAAS7E,gBAAkBC,IAC3BrD,EAAU0D,UAAYuE,EACtBQ,EAASR,GAGX,IAAIU,MACA5G,GAAAA,CAmCJN,GAAMgB,UAAUhL,UAAUgP,UAAY,WAChC7V,KAAK8S,WACP+E,EAAS7X,KAAK8S,aAGjBxT,EAAqBE,GCnFxB,SAAUC,EAAQoR,EAAOnR,GAEvB,QAASkV,GAAmB2D,GAC1B,MAAOA,GAAKpR,QAAQjC,MAAQqT,EAAKzT,eAAiByT,EAAKpR,QAAQhC,SAGjE,QAASqT,GAAYzD,EAAUnV,EAAayS,GAC1CrS,KAAKsS,IAAMD,EACXrS,KAAK+W,QAAU,KACf/W,KAAK+U,SAAWA,MAChB/U,KAAKyY,UAAUzY,KAAK+U,UACpBnV,EAAcH,EAAOmC,sBAAsBhC,GAC3CI,KAAKkH,aAAezH,EAAOE,iBAAiBC,GAC5CI,KAAKmH,QAAU1H,EAAOoC,qBAAqBjC,GAAAA,GAC3CI,KAAKiB,OAASxB,EAAOqB,WAAWlB,GAAAA,EAAmBI,MACnDA,KAAKiB,OAAOgG,QAAUjH,KAEQ,SAA1BA,KAAKmH,QAAQhG,WACfnB,KAAKmH,QAAQhG,SAAWnB,KAAK8E,gBAIjCgF,OAAOoK,eAAiB,WACtBsE,EAAY1U,MAAM9D,KAAM4X,YAG1B9N,OAAOqK,YAAc,WACnBqE,EAAY1U,MAAM9D,KAAM4X,YAG1BY,EAAY3R,WACV6R,YAAa,SAAS1X,GAEpB,IADA,GAAIe,GAAI/B,KACK,OAAN+B,GAAY,CACjB,GAAIA,GAAKf,EACP,OAAA,CACFe,GAAIA,EAAEgV,QAER,OAAA,GAEFC,SAAU,WAGR,IADA,GAAIuB,GAAOvY,KACJuY,GACwB,SAAzBA,EAAKtX,OAAOE,WACdoX,EAAKpR,QAAQhG,SAAWoX,EAAKzT,gBAE/ByT,EAAOA,EAAKxB,OAEV/W,MAAKoH,YACPpH,KAAKoH,WAAWC,+BAGpBoR,UAAW,SAASE,GAClB9H,EAAM+F,YAAY+B,EAClB,KAAK,GAAInQ,GAAI,EAAGA,EAAImQ,EAAYxP,OAAQX,IACtCmQ,EAAYnQ,GAAGuO,QAAU/W,MAG7B4Y,UAAW,SAASC,EAAMC,GAExB,IAAK,GADDrO,GAAUqO,EAAW,oCAAsC,qCACtDtQ,EAAI,EAAGA,EAAIqQ,EAAK1P,OAAQX,IAC/B,GAAIxI,KAAK0Y,YAAYG,EAAKrQ,IACxB,MACE6B,KAAMC,aAAayO,sBACnBvO,KAAM,wBACNC,QAASA,EAKf,KAAK,GAAIjC,GAAI,EAAGA,EAAIqQ,EAAK1P,OAAQX,IAC/BsQ,EAAW9Y,KAAK+U,SAASzL,KAAKuP,EAAKrQ,IAAMxI,KAAK+U,SAASiE,QAAQH,EAAKrQ,GAEtExI,MAAKyY,UAAUI,GACf7Y,KAAKgX,YAEPiC,OAAQ,WACNjZ,KAAK4Y,UAAUhB,WAAAA,IAEjBsB,QAAS,WACPlZ,KAAK4Y,UAAUhB,WAAAA,IAEjBN,aACE,MAAOtX,MAAK+W,SAEdoC,iBACE,MAAOnZ,MAAK+U,SAAS5L,OAASnJ,KAAK+U,SAAS,GAAK,MAEnDqE,gBACE,MAAOpZ,MAAK+U,SAAS5L,OAASnJ,KAAK+U,SAAS/U,KAAK+U,SAAS5L,OAAS,GAAK,MAE1EtJ,MAAO,WAGL,IAAK,GAFDwZ,GAAe5Z,EAAOE,iBAAiBK,KAAKkH,cAC5CoS,KACK9Q,EAAI,EAAGA,EAAIxI,KAAK+U,SAAS5L,OAAQX,IACxC8Q,EAAehQ,KAAKtJ,KAAK+U,SAASvM,GAAG3I,QAEvC,OAAQG,gBAAgBmU,aACpB,GAAIA,aAAYmF,EAAgBD,GAChC,GAAInF,gBAAeoF,EAAgBD,IAEzCrH,OAAQ,WACNnB,EAAM+F,aAAa5W,SAIvB8J,OAAOoK,eAAerN,UAAYxF,OAAOkY,OAAOf,EAAY3R,WAC5DxF,OAAO4Q,eACHnI,OAAOoK,eAAerN,UACtB,kBAEEsL,IAAK,WACH,GAAIqH,GAAQ,CAIZ,OAHAxZ,MAAK+U,SAASxT,QAAQ,SAAS2T,GAC7BsE,GAAS5E,EAAmBM,KAEvBvS,KAAK8W,IAAID,EAAO,MAI/B1P,OAAOqK,YAAYtN,UAAYxF,OAAOkY,OAAOf,EAAY3R,WACzDxF,OAAO4Q,eACHnI,OAAOqK,YAAYtN,UACnB,kBAEEsL,IAAK,WACH,GAAIsH,GAAM,CAIV,OAHAzZ,MAAK+U,SAASxT,QAAQ,SAAS2T,GAC7BuE,EAAM9W,KAAK8W,IAAIA,EAAK7E,EAAmBM,MAElCuE,KAIf5I,EAAMuD,+BAAiC,SAASsF,GAC9C,GAAIC,GACA1Y,EAAS,KACT2Y,EAAS,SAASC,GACpB,GAAIzK,GAAYuK,EAAoB7F,QACpC,IAAK1E,GAGsB,WAAvBA,EAAUuC,WAGTvC,EAAUpO,OAGf,MAAU,OAAN6Y,MACFzK,GAAU6F,yBAQF,GAAN4E,GAAWzK,EAAUzK,aAAe,IACjC1D,IACHA,EAASxB,EAAOoC,qBAAqBuN,EAAUpO,OAAOC,SAExD4Y,EAAKpa,EAAOgH,2BAA2BhH,EAAOgF,wBAAwBxD,IAAU,EAAGA,GAC/EG,MAAMyY,IAAa,MAANA,IACfzK,EAAU0G,cAAc,SAASZ,GAC/BA,EAAM3G,aAAe,QAEvBa,GAAU6F,8BAAAA,IAMZ6E,EAAmB,GAAI/F,gBAAe,QAAU2F,EAAMvS,QAASuS,EAAMpH,IAGzE,OAFAwH,GAAiB1C,SAAWwC,EAC5BD,EAAsB9I,EAAMG,SAASY,MAAMkI,IAI7CjJ,EAAMwD,sBAAwB,SAASjF,GACrCA,EAAUhI,WAAW0M,SAAW1E,EAChCA,EAAUwD,UAAAA,EACV/B,EAAMkF,eAAe3G,GACrBA,EAAU4F,4BACV5F,EAAU0F,sBAAsB1F,IAGlCyB,EAAM+D,mBAAqBA,GAE1BtV,EAAqBE,GV3LpBA,EAAAA,KAEJua,MACMC,WAAAA,MAAuBha","file":"web-animations-next-lite.min.js"} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.html b/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.html
new file mode 100644
index 00000000..536e092d
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.html
@@ -0,0 +1 @@
+<script src="./web-animations-next.min.js"></script>
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js b/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js
new file mode 100644
index 00000000..99e293f4
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js
@@ -0,0 +1,16 @@
+// Copyright 2014 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.
+
+!function(a,b){var c={},d={},e={};!function(a,b){function c(a){if("number"==typeof a)return a;var b={};for(var c in a)b[c]=a[c];return b}function d(){this._delay=0,this._endDelay=0,this._fill="none",this._iterationStart=0,this._iterations=1,this._duration=0,this._playbackRate=1,this._direction="normal",this._easing="linear",this._easingFunction=x}function e(){return a.isDeprecated("Invalid timing inputs","2016-03-02","TypeError exceptions will be thrown instead.",!0)}function f(b,c,e){var f=new d;return c&&(f.fill="both",f.duration="auto"),"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof f[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==v.indexOf(b[c]))return;if("direction"==c&&-1==w.indexOf(b[c]))return;if("playbackRate"==c&&1!==b[c]&&a.isDeprecated("AnimationEffectTiming.playbackRate","2014-11-28","Use Animation.playbackRate instead."))return;f[c]=b[c]}}):f.duration=b,f}function g(a){return"number"==typeof a&&(a=isNaN(a)?{duration:0}:{duration:a}),a}function h(b,c){return b=a.numericTimingToObject(b),f(b,c)}function i(a,b,c,d){return a<0||a>1||c<0||c>1?x:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}if(e<=0){var g=0;return a>0?g=b/a:!b&&c>0&&(g=d/c),g*e}if(e>=1){var h=0;return c<1?h=(d-1)/(c-1):1==c&&a<1&&(h=(b-1)/(a-1)),1+h*(e-1)}for(var i=0,j=1;i<j;){var k=(i+j)/2,l=f(a,c,k);if(Math.abs(e-l)<1e-5)return f(b,d,k);l<e?i=k:j=k}return f(b,d,k)}}function j(a,b){return function(c){if(c>=1)return 1;var d=1/a;return(c+=b*d)-c%d}}function k(a){C||(C=document.createElement("div").style),C.animationTimingFunction="",C.animationTimingFunction=a;var b=C.animationTimingFunction;if(""==b&&e())throw new TypeError(a+" is not a valid value for easing");return b}function l(a){if("linear"==a)return x;var b=E.exec(a);if(b)return i.apply(this,b.slice(1).map(Number));var c=F.exec(a);return c?j(Number(c[1]),{start:y,middle:z,end:A}[c[2]]):B[a]||x}function m(a){return Math.abs(n(a)/a.playbackRate)}function n(a){return 0===a.duration||0===a.iterations?0:a.duration*a.iterations}function o(a,b,c){if(null==b)return G;var d=c.delay+a+c.endDelay;return b<Math.min(c.delay,d)?H:b>=Math.min(c.delay+a,d)?I:J}function p(a,b,c,d,e){switch(d){case H:return"backwards"==b||"both"==b?0:null;case J:return c-e;case I:return"forwards"==b||"both"==b?a:null;case G:return null}}function q(a,b,c,d,e){var f=e;return 0===a?b!==H&&(f+=c):f+=d/a,f}function r(a,b,c,d,e,f){var g=a===1/0?b%1:a%1;return 0!==g||c!==I||0===d||0===e&&0!==f||(g=1),g}function s(a,b,c,d){return a===I&&b===1/0?1/0:1===c?Math.floor(d)-1:Math.floor(d)}function t(a,b,c){var d=a;if("normal"!==a&&"reverse"!==a){var e=b;"alternate-reverse"===a&&(e+=1),d="normal",e!==1/0&&e%2!=0&&(d="reverse")}return"normal"===d?c:1-c}function u(a,b,c){var d=o(a,b,c),e=p(a,c.fill,b,d,c.delay);if(null===e)return null;var f=q(c.duration,d,c.iterations,e,c.iterationStart),g=r(f,c.iterationStart,d,c.iterations,e,c.duration),h=s(d,c.iterations,g,f),i=t(c.direction,h,g);return c._easingFunction(i)}var v="backwards|forwards|both|none".split("|"),w="reverse|alternate|alternate-reverse".split("|"),x=function(a){return a};d.prototype={_setMember:function(b,c){this["_"+b]=c,this._effect&&(this._effect._timingInput[b]=c,this._effect._timing=a.normalizeTimingInput(this._effect._timingInput),this._effect.activeDuration=a.calculateActiveDuration(this._effect._timing),this._effect._animation&&this._effect._animation._rebuildUnderlyingAnimation())},get playbackRate(){return this._playbackRate},set delay(a){this._setMember("delay",a)},get delay(){return this._delay},set endDelay(a){this._setMember("endDelay",a)},get endDelay(){return this._endDelay},set fill(a){this._setMember("fill",a)},get fill(){return this._fill},set iterationStart(a){if((isNaN(a)||a<0)&&e())throw new TypeError("iterationStart must be a non-negative number, received: "+timing.iterationStart);this._setMember("iterationStart",a)},get iterationStart(){return this._iterationStart},set duration(a){if("auto"!=a&&(isNaN(a)||a<0)&&e())throw new TypeError("duration must be non-negative or auto, received: "+a);this._setMember("duration",a)},get duration(){return this._duration},set direction(a){this._setMember("direction",a)},get direction(){return this._direction},set easing(a){this._easingFunction=l(k(a)),this._setMember("easing",a)},get easing(){return this._easing},set iterations(a){if((isNaN(a)||a<0)&&e())throw new TypeError("iterations must be non-negative, received: "+a);this._setMember("iterations",a)},get iterations(){return this._iterations}};var y=1,z=.5,A=0,B={ease:i(.25,.1,.25,1),"ease-in":i(.42,0,1,1),"ease-out":i(0,0,.58,1),"ease-in-out":i(.42,0,.58,1),"step-start":j(1,y),"step-middle":j(1,z),"step-end":j(1,A)},C=null,D="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",E=new RegExp("cubic-bezier\\("+D+","+D+","+D+","+D+"\\)"),F=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,G=0,H=1,I=2,J=3;a.cloneTimingInput=c,a.makeTiming=f,a.numericTimingToObject=g,a.normalizeTimingInput=h,a.calculateActiveDuration=m,a.calculateIterationProgress=u,a.calculatePhase=o,a.normalizeEasing=k,a.parseEasingFunction=l}(c),function(a,b){function c(a,b){return a in k?k[a][b]||b:b}function d(a){return"display"===a||0===a.lastIndexOf("animation",0)||0===a.lastIndexOf("transition",0)}function e(a,b,e){if(!d(a)){var f=h[a];if(f){i.style[a]=b;for(var g in f){var j=f[g],k=i.style[j];e[j]=c(j,k)}}else e[a]=c(a,b)}}function f(a){var b=[];for(var c in a)if(!(c in["easing","offset","composite"])){var d=a[c];Array.isArray(d)||(d=[d]);for(var e,f=d.length,g=0;g<f;g++)e={},e.offset="offset"in a?a.offset:1==f?1:g/(f-1),"easing"in a&&(e.easing=a.easing),"composite"in a&&(e.composite=a.composite),e[c]=d[g],b.push(e)}return b.sort(function(a,b){return a.offset-b.offset}),b}function g(b){function c(){var a=d.length;null==d[a-1].offset&&(d[a-1].offset=1),a>1&&null==d[0].offset&&(d[0].offset=0);for(var b=0,c=d[0].offset,e=1;e<a;e++){var f=d[e].offset;if(null!=f){for(var g=1;g<e-b;g++)d[b+g].offset=c+(f-c)*g/(e-b);b=e,c=f}}}if(null==b)return[];window.Symbol&&Symbol.iterator&&Array.prototype.from&&b[Symbol.iterator]&&(b=Array.from(b)),Array.isArray(b)||(b=f(b));for(var d=b.map(function(b){var c={};for(var d in b){var f=b[d];if("offset"==d){if(null!=f){if(f=Number(f),!isFinite(f))throw new TypeError("Keyframe offsets must be numbers.");if(f<0||f>1)throw new TypeError("Keyframe offsets must be between 0 and 1.")}}else if("composite"==d){if("add"==f||"accumulate"==f)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"add compositing is not supported"};if("replace"!=f)throw new TypeError("Invalid composite mode "+f+".")}else f="easing"==d?a.normalizeEasing(f):""+f;e(d,f,c)}return void 0==c.offset&&(c.offset=null),void 0==c.easing&&(c.easing="linear"),c}),g=!0,h=-1/0,i=0;i<d.length;i++){var j=d[i].offset;if(null!=j){if(j<h)throw new TypeError("Keyframes are not loosely sorted by offset. Sort or specify offsets.");h=j}else g=!1}return d=d.filter(function(a){return a.offset>=0&&a.offset<=1}),g||c(),d}var h={background:["backgroundImage","backgroundPosition","backgroundSize","backgroundRepeat","backgroundAttachment","backgroundOrigin","backgroundClip","backgroundColor"],border:["borderTopColor","borderTopStyle","borderTopWidth","borderRightColor","borderRightStyle","borderRightWidth","borderBottomColor","borderBottomStyle","borderBottomWidth","borderLeftColor","borderLeftStyle","borderLeftWidth"],borderBottom:["borderBottomWidth","borderBottomStyle","borderBottomColor"],borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderLeft:["borderLeftWidth","borderLeftStyle","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderRight:["borderRightWidth","borderRightStyle","borderRightColor"],borderTop:["borderTopWidth","borderTopStyle","borderTopColor"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],flex:["flexGrow","flexShrink","flexBasis"],font:["fontFamily","fontSize","fontStyle","fontVariant","fontWeight","lineHeight"],margin:["marginTop","marginRight","marginBottom","marginLeft"],outline:["outlineColor","outlineStyle","outlineWidth"],padding:["paddingTop","paddingRight","paddingBottom","paddingLeft"]},i=document.createElementNS("http://www.w3.org/1999/xhtml","div"),j={thin:"1px",medium:"3px",thick:"5px"},k={borderBottomWidth:j,borderLeftWidth:j,borderRightWidth:j,borderTopWidth:j,fontSize:{"xx-small":"60%","x-small":"75%",small:"89%",medium:"100%",large:"120%","x-large":"150%","xx-large":"200%"},fontWeight:{normal:"400",bold:"700"},outlineWidth:j,textShadow:{none:"0px 0px 0px transparent"},boxShadow:{none:"0px 0px 0px 0px transparent"}};a.convertToArrayForm=f,a.normalizeKeyframes=g}(c),function(a){var b={};a.isDeprecated=function(a,c,d,e){var f=e?"are":"is",g=new Date,h=new Date(c);return h.setMonth(h.getMonth()+3),!(g<h&&(a in b||console.warn("Web Animations: "+a+" "+f+" deprecated and will stop working on "+h.toDateString()+". "+d),b[a]=!0,1))},a.deprecated=function(b,c,d,e){var f=e?"are":"is";if(a.isDeprecated(b,c,d,e))throw new Error(b+" "+f+" no longer supported. "+d)}}(c),function(){if(document.documentElement.animate){var a=document.documentElement.animate([],0),b=!0;if(a&&(b=!1,"play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(c){void 0===a[c]&&(b=!0)})),!b)return}!function(a,b,c){function d(a){for(var b={},c=0;c<a.length;c++)for(var d in a[c])if("offset"!=d&&"easing"!=d&&"composite"!=d){var e={offset:a[c].offset,easing:a[c].easing,value:a[c][d]};b[d]=b[d]||[],b[d].push(e)}for(var f in b){var g=b[f];if(0!=g[0].offset||1!=g[g.length-1].offset)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"Partial keyframes are not supported"}}return b}function e(c){var d=[];for(var e in c)for(var f=c[e],g=0;g<f.length-1;g++){var h=g,i=g+1,j=f[h].offset,k=f[i].offset,l=j,m=k;0==g&&(l=-1/0,0==k&&(i=h)),g==f.length-2&&(m=1/0,1==j&&(h=i)),d.push({applyFrom:l,applyTo:m,startOffset:f[h].offset,endOffset:f[i].offset,easingFunction:a.parseEasingFunction(f[h].easing),property:e,interpolation:b.propertyInterpolation(e,f[h].value,f[i].value)})}return d.sort(function(a,b){return a.startOffset-b.startOffset}),d}b.convertEffectInput=function(c){var f=a.normalizeKeyframes(c),g=d(f),h=e(g);return function(a,c){if(null!=c)h.filter(function(a){return c>=a.applyFrom&&c<a.applyTo}).forEach(function(d){var e=c-d.startOffset,f=d.endOffset-d.startOffset,g=0==f?0:d.easingFunction(e/f);b.apply(a,d.property,d.interpolation(g))});else for(var d in g)"offset"!=d&&"easing"!=d&&"composite"!=d&&b.clear(a,d)}}}(c,d),function(a,b,c){function d(a){return a.replace(/-(.)/g,function(a,b){return b.toUpperCase()})}function e(a,b,c){h[c]=h[c]||[],h[c].push([a,b])}function f(a,b,c){for(var f=0;f<c.length;f++){e(a,b,d(c[f]))}}function g(c,e,f){var g=c;/-/.test(c)&&!a.isDeprecated("Hyphenated property names","2016-03-22","Use camelCase instead.",!0)&&(g=d(c)),"initial"!=e&&"initial"!=f||("initial"==e&&(e=i[g]),"initial"==f&&(f=i[g]));for(var j=e==f?[]:h[g],k=0;j&&k<j.length;k++){var l=j[k][0](e),m=j[k][0](f);if(void 0!==l&&void 0!==m){var n=j[k][1](l,m);if(n){var o=b.Interpolation.apply(null,n);return function(a){return 0==a?e:1==a?f:o(a)}}}}return b.Interpolation(!1,!0,function(a){return a?f:e})}var h={};b.addPropertiesHandler=f;var i={backgroundColor:"transparent",backgroundPosition:"0% 0%",borderBottomColor:"currentColor",borderBottomLeftRadius:"0px",borderBottomRightRadius:"0px",borderBottomWidth:"3px",borderLeftColor:"currentColor",borderLeftWidth:"3px",borderRightColor:"currentColor",borderRightWidth:"3px",borderSpacing:"2px",borderTopColor:"currentColor",borderTopLeftRadius:"0px",borderTopRightRadius:"0px",borderTopWidth:"3px",bottom:"auto",clip:"rect(0px, 0px, 0px, 0px)",color:"black",fontSize:"100%",fontWeight:"400",height:"auto",left:"auto",letterSpacing:"normal",lineHeight:"120%",marginBottom:"0px",marginLeft:"0px",marginRight:"0px",marginTop:"0px",maxHeight:"none",maxWidth:"none",minHeight:"0px",minWidth:"0px",opacity:"1.0",outlineColor:"invert",outlineOffset:"0px",outlineWidth:"3px",paddingBottom:"0px",paddingLeft:"0px",paddingRight:"0px",paddingTop:"0px",right:"auto",strokeDasharray:"none",strokeDashoffset:"0px",textIndent:"0px",textShadow:"0px 0px 0px transparent",top:"auto",transform:"",verticalAlign:"0px",visibility:"visible",width:"auto",wordSpacing:"normal",zIndex:"auto"};b.propertyInterpolation=g}(c,d),function(a,b,c){function d(b){var c=a.calculateActiveDuration(b),d=function(d){return a.calculateIterationProgress(c,d,b)};return d._totalDuration=b.delay+c+b.endDelay,d}b.KeyframeEffect=function(c,e,f,g){var h,i=d(a.normalizeTimingInput(f)),j=b.convertEffectInput(e),k=function(){j(c,h)};return k._update=function(a){return null!==(h=i(a))},k._clear=function(){j(c,null)},k._hasSameTarget=function(a){return c===a},k._target=c,k._totalDuration=i._totalDuration,k._id=g,k}}(c,d),function(a,b){function c(a,b){return!(!b.namespaceURI||-1==b.namespaceURI.indexOf("/svg"))&&(g in a||(a[g]=/Trident|MSIE|IEMobile|Edge|Android 4/i.test(a.navigator.userAgent)),a[g])}function d(a,b,c){c.enumerable=!0,c.configurable=!0,Object.defineProperty(a,b,c)}function e(a){this._element=a,this._surrogateStyle=document.createElementNS("http://www.w3.org/1999/xhtml","div").style,this._style=a.style,this._length=0,this._isAnimatedProperty={},this._updateSvgTransformAttr=c(window,a),this._savedTransformAttr=null;for(var b=0;b<this._style.length;b++){var d=this._style[b];this._surrogateStyle[d]=this._style[d]}this._updateIndices()}function f(a){if(!a._webAnimationsPatchedStyle){var b=new e(a);try{d(a,"style",{get:function(){return b}})}catch(b){a.style._set=function(b,c){a.style[b]=c},a.style._clear=function(b){a.style[b]=""}}a._webAnimationsPatchedStyle=a.style}}var g="_webAnimationsUpdateSvgTransformAttr",h={cssText:1,length:1,parentRule:1},i={getPropertyCSSValue:1,getPropertyPriority:1,getPropertyValue:1,item:1,removeProperty:1,setProperty:1},j={removeProperty:1,setProperty:1};e.prototype={get cssText(){return this._surrogateStyle.cssText},set cssText(a){for(var b={},c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;this._surrogateStyle.cssText=a,this._updateIndices();for(var c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;for(var d in b)this._isAnimatedProperty[d]||this._style.setProperty(d,this._surrogateStyle.getPropertyValue(d))},get length(){return this._surrogateStyle.length},get parentRule(){return this._style.parentRule},_updateIndices:function(){for(;this._length<this._surrogateStyle.length;)Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,get:function(a){return function(){return this._surrogateStyle[a]}}(this._length)}),this._length++;for(;this._length>this._surrogateStyle.length;)this._length--,Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,value:void 0})},_set:function(b,c){this._style[b]=c,this._isAnimatedProperty[b]=!0,this._updateSvgTransformAttr&&"transform"==a.unprefixedPropertyName(b)&&(null==this._savedTransformAttr&&(this._savedTransformAttr=this._element.getAttribute("transform")),this._element.setAttribute("transform",a.transformToSvgMatrix(c)))},_clear:function(b){this._style[b]=this._surrogateStyle[b],this._updateSvgTransformAttr&&"transform"==a.unprefixedPropertyName(b)&&(this._savedTransformAttr?this._element.setAttribute("transform",this._savedTransformAttr):this._element.removeAttribute("transform"),this._savedTransformAttr=null),delete this._isAnimatedProperty[b]}};for(var k in i)e.prototype[k]=function(a,b){return function(){var c=this._surrogateStyle[a].apply(this._surrogateStyle,arguments);return b&&(this._isAnimatedProperty[arguments[0]]||this._style[a].apply(this._style,arguments),this._updateIndices()),c}}(k,k in j);for(var l in document.documentElement.style)l in h||l in i||function(a){d(e.prototype,a,{get:function(){return this._surrogateStyle[a]},set:function(b){this._surrogateStyle[a]=b,this._updateIndices(),this._isAnimatedProperty[a]||(this._style[a]=b)}})}(l);a.apply=function(b,c,d){f(b),b.style._set(a.propertyName(c),d)},a.clear=function(b,c){b._webAnimationsPatchedStyle&&b.style._clear(a.propertyName(c))}}(d),function(a){window.Element.prototype.animate=function(b,c){var d="";return c&&c.id&&(d=c.id),a.timeline._play(a.KeyframeEffect(this,b,c,d))}}(d),function(a,b){function c(a,b,d){if("number"==typeof a&&"number"==typeof b)return a*(1-d)+b*d;if("boolean"==typeof a&&"boolean"==typeof b)return d<.5?a:b;if(a.length==b.length){for(var e=[],f=0;f<a.length;f++)e.push(c(a[f],b[f],d));return e}throw"Mismatched interpolation arguments "+a+":"+b}a.Interpolation=function(a,b,d){return function(e){return d(c(a,b,e))}}}(d),function(a,b){function c(a,b,c){return Math.max(Math.min(a,c),b)}function d(b,d,e){var f=a.dot(b,d);f=c(f,-1,1);var g=[];if(1===f)g=b;else for(var h=Math.acos(f),i=1*Math.sin(e*h)/Math.sqrt(1-f*f),j=0;j<4;j++)g.push(b[j]*(Math.cos(e*h)-f*i)+d[j]*i);return g}var e=function(){function a(a,b){for(var c=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],d=0;d<4;d++)for(var e=0;e<4;e++)for(var f=0;f<4;f++)c[d][e]+=b[d][f]*a[f][e];return c}function b(a){return 0==a[0][2]&&0==a[0][3]&&0==a[1][2]&&0==a[1][3]&&0==a[2][0]&&0==a[2][1]&&1==a[2][2]&&0==a[2][3]&&0==a[3][2]&&1==a[3][3]}function c(c,d,e,f,g){for(var h=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],i=0;i<4;i++)h[i][3]=g[i];for(var i=0;i<3;i++)for(var j=0;j<3;j++)h[3][i]+=c[j]*h[j][i];var k=f[0],l=f[1],m=f[2],n=f[3],o=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];o[0][0]=1-2*(l*l+m*m),o[0][1]=2*(k*l-m*n),o[0][2]=2*(k*m+l*n),o[1][0]=2*(k*l+m*n),o[1][1]=1-2*(k*k+m*m),o[1][2]=2*(l*m-k*n),o[2][0]=2*(k*m-l*n),o[2][1]=2*(l*m+k*n),o[2][2]=1-2*(k*k+l*l),h=a(h,o);var p=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];e[2]&&(p[2][1]=e[2],h=a(h,p)),e[1]&&(p[2][1]=0,p[2][0]=e[0],h=a(h,p)),e[0]&&(p[2][0]=0,p[1][0]=e[0],h=a(h,p));for(var i=0;i<3;i++)for(var j=0;j<3;j++)h[i][j]*=d[i];return b(h)?[h[0][0],h[0][1],h[1][0],h[1][1],h[3][0],h[3][1]]:h[0].concat(h[1],h[2],h[3])}return c}();a.composeMatrix=e,a.quat=d}(d),function(a,b,c){a.sequenceNumber=0;var d=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="finish",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()};b.Animation=function(b){this.id="",b&&b._id&&(this.id=b._id),this._sequenceNumber=a.sequenceNumber++,this._currentTime=0,this._startTime=null,this._paused=!1,this._playbackRate=1,this._inTimeline=!0,this._finishedFlag=!0,this.onfinish=null,this._finishHandlers=[],this._effect=b,this._inEffect=this._effect._update(0),this._idle=!0,this._currentTimePending=!1},b.Animation.prototype={_ensureAlive:function(){this.playbackRate<0&&0===this.currentTime?this._inEffect=this._effect._update(-1):this._inEffect=this._effect._update(this.currentTime),this._inTimeline||!this._inEffect&&this._finishedFlag||(this._inTimeline=!0,b.timeline._animations.push(this))},_tickCurrentTime:function(a,b){a!=this._currentTime&&(this._currentTime=a,this._isFinished&&!b&&(this._currentTime=this._playbackRate>0?this._totalDuration:0),this._ensureAlive())},get currentTime(){return this._idle||this._currentTimePending?null:this._currentTime},set currentTime(a){a=+a,isNaN(a)||(b.restart(),this._paused||null==this._startTime||(this._startTime=this._timeline.currentTime-a/this._playbackRate),this._currentTimePending=!1,this._currentTime!=a&&(this._idle&&(this._idle=!1,this._paused=!0),this._tickCurrentTime(a,!0),b.applyDirtiedAnimation(this)))},get startTime(){return this._startTime},set startTime(a){a=+a,isNaN(a)||this._paused||this._idle||(this._startTime=a,this._tickCurrentTime((this._timeline.currentTime-this._startTime)*this.playbackRate),b.applyDirtiedAnimation(this))},get playbackRate(){return this._playbackRate},set playbackRate(a){if(a!=this._playbackRate){var c=this.currentTime;this._playbackRate=a,this._startTime=null,"paused"!=this.playState&&"idle"!=this.playState&&(this._finishedFlag=!1,this._idle=!1,this._ensureAlive(),b.applyDirtiedAnimation(this)),null!=c&&(this.currentTime=c)}},get _isFinished(){return!this._idle&&(this._playbackRate>0&&this._currentTime>=this._totalDuration||this._playbackRate<0&&this._currentTime<=0)},get _totalDuration(){return this._effect._totalDuration},get playState(){return this._idle?"idle":null==this._startTime&&!this._paused&&0!=this.playbackRate||this._currentTimePending?"pending":this._paused?"paused":this._isFinished?"finished":"running"},_rewind:function(){if(this._playbackRate>=0)this._currentTime=0;else{if(!(this._totalDuration<1/0))throw new DOMException("Unable to rewind negative playback rate animation with infinite duration","InvalidStateError");this._currentTime=this._totalDuration}},play:function(){this._paused=!1,(this._isFinished||this._idle)&&(this._rewind(),this._startTime=null),this._finishedFlag=!1,this._idle=!1,this._ensureAlive(),b.applyDirtiedAnimation(this)},pause:function(){this._isFinished||this._paused||this._idle?this._idle&&(this._rewind(),this._idle=!1):this._currentTimePending=!0,this._startTime=null,this._paused=!0},finish:function(){this._idle||(this.currentTime=this._playbackRate>0?this._totalDuration:0,this._startTime=this._totalDuration-this.currentTime,this._currentTimePending=!1,b.applyDirtiedAnimation(this))},cancel:function(){this._inEffect&&(this._inEffect=!1,this._idle=!0,this._paused=!1,this._isFinished=!0,this._finishedFlag=!0,this._currentTime=0,this._startTime=null,this._effect._update(null),b.applyDirtiedAnimation(this))},reverse:function(){this.playbackRate*=-1,this.play()},addEventListener:function(a,b){"function"==typeof b&&"finish"==a&&this._finishHandlers.push(b)},removeEventListener:function(a,b){if("finish"==a){var c=this._finishHandlers.indexOf(b);c>=0&&this._finishHandlers.splice(c,1)}},_fireEvents:function(a){if(this._isFinished){if(!this._finishedFlag){var b=new d(this,this._currentTime,a),c=this._finishHandlers.concat(this.onfinish?[this.onfinish]:[]);setTimeout(function(){c.forEach(function(a){a.call(b.target,b)})},0),this._finishedFlag=!0}}else this._finishedFlag=!1},_tick:function(a,b){this._idle||this._paused||(null==this._startTime?b&&(this.startTime=a-this._currentTime/this.playbackRate):this._isFinished||this._tickCurrentTime((a-this._startTime)*this.playbackRate)),b&&(this._currentTimePending=!1,this._fireEvents(a))},get _needsTick(){return this.playState in{pending:1,running:1}||!this._finishedFlag},_targetAnimations:function(){var a=this._effect._target;return a._activeAnimations||(a._activeAnimations=[]),a._activeAnimations},_markTarget:function(){var a=this._targetAnimations();-1===a.indexOf(this)&&a.push(this)},_unmarkTarget:function(){var a=this._targetAnimations(),b=a.indexOf(this);-1!==b&&a.splice(b,1)}}}(c,d),function(a,b,c){function d(a){var b=j;j=[],a<q.currentTime&&(a=q.currentTime),q._animations.sort(e),q._animations=h(a,!0,q._animations)[0],b.forEach(function(b){b[1](a)}),g(),l=void 0}function e(a,b){return a._sequenceNumber-b._sequenceNumber}function f(){this._animations=[],this.currentTime=window.performance&&performance.now?performance.now():0}function g(){o.forEach(function(a){a()}),o.length=0}function h(a,c,d){p=!0,n=!1,b.timeline.currentTime=a,m=!1;var e=[],f=[],g=[],h=[];return d.forEach(function(b){b._tick(a,c),b._inEffect?(f.push(b._effect),b._markTarget()):(e.push(b._effect),b._unmarkTarget()),b._needsTick&&(m=!0);var d=b._inEffect||b._needsTick;b._inTimeline=d,d?g.push(b):h.push(b)}),o.push.apply(o,e),o.push.apply(o,f),m&&requestAnimationFrame(function(){}),p=!1,[g,h]}var i=window.requestAnimationFrame,j=[],k=0;window.requestAnimationFrame=function(a){var b=k++;return 0==j.length&&i(d),j.push([b,a]),b},window.cancelAnimationFrame=function(a){j.forEach(function(b){b[0]==a&&(b[1]=function(){})})},f.prototype={_play:function(c){c._timing=a.normalizeTimingInput(c.timing);var d=new b.Animation(c);return d._idle=!1,d._timeline=this,this._animations.push(d),b.restart(),b.applyDirtiedAnimation(d),d}};var l=void 0,m=!1,n=!1;b.restart=function(){return m||(m=!0,requestAnimationFrame(function(){}),n=!0),n},b.applyDirtiedAnimation=function(a){if(!p){a._markTarget();var c=a._targetAnimations();c.sort(e),h(b.timeline.currentTime,!1,c.slice())[1].forEach(function(a){var b=q._animations.indexOf(a);-1!==b&&q._animations.splice(b,1)}),g()}};var o=[],p=!1,q=new f;b.timeline=q}(c,d),function(a,b){function c(a,b){for(var c=0,d=0;d<a.length;d++)c+=a[d]*b[d];return c}function d(a,b){return[a[0]*b[0]+a[4]*b[1]+a[8]*b[2]+a[12]*b[3],a[1]*b[0]+a[5]*b[1]+a[9]*b[2]+a[13]*b[3],a[2]*b[0]+a[6]*b[1]+a[10]*b[2]+a[14]*b[3],a[3]*b[0]+a[7]*b[1]+a[11]*b[2]+a[15]*b[3],a[0]*b[4]+a[4]*b[5]+a[8]*b[6]+a[12]*b[7],a[1]*b[4]+a[5]*b[5]+a[9]*b[6]+a[13]*b[7],a[2]*b[4]+a[6]*b[5]+a[10]*b[6]+a[14]*b[7],a[3]*b[4]+a[7]*b[5]+a[11]*b[6]+a[15]*b[7],a[0]*b[8]+a[4]*b[9]+a[8]*b[10]+a[12]*b[11],a[1]*b[8]+a[5]*b[9]+a[9]*b[10]+a[13]*b[11],a[2]*b[8]+a[6]*b[9]+a[10]*b[10]+a[14]*b[11],a[3]*b[8]+a[7]*b[9]+a[11]*b[10]+a[15]*b[11],a[0]*b[12]+a[4]*b[13]+a[8]*b[14]+a[12]*b[15],a[1]*b[12]+a[5]*b[13]+a[9]*b[14]+a[13]*b[15],a[2]*b[12]+a[6]*b[13]+a[10]*b[14]+a[14]*b[15],a[3]*b[12]+a[7]*b[13]+a[11]*b[14]+a[15]*b[15]]}function e(a){var b=a.rad||0;return((a.deg||0)/360+(a.grad||0)/400+(a.turn||0))*(2*Math.PI)+b}function f(a){switch(a.t){case"rotatex":var b=e(a.d[0]);return[1,0,0,0,0,Math.cos(b),Math.sin(b),0,0,-Math.sin(b),Math.cos(b),0,0,0,0,1];case"rotatey":var b=e(a.d[0]);return[Math.cos(b),0,-Math.sin(b),0,0,1,0,0,Math.sin(b),0,Math.cos(b),0,0,0,0,1];case"rotate":case"rotatez":var b=e(a.d[0]);return[Math.cos(b),Math.sin(b),0,0,-Math.sin(b),Math.cos(b),0,0,0,0,1,0,0,0,0,1];case"rotate3d":var c=a.d[0],d=a.d[1],f=a.d[2],b=e(a.d[3]),g=c*c+d*d+f*f;if(0===g)c=1,d=0,f=0;else if(1!==g){var h=Math.sqrt(g);c/=h,d/=h,f/=h}var i=Math.sin(b/2),j=i*Math.cos(b/2),k=i*i;return[1-2*(d*d+f*f)*k,2*(c*d*k+f*j),2*(c*f*k-d*j),0,2*(c*d*k-f*j),1-2*(c*c+f*f)*k,2*(d*f*k+c*j),0,2*(c*f*k+d*j),2*(d*f*k-c*j),1-2*(c*c+d*d)*k,0,0,0,0,1];case"scale":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,1,0,0,0,0,1];case"scalex":return[a.d[0],0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"scaley":return[1,0,0,0,0,a.d[0],0,0,0,0,1,0,0,0,0,1];case"scalez":return[1,0,0,0,0,1,0,0,0,0,a.d[0],0,0,0,0,1];case"scale3d":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,a.d[2],0,0,0,0,1];case"skew":var l=e(a.d[0]),m=e(a.d[1]);return[1,Math.tan(m),0,0,Math.tan(l),1,0,0,0,0,1,0,0,0,0,1];case"skewx":var b=e(a.d[0]);return[1,0,0,0,Math.tan(b),1,0,0,0,0,1,0,0,0,0,1];case"skewy":var b=e(a.d[0]);return[1,Math.tan(b),0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"translate":var c=a.d[0].px||0,d=a.d[1].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,c,d,0,1];case"translatex":var c=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,c,0,0,1];case"translatey":var d=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,d,0,1];case"translatez":var f=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,0,f,1];case"translate3d":var c=a.d[0].px||0,d=a.d[1].px||0,f=a.d[2].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,c,d,f,1];case"perspective":return[1,0,0,0,0,1,0,0,0,0,1,a.d[0].px?-1/a.d[0].px:0,0,0,0,1];case"matrix":return[a.d[0],a.d[1],0,0,a.d[2],a.d[3],0,0,0,0,1,0,a.d[4],a.d[5],0,1];case"matrix3d":return a.d}}function g(a){return 0===a.length?[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]:a.map(f).reduce(d)}function h(a){return[i(g(a))]}var i=function(){function a(a){return a[0][0]*a[1][1]*a[2][2]+a[1][0]*a[2][1]*a[0][2]+a[2][0]*a[0][1]*a[1][2]-a[0][2]*a[1][1]*a[2][0]-a[1][2]*a[2][1]*a[0][0]-a[2][2]*a[0][1]*a[1][0]}function b(b){for(var c=1/a(b),d=b[0][0],e=b[0][1],f=b[0][2],g=b[1][0],h=b[1][1],i=b[1][2],j=b[2][0],k=b[2][1],l=b[2][2],m=[[(h*l-i*k)*c,(f*k-e*l)*c,(e*i-f*h)*c,0],[(i*j-g*l)*c,(d*l-f*j)*c,(f*g-d*i)*c,0],[(g*k-h*j)*c,(j*e-d*k)*c,(d*h-e*g)*c,0]],n=[],o=0;o<3;o++){for(var p=0,q=0;q<3;q++)p+=b[3][q]*m[q][o];n.push(p)}return n.push(1),m.push(n),m}function d(a){return[[a[0][0],a[1][0],a[2][0],a[3][0]],[a[0][1],a[1][1],a[2][1],a[3][1]],[a[0][2],a[1][2],a[2][2],a[3][2]],[a[0][3],a[1][3],a[2][3],a[3][3]]]}function e(a,b){for(var c=[],d=0;d<4;d++){for(var e=0,f=0;f<4;f++)e+=a[f]*b[f][d];c.push(e)}return c}function f(a){var b=g(a);return[a[0]/b,a[1]/b,a[2]/b]}function g(a){return Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2])}function h(a,b,c,d){return[c*a[0]+d*b[0],c*a[1]+d*b[1],c*a[2]+d*b[2]]}function i(a,b){return[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]}function j(j){var k=[j.slice(0,4),j.slice(4,8),j.slice(8,12),j.slice(12,16)];if(1!==k[3][3])return null;for(var l=[],m=0;m<4;m++)l.push(k[m].slice());for(var m=0;m<3;m++)l[m][3]=0;if(0===a(l))return null;var n,o=[];k[0][3]||k[1][3]||k[2][3]?(o.push(k[0][3]),o.push(k[1][3]),o.push(k[2][3]),o.push(k[3][3]),n=e(o,d(b(l)))):n=[0,0,0,1];var p=k[3].slice(0,3),q=[];q.push(k[0].slice(0,3));var r=[];r.push(g(q[0])),q[0]=f(q[0]);var s=[];q.push(k[1].slice(0,3)),s.push(c(q[0],q[1])),q[1]=h(q[1],q[0],1,-s[0]),r.push(g(q[1])),q[1]=f(q[1]),s[0]/=r[1],q.push(k[2].slice(0,3)),s.push(c(q[0],q[2])),q[2]=h(q[2],q[0],1,-s[1]),s.push(c(q[1],q[2])),q[2]=h(q[2],q[1],1,-s[2]),r.push(g(q[2])),q[2]=f(q[2]),s[1]/=r[2],s[2]/=r[2];var t=i(q[1],q[2]);if(c(q[0],t)<0)for(var m=0;m<3;m++)r[m]*=-1,q[m][0]*=-1,q[m][1]*=-1,q[m][2]*=-1;var u,v,w=q[0][0]+q[1][1]+q[2][2]+1;return w>1e-4?(u=.5/Math.sqrt(w),v=[(q[2][1]-q[1][2])*u,(q[0][2]-q[2][0])*u,(q[1][0]-q[0][1])*u,.25/u]):q[0][0]>q[1][1]&&q[0][0]>q[2][2]?(u=2*Math.sqrt(1+q[0][0]-q[1][1]-q[2][2]),v=[.25*u,(q[0][1]+q[1][0])/u,(q[0][2]+q[2][0])/u,(q[2][1]-q[1][2])/u]):q[1][1]>q[2][2]?(u=2*Math.sqrt(1+q[1][1]-q[0][0]-q[2][2]),v=[(q[0][1]+q[1][0])/u,.25*u,(q[1][2]+q[2][1])/u,(q[0][2]-q[2][0])/u]):(u=2*Math.sqrt(1+q[2][2]-q[0][0]-q[1][1]),v=[(q[0][2]+q[2][0])/u,(q[1][2]+q[2][1])/u,.25*u,(q[1][0]-q[0][1])/u]),[p,r,s,v,n]}return j}();a.dot=c,a.makeMatrixDecomposition=h,a.transformListToMatrix=g}(d),function(a){function b(a,b){var c=a.exec(b);if(c)return c=a.ignoreCase?c[0].toLowerCase():c[0],[c,b.substr(c.length)]}function c(a,b){b=b.replace(/^\s*/,"");var c=a(b);if(c)return[c[0],c[1].replace(/^\s*/,"")]}function d(a,d,e){a=c.bind(null,a);for(var f=[];;){var g=a(e);if(!g)return[f,e];if(f.push(g[0]),e=g[1],!(g=b(d,e))||""==g[1])return[f,e];e=g[1]}}function e(a,b){for(var c=0,d=0;d<b.length&&(!/\s|,/.test(b[d])||0!=c);d++)if("("==b[d])c++;else if(")"==b[d]&&(c--,0==c&&d++,c<=0))break;var e=a(b.substr(0,d));return void 0==e?void 0:[e,b.substr(d)]}function f(a,b){for(var c=a,d=b;c&&d;)c>d?c%=d:d%=c;return c=a*b/(c+d)}function g(a){return function(b){var c=a(b);return c&&(c[0]=void 0),c}}function h(a,b){return function(c){return a(c)||[b,c]}}function i(b,c){for(var d=[],e=0;e<b.length;e++){var f=a.consumeTrimmed(b[e],c);if(!f||""==f[0])return;void 0!==f[0]&&d.push(f[0]),c=f[1]}if(""==c)return d}function j(a,b,c,d,e){for(var g=[],h=[],i=[],j=f(d.length,e.length),k=0;k<j;k++){var l=b(d[k%d.length],e[k%e.length]);if(!l)return;g.push(l[0]),h.push(l[1]),i.push(l[2])}return[g,h,function(b){var d=b.map(function(a,b){return i[b](a)}).join(c);return a?a(d):d}]}function k(a,b,c){for(var d=[],e=[],f=[],g=0,h=0;h<c.length;h++)if("function"==typeof c[h]){var i=c[h](a[g],b[g++]);d.push(i[0]),e.push(i[1]),f.push(i[2])}else!function(a){d.push(!1),e.push(!1),f.push(function(){return c[a]})}(h);return[d,e,function(a){for(var b="",c=0;c<a.length;c++)b+=f[c](a[c]);return b}]}a.consumeToken=b,a.consumeTrimmed=c,a.consumeRepeated=d,a.consumeParenthesised=e,a.ignore=g,a.optional=h,a.consumeList=i,a.mergeNestedRepeated=j.bind(null,null),a.mergeWrappedNestedRepeated=j,a.mergeList=k}(d),function(a){function b(b){function c(b){var c=a.consumeToken(/^inset/i,b);if(c)return d.inset=!0,c;var c=a.consumeLengthOrPercent(b);if(c)return d.lengths.push(c[0]),c;var c=a.consumeColor(b);return c?(d.color=c[0],c):void 0}var d={inset:!1,lengths:[],color:null},e=a.consumeRepeated(c,/^/,b);if(e&&e[0].length)return[d,e[1]]}function c(c){var d=a.consumeRepeated(b,/^,/,c);if(d&&""==d[1])return d[0]}function d(b,c){for(;b.lengths.length<Math.max(b.lengths.length,c.lengths.length);)b.lengths.push({px:0});for(;c.lengths.length<Math.max(b.lengths.length,c.lengths.length);)c.lengths.push({px:0});if(b.inset==c.inset&&!!b.color==!!c.color){for(var d,e=[],f=[[],0],g=[[],0],h=0;h<b.lengths.length;h++){var i=a.mergeDimensions(b.lengths[h],c.lengths[h],2==h);f[0].push(i[0]),g[0].push(i[1]),e.push(i[2])}if(b.color&&c.color){var j=a.mergeColors(b.color,c.color);f[1]=j[0],g[1]=j[1],d=j[2]}return[f,g,function(a){for(var c=b.inset?"inset ":" ",f=0;f<e.length;f++)c+=e[f](a[0][f])+" ";return d&&(c+=d(a[1])),c}]}}function e(b,c,d,e){function f(a){return{inset:a,color:[0,0,0,0],lengths:[{px:0},{px:0},{px:0},{px:0}]}}for(var g=[],h=[],i=0;i<d.length||i<e.length;i++){var j=d[i]||f(e[i].inset),k=e[i]||f(d[i].inset);g.push(j),h.push(k)}return a.mergeNestedRepeated(b,c,g,h)}var f=e.bind(null,d,", ");a.addPropertiesHandler(c,f,["box-shadow","text-shadow"])}(d),function(a,b){function c(a){return a.toFixed(3).replace(/0+$/,"").replace(/\.$/,"")}function d(a,b,c){return Math.min(b,Math.max(a,c))}function e(a){if(/^\s*[-+]?(\d*\.)?\d+\s*$/.test(a))return Number(a)}function f(a,b){return[a,b,c]}function g(a,b){if(0!=a)return i(0,1/0)(a,b)}function h(a,b){return[a,b,function(a){return Math.round(d(1,1/0,a))}]}function i(a,b){return function(e,f){return[e,f,function(e){return c(d(a,b,e))}]}}function j(a){var b=a.trim().split(/\s*[\s,]\s*/);if(0!==b.length){for(var c=[],d=0;d<b.length;d++){var f=e(b[d]);if(void 0===f)return;c.push(f)}return c}}function k(a,b){if(a.length==b.length)return[a,b,function(a){return a.map(c).join(" ")}]}function l(a,b){return[a,b,Math.round]}a.clamp=d,a.addPropertiesHandler(j,k,["stroke-dasharray"]),a.addPropertiesHandler(e,i(0,1/0),["border-image-width","line-height"]),a.addPropertiesHandler(e,i(0,1),["opacity","shape-image-threshold"]),a.addPropertiesHandler(e,g,["flex-grow","flex-shrink"]),a.addPropertiesHandler(e,h,["orphans","widows"]),a.addPropertiesHandler(e,l,["z-index"]),a.parseNumber=e,a.parseNumberList=j,a.mergeNumbers=f,a.numberToString=c}(d),function(a,b){function c(a,b){if("visible"==a||"visible"==b)return[0,1,function(c){return c<=0?a:c>=1?b:"visible"}]}a.addPropertiesHandler(String,c,["visibility"])}(d),function(a,b){function c(a){a=a.trim(),f.fillStyle="#000",f.fillStyle=a;var b=f.fillStyle;if(f.fillStyle="#fff",f.fillStyle=a,b==f.fillStyle){f.fillRect(0,0,1,1);var c=f.getImageData(0,0,1,1).data;f.clearRect(0,0,1,1);var d=c[3]/255;return[c[0]*d,c[1]*d,c[2]*d,d]}}function d(b,c){return[b,c,function(b){function c(a){return Math.max(0,Math.min(255,a))}if(b[3])for(var d=0;d<3;d++)b[d]=Math.round(c(b[d]/b[3]));return b[3]=a.numberToString(a.clamp(0,1,b[3])),"rgba("+b.join(",")+")"}]}var e=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");e.width=e.height=1;var f=e.getContext("2d");a.addPropertiesHandler(c,d,["background-color","border-bottom-color","border-left-color","border-right-color","border-top-color","color","fill","flood-color","lighting-color","outline-color","stop-color","stroke","text-decoration-color"]),a.consumeColor=a.consumeParenthesised.bind(null,c),a.mergeColors=d}(d),function(a,b){function c(a){function b(){var b=h.exec(a);g=b?b[0]:void 0}function c(){var a=Number(g);return b(),a}function d(){if("("!==g)return c();b();var a=f();return")"!==g?NaN:(b(),a)}function e(){for(var a=d();"*"===g||"/"===g;){var c=g;b();var e=d();"*"===c?a*=e:a/=e}return a}function f(){for(var a=e();"+"===g||"-"===g;){var c=g;b();var d=e();"+"===c?a+=d:a-=d}return a}var g,h=/([\+\-\w\.]+|[\(\)\*\/])/g;return b(),f()}function d(a,b){if("0"==(b=b.trim().toLowerCase())&&"px".search(a)>=0)return{px:0};if(/^[^(]*$|^calc/.test(b)){b=b.replace(/calc\(/g,"(");var d={};b=b.replace(a,function(a){return d[a]=null,"U"+a});for(var e="U("+a.source+")",f=b.replace(/[-+]?(\d*\.)?\d+([Ee][-+]?\d+)?/g,"N").replace(new RegExp("N"+e,"g"),"D").replace(/\s[+-]\s/g,"O").replace(/\s/g,""),g=[/N\*(D)/g,/(N|D)[*\/]N/g,/(N|D)O\1/g,/\((N|D)\)/g],h=0;h<g.length;)g[h].test(f)?(f=f.replace(g[h],"$1"),h=0):h++;if("D"==f){for(var i in d){var j=c(b.replace(new RegExp("U"+i,"g"),"").replace(new RegExp(e,"g"),"*0"));if(!isFinite(j))return;d[i]=j}return d}}}function e(a,b){return f(a,b,!0)}function f(b,c,d){var e,f=[];for(e in b)f.push(e);for(e in c)f.indexOf(e)<0&&f.push(e);return b=f.map(function(a){return b[a]||0}),c=f.map(function(a){return c[a]||0}),[b,c,function(b){var c=b.map(function(c,e){return 1==b.length&&d&&(c=Math.max(c,0)),a.numberToString(c)+f[e]}).join(" + ");return b.length>1?"calc("+c+")":c}]}var g="px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc",h=d.bind(null,new RegExp(g,"g")),i=d.bind(null,new RegExp(g+"|%","g")),j=d.bind(null,/deg|rad|grad|turn/g);a.parseLength=h,a.parseLengthOrPercent=i,a.consumeLengthOrPercent=a.consumeParenthesised.bind(null,i),a.parseAngle=j,a.mergeDimensions=f;var k=a.consumeParenthesised.bind(null,h),l=a.consumeRepeated.bind(void 0,k,/^/),m=a.consumeRepeated.bind(void 0,l,/^,/);a.consumeSizePairList=m;var n=function(a){var b=m(a);if(b&&""==b[1])return b[0]},o=a.mergeNestedRepeated.bind(void 0,e," "),p=a.mergeNestedRepeated.bind(void 0,o,",");a.mergeNonNegativeSizePair=o,a.addPropertiesHandler(n,p,["background-size"]),a.addPropertiesHandler(i,e,["border-bottom-width","border-image-width","border-left-width","border-right-width","border-top-width","flex-basis","font-size","height","line-height","max-height","max-width","outline-width","width"]),a.addPropertiesHandler(i,f,["border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","bottom","left","letter-spacing","margin-bottom","margin-left","margin-right","margin-top","min-height","min-width","outline-offset","padding-bottom","padding-left","padding-right","padding-top","perspective","right","shape-margin","stroke-dashoffset","text-indent","top","vertical-align","word-spacing"])}(d),function(a,b){function c(b){return a.consumeLengthOrPercent(b)||a.consumeToken(/^auto/,b)}function d(b){var d=a.consumeList([a.ignore(a.consumeToken.bind(null,/^rect/)),a.ignore(a.consumeToken.bind(null,/^\(/)),a.consumeRepeated.bind(null,c,/^,/),a.ignore(a.consumeToken.bind(null,/^\)/))],b);if(d&&4==d[0].length)return d[0]}function e(b,c){return"auto"==b||"auto"==c?[!0,!1,function(d){var e=d?b:c;if("auto"==e)return"auto";var f=a.mergeDimensions(e,e);return f[2](f[0])}]:a.mergeDimensions(b,c)}function f(a){return"rect("+a+")"}var g=a.mergeWrappedNestedRepeated.bind(null,f,e,", ");a.parseBox=d,a.mergeBoxes=g,a.addPropertiesHandler(d,g,["clip"])}(d),function(a,b){function c(a){return function(b){var c=0;return a.map(function(a){return a===k?b[c++]:a})}}function d(a){return a}function e(b){if("none"==(b=b.toLowerCase().trim()))return[];for(var c,d=/\s*(\w+)\(([^)]*)\)/g,e=[],f=0;c=d.exec(b);){if(c.index!=f)return;f=c.index+c[0].length;var g=c[1],h=n[g];if(!h)return;var i=c[2].split(","),j=h[0];if(j.length<i.length)return;for(var k=[],o=0;o<j.length;o++){var p,q=i[o],r=j[o];if(void 0===(p=q?{A:function(b){return"0"==b.trim()?m:a.parseAngle(b)},N:a.parseNumber,T:a.parseLengthOrPercent,L:a.parseLength}[r.toUpperCase()](q):{a:m,n:k[0],t:l}[r]))return;k.push(p)}if(e.push({t:g,d:k}),d.lastIndex==b.length)return e}}function f(a){return a.toFixed(6).replace(".000000","")}function g(b,c){if(b.decompositionPair!==c){b.decompositionPair=c;var d=a.makeMatrixDecomposition(b)}if(c.decompositionPair!==b){c.decompositionPair=b;var e=a.makeMatrixDecomposition(c)}return null==d[0]||null==e[0]?[[!1],[!0],function(a){return a?c[0].d:b[0].d}]:(d[0].push(0),e[0].push(1),[d,e,function(b){var c=a.quat(d[0][3],e[0][3],b[5]);return a.composeMatrix(b[0],b[1],b[2],c,b[4]).map(f).join(",")}])}function h(a){return a.replace(/[xy]/,"")}function i(a){return a.replace(/(x|y|z|3d)?$/,"3d")}function j(b,c){var d=a.makeMatrixDecomposition&&!0,e=!1;if(!b.length||!c.length){b.length||(e=!0,b=c,c=[]);for(var f=0;f<b.length;f++){var j=b[f].t,k=b[f].d,l="scale"==j.substr(0,5)?1:0;c.push({t:j,d:k.map(function(a){if("number"==typeof a)return l;var b={};for(var c in a)b[c]=l;return b})})}}var m=function(a,b){return"perspective"==a&&"perspective"==b||("matrix"==a||"matrix3d"==a)&&("matrix"==b||"matrix3d"==b)},o=[],p=[],q=[];if(b.length!=c.length){if(!d)return;var r=g(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]]}else for(var f=0;f<b.length;f++){var j,s=b[f].t,t=c[f].t,u=b[f].d,v=c[f].d,w=n[s],x=n[t];if(m(s,t)){if(!d)return;var r=g([b[f]],[c[f]]);o.push(r[0]),p.push(r[1]),q.push(["matrix",[r[2]]])}else{if(s==t)j=s;else if(w[2]&&x[2]&&h(s)==h(t))j=h(s),u=w[2](u),v=x[2](v);else{if(!w[1]||!x[1]||i(s)!=i(t)){if(!d)return;var r=g(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]];break}j=i(s),u=w[1](u),v=x[1](v)}for(var y=[],z=[],A=[],B=0;B<u.length;B++){var C="number"==typeof u[B]?a.mergeNumbers:a.mergeDimensions,r=C(u[B],v[B]);y[B]=r[0],z[B]=r[1],A.push(r[2])}o.push(y),p.push(z),q.push([j,A])}}if(e){var D=o;o=p,p=D}return[o,p,function(a){return a.map(function(a,b){var c=a.map(function(a,c){return q[b][1][c](a)}).join(",");return"matrix"==q[b][0]&&16==c.split(",").length&&(q[b][0]="matrix3d"),q[b][0]+"("+c+")"}).join(" ")}]}var k=null,l={px:0},m={deg:0},n={matrix:["NNNNNN",[k,k,0,0,k,k,0,0,0,0,1,0,k,k,0,1],d],matrix3d:["NNNNNNNNNNNNNNNN",d],rotate:["A"],rotatex:["A"],rotatey:["A"],rotatez:["A"],rotate3d:["NNNA"],perspective:["L"],scale:["Nn",c([k,k,1]),d],scalex:["N",c([k,1,1]),c([k,1])],scaley:["N",c([1,k,1]),c([1,k])],scalez:["N",c([1,1,k])],scale3d:["NNN",d],skew:["Aa",null,d],skewx:["A",null,c([k,m])],skewy:["A",null,c([m,k])],translate:["Tt",c([k,k,l]),d],translatex:["T",c([k,l,l]),c([k,l])],translatey:["T",c([l,k,l]),c([l,k])],translatez:["L",c([l,l,k])],translate3d:["TTL",d]};a.addPropertiesHandler(e,j,["transform"]),a.transformToSvgMatrix=function(b){var c=a.transformListToMatrix(e(b));return"matrix("+f(c[0])+" "+f(c[1])+" "+f(c[4])+" "+f(c[5])+" "+f(c[12])+" "+f(c[13])+")"}}(d),function(a){function b(a){var b=Number(a);if(!(isNaN(b)||b<100||b>900||b%100!=0))return b}function c(b){return b=100*Math.round(b/100),b=a.clamp(100,900,b),400===b?"normal":700===b?"bold":String(b)}function d(a,b){return[a,b,c]}a.addPropertiesHandler(b,d,["font-weight"])}(d),function(a){function b(a){var b={};for(var c in a)b[c]=-a[c];return b}function c(b){return a.consumeToken(/^(left|center|right|top|bottom)\b/i,b)||a.consumeLengthOrPercent(b)}function d(b,d){var e=a.consumeRepeated(c,/^/,d);if(e&&""==e[1]){var f=e[0];if(f[0]=f[0]||"center",f[1]=f[1]||"center",3==b&&(f[2]=f[2]||{px:0}),f.length==b){if(/top|bottom/.test(f[0])||/left|right/.test(f[1])){var h=f[0];f[0]=f[1],f[1]=h}if(/left|right|center|Object/.test(f[0])&&/top|bottom|center|Object/.test(f[1]))return f.map(function(a){return"object"==typeof a?a:g[a]})}}}function e(d){var e=a.consumeRepeated(c,/^/,d);if(e){for(var f=e[0],h=[{"%":50},{"%":50}],i=0,j=!1,k=0;k<f.length;k++){var l=f[k];"string"==typeof l?(j=/bottom|right/.test(l),i={left:0,right:0,center:i,top:1,bottom:1}[l],h[i]=g[l],"center"==l&&i++):(j&&(l=b(l),l["%"]=(l["%"]||0)+100),h[i]=l,i++,j=!1)}return[h,e[1]]}}function f(b){var c=a.consumeRepeated(e,/^,/,b);if(c&&""==c[1])return c[0]}var g={left:{"%":0},center:{"%":50},right:{"%":100},top:{"%":0},bottom:{"%":100}},h=a.mergeNestedRepeated.bind(null,a.mergeDimensions," ");a.addPropertiesHandler(d.bind(null,3),h,["transform-origin"]),a.addPropertiesHandler(d.bind(null,2),h,["perspective-origin"]),a.consumePosition=e,a.mergeOffsetList=h;var i=a.mergeNestedRepeated.bind(null,h,", ");a.addPropertiesHandler(f,i,["background-position","object-position"])}(d),function(a){function b(b){var c=a.consumeToken(/^circle/,b);if(c&&c[0])return["circle"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),d,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],c[1]));var f=a.consumeToken(/^ellipse/,b);if(f&&f[0])return["ellipse"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),e,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],f[1]));var g=a.consumeToken(/^polygon/,b);return g&&g[0]?["polygon"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),a.optional(a.consumeToken.bind(void 0,/^nonzero\s*,|^evenodd\s*,/),"nonzero,"),a.consumeSizePairList,a.ignore(a.consumeToken.bind(void 0,/^\)/))],g[1])):void 0}function c(b,c){if(b[0]===c[0])return"circle"==b[0]?a.mergeList(b.slice(1),c.slice(1),["circle(",a.mergeDimensions," at ",a.mergeOffsetList,")"]):"ellipse"==b[0]?a.mergeList(b.slice(1),c.slice(1),["ellipse(",a.mergeNonNegativeSizePair," at ",a.mergeOffsetList,")"]):"polygon"==b[0]&&b[1]==c[1]?a.mergeList(b.slice(2),c.slice(2),["polygon(",b[1],g,")"]):void 0}var d=a.consumeParenthesised.bind(null,a.parseLengthOrPercent),e=a.consumeRepeated.bind(void 0,d,/^/),f=a.mergeNestedRepeated.bind(void 0,a.mergeDimensions," "),g=a.mergeNestedRepeated.bind(void 0,f,",");a.addPropertiesHandler(b,c,["shape-outside"])}(d),function(a,b){function c(a,b){b.concat([a]).forEach(function(b){b in document.documentElement.style&&(d[a]=b),e[b]=a})}var d={},e={};c("transform",["webkitTransform","msTransform"]),c("transformOrigin",["webkitTransformOrigin"]),c("perspective",["webkitPerspective"]),c("perspectiveOrigin",["webkitPerspectiveOrigin"]),a.propertyName=function(a){return d[a]||a},a.unprefixedPropertyName=function(a){return e[a]||a}}(d)}(),function(){if(void 0===document.createElement("div").animate([]).oncancel){var a;if(window.performance&&performance.now)var a=function(){return performance.now()};else var a=function(){return Date.now()};var b=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="cancel",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()},c=window.Element.prototype.animate;window.Element.prototype.animate=function(d,e){var f=c.call(this,d,e);f._cancelHandlers=[],f.oncancel=null;var g=f.cancel;f.cancel=function(){g.call(this);var c=new b(this,null,a()),d=this._cancelHandlers.concat(this.oncancel?[this.oncancel]:[]);setTimeout(function(){d.forEach(function(a){a.call(c.target,c)})},0)};var h=f.addEventListener;f.addEventListener=function(a,b){"function"==typeof b&&"cancel"==a?this._cancelHandlers.push(b):h.call(this,a,b)};var i=f.removeEventListener;return f.removeEventListener=function(a,b){if("cancel"==a){var c=this._cancelHandlers.indexOf(b);c>=0&&this._cancelHandlers.splice(c,1)}else i.call(this,a,b)},f}}}(),function(a){var b=document.documentElement,c=null,d=!1;try{var e=getComputedStyle(b).getPropertyValue("opacity"),f="0"==e?"1":"0";c=b.animate({opacity:[f,f]},{duration:1}),c.currentTime=0,d=getComputedStyle(b).getPropertyValue("opacity")==f}catch(a){}finally{c&&c.cancel()}if(!d){var g=window.Element.prototype.animate;window.Element.prototype.animate=function(b,c){return window.Symbol&&Symbol.iterator&&Array.prototype.from&&b[Symbol.iterator]&&(b=Array.from(b)),Array.isArray(b)||null===b||(b=a.convertToArrayForm(b)),g.call(this,b,c)}}}(c),function(a,b,c){function d(a){var c=b.timeline;c.currentTime=a,c._discardAnimations(),0==c._animations.length?f=!1:requestAnimationFrame(d)}var e=window.requestAnimationFrame;window.requestAnimationFrame=function(a){return e(function(c){b.timeline._updateAnimationsPromises(),a(c),b.timeline._updateAnimationsPromises()})},b.AnimationTimeline=function(){this._animations=[],this.currentTime=void 0},b.AnimationTimeline.prototype={getAnimations:function(){return this._discardAnimations(),this._animations.slice()},_updateAnimationsPromises:function(){b.animationsWithPromises=b.animationsWithPromises.filter(function(a){return a._updatePromises()})},_discardAnimations:function(){this._updateAnimationsPromises(),this._animations=this._animations.filter(function(a){return"finished"!=a.playState&&"idle"!=a.playState})},_play:function(a){var c=new b.Animation(a,this);return this._animations.push(c),b.restartWebAnimationsNextTick(),c._updatePromises(),c._animation.play(),c._updatePromises(),c},play:function(a){return a&&a.remove(),this._play(a)}};var f=!1;b.restartWebAnimationsNextTick=function(){f||(f=!0,requestAnimationFrame(d))};var g=new b.AnimationTimeline;b.timeline=g;try{Object.defineProperty(window.document,"timeline",{configurable:!0,get:function(){return g}})}catch(a){}try{window.document.timeline=g}catch(a){}}(0,e),function(a,b,c){b.animationsWithPromises=[],b.Animation=function(b,c){if(this.id="",b&&b._id&&(this.id=b._id),this.effect=b,b&&(b._animation=this),!c)throw new Error("Animation with null timeline is not supported");this._timeline=c,this._sequenceNumber=a.sequenceNumber++,this._holdTime=0,this._paused=!1,this._isGroup=!1,this._animation=null,this._childAnimations=[],this._callback=null,this._oldPlayState="idle",this._rebuildUnderlyingAnimation(),this._animation.cancel(),this._updatePromises()},b.Animation.prototype={_updatePromises:function(){var a=this._oldPlayState,b=this.playState;return this._readyPromise&&b!==a&&("idle"==b?(this._rejectReadyPromise(),this._readyPromise=void 0):"pending"==a?this._resolveReadyPromise():"pending"==b&&(this._readyPromise=void 0)),this._finishedPromise&&b!==a&&("idle"==b?(this._rejectFinishedPromise(),this._finishedPromise=void 0):"finished"==b?this._resolveFinishedPromise():"finished"==a&&(this._finishedPromise=void 0)),this._oldPlayState=this.playState,this._readyPromise||this._finishedPromise},_rebuildUnderlyingAnimation:function(){this._updatePromises();var a,c,d,e,f=!!this._animation;f&&(a=this.playbackRate,c=this._paused,d=this.startTime,e=this.currentTime,this._animation.cancel(),this._animation._wrapper=null,this._animation=null),(!this.effect||this.effect instanceof window.KeyframeEffect)&&(this._animation=b.newUnderlyingAnimationForKeyframeEffect(this.effect),b.bindAnimationForKeyframeEffect(this)),(this.effect instanceof window.SequenceEffect||this.effect instanceof window.GroupEffect)&&(this._animation=b.newUnderlyingAnimationForGroup(this.effect),b.bindAnimationForGroup(this)),this.effect&&this.effect._onsample&&b.bindAnimationForCustomEffect(this),f&&(1!=a&&(this.playbackRate=a),null!==d?this.startTime=d:null!==e?this.currentTime=e:null!==this._holdTime&&(this.currentTime=this._holdTime),c&&this.pause()),this._updatePromises()},_updateChildren:function(){if(this.effect&&"idle"!=this.playState){var a=this.effect._timing.delay;this._childAnimations.forEach(function(c){this._arrangeChildren(c,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c.effect))}.bind(this))}},_setExternalAnimation:function(a){if(this.effect&&this._isGroup)for(var b=0;b<this.effect.children.length;b++)this.effect.children[b]._animation=a,this._childAnimations[b]._setExternalAnimation(a)},_constructChildAnimations:function(){if(this.effect&&this._isGroup){var a=this.effect._timing.delay;this._removeChildAnimations(),this.effect.children.forEach(function(c){var d=b.timeline._play(c);this._childAnimations.push(d),d.playbackRate=this.playbackRate,this._paused&&d.pause(),c._animation=this.effect._animation,this._arrangeChildren(d,a),this.effect instanceof window.SequenceEffect&&(a+=b.groupChildDuration(c))}.bind(this))}},_arrangeChildren:function(a,b){null===this.startTime?a.currentTime=this.currentTime-b/this.playbackRate:a.startTime!==this.startTime+b/this.playbackRate&&(a.startTime=this.startTime+b/this.playbackRate)},get timeline(){return this._timeline},get playState(){return this._animation?this._animation.playState:"idle"},get finished(){return window.Promise?(this._finishedPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._finishedPromise=new Promise(function(a,b){this._resolveFinishedPromise=function(){a(this)},this._rejectFinishedPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"finished"==this.playState&&this._resolveFinishedPromise()),this._finishedPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get ready(){return window.Promise?(this._readyPromise||(-1==b.animationsWithPromises.indexOf(this)&&b.animationsWithPromises.push(this),this._readyPromise=new Promise(function(a,b){this._resolveReadyPromise=function(){a(this)},this._rejectReadyPromise=function(){b({type:DOMException.ABORT_ERR,name:"AbortError"})}}.bind(this)),"pending"!==this.playState&&this._resolveReadyPromise()),this._readyPromise):(console.warn("Animation Promises require JavaScript Promise constructor"),null)},get onfinish(){return this._animation.onfinish},set onfinish(a){this._animation.onfinish="function"==typeof a?function(b){b.target=this,a.call(this,b)}.bind(this):a},get oncancel(){return this._animation.oncancel},set oncancel(a){this._animation.oncancel="function"==typeof a?function(b){b.target=this,a.call(this,b)}.bind(this):a},get currentTime(){this._updatePromises();var a=this._animation.currentTime;return this._updatePromises(),a},set currentTime(a){this._updatePromises(),this._animation.currentTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.currentTime=a-c}),this._updatePromises()},get startTime(){return this._animation.startTime},set startTime(a){this._updatePromises(),this._animation.startTime=isFinite(a)?a:Math.sign(a)*Number.MAX_VALUE,this._register(),this._forEachChild(function(b,c){b.startTime=a+c}),this._updatePromises()},get playbackRate(){return this._animation.playbackRate},set playbackRate(a){this._updatePromises();var b=this.currentTime;this._animation.playbackRate=a,this._forEachChild(function(b){b.playbackRate=a}),null!==b&&(this.currentTime=b),this._updatePromises()},play:function(){this._updatePromises(),this._paused=!1,this._animation.play(),-1==this._timeline._animations.indexOf(this)&&this._timeline._animations.push(this),this._register(),b.awaitStartTime(this),this._forEachChild(function(a){var b=a.currentTime;a.play(),a.currentTime=b}),this._updatePromises()},pause:function(){this._updatePromises(),this.currentTime&&(this._holdTime=this.currentTime),this._animation.pause(),this._register(),this._forEachChild(function(a){a.pause()}),this._paused=!0,this._updatePromises()},finish:function(){this._updatePromises(),this._animation.finish(),this._register(),this._updatePromises()},cancel:function(){this._updatePromises(),this._animation.cancel(),this._register(),this._removeChildAnimations(),this._updatePromises()},reverse:function(){this._updatePromises();var a=this.currentTime;this._animation.reverse(),this._forEachChild(function(a){a.reverse()}),null!==a&&(this.currentTime=a),this._updatePromises()},addEventListener:function(a,b){var c=b;"function"==typeof b&&(c=function(a){a.target=this,b.call(this,a)}.bind(this),b._wrapper=c),this._animation.addEventListener(a,c)},removeEventListener:function(a,b){this._animation.removeEventListener(a,b&&b._wrapper||b)},_removeChildAnimations:function(){for(;this._childAnimations.length;)this._childAnimations.pop().cancel()},_forEachChild:function(b){var c=0;if(this.effect.children&&this._childAnimations.length<this.effect.children.length&&this._constructChildAnimations(),this._childAnimations.forEach(function(a){b.call(this,a,c),this.effect instanceof window.SequenceEffect&&(c+=a.effect.activeDuration)}.bind(this)),"pending"!=this.playState){var d=this.effect._timing,e=this.currentTime;null!==e&&(e=a.calculateIterationProgress(a.calculateActiveDuration(d),e,d)),(null==e||isNaN(e))&&this._removeChildAnimations()}}},window.Animation=b.Animation}(c,e),function(a,b,c){function d(b){this._frames=a.normalizeKeyframes(b)}function e(){for(var a=!1;i.length;)i.shift()._updateChildren(),a=!0;return a}var f=function(a){if(a._animation=void 0,a instanceof window.SequenceEffect||a instanceof window.GroupEffect)for(var b=0;b<a.children.length;b++)f(a.children[b])};b.removeMulti=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c];d._parent?(-1==b.indexOf(d._parent)&&b.push(d._parent),d._parent.children.splice(d._parent.children.indexOf(d),1),d._parent=null,f(d)):d._animation&&d._animation.effect==d&&(d._animation.cancel(),d._animation.effect=new KeyframeEffect(null,[]),d._animation._callback&&(d._animation._callback._animation=null),d._animation._rebuildUnderlyingAnimation(),f(d))}for(c=0;c<b.length;c++)b[c]._rebuild()},b.KeyframeEffect=function(b,c,e,f){return this.target=b,this._parent=null,e=a.numericTimingToObject(e),this._timingInput=a.cloneTimingInput(e),this._timing=a.normalizeTimingInput(e),this.timing=a.makeTiming(e,!1,this),this.timing._effect=this,"function"==typeof c?(a.deprecated("Custom KeyframeEffect","2015-06-22","Use KeyframeEffect.onsample instead."),this._normalizedKeyframes=c):this._normalizedKeyframes=new d(c),this._keyframes=c,this.activeDuration=a.calculateActiveDuration(this._timing),this._id=f,this},b.KeyframeEffect.prototype={getFrames:function(){return"function"==typeof this._normalizedKeyframes?this._normalizedKeyframes:this._normalizedKeyframes._frames},set onsample(a){if("function"==typeof this.getFrames())throw new Error("Setting onsample on custom effect KeyframeEffect is not supported.");this._onsample=a,this._animation&&this._animation._rebuildUnderlyingAnimation()},get parent(){return this._parent},clone:function(){if("function"==typeof this.getFrames())throw new Error("Cloning custom effects is not supported.");var b=new KeyframeEffect(this.target,[],a.cloneTimingInput(this._timingInput),this._id);return b._normalizedKeyframes=this._normalizedKeyframes,b._keyframes=this._keyframes,b},remove:function(){b.removeMulti([this])}};var g=Element.prototype.animate;Element.prototype.animate=function(a,c){var d="";return c&&c.id&&(d=c.id),b.timeline._play(new b.KeyframeEffect(this,a,c,d))};var h=document.createElementNS("http://www.w3.org/1999/xhtml","div");b.newUnderlyingAnimationForKeyframeEffect=function(a){if(a){var b=a.target||h,c=a._keyframes;"function"==typeof c&&(c=[]);var d=a._timingInput;d.id=a._id}else var b=h,c=[],d=0;return g.apply(b,[c,d])},b.bindAnimationForKeyframeEffect=function(a){a.effect&&"function"==typeof a.effect._normalizedKeyframes&&b.bindAnimationForCustomEffect(a)};var i=[];b.awaitStartTime=function(a){null===a.startTime&&a._isGroup&&(0==i.length&&requestAnimationFrame(e),i.push(a))};var j=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){b.timeline._updateAnimationsPromises();var a=j.apply(this,arguments);return e()&&(a=j.apply(this,arguments)),b.timeline._updateAnimationsPromises(),a}}),window.KeyframeEffect=b.KeyframeEffect,window.Element.prototype.getAnimations=function(){return document.timeline.getAnimations().filter(function(a){return null!==a.effect&&a.effect.target==this}.bind(this))}}(c,e),function(a,b,c){function d(a){a._registered||(a._registered=!0,g.push(a),h||(h=!0,requestAnimationFrame(e)))}function e(a){var b=g;g=[],b.sort(function(a,b){return a._sequenceNumber-b._sequenceNumber}),b=b.filter(function(a){a();var b=a._animation?a._animation.playState:"idle";return"running"!=b&&"pending"!=b&&(a._registered=!1),a._registered}),g.push.apply(g,b),g.length?(h=!0,requestAnimationFrame(e)):h=!1}var f=(document.createElementNS("http://www.w3.org/1999/xhtml","div"),0);b.bindAnimationForCustomEffect=function(b){var c,e=b.effect.target,g="function"==typeof b.effect.getFrames();c=g?b.effect.getFrames():b.effect._onsample;var h=b.effect.timing,i=null;h=a.normalizeTimingInput(h);var j=function(){var d=j._animation?j._animation.currentTime:null;null!==d&&(d=a.calculateIterationProgress(a.calculateActiveDuration(h),d,h),isNaN(d)&&(d=null)),d!==i&&(g?c(d,e,b.effect):c(d,b.effect,b.effect._animation)),i=d};j._animation=b,j._registered=!1,j._sequenceNumber=f++,b._callback=j,d(j)};var g=[],h=!1;b.Animation.prototype._register=function(){this._callback&&d(this._callback)}}(c,e),function(a,b,c){function d(a){return a._timing.delay+a.activeDuration+a._timing.endDelay}function e(b,c,d){this._id=d,this._parent=null,this.children=b||[],this._reparent(this.children),c=a.numericTimingToObject(c),this._timingInput=a.cloneTimingInput(c),this._timing=a.normalizeTimingInput(c,!0),this.timing=a.makeTiming(c,!0,this),this.timing._effect=this,"auto"===this._timing.duration&&(this._timing.duration=this.activeDuration)}window.SequenceEffect=function(){e.apply(this,arguments)},window.GroupEffect=function(){e.apply(this,arguments)},e.prototype={_isAncestor:function(a){for(var b=this;null!==b;){if(b==a)return!0;b=b._parent}return!1},_rebuild:function(){for(var a=this;a;)"auto"===a.timing.duration&&(a._timing.duration=a.activeDuration),a=a._parent;this._animation&&this._animation._rebuildUnderlyingAnimation()},_reparent:function(a){b.removeMulti(a);for(var c=0;c<a.length;c++)a[c]._parent=this},_putChild:function(a,b){for(var c=b?"Cannot append an ancestor or self":"Cannot prepend an ancestor or self",d=0;d<a.length;d++)if(this._isAncestor(a[d]))throw{type:DOMException.HIERARCHY_REQUEST_ERR,name:"HierarchyRequestError",message:c};for(var d=0;d<a.length;d++)b?this.children.push(a[d]):this.children.unshift(a[d]);this._reparent(a),this._rebuild()},append:function(){this._putChild(arguments,!0)},prepend:function(){this._putChild(arguments,!1)},get parent(){return this._parent},get firstChild(){return this.children.length?this.children[0]:null},get lastChild(){return this.children.length?this.children[this.children.length-1]:null},clone:function(){for(var b=a.cloneTimingInput(this._timingInput),c=[],d=0;d<this.children.length;d++)c.push(this.children[d].clone());return this instanceof GroupEffect?new GroupEffect(c,b):new SequenceEffect(c,b)},remove:function(){b.removeMulti([this])}},window.SequenceEffect.prototype=Object.create(e.prototype),Object.defineProperty(window.SequenceEffect.prototype,"activeDuration",{get:function(){var a=0;return this.children.forEach(function(b){a+=d(b)}),Math.max(a,0)}}),window.GroupEffect.prototype=Object.create(e.prototype),Object.defineProperty(window.GroupEffect.prototype,"activeDuration",{get:function(){var a=0;return this.children.forEach(function(b){a=Math.max(a,d(b))}),a}}),b.newUnderlyingAnimationForGroup=function(c){var d,e=null,f=function(b){var c=d._wrapper;if(c&&"pending"!=c.playState&&c.effect)return null==b?void c._removeChildAnimations():0==b&&c.playbackRate<0&&(e||(e=a.normalizeTimingInput(c.effect.timing)),b=a.calculateIterationProgress(a.calculateActiveDuration(e),-1,e),isNaN(b)||null==b)?(c._forEachChild(function(a){a.currentTime=-1}),void c._removeChildAnimations()):void 0},g=new KeyframeEffect(null,[],c._timing,c._id);return g.onsample=f,d=b.timeline._play(g)},b.bindAnimationForGroup=function(a){a._animation._wrapper=a,a._isGroup=!0,b.awaitStartTime(a),a._constructChildAnimations(),a._setExternalAnimation(a)},b.groupChildDuration=d}(c,e),b.true=a}({},function(){return this}());
+//# sourceMappingURL=web-animations-next.min.js.map \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js.map b/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js.map
new file mode 100644
index 00000000..0646f1cf
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations-next.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["src/scope.js","src/timing-utilities.js","src/normalize-keyframes.js","src/deprecation.js","src/web-animations-bonus-cancel-events.js","src/web-animations-bonus-object-form-keyframes.js","src/timeline.js","src/web-animations-next-animation.js","src/keyframe-effect-constructor.js","src/effect-callback.js","src/group-constructors.js"],"names":["webAnimationsShared","webAnimations1","webAnimationsNext","shared","testing","cloneTimingInput","timingInput","clone","m","AnimationEffectTiming","this","_delay","_endDelay","_fill","_iterationStart","_iterations","_duration","_playbackRate","_direction","_easing","_easingFunction","linear","isInvalidTimingDeprecated","isDeprecated","makeTiming","forGroup","effect","timing","fill","duration","isNaN","Object","getOwnPropertyNames","forEach","property","fills","indexOf","directions","numericTimingToObject","normalizeTimingInput","cubic","a","b","c","d","x","f","start_gradient","end_gradient","start","end","mid","xEst","Math","abs","step","count","pos","stepSize","normalizeEasing","easing","styleForCleaning","document","createElement","style","animationTimingFunction","normalizedEasing","TypeError","parseEasingFunction","cubicData","cubicBezierRe","exec","apply","slice","map","Number","stepData","stepRe","Start","middle","Middle","End","presets","calculateActiveDuration","repeatedDuration","playbackRate","iterations","calculatePhase","activeDuration","localTime","PhaseNone","endTime","delay","endDelay","min","PhaseBefore","PhaseAfter","PhaseActive","calculateActiveTime","fillMode","phase","calculateOverallProgress","iterationDuration","activeTime","iterationStart","overallProgress","calculateSimpleIterationProgress","simpleIterationProgress","Infinity","calculateCurrentIteration","floor","calculateDirectedProgress","playbackDirection","currentIteration","currentDirection","calculateIterationProgress","directedProgress","direction","split","prototype","_setMember","member","value","_effect","_timingInput","_timing","_animation","_rebuildUnderlyingAnimation","ease","ease-in","ease-out","ease-in-out","step-start","step-middle","step-end","numberString","RegExp","antiAlias","aliases","isNotAnimatable","lastIndexOf","expandShorthandAndAntiAlias","result","longProperties","shorthandToLonghand","shorthandExpanderElem","i","longProperty","longhandValue","convertToArrayForm","effectInput","normalizedEffectInput","values","Array","isArray","keyframe","numKeyframes","length","offset","composite","push","sort","normalizeKeyframes","spaceKeyframes","keyframes","previousIndex","previousOffset","j","window","Symbol","iterator","from","originalKeyframe","memberValue","isFinite","type","DOMException","NOT_SUPPORTED_ERR","name","message","everyFrameHasOffset","filter","background","border","borderBottom","borderColor","borderLeft","borderRadius","borderRight","borderTop","borderWidth","flex","font","margin","outline","padding","createElementNS","borderWidthAliases","thin","medium","thick","borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopWidth","fontSize","xx-small","x-small","small","large","x-large","xx-large","fontWeight","normal","bold","outlineWidth","textShadow","none","boxShadow","silenced","feature","date","advice","plural","auxVerb","today","Date","expiry","setMonth","getMonth","console","warn","toDateString","deprecated","Error","animate","oncancel","now","performance","AnimationCancelEvent","target","currentTime","timelineTime","bubbles","cancelable","currentTarget","defaultPrevented","eventPhase","Event","AT_TARGET","timeStamp","originalElementAnimate","Element","options","animation","call","_cancelHandlers","originalCancel","cancel","event","handlers","concat","setTimeout","handler","originalAddEventListener","addEventListener","originalRemoveEventListener","removeEventListener","index","splice","element","documentElement","animated","originalOpacity","getComputedStyle","getPropertyValue","testOpacity","opacity","error","scope","webAnimationsNextTick","t","timeline","_discardAnimations","_animations","ticking","requestAnimationFrame","originalRequestAnimationFrame","_updateAnimationsPromises","AnimationTimeline","getAnimations","animationsWithPromises","_updatePromises","playState","_play","Animation","restartWebAnimationsNextTick","play","remove","defineProperty","configurable","get","e","id","_id","_timeline","_sequenceNumber","sequenceNumber","_holdTime","_paused","_isGroup","_childAnimations","_callback","_oldPlayState","oldPlayState","newPlayState","_readyPromise","_rejectReadyPromise","_resolveReadyPromise","_finishedPromise","_rejectFinishedPromise","_resolveFinishedPromise","oldPlaybackRate","oldPaused","oldStartTime","oldCurrentTime","hadUnderlying","startTime","_wrapper","KeyframeEffect","newUnderlyingAnimationForKeyframeEffect","bindAnimationForKeyframeEffect","SequenceEffect","GroupEffect","newUnderlyingAnimationForGroup","bindAnimationForGroup","_onsample","bindAnimationForCustomEffect","pause","_updateChildren","childAnimation","_arrangeChildren","groupChildDuration","bind","_setExternalAnimation","children","_constructChildAnimations","_removeChildAnimations","child","finished","Promise","resolve","reject","ABORT_ERR","ready","onfinish","v","sign","MAX_VALUE","_register","_forEachChild","awaitStartTime","time","finish","reverse","wrapped","pop","KeyframeList","_frames","updatePendingGroups","updated","pendingGroups","shift","disassociate","removeMulti","effects","oldParents","_parent","_rebuild","_normalizedKeyframes","_keyframes","getFrames","onsample","callback","parent","nullTarget","keyframeEffect","groupAnimation","originalGetComputedStyle","enumerable","arguments","register","_registered","callbacks","tick","updating","left","right","effectFunction","isKeyframeEffect","last","node","constructor","_reparent","_isAncestor","newChildren","_putChild","args","isAppend","HIERARCHY_REQUEST_ERR","unshift","append","prepend","firstChild","lastChild","clonedTiming","clonedChildren","create","total","max","group","underlyingAnimation","ticker","tf","underlyingEffect","exports","webAnimationsTesting"],"mappings":";;;;;;;;;;;;;;CAcA,SAAIA,EAAAA,GAAJ,GAAIA,MACAC,KACAC,MCFJ,SAAUC,EAAQC,GAMhB,QAASC,GAAiBC,GACxB,GAA0B,gBAAfA,GACT,MAAOA,EAET,IAAIC,KACJ,KAAK,GAAIC,KAAKF,GACZC,EAAMC,GAAKF,EAAYE,EAEzB,OAAOD,GAGT,QAASE,KACPC,KAAKC,OAAS,EACdD,KAAKE,UAAY,EACjBF,KAAKG,MAAQ,OACbH,KAAKI,gBAAkB,EACvBJ,KAAKK,YAAc,EACnBL,KAAKM,UAAY,EACjBN,KAAKO,cAAgB,EACrBP,KAAKQ,WAAa,SAClBR,KAAKS,QAAU,SACfT,KAAKU,gBAAkBC,EAGzB,QAASC,KACP,MAAOnB,GAAOoB,aAAa,wBAAyB,aAAc,gDAAA,GA8EpE,QAASC,GAAWlB,EAAamB,EAAUC,GACzC,GAAIC,GAAS,GAAIlB,EA4BjB,OA3BIgB,KACFE,EAAOC,KAAO,OACdD,EAAOE,SAAW,QAEM,gBAAfvB,IAA4BwB,MAAMxB,OAAAA,KAElCA,GACTyB,OAAOC,oBAAoB1B,GAAa2B,QAAQ,SAASC,GACvD,GAA6B,QAAzB5B,EAAY4B,GAAqB,CACnC,IAA+B,gBAApBP,GAAOO,IAAqC,YAAZA,KACL,gBAAzB5B,GAAY4B,IAAyBJ,MAAMxB,EAAY4B,KAChE,MAGJ,IAAiB,QAAZA,IAAiE,GAAzCC,EAAMC,QAAQ9B,EAAY4B,IACrD,MAEF,IAAiB,aAAZA,IAA2E,GAA9CG,EAAWD,QAAQ9B,EAAY4B,IAC/D,MAEF,IAAgB,gBAAZA,GAAwD,IAA1B5B,EAAY4B,IAAmB/B,EAAOoB,aAAa,qCAAsC,aAAc,uCACvI,MAEFI,GAAOO,GAAY5B,EAAY4B,MAlBnCP,EAAOE,SAAWvB,EAsBbqB,EAGT,QAASW,GAAsBhC,GAQ7B,MAP0B,gBAAfA,KAEPA,EADEwB,MAAMxB,IACQuB,SAAU,IAEVA,SAAUvB,IAGvBA,EAGT,QAASiC,GAAqBjC,EAAamB,GAEzC,MADAnB,GAAcH,EAAOmC,sBAAsBhC,GACpCkB,EAAWlB,EAAamB,GAGjC,QAASe,GAAMC,EAAGC,EAAGC,EAAGC,GACtB,MAAIH,GAAI,GAAKA,EAAI,GAAKE,EAAI,GAAKA,EAAI,EAC1BtB,EAEF,SAASwB,GAqBZ,QAASC,GAAEL,EAAGC,EAAGlC,GAAK,MAAO,GAAIiC,GAAK,EAAIjC,IAAM,EAAIA,GAAKA,EAAI,EAAIkC,GAAK,EAAIlC,GAAKA,EAAIA,EAAIA,EAAIA,EAAIA,EApBjG,GAAIqC,GAAK,EAAG,CACV,GAAIE,GAAiB,CAKrB,OAJIN,GAAI,EACNM,EAAiBL,EAAID,GACbC,GAAKC,EAAI,IACjBI,EAAiBH,EAAID,GAChBI,EAAiBF,EAE1B,GAAIA,GAAK,EAAG,CACV,GAAIG,GAAe,CAKnB,OAJIL,GAAI,EACNK,GAAgBJ,EAAI,IAAMD,EAAI,GAClB,GAALA,GAAUF,EAAI,IACrBO,GAAgBN,EAAI,IAAMD,EAAI,IACzB,EAAIO,GAAgBH,EAAI,GAIjC,IADA,GAAII,GAAQ,EAAGC,EAAM,EACdD,EAAQC,GAAK,CAClB,GAAIC,IAAOF,EAAQC,GAAO,EAEtBE,EAAON,EAAEL,EAAGE,EAAGQ,EACnB,IAAIE,KAAKC,IAAIT,EAAIO,GAAQ,KACvB,MAAON,GAAEJ,EAAGE,EAAGO,EAEbC,GAAOP,EACTI,EAAQE,EAERD,EAAMC,EAGV,MAAOL,GAAEJ,EAAGE,EAAGO,IAQnB,QAASI,GAAKC,EAAOC,GACnB,MAAO,UAASZ,GACd,GAAIA,GAAK,EACP,MAAO,EAET,IAAIa,GAAW,EAAIF,CAEnB,QADAX,GAAKY,EAAMC,GACAb,EAAIa,GAmBnB,QAASC,GAAgBC,GAClBC,IACHA,EAAmBC,SAASC,cAAc,OAAOC,OAEnDH,EAAiBI,wBAA0B,GAC3CJ,EAAiBI,wBAA0BL,CAC3C,IAAIM,GAAmBL,EAAiBI,uBACxC,IAAwB,IAApBC,GAA0B5C,IAC5B,KAAM,IAAI6C,WAAUP,EAAS,mCAE/B,OAAOM,GAGT,QAASE,GAAoBF,GAC3B,GAAwB,UAApBA,EACF,MAAO7C,EAET,IAAIgD,GAAYC,EAAcC,KAAKL,EACnC,IAAIG,EACF,MAAO7B,GAAMgC,MAAM9D,KAAM2D,EAAUI,MAAM,GAAGC,IAAIC,QAElD,IAAIC,GAAWC,EAAON,KAAKL,EAC3B,OAAIU,GACKrB,EAAKoB,OAAOC,EAAS,KAAM3B,MAAS6B,EAAOC,OAAUC,EAAQ9B,IAAO+B,GAAKL,EAAS,KAE9EM,EAAQhB,IAMd7C,EAGT,QAAS8D,GAAwBxD,GAC/B,MAAO0B,MAAKC,IAAI8B,EAAiBzD,GAAUA,EAAO0D,cAGpD,QAASD,GAAiBzD,GAExB,MAAwB,KAApBA,EAAOE,UAAwC,IAAtBF,EAAO2D,WAC3B,EAEF3D,EAAOE,SAAWF,EAAO2D,WAQlC,QAASC,GAAeC,EAAgBC,EAAW9D,GAEjD,GAAiB,MAAb8D,EACF,MAAOC,EAGT,IAAIC,GAAUhE,EAAOiE,MAAQJ,EAAiB7D,EAAOkE,QACrD,OAAIJ,GAAYpC,KAAKyC,IAAInE,EAAOiE,MAAOD,GAC9BI,EAELN,GAAapC,KAAKyC,IAAInE,EAAOiE,MAAQJ,EAAgBG,GAChDK,EAGFC,EAGT,QAASC,GAAoBV,EAAgBW,EAAUV,EAAWW,EAAOR,GAEvE,OAAQQ,GACN,IAAKL,GACH,MAAgB,aAAZI,GAAuC,QAAZA,EACtB,EACF,IACT,KAAKF,GACH,MAAOR,GAAYG,CACrB,KAAKI,GACH,MAAgB,YAAZG,GAAsC,QAAZA,EACrBX,EACF,IACT,KAAKE,GACH,MAAO,OAIb,QAASW,GAAyBC,EAAmBF,EAAOd,EAAYiB,EAAYC,GAElF,GAAIC,GAAkBD,CAQtB,OAP0B,KAAtBF,EACEF,IAAUL,IACZU,GAAmBnB,GAGrBmB,GAAmBF,EAAaD,EAE3BG,EAGT,QAASC,GAAiCD,EAAiBD,EAAgBJ,EAAOd,EAAYiB,EAAYD,GAGxG,GAAIK,GAA2BF,IAAoBG,EAAAA,EAAYJ,EAAiB,EAAIC,EAAkB,CAKtG,OAJgC,KAA5BE,GAAiCP,IAAUJ,GAA6B,IAAfV,GACzC,IAAfiB,GAA0C,IAAtBD,IACvBK,EAA0B,GAErBA,EAGT,QAASE,GAA0BT,EAAOd,EAAYqB,EAAyBF,GAE7E,MAAIL,KAAUJ,GAAcV,IAAesB,EAAAA,EAClCA,EAAAA,EAEuB,IAA5BD,EACKtD,KAAKyD,MAAML,GAAmB,EAEhCpD,KAAKyD,MAAML,GAGpB,QAASM,GAA0BC,EAAmBC,EAAkBN,GAEtE,GAAIO,GAAmBF,CACvB,IAA0B,WAAtBA,GAAwD,YAAtBA,EAAiC,CACrE,GAAIpE,GAAIqE,CACkB,uBAAtBD,IACFpE,GAAK,GAEPsE,EAAmB,SACftE,IAAMgE,EAAAA,GAAYhE,EAAI,GAAM,IAC9BsE,EAAmB,WAGvB,MAAyB,WAArBA,EACKP,EAEF,EAAIA,EAGb,QAASQ,GAA2B3B,EAAgBC,EAAW9D,GAC7D,GAAIyE,GAAQb,EAAeC,EAAgBC,EAAW9D,GAClD4E,EAAaL,EAAoBV,EAAgB7D,EAAOC,KAAM6D,EAAWW,EAAOzE,EAAOiE,MAC3F,IAAmB,OAAfW,EACF,MAAO,KAET,IAAIE,GAAkBJ,EAAyB1E,EAAOE,SAAUuE,EAAOzE,EAAO2D,WAAYiB,EAAY5E,EAAO6E,gBACzGG,EAA0BD,EAAiCD,EAAiB9E,EAAO6E,eAAgBJ,EAAOzE,EAAO2D,WAAYiB,EAAY5E,EAAOE,UAChJoF,EAAmBJ,EAA0BT,EAAOzE,EAAO2D,WAAYqB,EAAyBF,GAChGW,EAAmBL,EAA0BpF,EAAO0F,UAAWJ,EAAkBN,EAIrF,OAAOhF,GAAOP,gBAAgBgG,GA1XhC,GAAIjF,GAAQ,+BAA+BmF,MAAM,KAC7CjF,EAAa,sCAAsCiF,MAAM,KACzDjG,EAAS,SAASwB,GAAK,MAAOA,GA8BlCpC,GAAsB8G,WACpBC,WAAY,SAASC,EAAQC,GAC3BhH,KAAK,IAAM+G,GAAUC,EACjBhH,KAAKiH,UACPjH,KAAKiH,QAAQC,aAAaH,GAAUC,EACpChH,KAAKiH,QAAQE,QAAU1H,EAAOoC,qBAAqB7B,KAAKiH,QAAQC,cAChElH,KAAKiH,QAAQnC,eAAiBrF,EAAOgF,wBAAwBzE,KAAKiH,QAAQE,SACtEnH,KAAKiH,QAAQG,YACfpH,KAAKiH,QAAQG,WAAWC,gCAI9B1C,mBACE,MAAO3E,MAAKO,eAEd2E,UAAU8B,GACRhH,KAAK8G,WAAW,QAASE,IAE3B9B,YACE,MAAOlF,MAAKC,QAEdkF,aAAa6B,GACXhH,KAAK8G,WAAW,WAAYE,IAE9B7B,eACE,MAAOnF,MAAKE,WAEdgB,SAAS8F,GACPhH,KAAK8G,WAAW,OAAQE,IAE1B9F,WACE,MAAOlB,MAAKG,OAEd2F,mBAAmBkB,GACjB,IAAK5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACjC,KAAM,IAAI6C,WAAU,2DAA6DxC,OAAO6E,eAE1F9F,MAAK8G,WAAW,iBAAkBE,IAEpClB,qBACE,MAAO9F,MAAKI,iBAEde,aAAa6F,GACX,GAAa,QAATA,IAAoB5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACpD,KAAM,IAAI6C,WAAU,oDAAsDuD,EAE5EhH,MAAK8G,WAAW,WAAYE,IAE9B7F,eACE,MAAOnB,MAAKM,WAEdqG,cAAcK,GACZhH,KAAK8G,WAAW,YAAaE,IAE/BL,gBACE,MAAO3G,MAAKQ,YAEd0C,WAAW8D,GACThH,KAAKU,gBAAkBgD,EAAoBT,EAAgB+D,IAC3DhH,KAAK8G,WAAW,SAAUE,IAE5B9D,aACE,MAAOlD,MAAKS,SAEdmE,eAAeoC,GACb,IAAK5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACjC,KAAM,IAAI6C,WAAU,8CAAgDuD,EAEtEhH,MAAK8G,WAAW,aAAcE,IAEhCpC,iBACE,MAAO5E,MAAKK,aA4FhB,IAAI+D,GAAQ,EACRE,EAAS,GACTC,EAAM,EAaNC,GACF8C,KAAQxF,EAAM,IAAM,GAAK,IAAM,GAC/ByF,UAAWzF,EAAM,IAAM,EAAG,EAAG,GAC7B0F,WAAY1F,EAAM,EAAG,EAAG,IAAM,GAC9B2F,cAAe3F,EAAM,IAAM,EAAG,IAAM,GACpC4F,aAAc7E,EAAK,EAAGuB,GACtBuD,cAAe9E,EAAK,EAAGyB,GACvBsD,WAAY/E,EAAK,EAAG0B,IAGlBpB,EAAmB,KACnB0E,EAAe,qCACfjE,EAAgB,GAAIkE,QAAO,kBAAoBD,EAAe,IAAMA,EAAe,IAAMA,EAAe,IAAMA,EAAe,OAC7H1D,EAAS,gDAgDTa,EAAY,EACZK,EAAc,EACdC,EAAa,EACbC,EAAc,CA2GlB9F,GAAOE,iBAAmBA,EAC1BF,EAAOqB,WAAaA,EACpBrB,EAAOmC,sBAAwBA,EAC/BnC,EAAOoC,qBAAuBA,EAC9BpC,EAAOgF,wBAA0BA,EACjChF,EAAOgH,2BAA6BA,EACpChH,EAAOoF,eAAiBA,EACxBpF,EAAOwD,gBAAkBA,EACzBxD,EAAOiE,oBAAsBA,GAc5BpE,GCrZH,SAAUG,EAAQC,GAmIhB,QAASqI,GAAUvG,EAAUwF,GAC3B,MAAIxF,KAAYwG,GACPA,EAAQxG,GAAUwF,IAAUA,EAE9BA,EAGT,QAASiB,GAAgBzG,GAEvB,MAAoB,YAAbA,GAAmE,IAAzCA,EAAS0G,YAAY,YAAa,IAAsD,IAA1C1G,EAAS0G,YAAY,aAAc,GAIpH,QAASC,GAA4B3G,EAAUwF,EAAOoB,GACpD,IAAIH,EAAgBzG,GAApB,CAGA,GAAI6G,GAAiBC,EAAoB9G,EACzC,IAAI6G,EAAgB,CAClBE,EAAsBjF,MAAM9B,GAAYwF,CACxC,KAAK,GAAIwB,KAAKH,GAAgB,CAC5B,GAAII,GAAeJ,EAAeG,GAC9BE,EAAgBH,EAAsBjF,MAAMmF,EAChDL,GAAOK,GAAgBV,EAAUU,EAAcC,QAGjDN,GAAO5G,GAAYuG,EAAUvG,EAAUwF,IAI3C,QAAS2B,GAAmBC,GAC1B,GAAIC,KAEJ,KAAK,GAAIrH,KAAYoH,GACnB,KAAIpH,KAAa,SAAU,SAAU,cAArC,CAIA,GAAIsH,GAASF,EAAYpH,EACpBuH,OAAMC,QAAQF,KACjBA,GAAUA,GAKZ,KAAK,GAFDG,GACAC,EAAeJ,EAAOK,OACjBX,EAAI,EAAGA,EAAIU,EAAcV,IAChCS,KAGEA,EAASG,OADP,UAAYR,GACIA,EAAYQ,OACL,GAAhBF,EACS,EAEAV,GAAKU,EAAe,GAGpC,UAAYN,KACdK,EAAS/F,OAAS0F,EAAY1F,QAG5B,aAAe0F,KACjBK,EAASI,UAAYT,EAAYS,WAGnCJ,EAASzH,GAAYsH,EAAON,GAE5BK,EAAsBS,KAAKL,GAK/B,MADAJ,GAAsBU,KAAK,SAASxH,EAAGC,GAAK,MAAOD,GAAEqH,OAASpH,EAAEoH,SACzDP,EAGT,QAASW,GAAmBZ,GAqE1B,QAASa,KACP,GAAIN,GAASO,EAAUP,MACa,OAAhCO,EAAUP,EAAS,GAAGC,SACxBM,EAAUP,EAAS,GAAGC,OAAS,GAC7BD,EAAS,GAA4B,MAAvBO,EAAU,GAAGN,SAC7BM,EAAU,GAAGN,OAAS,EAIxB,KAAK,GAFDO,GAAgB,EAChBC,EAAiBF,EAAU,GAAGN,OACzBZ,EAAI,EAAGA,EAAIW,EAAQX,IAAK,CAC/B,GAAIY,GAASM,EAAUlB,GAAGY,MAC1B,IAAc,MAAVA,EAAgB,CAClB,IAAK,GAAIS,GAAI,EAAGA,EAAIrB,EAAImB,EAAeE,IACrCH,EAAUC,EAAgBE,GAAGT,OAASQ,GAAkBR,EAASQ,GAAkBC,GAAKrB,EAAImB,EAC9FA,GAAgBnB,EAChBoB,EAAiBR,IAnFvB,GAAmB,MAAfR,EACF,QAGEkB,QAAOC,QAAUA,OAAOC,UAAYjB,MAAMlC,UAAUoD,MAAQrB,EAAYmB,OAAOC,YAEjFpB,EAAcG,MAAMkB,KAAKrB,IAGtBG,MAAMC,QAAQJ,KACjBA,EAAcD,EAAmBC,GA0CnC,KAAK,GAvCDc,GAAYd,EAAY5E,IAAI,SAASkG,GACvC,GAAIjB,KACJ,KAAK,GAAIlC,KAAUmD,GAAkB,CACnC,GAAIC,GAAcD,EAAiBnD,EACnC,IAAc,UAAVA,GACF,GAAmB,MAAfoD,EAAqB,CAEvB,GADAA,EAAclG,OAAOkG,IAChBC,SAASD,GACZ,KAAM,IAAI1G,WAAU,oCACtB,IAAI0G,EAAc,GAAKA,EAAc,EACnC,KAAM,IAAI1G,WAAU,kDAEnB,IAAc,aAAVsD,EAAuB,CAChC,GAAmB,OAAfoD,GAAuC,cAAfA,EAC1B,MACEE,KAAMC,aAAaC,kBACnBC,KAAM,oBACNC,QAAS,mCAEN,IAAmB,WAAfN,EACT,KAAM,IAAI1G,WAAU,0BAA4B0G,EAAc,SAGhEA,GADmB,UAAVpD,EACKtH,EAAOwD,gBAAgBkH,GAEvB,GAAKA,CAErBhC,GAA4BpB,EAAQoD,EAAalB,GAMnD,WAAA,IAJIA,EAASG,SACXH,EAASG,OAAS,UAAA,IAChBH,EAAS/F,SACX+F,EAAS/F,OAAS,UACb+F,IAGLyB,GAAAA,EAEAd,GAAAA,EAAAA,EACKpB,EAAI,EAAGA,EAAIkB,EAAUP,OAAQX,IAAK,CACzC,GAAIY,GAASM,EAAUlB,GAAGY,MAC1B,IAAc,MAAVA,EAAgB,CAClB,GAAIA,EAASQ,EACX,KAAM,IAAInG,WAAU,uEAEtBmG,GAAiBR,MAEjBsB,IAAAA,EA8BJ,MA1BAhB,GAAYA,EAAUiB,OAAO,SAAS1B,GACpC,MAAOA,GAASG,QAAU,GAAKH,EAASG,QAAU,IAsB/CsB,GACHjB,IAEKC,EAvST,GAAIpB,IACFsC,YACE,kBACA,qBACA,iBACA,mBACA,uBACA,mBACA,iBACA,mBAEFC,QACE,iBACA,iBACA,iBACA,mBACA,mBACA,mBACA,oBACA,oBACA,oBACA,kBACA,kBACA,mBAEFC,cACE,oBACA,oBACA,qBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,YACE,kBACA,kBACA,mBAEFC,cACE,sBACA,uBACA,0BACA,0BAEFC,aACE,mBACA,mBACA,oBAEFC,WACE,iBACA,iBACA,kBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,MACE,WACA,aACA,aAEFC,MACE,aACA,WACA,YACA,cACA,aACA,cAEFC,QACE,YACA,cACA,eACA,cAEFC,SACE,eACA,eACA,gBAEFC,SACE,aACA,eACA,gBACA,gBAIAlD,EAAwBnF,SAASsI,gBAAgB,+BAAgC,OAEjFC,GACFC,KAAM,MACNC,OAAQ,MACRC,MAAO,OAGL9D,GACF+D,kBAAmBJ,EACnBK,gBAAiBL,EACjBM,iBAAkBN,EAClBO,eAAgBP,EAChBQ,UACEC,WAAY,MACZC,UAAW,MACXC,MAAS,MACTT,OAAU,OACVU,MAAS,OACTC,UAAW,OACXC,WAAY,QAEdC,YACEC,OAAQ,MACRC,KAAM,OAERC,aAAclB,EACdmB,YACEC,KAAM,2BAERC,WACED,KAAM,+BA4KVtN,GAAOkJ,mBAAqBA,EAC5BlJ,EAAO+J,mBAAqBA,GAM3BlK,GClTH,SAAUG,GAER,GAAIwN,KAEJxN,GAAOoB,aAAe,SAASqM,EAASC,EAAMC,EAAQC,GAKpD,GAAIC,GAAUD,EAAS,MAAQ,KAC3BE,EAAQ,GAAIC,MACZC,EAAS,GAAID,MAAKL,EAGtB,OAFAM,GAAOC,SAASD,EAAOE,WAAa,KAEhCJ,EAAQE,IACJP,IAAWD,IACfW,QAAQC,KAAK,mBAAqBX,EAAU,IAAMI,EAAU,wCAA0CG,EAAOK,eAAiB,KAAOV,GAEvIH,EAASC,IAAAA,EAAW,KAOxBzN,EAAOsO,WAAa,SAASb,EAASC,EAAMC,EAAQC,GAClD,GAAIC,GAAUD,EAAS,MAAQ,IAC/B,IAAI5N,EAAOoB,aAAaqM,EAASC,EAAMC,EAAQC,GAC7C,KAAM,IAAIW,OAAMd,EAAU,IAAMI,EAAU,yBAA2BF,KAIxE9N,62mCChCH,WAEE,OAAA,KAAI8D,SAASC,cAAc,OAAO4K,YAAYC,SAA9C,CAKE,GAAIC,EACC,IAAIrE,OAAOsE,aAAeA,YAAYD,IAC3C,GAAIA,GAAM,WAAa,MAAOC,aAAYD,WAE1C,IAAIA,GAAM,WAAa,MAAOX,MAAKW,MAGrC,IAAIE,GAAuB,SAASC,EAAQC,EAAaC,GACvDxO,KAAKsO,OAASA,EACdtO,KAAKuO,YAAcA,EACnBvO,KAAKwO,aAAeA,EAEpBxO,KAAKqK,KAAO,SACZrK,KAAKyO,SAAAA,EACLzO,KAAK0O,YAAAA,EACL1O,KAAK2O,cAAgBL,EACrBtO,KAAK4O,kBAAAA,EACL5O,KAAK6O,WAAaC,MAAMC,UACxB/O,KAAKgP,UAAYxB,KAAKW,OAGpBc,EAAyBnF,OAAOoF,QAAQrI,UAAUoH,OACtDnE,QAAOoF,QAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GACvD,GAAIC,GAAYH,EAAuBI,KAAKrP,KAAM4I,EAAauG,EAE/DC,GAAUE,mBACVF,EAAUlB,SAAW,IAErB,IAAIqB,GAAiBH,EAAUI,MAC/BJ,GAAUI,OAAS,WACjBD,EAAeF,KAAKrP,KACpB,IAAIyP,GAAQ,GAAIpB,GAAqBrO,KAAM,KAAMmO,KAC7CuB,EAAW1P,KAAKsP,gBAAgBK,OAAO3P,KAAKkO,UAAYlO,KAAKkO,aACjE0B,YAAW,WACTF,EAASnO,QAAQ,SAASsO,GACxBA,EAAQR,KAAKI,EAAMnB,OAAQmB,MAE5B,GAGL,IAAIK,GAA2BV,EAAUW,gBACzCX,GAAUW,iBAAmB,SAAS1F,EAAMwF,GACpB,kBAAXA,IAAiC,UAARxF,EAClCrK,KAAKsP,gBAAgBhG,KAAKuG,GAE1BC,EAAyBT,KAAKrP,KAAMqK,EAAMwF,GAG9C,IAAIG,GAA8BZ,EAAUa,mBAW5C,OAVAb,GAAUa,oBAAsB,SAAS5F,EAAMwF,GAC7C,GAAY,UAARxF,EAAkB,CACpB,GAAI6F,GAAQlQ,KAAKsP,gBAAgB5N,QAAQmO,EACrCK,IAAS,GACXlQ,KAAKsP,gBAAgBa,OAAOD,EAAO,OAErCF,GAA4BX,KAAKrP,KAAMqK,EAAMwF,IAI1CT,OClEX,SAAU3P,GAgBR,GAAI2Q,GAAUhN,SAASiN,gBACnBjB,EAAY,KACZkB,GAAAA,CACJ,KACE,GAAIC,GAAkBC,iBAAiBJ,GAASK,iBAAiB,WAC7DC,EAAiC,KAAnBH,EAAyB,IAAM,GACjDnB,GAAYgB,EAAQnC,SAAS0C,SAAYD,EAAaA,KACjDvP,SAAU,IACfiO,EAAUb,YAAc,EACxB+B,EAAWE,iBAAiBJ,GAASK,iBAAiB,YAAcC,EACpE,MAAOE,IACP,QACIxB,GACFA,EAAUI,SAEd,IAAIc,EAAJ,CAIA,GAAIrB,GAAyBnF,OAAOoF,QAAQrI,UAAUoH,OACtDnE,QAAOoF,QAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GAUvD,MATIrF,QAAOC,QAAUA,OAAOC,UAAYjB,MAAMlC,UAAUoD,MAAQrB,EAAYmB,OAAOC,YAEjFpB,EAAcG,MAAMkB,KAAKrB,IAGtBG,MAAMC,QAAQJ,IAAgC,OAAhBA,IACjCA,EAAcnJ,EAAOkJ,mBAAmBC,IAGnCqG,EAAuBI,KAAKrP,KAAM4I,EAAauG,MAEvD7P,GC/CH,SAAUG,EAAQoR,EAAOnR,GA8DvB,QAASoR,GAAsBC,GAC7B,GAAIC,GAAWH,EAAMG,QACrBA,GAASzC,YAAcwC,EACvBC,EAASC,qBAC0B,GAA/BD,EAASE,YAAY/H,OACvBgI,GAAAA,EAEAC,sBAAsBN,GApE1B,GAAIO,GAAgCvH,OAAOsH,qBAC3CtH,QAAOsH,sBAAwB,SAAShP,GACtC,MAAOiP,GAA8B,SAASlP,GAC5C0O,EAAMG,SAASM,4BACflP,EAAED,GACF0O,EAAMG,SAASM,+BAInBT,EAAMU,kBAAoB,WACxBvR,KAAKkR,eACLlR,KAAKuO,gBAAAA,IAGPsC,EAAMU,kBAAkB1K,WACtB2K,cAAe,WAEb,MADAxR,MAAKiR,qBACEjR,KAAKkR,YAAYnN,SAE1BuN,0BAA2B,WACzBT,EAAMY,uBAAyBZ,EAAMY,uBAAuB9G,OAAO,SAASyE,GAC1E,MAAOA,GAAUsC,qBAGrBT,mBAAoB,WAClBjR,KAAKsR,4BACLtR,KAAKkR,YAAclR,KAAKkR,YAAYvG,OAAO,SAASyE,GAClD,MAA8B,YAAvBA,EAAUuC,WAAkD,QAAvBvC,EAAUuC,aAG1DC,MAAO,SAAS5Q,GACd,GAAIoO,GAAY,GAAIyB,GAAMgB,UAAU7Q,EAAQhB,KAW5C,OAVAA,MAAKkR,YAAY5H,KAAK8F,GACtByB,EAAMiB,+BAMN1C,EAAUsC,kBACVtC,EAAUhI,WAAW2K,OACrB3C,EAAUsC,kBACHtC,GAET2C,KAAM,SAAS/Q,GAIb,MAHIA,IACFA,EAAOgR,SAEFhS,KAAK4R,MAAM5Q,IAItB,IAAImQ,IAAAA,CAEJN,GAAMiB,6BAA+B,WAC9BX,IACHA,GAAAA,EACAC,sBAAsBN,IAc1B,IAAIE,GAAW,GAAIH,GAAMU,iBACzBV,GAAMG,SAAWA,CAEjB,KACE3P,OAAO4Q,eAAenI,OAAO1G,SAAU,YACrC8O,cAAAA,EACAC,IAAK,WAAa,MAAOnB,MAE3B,MAAOoB,IACT,IACEtI,OAAO1G,SAAS4N,SAAWA,EAC3B,MAAOoB,MAER9S,EAAqBE,GCtFxB,SAAUC,EAAQoR,EAAOnR,GACvBmR,EAAMY,0BAENZ,EAAMgB,UAAY,SAAS7Q,EAAQgQ,GASjC,GARAhR,KAAKqS,GAAK,GACNrR,GAAUA,EAAOsR,MACnBtS,KAAKqS,GAAKrR,EAAOsR,KAEnBtS,KAAKgB,OAASA,EACVA,IACFA,EAAOoG,WAAapH,OAEjBgR,EACH,KAAM,IAAIhD,OAAM,gDAElBhO,MAAKuS,UAAYvB,EACjBhR,KAAKwS,gBAAkB/S,EAAOgT,iBAC9BzS,KAAK0S,UAAY,EACjB1S,KAAK2S,SAAAA,EACL3S,KAAK4S,UAAAA,EACL5S,KAAKoH,WAAa,KAClBpH,KAAK6S,oBACL7S,KAAK8S,UAAY,KACjB9S,KAAK+S,cAAgB,OACrB/S,KAAKqH,8BAELrH,KAAKoH,WAAWoI,SAChBxP,KAAK0R,mBAGPb,EAAMgB,UAAUhL,WACd6K,gBAAiB,WACf,GAAIsB,GAAehT,KAAK+S,cACpBE,EAAejT,KAAK2R,SAsBxB,OArBI3R,MAAKkT,eAAiBD,IAAiBD,IACrB,QAAhBC,GACFjT,KAAKmT,sBACLnT,KAAKkT,kBAAAA,IACoB,WAAhBF,EACThT,KAAKoT,uBACoB,WAAhBH,IACTjT,KAAKkT,kBAAAA,KAGLlT,KAAKqT,kBAAoBJ,IAAiBD,IACxB,QAAhBC,GACFjT,KAAKsT,yBACLtT,KAAKqT,qBAAAA,IACoB,YAAhBJ,EACTjT,KAAKuT,0BACoB,YAAhBP,IACThT,KAAKqT,qBAAAA,KAGTrT,KAAK+S,cAAgB/S,KAAK2R,UAClB3R,KAAKkT,eAAiBlT,KAAKqT,kBAErChM,4BAA6B,WAC3BrH,KAAK0R,iBACL,IAAI8B,GACAC,EACAC,EACAC,EACAC,IAAgB5T,KAAKoH,UACrBwM,KACFJ,EAAkBxT,KAAK2E,aACvB8O,EAAYzT,KAAK2S,QACjBe,EAAe1T,KAAK6T,UACpBF,EAAiB3T,KAAKuO,YACtBvO,KAAKoH,WAAWoI,SAChBxP,KAAKoH,WAAW0M,SAAW,KAC3B9T,KAAKoH,WAAa,QAGfpH,KAAKgB,QAAUhB,KAAKgB,iBAAkB8I,QAAOiK,kBAChD/T,KAAKoH,WAAayJ,EAAMmD,wCAAwChU,KAAKgB,QACrE6P,EAAMoD,+BAA+BjU,QAEnCA,KAAKgB,iBAAkB8I,QAAOoK,gBAAkBlU,KAAKgB,iBAAkB8I,QAAOqK,eAChFnU,KAAKoH,WAAayJ,EAAMuD,+BAA+BpU,KAAKgB,QAC5D6P,EAAMwD,sBAAsBrU,OAE1BA,KAAKgB,QAAUhB,KAAKgB,OAAOsT,WAC7BzD,EAAM0D,6BAA6BvU,MAEjC4T,IACqB,GAAnBJ,IACFxT,KAAK2E,aAAe6O,GAED,OAAjBE,EACF1T,KAAK6T,UAAYH,EACW,OAAnBC,EACT3T,KAAKuO,YAAcoF,EACS,OAAnB3T,KAAK0S,YACd1S,KAAKuO,YAAcvO,KAAK0S,WAEtBe,GACFzT,KAAKwU,SAGTxU,KAAK0R,mBAEP+C,gBAAiB,WACf,GAAKzU,KAAKgB,QAA4B,QAAlBhB,KAAK2R,UAAzB,CAGA,GAAIvI,GAASpJ,KAAKgB,OAAOmG,QAAQjC,KACjClF,MAAK6S,iBAAiBtR,QAAQ,SAASmT,GACrC1U,KAAK2U,iBAAiBD,EAAgBtL,GAClCpJ,KAAKgB,iBAAkB8I,QAAOoK,iBAChC9K,GAAUyH,EAAM+D,mBAAmBF,EAAe1T,UACpD6T,KAAK7U,SAET8U,sBAAuB,SAAS1F,GAC9B,GAAKpP,KAAKgB,QAAWhB,KAAK4S,SAE1B,IAAK,GAAIpK,GAAI,EAAGA,EAAIxI,KAAKgB,OAAO+T,SAAS5L,OAAQX,IAC/CxI,KAAKgB,OAAO+T,SAASvM,GAAGpB,WAAagI,EACrCpP,KAAK6S,iBAAiBrK,GAAGsM,sBAAsB1F,IAGnD4F,0BAA2B,WACzB,GAAKhV,KAAKgB,QAAWhB,KAAK4S,SAA1B,CAEA,GAAIxJ,GAASpJ,KAAKgB,OAAOmG,QAAQjC,KACjClF,MAAKiV,yBACLjV,KAAKgB,OAAO+T,SAASxT,QAAQ,SAAS2T,GACpC,GAAIR,GAAiB7D,EAAMG,SAASY,MAAMsD,EAC1ClV,MAAK6S,iBAAiBvJ,KAAKoL,GAC3BA,EAAe/P,aAAe3E,KAAK2E,aAC/B3E,KAAK2S,SACP+B,EAAeF,QACjBU,EAAM9N,WAAapH,KAAKgB,OAAOoG,WAE/BpH,KAAK2U,iBAAiBD,EAAgBtL,GAElCpJ,KAAKgB,iBAAkB8I,QAAOoK,iBAChC9K,GAAUyH,EAAM+D,mBAAmBM,KACrCL,KAAK7U,SAET2U,iBAAkB,SAASD,EAAgBtL,GAClB,OAAnBpJ,KAAK6T,UACPa,EAAenG,YAAcvO,KAAKuO,YAAcnF,EAASpJ,KAAK2E,aACrD+P,EAAeb,YAAc7T,KAAK6T,UAAYzK,EAASpJ,KAAK2E,eACrE+P,EAAeb,UAAY7T,KAAK6T,UAAYzK,EAASpJ,KAAK2E,eAG9DqM,eACE,MAAOhR,MAAKuS,WAEdZ,gBACE,MAAO3R,MAAKoH,WAAapH,KAAKoH,WAAWuK,UAAY,QAEvDwD,eACE,MAAKrL,QAAOsL,SAIPpV,KAAKqT,oBAC2C,GAA/CxC,EAAMY,uBAAuB/P,QAAQ1B,OACvC6Q,EAAMY,uBAAuBnI,KAAKtJ,MAEpCA,KAAKqT,iBAAmB,GAAI+B,SACxB,SAASC,EAASC,GAChBtV,KAAKuT,wBAA0B,WAC7B8B,EAAQrV,OAEVA,KAAKsT,uBAAyB,WAC5BgC,GAAQjL,KAAMC,aAAaiL,UAAW/K,KAAM,iBAE9CqK,KAAK7U,OACW,YAAlBA,KAAK2R,WACP3R,KAAKuT,2BAGFvT,KAAKqT,mBApBVzF,QAAQC,KAAK,6DACN,OAqBX2H,YACE,MAAK1L,QAAOsL,SAIPpV,KAAKkT,iBAC2C,GAA/CrC,EAAMY,uBAAuB/P,QAAQ1B,OACvC6Q,EAAMY,uBAAuBnI,KAAKtJ,MAEpCA,KAAKkT,cAAgB,GAAIkC,SACrB,SAASC,EAASC,GAChBtV,KAAKoT,qBAAuB,WAC1BiC,EAAQrV,OAEVA,KAAKmT,oBAAsB,WACzBmC,GAAQjL,KAAMC,aAAaiL,UAAW/K,KAAM,iBAE9CqK,KAAK7U,OACY,YAAnBA,KAAK2R,WACP3R,KAAKoT,wBAGFpT,KAAKkT,gBApBVtF,QAAQC,KAAK,6DACN,OAqBX4H,eACE,MAAOzV,MAAKoH,WAAWqO,UAEzBA,aAAaC,GAET1V,KAAKoH,WAAWqO,SADF,kBAALC,GACkB,SAAUtD,GACnCA,EAAE9D,OAAStO,KACX0V,EAAErG,KAAKrP,KAAMoS,IACZyC,KAAK7U,MAEmB0V,GAG/BxH,eACE,MAAOlO,MAAKoH,WAAW8G,UAEzBA,aAAawH,GAET1V,KAAKoH,WAAW8G,SADF,kBAALwH,GACkB,SAAUtD,GACnCA,EAAE9D,OAAStO,KACX0V,EAAErG,KAAKrP,KAAMoS,IACZyC,KAAK7U,MAEmB0V,GAG/BnH,kBACEvO,KAAK0R,iBACL,IAAInD,GAAcvO,KAAKoH,WAAWmH,WAElC,OADAvO,MAAK0R,kBACEnD,GAETA,gBAAgBmH,GACd1V,KAAK0R,kBACL1R,KAAKoH,WAAWmH,YAAcnE,SAASsL,GAAKA,EAAI/S,KAAKgT,KAAKD,GAAKzR,OAAO2R,UACtE5V,KAAK6V,YACL7V,KAAK8V,cAAc,SAASZ,EAAO9L,GACjC8L,EAAM3G,YAAcmH,EAAItM,IAE1BpJ,KAAK0R,mBAEPmC,gBACE,MAAO7T,MAAKoH,WAAWyM,WAEzBA,cAAc6B,GACZ1V,KAAK0R,kBACL1R,KAAKoH,WAAWyM,UAAYzJ,SAASsL,GAAKA,EAAI/S,KAAKgT,KAAKD,GAAKzR,OAAO2R,UACpE5V,KAAK6V,YACL7V,KAAK8V,cAAc,SAASZ,EAAO9L,GACjC8L,EAAMrB,UAAY6B,EAAItM,IAExBpJ,KAAK0R,mBAEP/M,mBACE,MAAO3E,MAAKoH,WAAWzC,cAEzBA,iBAAiBqC,GACfhH,KAAK0R,iBACL,IAAIiC,GAAiB3T,KAAKuO,WAC1BvO,MAAKoH,WAAWzC,aAAeqC,EAC/BhH,KAAK8V,cAAc,SAASpB,GAC1BA,EAAe/P,aAAeqC,IAET,OAAnB2M,IACF3T,KAAKuO,YAAcoF,GAErB3T,KAAK0R,mBAEPK,KAAM,WACJ/R,KAAK0R,kBACL1R,KAAK2S,SAAAA,EACL3S,KAAKoH,WAAW2K,QACiC,GAA7C/R,KAAKuS,UAAUrB,YAAYxP,QAAQ1B,OACrCA,KAAKuS,UAAUrB,YAAY5H,KAAKtJ,MAElCA,KAAK6V,YACLhF,EAAMkF,eAAe/V,MACrBA,KAAK8V,cAAc,SAASZ,GAC1B,GAAIc,GAAOd,EAAM3G,WACjB2G,GAAMnD,OACNmD,EAAM3G,YAAcyH,IAEtBhW,KAAK0R,mBAEP8C,MAAO,WACLxU,KAAK0R,kBACD1R,KAAKuO,cACPvO,KAAK0S,UAAY1S,KAAKuO,aAExBvO,KAAKoH,WAAWoN,QAChBxU,KAAK6V,YACL7V,KAAK8V,cAAc,SAASZ,GAC1BA,EAAMV,UAERxU,KAAK2S,SAAAA,EACL3S,KAAK0R,mBAEPuE,OAAQ,WACNjW,KAAK0R,kBACL1R,KAAKoH,WAAW6O,SAChBjW,KAAK6V,YACL7V,KAAK0R,mBAEPlC,OAAQ,WACNxP,KAAK0R,kBACL1R,KAAKoH,WAAWoI,SAChBxP,KAAK6V,YACL7V,KAAKiV,yBACLjV,KAAK0R,mBAEPwE,QAAS,WACPlW,KAAK0R,iBACL,IAAIiC,GAAiB3T,KAAKuO,WAC1BvO,MAAKoH,WAAW8O,UAChBlW,KAAK8V,cAAc,SAASpB,GAC1BA,EAAewB,YAEM,OAAnBvC,IACF3T,KAAKuO,YAAcoF,GAErB3T,KAAK0R,mBAEP3B,iBAAkB,SAAS1F,EAAMwF,GAC/B,GAAIsG,GAAUtG,CACQ,mBAAXA,KACTsG,EAAU,SAAU/D,GAClBA,EAAE9D,OAAStO,KACX6P,EAAQR,KAAKrP,KAAMoS,IAClByC,KAAK7U,MACR6P,EAAQiE,SAAWqC,GAErBnW,KAAKoH,WAAW2I,iBAAiB1F,EAAM8L,IAEzClG,oBAAqB,SAAS5F,EAAMwF,GAClC7P,KAAKoH,WAAW6I,oBAAoB5F,EAAOwF,GAAWA,EAAQiE,UAAajE,IAE7EoF,uBAAwB,WACtB,KAAOjV,KAAK6S,iBAAiB1J,QAC3BnJ,KAAK6S,iBAAiBuD,MAAM5G,UAEhCsG,cAAe,SAAS1T,GACtB,GAAIgH,GAAS,CASb,IARIpJ,KAAKgB,OAAO+T,UAAY/U,KAAK6S,iBAAiB1J,OAASnJ,KAAKgB,OAAO+T,SAAS5L,QAC9EnJ,KAAKgV,4BACPhV,KAAK6S,iBAAiBtR,QAAQ,SAAS2T,GACrC9S,EAAEiN,KAAKrP,KAAMkV,EAAO9L,GAChBpJ,KAAKgB,iBAAkB8I,QAAOoK,iBAChC9K,GAAU8L,EAAMlU,OAAO8D,iBACzB+P,KAAK7U,OAEe,WAAlBA,KAAK2R,UAAT,CAEA,GAAI1Q,GAASjB,KAAKgB,OAAOmG,QACrB4J,EAAI/Q,KAAKuO,WACH,QAANwC,IACFA,EAAItR,EAAOgH,2BAA2BhH,EAAOgF,wBAAwBxD,GAAS8P,EAAG9P,KAC1E,MAAL8P,GAAa3P,MAAM2P,KACrB/Q,KAAKiV,4BAIXnL,OAAO+H,UAAYhB,EAAMgB,WAMxBvS,EAAqBE,GChXvB,SAASC,EAAQoR,EAAOnR,GAqCvB,QAAS2W,GAAazN,GACpB5I,KAAKsW,QAAU7W,EAAO+J,mBAAmBZ,GAoG3C,QAAS2N,KAEP,IADA,GAAIC,IAAAA,EACGC,EAActN,QACPsN,EAAcC,QACpBjC,kBACN+B,GAAAA,CAEF,OAAOA,GA/IT,GAAIG,GAAe,SAAS3V,GAE1B,GADAA,EAAOoG,eAAAA,GACHpG,YAAkB8I,QAAOoK,gBAAkBlT,YAAkB8I,QAAOqK,YACtE,IAAK,GAAI3L,GAAI,EAAGA,EAAIxH,EAAO+T,SAAS5L,OAAQX,IAC1CmO,EAAa3V,EAAO+T,SAASvM,IAKnCqI,GAAM+F,YAAc,SAASC,GAE3B,IAAK,GADDC,MACKtO,EAAI,EAAGA,EAAIqO,EAAQ1N,OAAQX,IAAK,CACvC,GAAIxH,GAAS6V,EAAQrO,EACjBxH,GAAO+V,UACkC,GAAvCD,EAAWpV,QAAQV,EAAO+V,UAC5BD,EAAWxN,KAAKtI,EAAO+V,SAEzB/V,EAAO+V,QAAQhC,SAAS5E,OAAOnP,EAAO+V,QAAQhC,SAASrT,QAAQV,GAAS,GACxEA,EAAO+V,QAAU,KACjBJ,EAAa3V,IACJA,EAAOoG,YAAepG,EAAOoG,WAAWpG,QAAUA,IAC3DA,EAAOoG,WAAWoI,SAClBxO,EAAOoG,WAAWpG,OAAS,GAAI+S,gBAAe,SAC1C/S,EAAOoG,WAAW0L,YACpB9R,EAAOoG,WAAW0L,UAAU1L,WAAa,MAE3CpG,EAAOoG,WAAWC,8BAClBsP,EAAa3V,IAGjB,IAAKwH,EAAI,EAAGA,EAAIsO,EAAW3N,OAAQX,IACjCsO,EAAWtO,GAAGwO,YAQlBnG,EAAMkD,eAAiB,SAASzF,EAAQ1F,EAAahJ,EAAayS,GAmBhE,MAlBArS,MAAKsO,OAASA,EACdtO,KAAK+W,QAAU,KAEfnX,EAAcH,EAAOmC,sBAAsBhC,GAC3CI,KAAKkH,aAAezH,EAAOE,iBAAiBC,GAC5CI,KAAKmH,QAAU1H,EAAOoC,qBAAqBjC,GAE3CI,KAAKiB,OAASxB,EAAOqB,WAAWlB,GAAAA,EAAoBI,MACpDA,KAAKiB,OAAOgG,QAAUjH,KACI,kBAAf4I,IACTnJ,EAAOsO,WAAW,wBAAyB,aAAc,wCACzD/N,KAAKiX,qBAAuBrO,GAE5B5I,KAAKiX,qBAAuB,GAAIZ,GAAazN,GAE/C5I,KAAKkX,WAAatO,EAClB5I,KAAK8E,eAAiBrF,EAAOgF,wBAAwBzE,KAAKmH,SAC1DnH,KAAKsS,IAAMD,EACJrS,MAGT6Q,EAAMkD,eAAelN,WACnBsQ,UAAW,WACT,MAAwC,kBAA7BnX,MAAKiX,qBACPjX,KAAKiX,qBACPjX,KAAKiX,qBAAqBX,SAEnCc,aAAaC,GACX,GAA+B,kBAApBrX,MAAKmX,YACd,KAAM,IAAInJ,OAAM,qEAElBhO,MAAKsU,UAAY+C,EACbrX,KAAKoH,YACPpH,KAAKoH,WAAWC,+BAGpBiQ,aACE,MAAOtX,MAAK+W,SAEdlX,MAAO,WACL,GAA+B,kBAApBG,MAAKmX,YACd,KAAM,IAAInJ,OAAM,2CAElB,IAAInO,GAAQ,GAAIkU,gBAAe/T,KAAKsO,UAAY7O,EAAOE,iBAAiBK,KAAKkH,cAAelH,KAAKsS,IAGjG,OAFAzS,GAAMoX,qBAAuBjX,KAAKiX,qBAClCpX,EAAMqX,WAAalX,KAAKkX,WACjBrX,GAETmS,OAAQ,WACNnB,EAAM+F,aAAa5W,QAIvB,IAAIiP,GAAyBC,QAAQrI,UAAUoH,OAC/CiB,SAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GAChD,GAAIkD,GAAK,EAIT,OAHIlD,IAAWA,EAAQkD,KACrBA,EAAKlD,EAAQkD,IAERxB,EAAMG,SAASY,MAAM,GAAIf,GAAMkD,eAAe/T,KAAM4I,EAAauG,EAASkD,IAGnF,IAAIkF,GAAanU,SAASsI,gBAAgB,+BAAgC,MAC1EmF,GAAMmD,wCAA0C,SAASwD,GACvD,GAAIA,EAAgB,CAClB,GAAIlJ,GAASkJ,EAAelJ,QAAUiJ,EAClC7N,EAAY8N,EAAeN,UACP,mBAAbxN,KACTA,KAEF,IAAIyF,GAAUqI,EAAetQ,YAC7BiI,GAAQkD,GAAKmF,EAAelF,QAE5B,IAAIhE,GAASiJ,EACT7N,KACAyF,EAAU,CAEhB,OAAOF,GAAuBnL,MAAMwK,GAAS5E,EAAWyF,KAI1D0B,EAAMoD,+BAAiC,SAAS7E,GAC1CA,EAAUpO,QAA0D,kBAAzCoO,GAAUpO,OAAOiW,sBAC9CpG,EAAM0D,6BAA6BnF,GAIvC,IAAIqH,KACJ5F,GAAMkF,eAAiB,SAAS0B,GACG,OAA7BA,EAAe5D,WAAuB4D,EAAe7E,WAE7B,GAAxB6D,EAActN,QAChBiI,sBAAsBmF,GAExBE,EAAcnN,KAAKmO,IAWrB,IAAIC,GAA2B5N,OAAO0G,gBACtCnP,QAAO4Q,eAAenI,OAAQ,oBAC5BoI,cAAAA,EACAyF,YAAAA,EACA3Q,MAAO,WACL6J,EAAMG,SAASM,2BACf,IAAIlJ,GAASsP,EAAyB5T,MAAM9D,KAAM4X,UAIlD,OAHIrB,OACFnO,EAASsP,EAAyB5T,MAAM9D,KAAM4X,YAChD/G,EAAMG,SAASM,4BACRlJ,KAIX0B,OAAOiK,eAAiBlD,EAAMkD,eAC9BjK,OAAOoF,QAAQrI,UAAU2K,cAAgB,WACvC,MAAOpO,UAAS4N,SAASQ,gBAAgB7G,OAAO,SAASyE,GACvD,MAA4B,QAArBA,EAAUpO,QAAmBoO,EAAUpO,OAAOsN,QAAUtO,MAC/D6U,KAAK7U,SAGTV,EAAqBE,GCzKvB,SAAUC,EAAQoR,EAAOnR,GA6CvB,QAASmY,GAASR,GACZA,EAASS,cAEbT,EAASS,aAAAA,EACTC,EAAUzO,KAAK+N,GACVlG,IACHA,GAAAA,EACAC,sBAAsB4G,KAI1B,QAASA,GAAKjH,GACZ,GAAIkH,GAAWF,CACfA,MACAE,EAAS1O,KAAK,SAAS2O,EAAMC,GAC3B,MAAOD,GAAK1F,gBAAkB2F,EAAM3F,kBAEtCyF,EAAWA,EAAStN,OAAO,SAAS0M,GAClCA,GACA,IAAI1F,GAAY0F,EAASjQ,WAAaiQ,EAASjQ,WAAWuK,UAAY,MAGtE,OAFiB,WAAbA,GAAuC,WAAbA,IAC5B0F,EAASS,aAAAA,GACJT,EAASS,cAElBC,EAAUzO,KAAKxF,MAAMiU,EAAWE,GAE5BF,EAAU5O,QACZgI,GAAAA,EACAC,sBAAsB4G,IAEtB7G,GAAAA,EAzEJ,GAEIsB,IAFarP,SAASsI,gBAAgB,+BAAgC,OAErD,EACrBmF,GAAM0D,6BAA+B,SAASnF,GAC5C,GACIgJ,GADA9J,EAASc,EAAUpO,OAAOsN,OAE1B+J,EAA0D,kBAAhCjJ,GAAUpO,OAAOmW,WAE7CiB,GADEC,EACejJ,EAAUpO,OAAOmW,YAEjB/H,EAAUpO,OAAOsT,SAEpC,IAAIrT,GAASmO,EAAUpO,OAAOC,OAC1BqX,EAAO,IACXrX,GAASxB,EAAOoC,qBAAqBZ,EACrC,IAAIoW,GAAW,WACb,GAAItG,GAAIsG,EAASjQ,WAAaiQ,EAASjQ,WAAWmH,YAAc,IACtD,QAANwC,IACFA,EAAItR,EAAOgH,2BAA2BhH,EAAOgF,wBAAwBxD,GAAS8P,EAAG9P,GAC7EG,MAAM2P,KACRA,EAAI,OAIJA,IAAMuH,IACJD,EACFD,EAAerH,EAAGzC,EAAQc,EAAUpO,QAEpCoX,EAAerH,EAAG3B,EAAUpO,OAAQoO,EAAUpO,OAAOoG,aAGzDkR,EAAOvH,EAGTsG,GAASjQ,WAAagI,EACtBiI,EAASS,aAAAA,EACTT,EAAS7E,gBAAkBC,IAC3BrD,EAAU0D,UAAYuE,EACtBQ,EAASR,GAGX,IAAIU,MACA5G,GAAAA,CAmCJN,GAAMgB,UAAUhL,UAAUgP,UAAY,WAChC7V,KAAK8S,WACP+E,EAAS7X,KAAK8S,aAGjBxT,EAAqBE,GCnFxB,SAAUC,EAAQoR,EAAOnR,GAEvB,QAASkV,GAAmB2D,GAC1B,MAAOA,GAAKpR,QAAQjC,MAAQqT,EAAKzT,eAAiByT,EAAKpR,QAAQhC,SAGjE,QAASqT,GAAYzD,EAAUnV,EAAayS,GAC1CrS,KAAKsS,IAAMD,EACXrS,KAAK+W,QAAU,KACf/W,KAAK+U,SAAWA,MAChB/U,KAAKyY,UAAUzY,KAAK+U,UACpBnV,EAAcH,EAAOmC,sBAAsBhC,GAC3CI,KAAKkH,aAAezH,EAAOE,iBAAiBC,GAC5CI,KAAKmH,QAAU1H,EAAOoC,qBAAqBjC,GAAAA,GAC3CI,KAAKiB,OAASxB,EAAOqB,WAAWlB,GAAAA,EAAmBI,MACnDA,KAAKiB,OAAOgG,QAAUjH,KAEQ,SAA1BA,KAAKmH,QAAQhG,WACfnB,KAAKmH,QAAQhG,SAAWnB,KAAK8E,gBAIjCgF,OAAOoK,eAAiB,WACtBsE,EAAY1U,MAAM9D,KAAM4X,YAG1B9N,OAAOqK,YAAc,WACnBqE,EAAY1U,MAAM9D,KAAM4X,YAG1BY,EAAY3R,WACV6R,YAAa,SAAS1X,GAEpB,IADA,GAAIe,GAAI/B,KACK,OAAN+B,GAAY,CACjB,GAAIA,GAAKf,EACP,OAAA,CACFe,GAAIA,EAAEgV,QAER,OAAA,GAEFC,SAAU,WAGR,IADA,GAAIuB,GAAOvY,KACJuY,GACwB,SAAzBA,EAAKtX,OAAOE,WACdoX,EAAKpR,QAAQhG,SAAWoX,EAAKzT,gBAE/ByT,EAAOA,EAAKxB,OAEV/W,MAAKoH,YACPpH,KAAKoH,WAAWC,+BAGpBoR,UAAW,SAASE,GAClB9H,EAAM+F,YAAY+B,EAClB,KAAK,GAAInQ,GAAI,EAAGA,EAAImQ,EAAYxP,OAAQX,IACtCmQ,EAAYnQ,GAAGuO,QAAU/W,MAG7B4Y,UAAW,SAASC,EAAMC,GAExB,IAAK,GADDrO,GAAUqO,EAAW,oCAAsC,qCACtDtQ,EAAI,EAAGA,EAAIqQ,EAAK1P,OAAQX,IAC/B,GAAIxI,KAAK0Y,YAAYG,EAAKrQ,IACxB,MACE6B,KAAMC,aAAayO,sBACnBvO,KAAM,wBACNC,QAASA,EAKf,KAAK,GAAIjC,GAAI,EAAGA,EAAIqQ,EAAK1P,OAAQX,IAC/BsQ,EAAW9Y,KAAK+U,SAASzL,KAAKuP,EAAKrQ,IAAMxI,KAAK+U,SAASiE,QAAQH,EAAKrQ,GAEtExI,MAAKyY,UAAUI,GACf7Y,KAAKgX,YAEPiC,OAAQ,WACNjZ,KAAK4Y,UAAUhB,WAAAA,IAEjBsB,QAAS,WACPlZ,KAAK4Y,UAAUhB,WAAAA,IAEjBN,aACE,MAAOtX,MAAK+W,SAEdoC,iBACE,MAAOnZ,MAAK+U,SAAS5L,OAASnJ,KAAK+U,SAAS,GAAK,MAEnDqE,gBACE,MAAOpZ,MAAK+U,SAAS5L,OAASnJ,KAAK+U,SAAS/U,KAAK+U,SAAS5L,OAAS,GAAK,MAE1EtJ,MAAO,WAGL,IAAK,GAFDwZ,GAAe5Z,EAAOE,iBAAiBK,KAAKkH,cAC5CoS,KACK9Q,EAAI,EAAGA,EAAIxI,KAAK+U,SAAS5L,OAAQX,IACxC8Q,EAAehQ,KAAKtJ,KAAK+U,SAASvM,GAAG3I,QAEvC,OAAQG,gBAAgBmU,aACpB,GAAIA,aAAYmF,EAAgBD,GAChC,GAAInF,gBAAeoF,EAAgBD,IAEzCrH,OAAQ,WACNnB,EAAM+F,aAAa5W,SAIvB8J,OAAOoK,eAAerN,UAAYxF,OAAOkY,OAAOf,EAAY3R,WAC5DxF,OAAO4Q,eACHnI,OAAOoK,eAAerN,UACtB,kBAEEsL,IAAK,WACH,GAAIqH,GAAQ,CAIZ,OAHAxZ,MAAK+U,SAASxT,QAAQ,SAAS2T,GAC7BsE,GAAS5E,EAAmBM,KAEvBvS,KAAK8W,IAAID,EAAO,MAI/B1P,OAAOqK,YAAYtN,UAAYxF,OAAOkY,OAAOf,EAAY3R,WACzDxF,OAAO4Q,eACHnI,OAAOqK,YAAYtN,UACnB,kBAEEsL,IAAK,WACH,GAAIsH,GAAM,CAIV,OAHAzZ,MAAK+U,SAASxT,QAAQ,SAAS2T,GAC7BuE,EAAM9W,KAAK8W,IAAIA,EAAK7E,EAAmBM,MAElCuE,KAIf5I,EAAMuD,+BAAiC,SAASsF,GAC9C,GAAIC,GACA1Y,EAAS,KACT2Y,EAAS,SAASC,GACpB,GAAIzK,GAAYuK,EAAoB7F,QACpC,IAAK1E,GAGsB,WAAvBA,EAAUuC,WAGTvC,EAAUpO,OAGf,MAAU,OAAN6Y,MACFzK,GAAU6F,yBAQF,GAAN4E,GAAWzK,EAAUzK,aAAe,IACjC1D,IACHA,EAASxB,EAAOoC,qBAAqBuN,EAAUpO,OAAOC,SAExD4Y,EAAKpa,EAAOgH,2BAA2BhH,EAAOgF,wBAAwBxD,IAAU,EAAGA,GAC/EG,MAAMyY,IAAa,MAANA,IACfzK,EAAU0G,cAAc,SAASZ,GAC/BA,EAAM3G,aAAe,QAEvBa,GAAU6F,8BAAAA,IAMZ6E,EAAmB,GAAI/F,gBAAe,QAAU2F,EAAMvS,QAASuS,EAAMpH,IAGzE,OAFAwH,GAAiB1C,SAAWwC,EAC5BD,EAAsB9I,EAAMG,SAASY,MAAMkI,IAI7CjJ,EAAMwD,sBAAwB,SAASjF,GACrCA,EAAUhI,WAAW0M,SAAW1E,EAChCA,EAAUwD,UAAAA,EACV/B,EAAMkF,eAAe3G,GACrBA,EAAU4F,4BACV5F,EAAU0F,sBAAsB1F,IAGlCyB,EAAM+D,mBAAqBA,GAE1BtV,EAAqBE,GV3LpBA,EAAAA,KAEJua,MACMC,WAAAA,MAAuBha","file":"web-animations-next.min.js"} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations.html b/catapult/third_party/polymer/components/web-animations-js/web-animations.html
new file mode 100644
index 00000000..b5de36c8
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations.html
@@ -0,0 +1,50 @@
+<!--
+ Copyright 2014 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.
+-->
+
+<!-- WARNING: This file is DEPRECATED, for development purposes use
+ web-animations*.dev.html instead or depend on
+ web-animations*.min.js in the web-animations-js
+ repository -->
+
+<script src="src/dev.js"></script>
+<script src="src/scope.js"></script>
+<script src="src/deprecation.js"></script>
+<script src="src/timing-utilities.js"></script>
+<script src="src/normalize-keyframes.js"></script>
+<script src="src/animation-node.js"></script>
+<script src="src/effect.js"></script>
+<script src="src/property-interpolation.js"></script>
+<script src="src/animation.js"></script>
+<script src="src/apply.js"></script>
+<script src="src/element-animatable.js"></script>
+<script src="src/interpolation.js"></script>
+<script src="src/player.js"></script>
+<script src="src/tick.js"></script>
+<script src="src/handler-utils.js"></script>
+<script src="src/shadow-handler.js"></script>
+<script src="src/number-handler.js"></script>
+<script src="src/visibility-handler.js"></script>
+<script src="src/color-handler.js"></script>
+<script src="src/dimension-handler.js"></script>
+<script src="src/box-handler.js"></script>
+<script src="src/transform-handler.js"></script>
+<script src="src/property-names.js"></script>
+<script src="src/timeline.js"></script>
+<script src="src/maxifill-player.js"></script>
+<script src="src/animation-constructor.js"></script>
+<script src="src/effect-callback.js"></script>
+<script src="src/group-constructors.js"></script>
+
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations.min.html b/catapult/third_party/polymer/components/web-animations-js/web-animations.min.html
new file mode 100644
index 00000000..b36940d3
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations.min.html
@@ -0,0 +1 @@
+<script src="./web-animations.min.js"></script>
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations.min.js b/catapult/third_party/polymer/components/web-animations-js/web-animations.min.js
new file mode 100644
index 00000000..46156b20
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations.min.js
@@ -0,0 +1,16 @@
+// Copyright 2014 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.
+
+!function(a,b){var c={},d={};!function(a,b){function c(a){if("number"==typeof a)return a;var b={};for(var c in a)b[c]=a[c];return b}function d(){this._delay=0,this._endDelay=0,this._fill="none",this._iterationStart=0,this._iterations=1,this._duration=0,this._playbackRate=1,this._direction="normal",this._easing="linear",this._easingFunction=x}function e(){return a.isDeprecated("Invalid timing inputs","2016-03-02","TypeError exceptions will be thrown instead.",!0)}function f(b,c,e){var f=new d;return c&&(f.fill="both",f.duration="auto"),"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof f[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==v.indexOf(b[c]))return;if("direction"==c&&-1==w.indexOf(b[c]))return;if("playbackRate"==c&&1!==b[c]&&a.isDeprecated("AnimationEffectTiming.playbackRate","2014-11-28","Use Animation.playbackRate instead."))return;f[c]=b[c]}}):f.duration=b,f}function g(a){return"number"==typeof a&&(a=isNaN(a)?{duration:0}:{duration:a}),a}function h(b,c){return b=a.numericTimingToObject(b),f(b,c)}function i(a,b,c,d){return a<0||a>1||c<0||c>1?x:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}if(e<=0){var g=0;return a>0?g=b/a:!b&&c>0&&(g=d/c),g*e}if(e>=1){var h=0;return c<1?h=(d-1)/(c-1):1==c&&a<1&&(h=(b-1)/(a-1)),1+h*(e-1)}for(var i=0,j=1;i<j;){var k=(i+j)/2,l=f(a,c,k);if(Math.abs(e-l)<1e-5)return f(b,d,k);l<e?i=k:j=k}return f(b,d,k)}}function j(a,b){return function(c){if(c>=1)return 1;var d=1/a;return(c+=b*d)-c%d}}function k(a){C||(C=document.createElement("div").style),C.animationTimingFunction="",C.animationTimingFunction=a;var b=C.animationTimingFunction;if(""==b&&e())throw new TypeError(a+" is not a valid value for easing");return b}function l(a){if("linear"==a)return x;var b=E.exec(a);if(b)return i.apply(this,b.slice(1).map(Number));var c=F.exec(a);return c?j(Number(c[1]),{start:y,middle:z,end:A}[c[2]]):B[a]||x}function m(a){return Math.abs(n(a)/a.playbackRate)}function n(a){return 0===a.duration||0===a.iterations?0:a.duration*a.iterations}function o(a,b,c){if(null==b)return G;var d=c.delay+a+c.endDelay;return b<Math.min(c.delay,d)?H:b>=Math.min(c.delay+a,d)?I:J}function p(a,b,c,d,e){switch(d){case H:return"backwards"==b||"both"==b?0:null;case J:return c-e;case I:return"forwards"==b||"both"==b?a:null;case G:return null}}function q(a,b,c,d,e){var f=e;return 0===a?b!==H&&(f+=c):f+=d/a,f}function r(a,b,c,d,e,f){var g=a===1/0?b%1:a%1;return 0!==g||c!==I||0===d||0===e&&0!==f||(g=1),g}function s(a,b,c,d){return a===I&&b===1/0?1/0:1===c?Math.floor(d)-1:Math.floor(d)}function t(a,b,c){var d=a;if("normal"!==a&&"reverse"!==a){var e=b;"alternate-reverse"===a&&(e+=1),d="normal",e!==1/0&&e%2!=0&&(d="reverse")}return"normal"===d?c:1-c}function u(a,b,c){var d=o(a,b,c),e=p(a,c.fill,b,d,c.delay);if(null===e)return null;var f=q(c.duration,d,c.iterations,e,c.iterationStart),g=r(f,c.iterationStart,d,c.iterations,e,c.duration),h=s(d,c.iterations,g,f),i=t(c.direction,h,g);return c._easingFunction(i)}var v="backwards|forwards|both|none".split("|"),w="reverse|alternate|alternate-reverse".split("|"),x=function(a){return a};d.prototype={_setMember:function(b,c){this["_"+b]=c,this._effect&&(this._effect._timingInput[b]=c,this._effect._timing=a.normalizeTimingInput(this._effect._timingInput),this._effect.activeDuration=a.calculateActiveDuration(this._effect._timing),this._effect._animation&&this._effect._animation._rebuildUnderlyingAnimation())},get playbackRate(){return this._playbackRate},set delay(a){this._setMember("delay",a)},get delay(){return this._delay},set endDelay(a){this._setMember("endDelay",a)},get endDelay(){return this._endDelay},set fill(a){this._setMember("fill",a)},get fill(){return this._fill},set iterationStart(a){if((isNaN(a)||a<0)&&e())throw new TypeError("iterationStart must be a non-negative number, received: "+timing.iterationStart);this._setMember("iterationStart",a)},get iterationStart(){return this._iterationStart},set duration(a){if("auto"!=a&&(isNaN(a)||a<0)&&e())throw new TypeError("duration must be non-negative or auto, received: "+a);this._setMember("duration",a)},get duration(){return this._duration},set direction(a){this._setMember("direction",a)},get direction(){return this._direction},set easing(a){this._easingFunction=l(k(a)),this._setMember("easing",a)},get easing(){return this._easing},set iterations(a){if((isNaN(a)||a<0)&&e())throw new TypeError("iterations must be non-negative, received: "+a);this._setMember("iterations",a)},get iterations(){return this._iterations}};var y=1,z=.5,A=0,B={ease:i(.25,.1,.25,1),"ease-in":i(.42,0,1,1),"ease-out":i(0,0,.58,1),"ease-in-out":i(.42,0,.58,1),"step-start":j(1,y),"step-middle":j(1,z),"step-end":j(1,A)},C=null,D="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",E=new RegExp("cubic-bezier\\("+D+","+D+","+D+","+D+"\\)"),F=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,G=0,H=1,I=2,J=3;a.cloneTimingInput=c,a.makeTiming=f,a.numericTimingToObject=g,a.normalizeTimingInput=h,a.calculateActiveDuration=m,a.calculateIterationProgress=u,a.calculatePhase=o,a.normalizeEasing=k,a.parseEasingFunction=l}(c),function(a,b){function c(a,b){return a in k?k[a][b]||b:b}function d(a){return"display"===a||0===a.lastIndexOf("animation",0)||0===a.lastIndexOf("transition",0)}function e(a,b,e){if(!d(a)){var f=h[a];if(f){i.style[a]=b;for(var g in f){var j=f[g],k=i.style[j];e[j]=c(j,k)}}else e[a]=c(a,b)}}function f(a){var b=[];for(var c in a)if(!(c in["easing","offset","composite"])){var d=a[c];Array.isArray(d)||(d=[d]);for(var e,f=d.length,g=0;g<f;g++)e={},e.offset="offset"in a?a.offset:1==f?1:g/(f-1),"easing"in a&&(e.easing=a.easing),"composite"in a&&(e.composite=a.composite),e[c]=d[g],b.push(e)}return b.sort(function(a,b){return a.offset-b.offset}),b}function g(b){function c(){var a=d.length;null==d[a-1].offset&&(d[a-1].offset=1),a>1&&null==d[0].offset&&(d[0].offset=0);for(var b=0,c=d[0].offset,e=1;e<a;e++){var f=d[e].offset;if(null!=f){for(var g=1;g<e-b;g++)d[b+g].offset=c+(f-c)*g/(e-b);b=e,c=f}}}if(null==b)return[];window.Symbol&&Symbol.iterator&&Array.prototype.from&&b[Symbol.iterator]&&(b=Array.from(b)),Array.isArray(b)||(b=f(b));for(var d=b.map(function(b){var c={};for(var d in b){var f=b[d];if("offset"==d){if(null!=f){if(f=Number(f),!isFinite(f))throw new TypeError("Keyframe offsets must be numbers.");if(f<0||f>1)throw new TypeError("Keyframe offsets must be between 0 and 1.")}}else if("composite"==d){if("add"==f||"accumulate"==f)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"add compositing is not supported"};if("replace"!=f)throw new TypeError("Invalid composite mode "+f+".")}else f="easing"==d?a.normalizeEasing(f):""+f;e(d,f,c)}return void 0==c.offset&&(c.offset=null),void 0==c.easing&&(c.easing="linear"),c}),g=!0,h=-1/0,i=0;i<d.length;i++){var j=d[i].offset;if(null!=j){if(j<h)throw new TypeError("Keyframes are not loosely sorted by offset. Sort or specify offsets.");h=j}else g=!1}return d=d.filter(function(a){return a.offset>=0&&a.offset<=1}),g||c(),d}var h={background:["backgroundImage","backgroundPosition","backgroundSize","backgroundRepeat","backgroundAttachment","backgroundOrigin","backgroundClip","backgroundColor"],border:["borderTopColor","borderTopStyle","borderTopWidth","borderRightColor","borderRightStyle","borderRightWidth","borderBottomColor","borderBottomStyle","borderBottomWidth","borderLeftColor","borderLeftStyle","borderLeftWidth"],borderBottom:["borderBottomWidth","borderBottomStyle","borderBottomColor"],borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderLeft:["borderLeftWidth","borderLeftStyle","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderRight:["borderRightWidth","borderRightStyle","borderRightColor"],borderTop:["borderTopWidth","borderTopStyle","borderTopColor"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],flex:["flexGrow","flexShrink","flexBasis"],font:["fontFamily","fontSize","fontStyle","fontVariant","fontWeight","lineHeight"],margin:["marginTop","marginRight","marginBottom","marginLeft"],outline:["outlineColor","outlineStyle","outlineWidth"],padding:["paddingTop","paddingRight","paddingBottom","paddingLeft"]},i=document.createElementNS("http://www.w3.org/1999/xhtml","div"),j={thin:"1px",medium:"3px",thick:"5px"},k={borderBottomWidth:j,borderLeftWidth:j,borderRightWidth:j,borderTopWidth:j,fontSize:{"xx-small":"60%","x-small":"75%",small:"89%",medium:"100%",large:"120%","x-large":"150%","xx-large":"200%"},fontWeight:{normal:"400",bold:"700"},outlineWidth:j,textShadow:{none:"0px 0px 0px transparent"},boxShadow:{none:"0px 0px 0px 0px transparent"}};a.convertToArrayForm=f,a.normalizeKeyframes=g}(c),function(a){var b={};a.isDeprecated=function(a,c,d,e){var f=e?"are":"is",g=new Date,h=new Date(c);return h.setMonth(h.getMonth()+3),!(g<h&&(a in b||console.warn("Web Animations: "+a+" "+f+" deprecated and will stop working on "+h.toDateString()+". "+d),b[a]=!0,1))},a.deprecated=function(b,c,d,e){var f=e?"are":"is";if(a.isDeprecated(b,c,d,e))throw new Error(b+" "+f+" no longer supported. "+d)}}(c),function(){if(document.documentElement.animate){var a=document.documentElement.animate([],0),b=!0;if(a&&(b=!1,"play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(c){void 0===a[c]&&(b=!0)})),!b)return}!function(a,b,c){function d(a){for(var b={},c=0;c<a.length;c++)for(var d in a[c])if("offset"!=d&&"easing"!=d&&"composite"!=d){var e={offset:a[c].offset,easing:a[c].easing,value:a[c][d]};b[d]=b[d]||[],b[d].push(e)}for(var f in b){var g=b[f];if(0!=g[0].offset||1!=g[g.length-1].offset)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"Partial keyframes are not supported"}}return b}function e(c){var d=[];for(var e in c)for(var f=c[e],g=0;g<f.length-1;g++){var h=g,i=g+1,j=f[h].offset,k=f[i].offset,l=j,m=k;0==g&&(l=-1/0,0==k&&(i=h)),g==f.length-2&&(m=1/0,1==j&&(h=i)),d.push({applyFrom:l,applyTo:m,startOffset:f[h].offset,endOffset:f[i].offset,easingFunction:a.parseEasingFunction(f[h].easing),property:e,interpolation:b.propertyInterpolation(e,f[h].value,f[i].value)})}return d.sort(function(a,b){return a.startOffset-b.startOffset}),d}b.convertEffectInput=function(c){var f=a.normalizeKeyframes(c),g=d(f),h=e(g);return function(a,c){if(null!=c)h.filter(function(a){return c>=a.applyFrom&&c<a.applyTo}).forEach(function(d){var e=c-d.startOffset,f=d.endOffset-d.startOffset,g=0==f?0:d.easingFunction(e/f);b.apply(a,d.property,d.interpolation(g))});else for(var d in g)"offset"!=d&&"easing"!=d&&"composite"!=d&&b.clear(a,d)}}}(c,d),function(a,b,c){function d(a){return a.replace(/-(.)/g,function(a,b){return b.toUpperCase()})}function e(a,b,c){h[c]=h[c]||[],h[c].push([a,b])}function f(a,b,c){for(var f=0;f<c.length;f++){e(a,b,d(c[f]))}}function g(c,e,f){var g=c;/-/.test(c)&&!a.isDeprecated("Hyphenated property names","2016-03-22","Use camelCase instead.",!0)&&(g=d(c)),"initial"!=e&&"initial"!=f||("initial"==e&&(e=i[g]),"initial"==f&&(f=i[g]));for(var j=e==f?[]:h[g],k=0;j&&k<j.length;k++){var l=j[k][0](e),m=j[k][0](f);if(void 0!==l&&void 0!==m){var n=j[k][1](l,m);if(n){var o=b.Interpolation.apply(null,n);return function(a){return 0==a?e:1==a?f:o(a)}}}}return b.Interpolation(!1,!0,function(a){return a?f:e})}var h={};b.addPropertiesHandler=f;var i={backgroundColor:"transparent",backgroundPosition:"0% 0%",borderBottomColor:"currentColor",borderBottomLeftRadius:"0px",borderBottomRightRadius:"0px",borderBottomWidth:"3px",borderLeftColor:"currentColor",borderLeftWidth:"3px",borderRightColor:"currentColor",borderRightWidth:"3px",borderSpacing:"2px",borderTopColor:"currentColor",borderTopLeftRadius:"0px",borderTopRightRadius:"0px",borderTopWidth:"3px",bottom:"auto",clip:"rect(0px, 0px, 0px, 0px)",color:"black",fontSize:"100%",fontWeight:"400",height:"auto",left:"auto",letterSpacing:"normal",lineHeight:"120%",marginBottom:"0px",marginLeft:"0px",marginRight:"0px",marginTop:"0px",maxHeight:"none",maxWidth:"none",minHeight:"0px",minWidth:"0px",opacity:"1.0",outlineColor:"invert",outlineOffset:"0px",outlineWidth:"3px",paddingBottom:"0px",paddingLeft:"0px",paddingRight:"0px",paddingTop:"0px",right:"auto",strokeDasharray:"none",strokeDashoffset:"0px",textIndent:"0px",textShadow:"0px 0px 0px transparent",top:"auto",transform:"",verticalAlign:"0px",visibility:"visible",width:"auto",wordSpacing:"normal",zIndex:"auto"};b.propertyInterpolation=g}(c,d),function(a,b,c){function d(b){var c=a.calculateActiveDuration(b),d=function(d){return a.calculateIterationProgress(c,d,b)};return d._totalDuration=b.delay+c+b.endDelay,d}b.KeyframeEffect=function(c,e,f,g){var h,i=d(a.normalizeTimingInput(f)),j=b.convertEffectInput(e),k=function(){j(c,h)};return k._update=function(a){return null!==(h=i(a))},k._clear=function(){j(c,null)},k._hasSameTarget=function(a){return c===a},k._target=c,k._totalDuration=i._totalDuration,k._id=g,k}}(c,d),function(a,b){function c(a,b){return!(!b.namespaceURI||-1==b.namespaceURI.indexOf("/svg"))&&(g in a||(a[g]=/Trident|MSIE|IEMobile|Edge|Android 4/i.test(a.navigator.userAgent)),a[g])}function d(a,b,c){c.enumerable=!0,c.configurable=!0,Object.defineProperty(a,b,c)}function e(a){this._element=a,this._surrogateStyle=document.createElementNS("http://www.w3.org/1999/xhtml","div").style,this._style=a.style,this._length=0,this._isAnimatedProperty={},this._updateSvgTransformAttr=c(window,a),this._savedTransformAttr=null;for(var b=0;b<this._style.length;b++){var d=this._style[b];this._surrogateStyle[d]=this._style[d]}this._updateIndices()}function f(a){if(!a._webAnimationsPatchedStyle){var b=new e(a);try{d(a,"style",{get:function(){return b}})}catch(b){a.style._set=function(b,c){a.style[b]=c},a.style._clear=function(b){a.style[b]=""}}a._webAnimationsPatchedStyle=a.style}}var g="_webAnimationsUpdateSvgTransformAttr",h={cssText:1,length:1,parentRule:1},i={getPropertyCSSValue:1,getPropertyPriority:1,getPropertyValue:1,item:1,removeProperty:1,setProperty:1},j={removeProperty:1,setProperty:1};e.prototype={get cssText(){return this._surrogateStyle.cssText},set cssText(a){for(var b={},c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;this._surrogateStyle.cssText=a,this._updateIndices();for(var c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;for(var d in b)this._isAnimatedProperty[d]||this._style.setProperty(d,this._surrogateStyle.getPropertyValue(d))},get length(){return this._surrogateStyle.length},get parentRule(){return this._style.parentRule},_updateIndices:function(){for(;this._length<this._surrogateStyle.length;)Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,get:function(a){return function(){return this._surrogateStyle[a]}}(this._length)}),this._length++;for(;this._length>this._surrogateStyle.length;)this._length--,Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,value:void 0})},_set:function(b,c){this._style[b]=c,this._isAnimatedProperty[b]=!0,this._updateSvgTransformAttr&&"transform"==a.unprefixedPropertyName(b)&&(null==this._savedTransformAttr&&(this._savedTransformAttr=this._element.getAttribute("transform")),this._element.setAttribute("transform",a.transformToSvgMatrix(c)))},_clear:function(b){this._style[b]=this._surrogateStyle[b],this._updateSvgTransformAttr&&"transform"==a.unprefixedPropertyName(b)&&(this._savedTransformAttr?this._element.setAttribute("transform",this._savedTransformAttr):this._element.removeAttribute("transform"),this._savedTransformAttr=null),delete this._isAnimatedProperty[b]}};for(var k in i)e.prototype[k]=function(a,b){return function(){var c=this._surrogateStyle[a].apply(this._surrogateStyle,arguments);return b&&(this._isAnimatedProperty[arguments[0]]||this._style[a].apply(this._style,arguments),this._updateIndices()),c}}(k,k in j);for(var l in document.documentElement.style)l in h||l in i||function(a){d(e.prototype,a,{get:function(){return this._surrogateStyle[a]},set:function(b){this._surrogateStyle[a]=b,this._updateIndices(),this._isAnimatedProperty[a]||(this._style[a]=b)}})}(l);a.apply=function(b,c,d){f(b),b.style._set(a.propertyName(c),d)},a.clear=function(b,c){b._webAnimationsPatchedStyle&&b.style._clear(a.propertyName(c))}}(d),function(a){window.Element.prototype.animate=function(b,c){var d="";return c&&c.id&&(d=c.id),a.timeline._play(a.KeyframeEffect(this,b,c,d))}}(d),function(a,b){function c(a,b,d){if("number"==typeof a&&"number"==typeof b)return a*(1-d)+b*d;if("boolean"==typeof a&&"boolean"==typeof b)return d<.5?a:b;if(a.length==b.length){for(var e=[],f=0;f<a.length;f++)e.push(c(a[f],b[f],d));return e}throw"Mismatched interpolation arguments "+a+":"+b}a.Interpolation=function(a,b,d){return function(e){return d(c(a,b,e))}}}(d),function(a,b){function c(a,b,c){return Math.max(Math.min(a,c),b)}function d(b,d,e){var f=a.dot(b,d);f=c(f,-1,1);var g=[];if(1===f)g=b;else for(var h=Math.acos(f),i=1*Math.sin(e*h)/Math.sqrt(1-f*f),j=0;j<4;j++)g.push(b[j]*(Math.cos(e*h)-f*i)+d[j]*i);return g}var e=function(){function a(a,b){for(var c=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],d=0;d<4;d++)for(var e=0;e<4;e++)for(var f=0;f<4;f++)c[d][e]+=b[d][f]*a[f][e];return c}function b(a){return 0==a[0][2]&&0==a[0][3]&&0==a[1][2]&&0==a[1][3]&&0==a[2][0]&&0==a[2][1]&&1==a[2][2]&&0==a[2][3]&&0==a[3][2]&&1==a[3][3]}function c(c,d,e,f,g){for(var h=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],i=0;i<4;i++)h[i][3]=g[i];for(var i=0;i<3;i++)for(var j=0;j<3;j++)h[3][i]+=c[j]*h[j][i];var k=f[0],l=f[1],m=f[2],n=f[3],o=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];o[0][0]=1-2*(l*l+m*m),o[0][1]=2*(k*l-m*n),o[0][2]=2*(k*m+l*n),o[1][0]=2*(k*l+m*n),o[1][1]=1-2*(k*k+m*m),o[1][2]=2*(l*m-k*n),o[2][0]=2*(k*m-l*n),o[2][1]=2*(l*m+k*n),o[2][2]=1-2*(k*k+l*l),h=a(h,o);var p=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];e[2]&&(p[2][1]=e[2],h=a(h,p)),e[1]&&(p[2][1]=0,p[2][0]=e[0],h=a(h,p)),e[0]&&(p[2][0]=0,p[1][0]=e[0],h=a(h,p));for(var i=0;i<3;i++)for(var j=0;j<3;j++)h[i][j]*=d[i];return b(h)?[h[0][0],h[0][1],h[1][0],h[1][1],h[3][0],h[3][1]]:h[0].concat(h[1],h[2],h[3])}return c}();a.composeMatrix=e,a.quat=d}(d),function(a,b,c){a.sequenceNumber=0;var d=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="finish",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()};b.Animation=function(b){this.id="",b&&b._id&&(this.id=b._id),this._sequenceNumber=a.sequenceNumber++,this._currentTime=0,this._startTime=null,this._paused=!1,this._playbackRate=1,this._inTimeline=!0,this._finishedFlag=!0,this.onfinish=null,this._finishHandlers=[],this._effect=b,this._inEffect=this._effect._update(0),this._idle=!0,this._currentTimePending=!1},b.Animation.prototype={_ensureAlive:function(){this.playbackRate<0&&0===this.currentTime?this._inEffect=this._effect._update(-1):this._inEffect=this._effect._update(this.currentTime),this._inTimeline||!this._inEffect&&this._finishedFlag||(this._inTimeline=!0,b.timeline._animations.push(this))},_tickCurrentTime:function(a,b){a!=this._currentTime&&(this._currentTime=a,this._isFinished&&!b&&(this._currentTime=this._playbackRate>0?this._totalDuration:0),this._ensureAlive())},get currentTime(){return this._idle||this._currentTimePending?null:this._currentTime},set currentTime(a){a=+a,isNaN(a)||(b.restart(),this._paused||null==this._startTime||(this._startTime=this._timeline.currentTime-a/this._playbackRate),this._currentTimePending=!1,this._currentTime!=a&&(this._idle&&(this._idle=!1,this._paused=!0),this._tickCurrentTime(a,!0),b.applyDirtiedAnimation(this)))},get startTime(){return this._startTime},set startTime(a){a=+a,isNaN(a)||this._paused||this._idle||(this._startTime=a,this._tickCurrentTime((this._timeline.currentTime-this._startTime)*this.playbackRate),b.applyDirtiedAnimation(this))},get playbackRate(){return this._playbackRate},set playbackRate(a){if(a!=this._playbackRate){var c=this.currentTime;this._playbackRate=a,this._startTime=null,"paused"!=this.playState&&"idle"!=this.playState&&(this._finishedFlag=!1,this._idle=!1,this._ensureAlive(),b.applyDirtiedAnimation(this)),null!=c&&(this.currentTime=c)}},get _isFinished(){return!this._idle&&(this._playbackRate>0&&this._currentTime>=this._totalDuration||this._playbackRate<0&&this._currentTime<=0)},get _totalDuration(){return this._effect._totalDuration},get playState(){return this._idle?"idle":null==this._startTime&&!this._paused&&0!=this.playbackRate||this._currentTimePending?"pending":this._paused?"paused":this._isFinished?"finished":"running"},_rewind:function(){if(this._playbackRate>=0)this._currentTime=0;else{if(!(this._totalDuration<1/0))throw new DOMException("Unable to rewind negative playback rate animation with infinite duration","InvalidStateError");this._currentTime=this._totalDuration}},play:function(){this._paused=!1,(this._isFinished||this._idle)&&(this._rewind(),this._startTime=null),this._finishedFlag=!1,this._idle=!1,this._ensureAlive(),b.applyDirtiedAnimation(this)},pause:function(){this._isFinished||this._paused||this._idle?this._idle&&(this._rewind(),this._idle=!1):this._currentTimePending=!0,this._startTime=null,this._paused=!0},finish:function(){this._idle||(this.currentTime=this._playbackRate>0?this._totalDuration:0,this._startTime=this._totalDuration-this.currentTime,this._currentTimePending=!1,b.applyDirtiedAnimation(this))},cancel:function(){this._inEffect&&(this._inEffect=!1,this._idle=!0,this._paused=!1,this._isFinished=!0,this._finishedFlag=!0,this._currentTime=0,this._startTime=null,this._effect._update(null),b.applyDirtiedAnimation(this))},reverse:function(){this.playbackRate*=-1,this.play()},addEventListener:function(a,b){"function"==typeof b&&"finish"==a&&this._finishHandlers.push(b)},removeEventListener:function(a,b){if("finish"==a){var c=this._finishHandlers.indexOf(b);c>=0&&this._finishHandlers.splice(c,1)}},_fireEvents:function(a){if(this._isFinished){if(!this._finishedFlag){var b=new d(this,this._currentTime,a),c=this._finishHandlers.concat(this.onfinish?[this.onfinish]:[]);setTimeout(function(){c.forEach(function(a){a.call(b.target,b)})},0),this._finishedFlag=!0}}else this._finishedFlag=!1},_tick:function(a,b){this._idle||this._paused||(null==this._startTime?b&&(this.startTime=a-this._currentTime/this.playbackRate):this._isFinished||this._tickCurrentTime((a-this._startTime)*this.playbackRate)),b&&(this._currentTimePending=!1,this._fireEvents(a))},get _needsTick(){return this.playState in{pending:1,running:1}||!this._finishedFlag},_targetAnimations:function(){var a=this._effect._target;return a._activeAnimations||(a._activeAnimations=[]),a._activeAnimations},_markTarget:function(){var a=this._targetAnimations();-1===a.indexOf(this)&&a.push(this)},_unmarkTarget:function(){var a=this._targetAnimations(),b=a.indexOf(this);-1!==b&&a.splice(b,1)}}}(c,d),function(a,b,c){function d(a){var b=j;j=[],a<q.currentTime&&(a=q.currentTime),q._animations.sort(e),q._animations=h(a,!0,q._animations)[0],b.forEach(function(b){b[1](a)}),g(),l=void 0}function e(a,b){return a._sequenceNumber-b._sequenceNumber}function f(){this._animations=[],this.currentTime=window.performance&&performance.now?performance.now():0}function g(){o.forEach(function(a){a()}),o.length=0}function h(a,c,d){p=!0,n=!1,b.timeline.currentTime=a,m=!1;var e=[],f=[],g=[],h=[];return d.forEach(function(b){b._tick(a,c),b._inEffect?(f.push(b._effect),b._markTarget()):(e.push(b._effect),b._unmarkTarget()),b._needsTick&&(m=!0);var d=b._inEffect||b._needsTick;b._inTimeline=d,d?g.push(b):h.push(b)}),o.push.apply(o,e),o.push.apply(o,f),m&&requestAnimationFrame(function(){}),p=!1,[g,h]}var i=window.requestAnimationFrame,j=[],k=0;window.requestAnimationFrame=function(a){var b=k++;return 0==j.length&&i(d),j.push([b,a]),b},window.cancelAnimationFrame=function(a){j.forEach(function(b){b[0]==a&&(b[1]=function(){})})},f.prototype={_play:function(c){c._timing=a.normalizeTimingInput(c.timing);var d=new b.Animation(c);return d._idle=!1,d._timeline=this,this._animations.push(d),b.restart(),b.applyDirtiedAnimation(d),d}};var l=void 0,m=!1,n=!1;b.restart=function(){return m||(m=!0,requestAnimationFrame(function(){}),n=!0),n},b.applyDirtiedAnimation=function(a){if(!p){a._markTarget();var c=a._targetAnimations();c.sort(e),h(b.timeline.currentTime,!1,c.slice())[1].forEach(function(a){var b=q._animations.indexOf(a);-1!==b&&q._animations.splice(b,1)}),g()}};var o=[],p=!1,q=new f;b.timeline=q}(c,d),function(a,b){function c(a,b){for(var c=0,d=0;d<a.length;d++)c+=a[d]*b[d];return c}function d(a,b){return[a[0]*b[0]+a[4]*b[1]+a[8]*b[2]+a[12]*b[3],a[1]*b[0]+a[5]*b[1]+a[9]*b[2]+a[13]*b[3],a[2]*b[0]+a[6]*b[1]+a[10]*b[2]+a[14]*b[3],a[3]*b[0]+a[7]*b[1]+a[11]*b[2]+a[15]*b[3],a[0]*b[4]+a[4]*b[5]+a[8]*b[6]+a[12]*b[7],a[1]*b[4]+a[5]*b[5]+a[9]*b[6]+a[13]*b[7],a[2]*b[4]+a[6]*b[5]+a[10]*b[6]+a[14]*b[7],a[3]*b[4]+a[7]*b[5]+a[11]*b[6]+a[15]*b[7],a[0]*b[8]+a[4]*b[9]+a[8]*b[10]+a[12]*b[11],a[1]*b[8]+a[5]*b[9]+a[9]*b[10]+a[13]*b[11],a[2]*b[8]+a[6]*b[9]+a[10]*b[10]+a[14]*b[11],a[3]*b[8]+a[7]*b[9]+a[11]*b[10]+a[15]*b[11],a[0]*b[12]+a[4]*b[13]+a[8]*b[14]+a[12]*b[15],a[1]*b[12]+a[5]*b[13]+a[9]*b[14]+a[13]*b[15],a[2]*b[12]+a[6]*b[13]+a[10]*b[14]+a[14]*b[15],a[3]*b[12]+a[7]*b[13]+a[11]*b[14]+a[15]*b[15]]}function e(a){var b=a.rad||0;return((a.deg||0)/360+(a.grad||0)/400+(a.turn||0))*(2*Math.PI)+b}function f(a){switch(a.t){case"rotatex":var b=e(a.d[0]);return[1,0,0,0,0,Math.cos(b),Math.sin(b),0,0,-Math.sin(b),Math.cos(b),0,0,0,0,1];case"rotatey":var b=e(a.d[0]);return[Math.cos(b),0,-Math.sin(b),0,0,1,0,0,Math.sin(b),0,Math.cos(b),0,0,0,0,1];case"rotate":case"rotatez":var b=e(a.d[0]);return[Math.cos(b),Math.sin(b),0,0,-Math.sin(b),Math.cos(b),0,0,0,0,1,0,0,0,0,1];case"rotate3d":var c=a.d[0],d=a.d[1],f=a.d[2],b=e(a.d[3]),g=c*c+d*d+f*f;if(0===g)c=1,d=0,f=0;else if(1!==g){var h=Math.sqrt(g);c/=h,d/=h,f/=h}var i=Math.sin(b/2),j=i*Math.cos(b/2),k=i*i;return[1-2*(d*d+f*f)*k,2*(c*d*k+f*j),2*(c*f*k-d*j),0,2*(c*d*k-f*j),1-2*(c*c+f*f)*k,2*(d*f*k+c*j),0,2*(c*f*k+d*j),2*(d*f*k-c*j),1-2*(c*c+d*d)*k,0,0,0,0,1];case"scale":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,1,0,0,0,0,1];case"scalex":return[a.d[0],0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"scaley":return[1,0,0,0,0,a.d[0],0,0,0,0,1,0,0,0,0,1];case"scalez":return[1,0,0,0,0,1,0,0,0,0,a.d[0],0,0,0,0,1];case"scale3d":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,a.d[2],0,0,0,0,1];case"skew":var l=e(a.d[0]),m=e(a.d[1]);return[1,Math.tan(m),0,0,Math.tan(l),1,0,0,0,0,1,0,0,0,0,1];case"skewx":var b=e(a.d[0]);return[1,0,0,0,Math.tan(b),1,0,0,0,0,1,0,0,0,0,1];case"skewy":var b=e(a.d[0]);return[1,Math.tan(b),0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"translate":var c=a.d[0].px||0,d=a.d[1].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,c,d,0,1];case"translatex":var c=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,c,0,0,1];case"translatey":var d=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,d,0,1];case"translatez":var f=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,0,f,1];case"translate3d":var c=a.d[0].px||0,d=a.d[1].px||0,f=a.d[2].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,c,d,f,1];case"perspective":return[1,0,0,0,0,1,0,0,0,0,1,a.d[0].px?-1/a.d[0].px:0,0,0,0,1];case"matrix":return[a.d[0],a.d[1],0,0,a.d[2],a.d[3],0,0,0,0,1,0,a.d[4],a.d[5],0,1];case"matrix3d":return a.d}}function g(a){return 0===a.length?[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]:a.map(f).reduce(d)}function h(a){return[i(g(a))]}var i=function(){function a(a){return a[0][0]*a[1][1]*a[2][2]+a[1][0]*a[2][1]*a[0][2]+a[2][0]*a[0][1]*a[1][2]-a[0][2]*a[1][1]*a[2][0]-a[1][2]*a[2][1]*a[0][0]-a[2][2]*a[0][1]*a[1][0]}function b(b){for(var c=1/a(b),d=b[0][0],e=b[0][1],f=b[0][2],g=b[1][0],h=b[1][1],i=b[1][2],j=b[2][0],k=b[2][1],l=b[2][2],m=[[(h*l-i*k)*c,(f*k-e*l)*c,(e*i-f*h)*c,0],[(i*j-g*l)*c,(d*l-f*j)*c,(f*g-d*i)*c,0],[(g*k-h*j)*c,(j*e-d*k)*c,(d*h-e*g)*c,0]],n=[],o=0;o<3;o++){for(var p=0,q=0;q<3;q++)p+=b[3][q]*m[q][o];n.push(p)}return n.push(1),m.push(n),m}function d(a){return[[a[0][0],a[1][0],a[2][0],a[3][0]],[a[0][1],a[1][1],a[2][1],a[3][1]],[a[0][2],a[1][2],a[2][2],a[3][2]],[a[0][3],a[1][3],a[2][3],a[3][3]]]}function e(a,b){for(var c=[],d=0;d<4;d++){for(var e=0,f=0;f<4;f++)e+=a[f]*b[f][d];c.push(e)}return c}function f(a){var b=g(a);return[a[0]/b,a[1]/b,a[2]/b]}function g(a){return Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2])}function h(a,b,c,d){return[c*a[0]+d*b[0],c*a[1]+d*b[1],c*a[2]+d*b[2]]}function i(a,b){return[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]}function j(j){var k=[j.slice(0,4),j.slice(4,8),j.slice(8,12),j.slice(12,16)];if(1!==k[3][3])return null;for(var l=[],m=0;m<4;m++)l.push(k[m].slice());for(var m=0;m<3;m++)l[m][3]=0;if(0===a(l))return null;var n,o=[];k[0][3]||k[1][3]||k[2][3]?(o.push(k[0][3]),o.push(k[1][3]),o.push(k[2][3]),o.push(k[3][3]),n=e(o,d(b(l)))):n=[0,0,0,1];var p=k[3].slice(0,3),q=[];q.push(k[0].slice(0,3));var r=[];r.push(g(q[0])),q[0]=f(q[0]);var s=[];q.push(k[1].slice(0,3)),s.push(c(q[0],q[1])),q[1]=h(q[1],q[0],1,-s[0]),r.push(g(q[1])),q[1]=f(q[1]),s[0]/=r[1],q.push(k[2].slice(0,3)),s.push(c(q[0],q[2])),q[2]=h(q[2],q[0],1,-s[1]),s.push(c(q[1],q[2])),q[2]=h(q[2],q[1],1,-s[2]),r.push(g(q[2])),q[2]=f(q[2]),s[1]/=r[2],s[2]/=r[2];var t=i(q[1],q[2]);if(c(q[0],t)<0)for(var m=0;m<3;m++)r[m]*=-1,q[m][0]*=-1,q[m][1]*=-1,q[m][2]*=-1;var u,v,w=q[0][0]+q[1][1]+q[2][2]+1;return w>1e-4?(u=.5/Math.sqrt(w),v=[(q[2][1]-q[1][2])*u,(q[0][2]-q[2][0])*u,(q[1][0]-q[0][1])*u,.25/u]):q[0][0]>q[1][1]&&q[0][0]>q[2][2]?(u=2*Math.sqrt(1+q[0][0]-q[1][1]-q[2][2]),v=[.25*u,(q[0][1]+q[1][0])/u,(q[0][2]+q[2][0])/u,(q[2][1]-q[1][2])/u]):q[1][1]>q[2][2]?(u=2*Math.sqrt(1+q[1][1]-q[0][0]-q[2][2]),v=[(q[0][1]+q[1][0])/u,.25*u,(q[1][2]+q[2][1])/u,(q[0][2]-q[2][0])/u]):(u=2*Math.sqrt(1+q[2][2]-q[0][0]-q[1][1]),v=[(q[0][2]+q[2][0])/u,(q[1][2]+q[2][1])/u,.25*u,(q[1][0]-q[0][1])/u]),[p,r,s,v,n]}return j}();a.dot=c,a.makeMatrixDecomposition=h,a.transformListToMatrix=g}(d),function(a){function b(a,b){var c=a.exec(b);if(c)return c=a.ignoreCase?c[0].toLowerCase():c[0],[c,b.substr(c.length)]}function c(a,b){b=b.replace(/^\s*/,"");var c=a(b);if(c)return[c[0],c[1].replace(/^\s*/,"")]}function d(a,d,e){a=c.bind(null,a);for(var f=[];;){var g=a(e);if(!g)return[f,e];if(f.push(g[0]),e=g[1],!(g=b(d,e))||""==g[1])return[f,e];e=g[1]}}function e(a,b){for(var c=0,d=0;d<b.length&&(!/\s|,/.test(b[d])||0!=c);d++)if("("==b[d])c++;else if(")"==b[d]&&(c--,0==c&&d++,c<=0))break;var e=a(b.substr(0,d));return void 0==e?void 0:[e,b.substr(d)]}function f(a,b){for(var c=a,d=b;c&&d;)c>d?c%=d:d%=c;return c=a*b/(c+d)}function g(a){return function(b){var c=a(b);return c&&(c[0]=void 0),c}}function h(a,b){return function(c){return a(c)||[b,c]}}function i(b,c){for(var d=[],e=0;e<b.length;e++){var f=a.consumeTrimmed(b[e],c);if(!f||""==f[0])return;void 0!==f[0]&&d.push(f[0]),c=f[1]}if(""==c)return d}function j(a,b,c,d,e){for(var g=[],h=[],i=[],j=f(d.length,e.length),k=0;k<j;k++){var l=b(d[k%d.length],e[k%e.length]);if(!l)return;g.push(l[0]),h.push(l[1]),i.push(l[2])}return[g,h,function(b){var d=b.map(function(a,b){return i[b](a)}).join(c);return a?a(d):d}]}function k(a,b,c){for(var d=[],e=[],f=[],g=0,h=0;h<c.length;h++)if("function"==typeof c[h]){var i=c[h](a[g],b[g++]);d.push(i[0]),e.push(i[1]),f.push(i[2])}else!function(a){d.push(!1),e.push(!1),f.push(function(){return c[a]})}(h);return[d,e,function(a){for(var b="",c=0;c<a.length;c++)b+=f[c](a[c]);return b}]}a.consumeToken=b,a.consumeTrimmed=c,a.consumeRepeated=d,a.consumeParenthesised=e,a.ignore=g,a.optional=h,a.consumeList=i,a.mergeNestedRepeated=j.bind(null,null),a.mergeWrappedNestedRepeated=j,a.mergeList=k}(d),function(a){function b(b){function c(b){var c=a.consumeToken(/^inset/i,b);if(c)return d.inset=!0,c;var c=a.consumeLengthOrPercent(b);if(c)return d.lengths.push(c[0]),c;var c=a.consumeColor(b);return c?(d.color=c[0],c):void 0}var d={inset:!1,lengths:[],color:null},e=a.consumeRepeated(c,/^/,b);if(e&&e[0].length)return[d,e[1]]}function c(c){var d=a.consumeRepeated(b,/^,/,c);if(d&&""==d[1])return d[0]}function d(b,c){for(;b.lengths.length<Math.max(b.lengths.length,c.lengths.length);)b.lengths.push({px:0});for(;c.lengths.length<Math.max(b.lengths.length,c.lengths.length);)c.lengths.push({px:0});if(b.inset==c.inset&&!!b.color==!!c.color){for(var d,e=[],f=[[],0],g=[[],0],h=0;h<b.lengths.length;h++){var i=a.mergeDimensions(b.lengths[h],c.lengths[h],2==h);f[0].push(i[0]),g[0].push(i[1]),e.push(i[2])}if(b.color&&c.color){var j=a.mergeColors(b.color,c.color);f[1]=j[0],g[1]=j[1],d=j[2]}return[f,g,function(a){for(var c=b.inset?"inset ":" ",f=0;f<e.length;f++)c+=e[f](a[0][f])+" ";return d&&(c+=d(a[1])),c}]}}function e(b,c,d,e){function f(a){return{inset:a,color:[0,0,0,0],lengths:[{px:0},{px:0},{px:0},{px:0}]}}for(var g=[],h=[],i=0;i<d.length||i<e.length;i++){var j=d[i]||f(e[i].inset),k=e[i]||f(d[i].inset);g.push(j),h.push(k)}return a.mergeNestedRepeated(b,c,g,h)}var f=e.bind(null,d,", ");a.addPropertiesHandler(c,f,["box-shadow","text-shadow"])}(d),function(a,b){function c(a){return a.toFixed(3).replace(/0+$/,"").replace(/\.$/,"")}function d(a,b,c){return Math.min(b,Math.max(a,c))}function e(a){if(/^\s*[-+]?(\d*\.)?\d+\s*$/.test(a))return Number(a)}function f(a,b){return[a,b,c]}function g(a,b){if(0!=a)return i(0,1/0)(a,b)}function h(a,b){return[a,b,function(a){return Math.round(d(1,1/0,a))}]}function i(a,b){return function(e,f){return[e,f,function(e){return c(d(a,b,e))}]}}function j(a){var b=a.trim().split(/\s*[\s,]\s*/);if(0!==b.length){for(var c=[],d=0;d<b.length;d++){var f=e(b[d]);if(void 0===f)return;c.push(f)}return c}}function k(a,b){if(a.length==b.length)return[a,b,function(a){return a.map(c).join(" ")}]}function l(a,b){return[a,b,Math.round]}a.clamp=d,a.addPropertiesHandler(j,k,["stroke-dasharray"]),a.addPropertiesHandler(e,i(0,1/0),["border-image-width","line-height"]),a.addPropertiesHandler(e,i(0,1),["opacity","shape-image-threshold"]),a.addPropertiesHandler(e,g,["flex-grow","flex-shrink"]),a.addPropertiesHandler(e,h,["orphans","widows"]),a.addPropertiesHandler(e,l,["z-index"]),a.parseNumber=e,a.parseNumberList=j,a.mergeNumbers=f,a.numberToString=c}(d),function(a,b){function c(a,b){if("visible"==a||"visible"==b)return[0,1,function(c){return c<=0?a:c>=1?b:"visible"}]}a.addPropertiesHandler(String,c,["visibility"])}(d),function(a,b){function c(a){a=a.trim(),f.fillStyle="#000",f.fillStyle=a;var b=f.fillStyle;if(f.fillStyle="#fff",f.fillStyle=a,b==f.fillStyle){f.fillRect(0,0,1,1);var c=f.getImageData(0,0,1,1).data;f.clearRect(0,0,1,1);var d=c[3]/255;return[c[0]*d,c[1]*d,c[2]*d,d]}}function d(b,c){return[b,c,function(b){function c(a){return Math.max(0,Math.min(255,a))}if(b[3])for(var d=0;d<3;d++)b[d]=Math.round(c(b[d]/b[3]));return b[3]=a.numberToString(a.clamp(0,1,b[3])),"rgba("+b.join(",")+")"}]}var e=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");e.width=e.height=1;var f=e.getContext("2d");a.addPropertiesHandler(c,d,["background-color","border-bottom-color","border-left-color","border-right-color","border-top-color","color","fill","flood-color","lighting-color","outline-color","stop-color","stroke","text-decoration-color"]),a.consumeColor=a.consumeParenthesised.bind(null,c),a.mergeColors=d}(d),function(a,b){function c(a){function b(){var b=h.exec(a);g=b?b[0]:void 0}function c(){var a=Number(g);return b(),a}function d(){if("("!==g)return c();b();var a=f();return")"!==g?NaN:(b(),a)}function e(){for(var a=d();"*"===g||"/"===g;){var c=g;b();var e=d();"*"===c?a*=e:a/=e}return a}function f(){for(var a=e();"+"===g||"-"===g;){var c=g;b();var d=e();"+"===c?a+=d:a-=d}return a}var g,h=/([\+\-\w\.]+|[\(\)\*\/])/g;return b(),f()}function d(a,b){if("0"==(b=b.trim().toLowerCase())&&"px".search(a)>=0)return{px:0};if(/^[^(]*$|^calc/.test(b)){b=b.replace(/calc\(/g,"(");var d={};b=b.replace(a,function(a){return d[a]=null,"U"+a});for(var e="U("+a.source+")",f=b.replace(/[-+]?(\d*\.)?\d+([Ee][-+]?\d+)?/g,"N").replace(new RegExp("N"+e,"g"),"D").replace(/\s[+-]\s/g,"O").replace(/\s/g,""),g=[/N\*(D)/g,/(N|D)[*\/]N/g,/(N|D)O\1/g,/\((N|D)\)/g],h=0;h<g.length;)g[h].test(f)?(f=f.replace(g[h],"$1"),h=0):h++;if("D"==f){for(var i in d){var j=c(b.replace(new RegExp("U"+i,"g"),"").replace(new RegExp(e,"g"),"*0"));if(!isFinite(j))return;d[i]=j}return d}}}function e(a,b){return f(a,b,!0)}function f(b,c,d){var e,f=[];for(e in b)f.push(e);for(e in c)f.indexOf(e)<0&&f.push(e);return b=f.map(function(a){return b[a]||0}),c=f.map(function(a){return c[a]||0}),[b,c,function(b){var c=b.map(function(c,e){return 1==b.length&&d&&(c=Math.max(c,0)),a.numberToString(c)+f[e]}).join(" + ");return b.length>1?"calc("+c+")":c}]}var g="px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc",h=d.bind(null,new RegExp(g,"g")),i=d.bind(null,new RegExp(g+"|%","g")),j=d.bind(null,/deg|rad|grad|turn/g);a.parseLength=h,a.parseLengthOrPercent=i,a.consumeLengthOrPercent=a.consumeParenthesised.bind(null,i),a.parseAngle=j,a.mergeDimensions=f;var k=a.consumeParenthesised.bind(null,h),l=a.consumeRepeated.bind(void 0,k,/^/),m=a.consumeRepeated.bind(void 0,l,/^,/);a.consumeSizePairList=m;var n=function(a){var b=m(a);if(b&&""==b[1])return b[0]},o=a.mergeNestedRepeated.bind(void 0,e," "),p=a.mergeNestedRepeated.bind(void 0,o,",");a.mergeNonNegativeSizePair=o,a.addPropertiesHandler(n,p,["background-size"]),a.addPropertiesHandler(i,e,["border-bottom-width","border-image-width","border-left-width","border-right-width","border-top-width","flex-basis","font-size","height","line-height","max-height","max-width","outline-width","width"]),a.addPropertiesHandler(i,f,["border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","bottom","left","letter-spacing","margin-bottom","margin-left","margin-right","margin-top","min-height","min-width","outline-offset","padding-bottom","padding-left","padding-right","padding-top","perspective","right","shape-margin","stroke-dashoffset","text-indent","top","vertical-align","word-spacing"])}(d),function(a,b){function c(b){return a.consumeLengthOrPercent(b)||a.consumeToken(/^auto/,b)}function d(b){var d=a.consumeList([a.ignore(a.consumeToken.bind(null,/^rect/)),a.ignore(a.consumeToken.bind(null,/^\(/)),a.consumeRepeated.bind(null,c,/^,/),a.ignore(a.consumeToken.bind(null,/^\)/))],b);if(d&&4==d[0].length)return d[0]}function e(b,c){return"auto"==b||"auto"==c?[!0,!1,function(d){var e=d?b:c;if("auto"==e)return"auto";var f=a.mergeDimensions(e,e);return f[2](f[0])}]:a.mergeDimensions(b,c)}function f(a){return"rect("+a+")"}var g=a.mergeWrappedNestedRepeated.bind(null,f,e,", ");a.parseBox=d,a.mergeBoxes=g,a.addPropertiesHandler(d,g,["clip"])}(d),function(a,b){function c(a){return function(b){var c=0;return a.map(function(a){return a===k?b[c++]:a})}}function d(a){return a}function e(b){if("none"==(b=b.toLowerCase().trim()))return[];for(var c,d=/\s*(\w+)\(([^)]*)\)/g,e=[],f=0;c=d.exec(b);){if(c.index!=f)return;f=c.index+c[0].length;var g=c[1],h=n[g];if(!h)return;var i=c[2].split(","),j=h[0];if(j.length<i.length)return;for(var k=[],o=0;o<j.length;o++){var p,q=i[o],r=j[o];if(void 0===(p=q?{A:function(b){return"0"==b.trim()?m:a.parseAngle(b)},N:a.parseNumber,T:a.parseLengthOrPercent,L:a.parseLength}[r.toUpperCase()](q):{a:m,n:k[0],t:l}[r]))return;k.push(p)}if(e.push({t:g,d:k}),d.lastIndex==b.length)return e}}function f(a){return a.toFixed(6).replace(".000000","")}function g(b,c){if(b.decompositionPair!==c){b.decompositionPair=c;var d=a.makeMatrixDecomposition(b)}if(c.decompositionPair!==b){c.decompositionPair=b;var e=a.makeMatrixDecomposition(c)}return null==d[0]||null==e[0]?[[!1],[!0],function(a){return a?c[0].d:b[0].d}]:(d[0].push(0),e[0].push(1),[d,e,function(b){var c=a.quat(d[0][3],e[0][3],b[5]);return a.composeMatrix(b[0],b[1],b[2],c,b[4]).map(f).join(",")}])}function h(a){return a.replace(/[xy]/,"")}function i(a){return a.replace(/(x|y|z|3d)?$/,"3d")}function j(b,c){var d=a.makeMatrixDecomposition&&!0,e=!1;if(!b.length||!c.length){b.length||(e=!0,b=c,c=[]);for(var f=0;f<b.length;f++){var j=b[f].t,k=b[f].d,l="scale"==j.substr(0,5)?1:0;c.push({t:j,d:k.map(function(a){if("number"==typeof a)return l;var b={};for(var c in a)b[c]=l;return b})})}}var m=function(a,b){return"perspective"==a&&"perspective"==b||("matrix"==a||"matrix3d"==a)&&("matrix"==b||"matrix3d"==b)},o=[],p=[],q=[];if(b.length!=c.length){if(!d)return;var r=g(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]]}else for(var f=0;f<b.length;f++){var j,s=b[f].t,t=c[f].t,u=b[f].d,v=c[f].d,w=n[s],x=n[t];if(m(s,t)){if(!d)return;var r=g([b[f]],[c[f]]);o.push(r[0]),p.push(r[1]),q.push(["matrix",[r[2]]])}else{if(s==t)j=s;else if(w[2]&&x[2]&&h(s)==h(t))j=h(s),u=w[2](u),v=x[2](v);else{if(!w[1]||!x[1]||i(s)!=i(t)){if(!d)return;var r=g(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]];break}j=i(s),u=w[1](u),v=x[1](v)}for(var y=[],z=[],A=[],B=0;B<u.length;B++){var C="number"==typeof u[B]?a.mergeNumbers:a.mergeDimensions,r=C(u[B],v[B]);y[B]=r[0],z[B]=r[1],A.push(r[2])}o.push(y),p.push(z),q.push([j,A])}}if(e){var D=o;o=p,p=D}return[o,p,function(a){return a.map(function(a,b){var c=a.map(function(a,c){return q[b][1][c](a)}).join(",");return"matrix"==q[b][0]&&16==c.split(",").length&&(q[b][0]="matrix3d"),q[b][0]+"("+c+")"}).join(" ")}]}var k=null,l={px:0},m={deg:0},n={matrix:["NNNNNN",[k,k,0,0,k,k,0,0,0,0,1,0,k,k,0,1],d],matrix3d:["NNNNNNNNNNNNNNNN",d],rotate:["A"],rotatex:["A"],rotatey:["A"],rotatez:["A"],rotate3d:["NNNA"],perspective:["L"],scale:["Nn",c([k,k,1]),d],scalex:["N",c([k,1,1]),c([k,1])],scaley:["N",c([1,k,1]),c([1,k])],scalez:["N",c([1,1,k])],scale3d:["NNN",d],skew:["Aa",null,d],skewx:["A",null,c([k,m])],skewy:["A",null,c([m,k])],translate:["Tt",c([k,k,l]),d],translatex:["T",c([k,l,l]),c([k,l])],translatey:["T",c([l,k,l]),c([l,k])],translatez:["L",c([l,l,k])],translate3d:["TTL",d]};a.addPropertiesHandler(e,j,["transform"]),a.transformToSvgMatrix=function(b){var c=a.transformListToMatrix(e(b));return"matrix("+f(c[0])+" "+f(c[1])+" "+f(c[4])+" "+f(c[5])+" "+f(c[12])+" "+f(c[13])+")"}}(d),function(a){function b(a){var b=Number(a);if(!(isNaN(b)||b<100||b>900||b%100!=0))return b}function c(b){return b=100*Math.round(b/100),b=a.clamp(100,900,b),400===b?"normal":700===b?"bold":String(b)}function d(a,b){return[a,b,c]}a.addPropertiesHandler(b,d,["font-weight"])}(d),function(a){function b(a){var b={};for(var c in a)b[c]=-a[c];return b}function c(b){return a.consumeToken(/^(left|center|right|top|bottom)\b/i,b)||a.consumeLengthOrPercent(b)}function d(b,d){var e=a.consumeRepeated(c,/^/,d);if(e&&""==e[1]){var f=e[0];if(f[0]=f[0]||"center",f[1]=f[1]||"center",3==b&&(f[2]=f[2]||{px:0}),f.length==b){if(/top|bottom/.test(f[0])||/left|right/.test(f[1])){var h=f[0];f[0]=f[1],f[1]=h}if(/left|right|center|Object/.test(f[0])&&/top|bottom|center|Object/.test(f[1]))return f.map(function(a){return"object"==typeof a?a:g[a]})}}}function e(d){var e=a.consumeRepeated(c,/^/,d);if(e){for(var f=e[0],h=[{"%":50},{"%":50}],i=0,j=!1,k=0;k<f.length;k++){var l=f[k];"string"==typeof l?(j=/bottom|right/.test(l),i={left:0,right:0,center:i,top:1,bottom:1}[l],h[i]=g[l],"center"==l&&i++):(j&&(l=b(l),l["%"]=(l["%"]||0)+100),h[i]=l,i++,j=!1)}return[h,e[1]]}}function f(b){var c=a.consumeRepeated(e,/^,/,b);if(c&&""==c[1])return c[0]}var g={left:{"%":0},center:{"%":50},right:{"%":100},top:{"%":0},bottom:{"%":100}},h=a.mergeNestedRepeated.bind(null,a.mergeDimensions," ");a.addPropertiesHandler(d.bind(null,3),h,["transform-origin"]),a.addPropertiesHandler(d.bind(null,2),h,["perspective-origin"]),a.consumePosition=e,a.mergeOffsetList=h;var i=a.mergeNestedRepeated.bind(null,h,", ");a.addPropertiesHandler(f,i,["background-position","object-position"])}(d),function(a){function b(b){var c=a.consumeToken(/^circle/,b);if(c&&c[0])return["circle"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),d,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],c[1]));var f=a.consumeToken(/^ellipse/,b);if(f&&f[0])return["ellipse"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),e,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],f[1]));var g=a.consumeToken(/^polygon/,b);return g&&g[0]?["polygon"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),a.optional(a.consumeToken.bind(void 0,/^nonzero\s*,|^evenodd\s*,/),"nonzero,"),a.consumeSizePairList,a.ignore(a.consumeToken.bind(void 0,/^\)/))],g[1])):void 0}function c(b,c){if(b[0]===c[0])return"circle"==b[0]?a.mergeList(b.slice(1),c.slice(1),["circle(",a.mergeDimensions," at ",a.mergeOffsetList,")"]):"ellipse"==b[0]?a.mergeList(b.slice(1),c.slice(1),["ellipse(",a.mergeNonNegativeSizePair," at ",a.mergeOffsetList,")"]):"polygon"==b[0]&&b[1]==c[1]?a.mergeList(b.slice(2),c.slice(2),["polygon(",b[1],g,")"]):void 0}var d=a.consumeParenthesised.bind(null,a.parseLengthOrPercent),e=a.consumeRepeated.bind(void 0,d,/^/),f=a.mergeNestedRepeated.bind(void 0,a.mergeDimensions," "),g=a.mergeNestedRepeated.bind(void 0,f,",");a.addPropertiesHandler(b,c,["shape-outside"])}(d),function(a,b){function c(a,b){b.concat([a]).forEach(function(b){b in document.documentElement.style&&(d[a]=b),e[b]=a})}var d={},e={};c("transform",["webkitTransform","msTransform"]),c("transformOrigin",["webkitTransformOrigin"]),c("perspective",["webkitPerspective"]),c("perspectiveOrigin",["webkitPerspectiveOrigin"]),a.propertyName=function(a){return d[a]||a},a.unprefixedPropertyName=function(a){return e[a]||a}}(d)}(),function(){if(void 0===document.createElement("div").animate([]).oncancel){var a;if(window.performance&&performance.now)var a=function(){return performance.now()};else var a=function(){return Date.now()};var b=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="cancel",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()},c=window.Element.prototype.animate;window.Element.prototype.animate=function(d,e){var f=c.call(this,d,e);f._cancelHandlers=[],f.oncancel=null;var g=f.cancel;f.cancel=function(){g.call(this);var c=new b(this,null,a()),d=this._cancelHandlers.concat(this.oncancel?[this.oncancel]:[]);setTimeout(function(){d.forEach(function(a){a.call(c.target,c)})},0)};var h=f.addEventListener;f.addEventListener=function(a,b){"function"==typeof b&&"cancel"==a?this._cancelHandlers.push(b):h.call(this,a,b)};var i=f.removeEventListener;return f.removeEventListener=function(a,b){if("cancel"==a){var c=this._cancelHandlers.indexOf(b);c>=0&&this._cancelHandlers.splice(c,1)}else i.call(this,a,b)},f}}}(),function(a){var b=document.documentElement,c=null,d=!1;try{var e=getComputedStyle(b).getPropertyValue("opacity"),f="0"==e?"1":"0";c=b.animate({opacity:[f,f]},{duration:1}),c.currentTime=0,d=getComputedStyle(b).getPropertyValue("opacity")==f}catch(a){}finally{c&&c.cancel()}if(!d){var g=window.Element.prototype.animate;window.Element.prototype.animate=function(b,c){return window.Symbol&&Symbol.iterator&&Array.prototype.from&&b[Symbol.iterator]&&(b=Array.from(b)),Array.isArray(b)||null===b||(b=a.convertToArrayForm(b)),g.call(this,b,c)}}}(c),b.true=a}({},function(){return this}());
+//# sourceMappingURL=web-animations.min.js.map \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/web-animations-js/web-animations.min.js.map b/catapult/third_party/polymer/components/web-animations-js/web-animations.min.js.map
new file mode 100644
index 00000000..c78ace0f
--- /dev/null
+++ b/catapult/third_party/polymer/components/web-animations-js/web-animations.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["src/scope.js","src/timing-utilities.js","src/normalize-keyframes.js","src/deprecation.js","src/web-animations-bonus-cancel-events.js","src/web-animations-bonus-object-form-keyframes.js"],"names":["webAnimationsShared","webAnimations1","shared","testing","cloneTimingInput","timingInput","clone","m","AnimationEffectTiming","this","_delay","_endDelay","_fill","_iterationStart","_iterations","_duration","_playbackRate","_direction","_easing","_easingFunction","linear","isInvalidTimingDeprecated","isDeprecated","makeTiming","forGroup","effect","timing","fill","duration","isNaN","Object","getOwnPropertyNames","forEach","property","fills","indexOf","directions","numericTimingToObject","normalizeTimingInput","cubic","a","b","c","d","x","f","start_gradient","end_gradient","start","end","mid","xEst","Math","abs","step","count","pos","stepSize","normalizeEasing","easing","styleForCleaning","document","createElement","style","animationTimingFunction","normalizedEasing","TypeError","parseEasingFunction","cubicData","cubicBezierRe","exec","apply","slice","map","Number","stepData","stepRe","Start","middle","Middle","End","presets","calculateActiveDuration","repeatedDuration","playbackRate","iterations","calculatePhase","activeDuration","localTime","PhaseNone","endTime","delay","endDelay","min","PhaseBefore","PhaseAfter","PhaseActive","calculateActiveTime","fillMode","phase","calculateOverallProgress","iterationDuration","activeTime","iterationStart","overallProgress","calculateSimpleIterationProgress","simpleIterationProgress","Infinity","calculateCurrentIteration","floor","calculateDirectedProgress","playbackDirection","currentIteration","currentDirection","calculateIterationProgress","directedProgress","direction","split","prototype","_setMember","member","value","_effect","_timingInput","_timing","_animation","_rebuildUnderlyingAnimation","ease","ease-in","ease-out","ease-in-out","step-start","step-middle","step-end","numberString","RegExp","antiAlias","aliases","isNotAnimatable","lastIndexOf","expandShorthandAndAntiAlias","result","longProperties","shorthandToLonghand","shorthandExpanderElem","i","longProperty","longhandValue","convertToArrayForm","effectInput","normalizedEffectInput","values","Array","isArray","keyframe","numKeyframes","length","offset","composite","push","sort","normalizeKeyframes","spaceKeyframes","keyframes","previousIndex","previousOffset","j","window","Symbol","iterator","from","originalKeyframe","memberValue","isFinite","type","DOMException","NOT_SUPPORTED_ERR","name","message","everyFrameHasOffset","filter","background","border","borderBottom","borderColor","borderLeft","borderRadius","borderRight","borderTop","borderWidth","flex","font","margin","outline","padding","createElementNS","borderWidthAliases","thin","medium","thick","borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopWidth","fontSize","xx-small","x-small","small","large","x-large","xx-large","fontWeight","normal","bold","outlineWidth","textShadow","none","boxShadow","silenced","feature","date","advice","plural","auxVerb","today","Date","expiry","setMonth","getMonth","console","warn","toDateString","deprecated","Error","animate","oncancel","now","performance","AnimationCancelEvent","target","currentTime","timelineTime","bubbles","cancelable","currentTarget","defaultPrevented","eventPhase","Event","AT_TARGET","timeStamp","originalElementAnimate","Element","options","animation","call","_cancelHandlers","originalCancel","cancel","event","handlers","concat","setTimeout","handler","originalAddEventListener","addEventListener","originalRemoveEventListener","removeEventListener","index","splice","element","documentElement","animated","originalOpacity","getComputedStyle","getPropertyValue","testOpacity","opacity","error","webAnimationsNext","exports","webAnimationsTesting"],"mappings":";;;;;;;;;;;;;;CAcA,SAAIA,EAAAA,GAAJ,GAAIA,MACAC,MCDJ,SAAUC,EAAQC,GAMhB,QAASC,GAAiBC,GACxB,GAA0B,gBAAfA,GACT,MAAOA,EAET,IAAIC,KACJ,KAAK,GAAIC,KAAKF,GACZC,EAAMC,GAAKF,EAAYE,EAEzB,OAAOD,GAGT,QAASE,KACPC,KAAKC,OAAS,EACdD,KAAKE,UAAY,EACjBF,KAAKG,MAAQ,OACbH,KAAKI,gBAAkB,EACvBJ,KAAKK,YAAc,EACnBL,KAAKM,UAAY,EACjBN,KAAKO,cAAgB,EACrBP,KAAKQ,WAAa,SAClBR,KAAKS,QAAU,SACfT,KAAKU,gBAAkBC,EAGzB,QAASC,KACP,MAAOnB,GAAOoB,aAAa,wBAAyB,aAAc,gDAAA,GA8EpE,QAASC,GAAWlB,EAAamB,EAAUC,GACzC,GAAIC,GAAS,GAAIlB,EA4BjB,OA3BIgB,KACFE,EAAOC,KAAO,OACdD,EAAOE,SAAW,QAEM,gBAAfvB,IAA4BwB,MAAMxB,OAAAA,KAElCA,GACTyB,OAAOC,oBAAoB1B,GAAa2B,QAAQ,SAASC,GACvD,GAA6B,QAAzB5B,EAAY4B,GAAqB,CACnC,IAA+B,gBAApBP,GAAOO,IAAqC,YAAZA,KACL,gBAAzB5B,GAAY4B,IAAyBJ,MAAMxB,EAAY4B,KAChE,MAGJ,IAAiB,QAAZA,IAAiE,GAAzCC,EAAMC,QAAQ9B,EAAY4B,IACrD,MAEF,IAAiB,aAAZA,IAA2E,GAA9CG,EAAWD,QAAQ9B,EAAY4B,IAC/D,MAEF,IAAgB,gBAAZA,GAAwD,IAA1B5B,EAAY4B,IAAmB/B,EAAOoB,aAAa,qCAAsC,aAAc,uCACvI,MAEFI,GAAOO,GAAY5B,EAAY4B,MAlBnCP,EAAOE,SAAWvB,EAsBbqB,EAGT,QAASW,GAAsBhC,GAQ7B,MAP0B,gBAAfA,KAEPA,EADEwB,MAAMxB,IACQuB,SAAU,IAEVA,SAAUvB,IAGvBA,EAGT,QAASiC,GAAqBjC,EAAamB,GAEzC,MADAnB,GAAcH,EAAOmC,sBAAsBhC,GACpCkB,EAAWlB,EAAamB,GAGjC,QAASe,GAAMC,EAAGC,EAAGC,EAAGC,GACtB,MAAIH,GAAI,GAAKA,EAAI,GAAKE,EAAI,GAAKA,EAAI,EAC1BtB,EAEF,SAASwB,GAqBZ,QAASC,GAAEL,EAAGC,EAAGlC,GAAK,MAAO,GAAIiC,GAAK,EAAIjC,IAAM,EAAIA,GAAKA,EAAI,EAAIkC,GAAK,EAAIlC,GAAKA,EAAIA,EAAIA,EAAIA,EAAIA,EApBjG,GAAIqC,GAAK,EAAG,CACV,GAAIE,GAAiB,CAKrB,OAJIN,GAAI,EACNM,EAAiBL,EAAID,GACbC,GAAKC,EAAI,IACjBI,EAAiBH,EAAID,GAChBI,EAAiBF,EAE1B,GAAIA,GAAK,EAAG,CACV,GAAIG,GAAe,CAKnB,OAJIL,GAAI,EACNK,GAAgBJ,EAAI,IAAMD,EAAI,GAClB,GAALA,GAAUF,EAAI,IACrBO,GAAgBN,EAAI,IAAMD,EAAI,IACzB,EAAIO,GAAgBH,EAAI,GAIjC,IADA,GAAII,GAAQ,EAAGC,EAAM,EACdD,EAAQC,GAAK,CAClB,GAAIC,IAAOF,EAAQC,GAAO,EAEtBE,EAAON,EAAEL,EAAGE,EAAGQ,EACnB,IAAIE,KAAKC,IAAIT,EAAIO,GAAQ,KACvB,MAAON,GAAEJ,EAAGE,EAAGO,EAEbC,GAAOP,EACTI,EAAQE,EAERD,EAAMC,EAGV,MAAOL,GAAEJ,EAAGE,EAAGO,IAQnB,QAASI,GAAKC,EAAOC,GACnB,MAAO,UAASZ,GACd,GAAIA,GAAK,EACP,MAAO,EAET,IAAIa,GAAW,EAAIF,CAEnB,QADAX,GAAKY,EAAMC,GACAb,EAAIa,GAmBnB,QAASC,GAAgBC,GAClBC,IACHA,EAAmBC,SAASC,cAAc,OAAOC,OAEnDH,EAAiBI,wBAA0B,GAC3CJ,EAAiBI,wBAA0BL,CAC3C,IAAIM,GAAmBL,EAAiBI,uBACxC,IAAwB,IAApBC,GAA0B5C,IAC5B,KAAM,IAAI6C,WAAUP,EAAS,mCAE/B,OAAOM,GAGT,QAASE,GAAoBF,GAC3B,GAAwB,UAApBA,EACF,MAAO7C,EAET,IAAIgD,GAAYC,EAAcC,KAAKL,EACnC,IAAIG,EACF,MAAO7B,GAAMgC,MAAM9D,KAAM2D,EAAUI,MAAM,GAAGC,IAAIC,QAElD,IAAIC,GAAWC,EAAON,KAAKL,EAC3B,OAAIU,GACKrB,EAAKoB,OAAOC,EAAS,KAAM3B,MAAS6B,EAAOC,OAAUC,EAAQ9B,IAAO+B,GAAKL,EAAS,KAE9EM,EAAQhB,IAMd7C,EAGT,QAAS8D,GAAwBxD,GAC/B,MAAO0B,MAAKC,IAAI8B,EAAiBzD,GAAUA,EAAO0D,cAGpD,QAASD,GAAiBzD,GAExB,MAAwB,KAApBA,EAAOE,UAAwC,IAAtBF,EAAO2D,WAC3B,EAEF3D,EAAOE,SAAWF,EAAO2D,WAQlC,QAASC,GAAeC,EAAgBC,EAAW9D,GAEjD,GAAiB,MAAb8D,EACF,MAAOC,EAGT,IAAIC,GAAUhE,EAAOiE,MAAQJ,EAAiB7D,EAAOkE,QACrD,OAAIJ,GAAYpC,KAAKyC,IAAInE,EAAOiE,MAAOD,GAC9BI,EAELN,GAAapC,KAAKyC,IAAInE,EAAOiE,MAAQJ,EAAgBG,GAChDK,EAGFC,EAGT,QAASC,GAAoBV,EAAgBW,EAAUV,EAAWW,EAAOR,GAEvE,OAAQQ,GACN,IAAKL,GACH,MAAgB,aAAZI,GAAuC,QAAZA,EACtB,EACF,IACT,KAAKF,GACH,MAAOR,GAAYG,CACrB,KAAKI,GACH,MAAgB,YAAZG,GAAsC,QAAZA,EACrBX,EACF,IACT,KAAKE,GACH,MAAO,OAIb,QAASW,GAAyBC,EAAmBF,EAAOd,EAAYiB,EAAYC,GAElF,GAAIC,GAAkBD,CAQtB,OAP0B,KAAtBF,EACEF,IAAUL,IACZU,GAAmBnB,GAGrBmB,GAAmBF,EAAaD,EAE3BG,EAGT,QAASC,GAAiCD,EAAiBD,EAAgBJ,EAAOd,EAAYiB,EAAYD,GAGxG,GAAIK,GAA2BF,IAAoBG,EAAAA,EAAYJ,EAAiB,EAAIC,EAAkB,CAKtG,OAJgC,KAA5BE,GAAiCP,IAAUJ,GAA6B,IAAfV,GACzC,IAAfiB,GAA0C,IAAtBD,IACvBK,EAA0B,GAErBA,EAGT,QAASE,GAA0BT,EAAOd,EAAYqB,EAAyBF,GAE7E,MAAIL,KAAUJ,GAAcV,IAAesB,EAAAA,EAClCA,EAAAA,EAEuB,IAA5BD,EACKtD,KAAKyD,MAAML,GAAmB,EAEhCpD,KAAKyD,MAAML,GAGpB,QAASM,GAA0BC,EAAmBC,EAAkBN,GAEtE,GAAIO,GAAmBF,CACvB,IAA0B,WAAtBA,GAAwD,YAAtBA,EAAiC,CACrE,GAAIpE,GAAIqE,CACkB,uBAAtBD,IACFpE,GAAK,GAEPsE,EAAmB,SACftE,IAAMgE,EAAAA,GAAYhE,EAAI,GAAM,IAC9BsE,EAAmB,WAGvB,MAAyB,WAArBA,EACKP,EAEF,EAAIA,EAGb,QAASQ,GAA2B3B,EAAgBC,EAAW9D,GAC7D,GAAIyE,GAAQb,EAAeC,EAAgBC,EAAW9D,GAClD4E,EAAaL,EAAoBV,EAAgB7D,EAAOC,KAAM6D,EAAWW,EAAOzE,EAAOiE,MAC3F,IAAmB,OAAfW,EACF,MAAO,KAET,IAAIE,GAAkBJ,EAAyB1E,EAAOE,SAAUuE,EAAOzE,EAAO2D,WAAYiB,EAAY5E,EAAO6E,gBACzGG,EAA0BD,EAAiCD,EAAiB9E,EAAO6E,eAAgBJ,EAAOzE,EAAO2D,WAAYiB,EAAY5E,EAAOE,UAChJoF,EAAmBJ,EAA0BT,EAAOzE,EAAO2D,WAAYqB,EAAyBF,GAChGW,EAAmBL,EAA0BpF,EAAO0F,UAAWJ,EAAkBN,EAIrF,OAAOhF,GAAOP,gBAAgBgG,GA1XhC,GAAIjF,GAAQ,+BAA+BmF,MAAM,KAC7CjF,EAAa,sCAAsCiF,MAAM,KACzDjG,EAAS,SAASwB,GAAK,MAAOA,GA8BlCpC,GAAsB8G,WACpBC,WAAY,SAASC,EAAQC,GAC3BhH,KAAK,IAAM+G,GAAUC,EACjBhH,KAAKiH,UACPjH,KAAKiH,QAAQC,aAAaH,GAAUC,EACpChH,KAAKiH,QAAQE,QAAU1H,EAAOoC,qBAAqB7B,KAAKiH,QAAQC,cAChElH,KAAKiH,QAAQnC,eAAiBrF,EAAOgF,wBAAwBzE,KAAKiH,QAAQE,SACtEnH,KAAKiH,QAAQG,YACfpH,KAAKiH,QAAQG,WAAWC,gCAI9B1C,mBACE,MAAO3E,MAAKO,eAEd2E,UAAU8B,GACRhH,KAAK8G,WAAW,QAASE,IAE3B9B,YACE,MAAOlF,MAAKC,QAEdkF,aAAa6B,GACXhH,KAAK8G,WAAW,WAAYE,IAE9B7B,eACE,MAAOnF,MAAKE,WAEdgB,SAAS8F,GACPhH,KAAK8G,WAAW,OAAQE,IAE1B9F,WACE,MAAOlB,MAAKG,OAEd2F,mBAAmBkB,GACjB,IAAK5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACjC,KAAM,IAAI6C,WAAU,2DAA6DxC,OAAO6E,eAE1F9F,MAAK8G,WAAW,iBAAkBE,IAEpClB,qBACE,MAAO9F,MAAKI,iBAEde,aAAa6F,GACX,GAAa,QAATA,IAAoB5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACpD,KAAM,IAAI6C,WAAU,oDAAsDuD,EAE5EhH,MAAK8G,WAAW,WAAYE,IAE9B7F,eACE,MAAOnB,MAAKM,WAEdqG,cAAcK,GACZhH,KAAK8G,WAAW,YAAaE,IAE/BL,gBACE,MAAO3G,MAAKQ,YAEd0C,WAAW8D,GACThH,KAAKU,gBAAkBgD,EAAoBT,EAAgB+D,IAC3DhH,KAAK8G,WAAW,SAAUE,IAE5B9D,aACE,MAAOlD,MAAKS,SAEdmE,eAAeoC,GACb,IAAK5F,MAAM4F,IAAUA,EAAQ,IAAMpG,IACjC,KAAM,IAAI6C,WAAU,8CAAgDuD,EAEtEhH,MAAK8G,WAAW,aAAcE,IAEhCpC,iBACE,MAAO5E,MAAKK,aA4FhB,IAAI+D,GAAQ,EACRE,EAAS,GACTC,EAAM,EAaNC,GACF8C,KAAQxF,EAAM,IAAM,GAAK,IAAM,GAC/ByF,UAAWzF,EAAM,IAAM,EAAG,EAAG,GAC7B0F,WAAY1F,EAAM,EAAG,EAAG,IAAM,GAC9B2F,cAAe3F,EAAM,IAAM,EAAG,IAAM,GACpC4F,aAAc7E,EAAK,EAAGuB,GACtBuD,cAAe9E,EAAK,EAAGyB,GACvBsD,WAAY/E,EAAK,EAAG0B,IAGlBpB,EAAmB,KACnB0E,EAAe,qCACfjE,EAAgB,GAAIkE,QAAO,kBAAoBD,EAAe,IAAMA,EAAe,IAAMA,EAAe,IAAMA,EAAe,OAC7H1D,EAAS,gDAgDTa,EAAY,EACZK,EAAc,EACdC,EAAa,EACbC,EAAc,CA2GlB9F,GAAOE,iBAAmBA,EAC1BF,EAAOqB,WAAaA,EACpBrB,EAAOmC,sBAAwBA,EAC/BnC,EAAOoC,qBAAuBA,EAC9BpC,EAAOgF,wBAA0BA,EACjChF,EAAOgH,2BAA6BA,EACpChH,EAAOoF,eAAiBA,EACxBpF,EAAOwD,gBAAkBA,EACzBxD,EAAOiE,oBAAsBA,GAc5BnE,GCrZH,SAAUE,EAAQC,GAmIhB,QAASqI,GAAUvG,EAAUwF,GAC3B,MAAIxF,KAAYwG,GACPA,EAAQxG,GAAUwF,IAAUA,EAE9BA,EAGT,QAASiB,GAAgBzG,GAEvB,MAAoB,YAAbA,GAAmE,IAAzCA,EAAS0G,YAAY,YAAa,IAAsD,IAA1C1G,EAAS0G,YAAY,aAAc,GAIpH,QAASC,GAA4B3G,EAAUwF,EAAOoB,GACpD,IAAIH,EAAgBzG,GAApB,CAGA,GAAI6G,GAAiBC,EAAoB9G,EACzC,IAAI6G,EAAgB,CAClBE,EAAsBjF,MAAM9B,GAAYwF,CACxC,KAAK,GAAIwB,KAAKH,GAAgB,CAC5B,GAAII,GAAeJ,EAAeG,GAC9BE,EAAgBH,EAAsBjF,MAAMmF,EAChDL,GAAOK,GAAgBV,EAAUU,EAAcC,QAGjDN,GAAO5G,GAAYuG,EAAUvG,EAAUwF,IAI3C,QAAS2B,GAAmBC,GAC1B,GAAIC,KAEJ,KAAK,GAAIrH,KAAYoH,GACnB,KAAIpH,KAAa,SAAU,SAAU,cAArC,CAIA,GAAIsH,GAASF,EAAYpH,EACpBuH,OAAMC,QAAQF,KACjBA,GAAUA,GAKZ,KAAK,GAFDG,GACAC,EAAeJ,EAAOK,OACjBX,EAAI,EAAGA,EAAIU,EAAcV,IAChCS,KAGEA,EAASG,OADP,UAAYR,GACIA,EAAYQ,OACL,GAAhBF,EACS,EAEAV,GAAKU,EAAe,GAGpC,UAAYN,KACdK,EAAS/F,OAAS0F,EAAY1F,QAG5B,aAAe0F,KACjBK,EAASI,UAAYT,EAAYS,WAGnCJ,EAASzH,GAAYsH,EAAON,GAE5BK,EAAsBS,KAAKL,GAK/B,MADAJ,GAAsBU,KAAK,SAASxH,EAAGC,GAAK,MAAOD,GAAEqH,OAASpH,EAAEoH,SACzDP,EAGT,QAASW,GAAmBZ,GAqE1B,QAASa,KACP,GAAIN,GAASO,EAAUP,MACa,OAAhCO,EAAUP,EAAS,GAAGC,SACxBM,EAAUP,EAAS,GAAGC,OAAS,GAC7BD,EAAS,GAA4B,MAAvBO,EAAU,GAAGN,SAC7BM,EAAU,GAAGN,OAAS,EAIxB,KAAK,GAFDO,GAAgB,EAChBC,EAAiBF,EAAU,GAAGN,OACzBZ,EAAI,EAAGA,EAAIW,EAAQX,IAAK,CAC/B,GAAIY,GAASM,EAAUlB,GAAGY,MAC1B,IAAc,MAAVA,EAAgB,CAClB,IAAK,GAAIS,GAAI,EAAGA,EAAIrB,EAAImB,EAAeE,IACrCH,EAAUC,EAAgBE,GAAGT,OAASQ,GAAkBR,EAASQ,GAAkBC,GAAKrB,EAAImB,EAC9FA,GAAgBnB,EAChBoB,EAAiBR,IAnFvB,GAAmB,MAAfR,EACF,QAGEkB,QAAOC,QAAUA,OAAOC,UAAYjB,MAAMlC,UAAUoD,MAAQrB,EAAYmB,OAAOC,YAEjFpB,EAAcG,MAAMkB,KAAKrB,IAGtBG,MAAMC,QAAQJ,KACjBA,EAAcD,EAAmBC,GA0CnC,KAAK,GAvCDc,GAAYd,EAAY5E,IAAI,SAASkG,GACvC,GAAIjB,KACJ,KAAK,GAAIlC,KAAUmD,GAAkB,CACnC,GAAIC,GAAcD,EAAiBnD,EACnC,IAAc,UAAVA,GACF,GAAmB,MAAfoD,EAAqB,CAEvB,GADAA,EAAclG,OAAOkG,IAChBC,SAASD,GACZ,KAAM,IAAI1G,WAAU,oCACtB,IAAI0G,EAAc,GAAKA,EAAc,EACnC,KAAM,IAAI1G,WAAU,kDAEnB,IAAc,aAAVsD,EAAuB,CAChC,GAAmB,OAAfoD,GAAuC,cAAfA,EAC1B,MACEE,KAAMC,aAAaC,kBACnBC,KAAM,oBACNC,QAAS,mCAEN,IAAmB,WAAfN,EACT,KAAM,IAAI1G,WAAU,0BAA4B0G,EAAc,SAGhEA,GADmB,UAAVpD,EACKtH,EAAOwD,gBAAgBkH,GAEvB,GAAKA,CAErBhC,GAA4BpB,EAAQoD,EAAalB,GAMnD,WAAA,IAJIA,EAASG,SACXH,EAASG,OAAS,UAAA,IAChBH,EAAS/F,SACX+F,EAAS/F,OAAS,UACb+F,IAGLyB,GAAAA,EAEAd,GAAAA,EAAAA,EACKpB,EAAI,EAAGA,EAAIkB,EAAUP,OAAQX,IAAK,CACzC,GAAIY,GAASM,EAAUlB,GAAGY,MAC1B,IAAc,MAAVA,EAAgB,CAClB,GAAIA,EAASQ,EACX,KAAM,IAAInG,WAAU,uEAEtBmG,GAAiBR,MAEjBsB,IAAAA,EA8BJ,MA1BAhB,GAAYA,EAAUiB,OAAO,SAAS1B,GACpC,MAAOA,GAASG,QAAU,GAAKH,EAASG,QAAU,IAsB/CsB,GACHjB,IAEKC,EAvST,GAAIpB,IACFsC,YACE,kBACA,qBACA,iBACA,mBACA,uBACA,mBACA,iBACA,mBAEFC,QACE,iBACA,iBACA,iBACA,mBACA,mBACA,mBACA,oBACA,oBACA,oBACA,kBACA,kBACA,mBAEFC,cACE,oBACA,oBACA,qBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,YACE,kBACA,kBACA,mBAEFC,cACE,sBACA,uBACA,0BACA,0BAEFC,aACE,mBACA,mBACA,oBAEFC,WACE,iBACA,iBACA,kBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,MACE,WACA,aACA,aAEFC,MACE,aACA,WACA,YACA,cACA,aACA,cAEFC,QACE,YACA,cACA,eACA,cAEFC,SACE,eACA,eACA,gBAEFC,SACE,aACA,eACA,gBACA,gBAIAlD,EAAwBnF,SAASsI,gBAAgB,+BAAgC,OAEjFC,GACFC,KAAM,MACNC,OAAQ,MACRC,MAAO,OAGL9D,GACF+D,kBAAmBJ,EACnBK,gBAAiBL,EACjBM,iBAAkBN,EAClBO,eAAgBP,EAChBQ,UACEC,WAAY,MACZC,UAAW,MACXC,MAAS,MACTT,OAAU,OACVU,MAAS,OACTC,UAAW,OACXC,WAAY,QAEdC,YACEC,OAAQ,MACRC,KAAM,OAERC,aAAclB,EACdmB,YACEC,KAAM,2BAERC,WACED,KAAM,+BA4KVtN,GAAOkJ,mBAAqBA,EAC5BlJ,EAAO+J,mBAAqBA,GAM3BjK,GClTH,SAAUE,GAER,GAAIwN,KAEJxN,GAAOoB,aAAe,SAASqM,EAASC,EAAMC,EAAQC,GAKpD,GAAIC,GAAUD,EAAS,MAAQ,KAC3BE,EAAQ,GAAIC,MACZC,EAAS,GAAID,MAAKL,EAGtB,OAFAM,GAAOC,SAASD,EAAOE,WAAa,KAEhCJ,EAAQE,IACJP,IAAWD,IACfW,QAAQC,KAAK,mBAAqBX,EAAU,IAAMI,EAAU,wCAA0CG,EAAOK,eAAiB,KAAOV,GAEvIH,EAASC,IAAAA,EAAW,KAOxBzN,EAAOsO,WAAa,SAASb,EAASC,EAAMC,EAAQC,GAClD,GAAIC,GAAUD,EAAS,MAAQ,IAC/B,IAAI5N,EAAOoB,aAAaqM,EAASC,EAAMC,EAAQC,GAC7C,KAAM,IAAIW,OAAMd,EAAU,IAAMI,EAAU,yBAA2BF,KAIxE7N,62mCChCH,WAEE,OAAA,KAAI6D,SAASC,cAAc,OAAO4K,YAAYC,SAA9C,CAKE,GAAIC,EACC,IAAIrE,OAAOsE,aAAeA,YAAYD,IAC3C,GAAIA,GAAM,WAAa,MAAOC,aAAYD,WAE1C,IAAIA,GAAM,WAAa,MAAOX,MAAKW,MAGrC,IAAIE,GAAuB,SAASC,EAAQC,EAAaC,GACvDxO,KAAKsO,OAASA,EACdtO,KAAKuO,YAAcA,EACnBvO,KAAKwO,aAAeA,EAEpBxO,KAAKqK,KAAO,SACZrK,KAAKyO,SAAAA,EACLzO,KAAK0O,YAAAA,EACL1O,KAAK2O,cAAgBL,EACrBtO,KAAK4O,kBAAAA,EACL5O,KAAK6O,WAAaC,MAAMC,UACxB/O,KAAKgP,UAAYxB,KAAKW,OAGpBc,EAAyBnF,OAAOoF,QAAQrI,UAAUoH,OACtDnE,QAAOoF,QAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GACvD,GAAIC,GAAYH,EAAuBI,KAAKrP,KAAM4I,EAAauG,EAE/DC,GAAUE,mBACVF,EAAUlB,SAAW,IAErB,IAAIqB,GAAiBH,EAAUI,MAC/BJ,GAAUI,OAAS,WACjBD,EAAeF,KAAKrP,KACpB,IAAIyP,GAAQ,GAAIpB,GAAqBrO,KAAM,KAAMmO,KAC7CuB,EAAW1P,KAAKsP,gBAAgBK,OAAO3P,KAAKkO,UAAYlO,KAAKkO,aACjE0B,YAAW,WACTF,EAASnO,QAAQ,SAASsO,GACxBA,EAAQR,KAAKI,EAAMnB,OAAQmB,MAE5B,GAGL,IAAIK,GAA2BV,EAAUW,gBACzCX,GAAUW,iBAAmB,SAAS1F,EAAMwF,GACpB,kBAAXA,IAAiC,UAARxF,EAClCrK,KAAKsP,gBAAgBhG,KAAKuG,GAE1BC,EAAyBT,KAAKrP,KAAMqK,EAAMwF,GAG9C,IAAIG,GAA8BZ,EAAUa,mBAW5C,OAVAb,GAAUa,oBAAsB,SAAS5F,EAAMwF,GAC7C,GAAY,UAARxF,EAAkB,CACpB,GAAI6F,GAAQlQ,KAAKsP,gBAAgB5N,QAAQmO,EACrCK,IAAS,GACXlQ,KAAKsP,gBAAgBa,OAAOD,EAAO,OAErCF,GAA4BX,KAAKrP,KAAMqK,EAAMwF,IAI1CT,OClEX,SAAU3P,GAgBR,GAAI2Q,GAAUhN,SAASiN,gBACnBjB,EAAY,KACZkB,GAAAA,CACJ,KACE,GAAIC,GAAkBC,iBAAiBJ,GAASK,iBAAiB,WAC7DC,EAAiC,KAAnBH,EAAyB,IAAM,GACjDnB,GAAYgB,EAAQnC,SAAS0C,SAAYD,EAAaA,KACjDvP,SAAU,IACfiO,EAAUb,YAAc,EACxB+B,EAAWE,iBAAiBJ,GAASK,iBAAiB,YAAcC,EACpE,MAAOE,IACP,QACIxB,GACFA,EAAUI,SAEd,IAAIc,EAAJ,CAIA,GAAIrB,GAAyBnF,OAAOoF,QAAQrI,UAAUoH,OACtDnE,QAAOoF,QAAQrI,UAAUoH,QAAU,SAASrF,EAAauG,GAUvD,MATIrF,QAAOC,QAAUA,OAAOC,UAAYjB,MAAMlC,UAAUoD,MAAQrB,EAAYmB,OAAOC,YAEjFpB,EAAcG,MAAMkB,KAAKrB,IAGtBG,MAAMC,QAAQJ,IAAgC,OAAhBA,IACjCA,EAAcnJ,EAAOkJ,mBAAmBC,IAGnCqG,EAAuBI,KAAKrP,KAAM4I,EAAauG,MAEvD5P,GL9CCsR,EAAAA,KAEJC,MACMC,WAAAA,MAAuB/Q","file":"web-animations.min.js"} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/.bower.json b/catapult/third_party/polymer/components/webcomponentsjs/.bower.json
new file mode 100644
index 00000000..a5b24cc5
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/.bower.json
@@ -0,0 +1,30 @@
+{
+ "name": "webcomponentsjs",
+ "main": "webcomponents.js",
+ "version": "0.7.24",
+ "homepage": "http://webcomponents.org",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/webcomponents/webcomponentsjs.git"
+ },
+ "keywords": [
+ "webcomponents"
+ ],
+ "license": "BSD",
+ "ignore": [],
+ "devDependencies": {
+ "web-component-tester": "^4.0.1"
+ },
+ "_release": "0.7.24",
+ "_resolution": {
+ "type": "version",
+ "tag": "v0.7.24",
+ "commit": "8a2e40557b177e2cca0def2553f84c8269c8f93e"
+ },
+ "_source": "https://github.com/webcomponents/webcomponentsjs.git",
+ "_target": "^0.7.24",
+ "_originalSource": "webcomponents/webcomponentsjs"
+} \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/CustomElements.js b/catapult/third_party/polymer/components/webcomponentsjs/CustomElements.js
new file mode 100644
index 00000000..dcd9dde6
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/CustomElements.js
@@ -0,0 +1,1029 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+if (typeof WeakMap === "undefined") {
+ (function() {
+ var defineProperty = Object.defineProperty;
+ var counter = Date.now() % 1e9;
+ var WeakMap = function() {
+ this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
+ };
+ WeakMap.prototype = {
+ set: function(key, value) {
+ var entry = key[this.name];
+ if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
+ value: [ key, value ],
+ writable: true
+ });
+ return this;
+ },
+ get: function(key) {
+ var entry;
+ return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
+ },
+ "delete": function(key) {
+ var entry = key[this.name];
+ if (!entry || entry[0] !== key) return false;
+ entry[0] = entry[1] = undefined;
+ return true;
+ },
+ has: function(key) {
+ var entry = key[this.name];
+ if (!entry) return false;
+ return entry[0] === key;
+ }
+ };
+ window.WeakMap = WeakMap;
+ })();
+}
+
+(function(global) {
+ if (global.JsMutationObserver) {
+ return;
+ }
+ var registrationsTable = new WeakMap();
+ var setImmediate;
+ if (/Trident|Edge/.test(navigator.userAgent)) {
+ setImmediate = setTimeout;
+ } else if (window.setImmediate) {
+ setImmediate = window.setImmediate;
+ } else {
+ var setImmediateQueue = [];
+ var sentinel = String(Math.random());
+ window.addEventListener("message", function(e) {
+ if (e.data === sentinel) {
+ var queue = setImmediateQueue;
+ setImmediateQueue = [];
+ queue.forEach(function(func) {
+ func();
+ });
+ }
+ });
+ setImmediate = function(func) {
+ setImmediateQueue.push(func);
+ window.postMessage(sentinel, "*");
+ };
+ }
+ var isScheduled = false;
+ var scheduledObservers = [];
+ function scheduleCallback(observer) {
+ scheduledObservers.push(observer);
+ if (!isScheduled) {
+ isScheduled = true;
+ setImmediate(dispatchCallbacks);
+ }
+ }
+ function wrapIfNeeded(node) {
+ return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
+ }
+ function dispatchCallbacks() {
+ isScheduled = false;
+ var observers = scheduledObservers;
+ scheduledObservers = [];
+ observers.sort(function(o1, o2) {
+ return o1.uid_ - o2.uid_;
+ });
+ var anyNonEmpty = false;
+ observers.forEach(function(observer) {
+ var queue = observer.takeRecords();
+ removeTransientObserversFor(observer);
+ if (queue.length) {
+ observer.callback_(queue, observer);
+ anyNonEmpty = true;
+ }
+ });
+ if (anyNonEmpty) dispatchCallbacks();
+ }
+ function removeTransientObserversFor(observer) {
+ observer.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ if (!registrations) return;
+ registrations.forEach(function(registration) {
+ if (registration.observer === observer) registration.removeTransientObservers();
+ });
+ });
+ }
+ function forEachAncestorAndObserverEnqueueRecord(target, callback) {
+ for (var node = target; node; node = node.parentNode) {
+ var registrations = registrationsTable.get(node);
+ if (registrations) {
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ if (node !== target && !options.subtree) continue;
+ var record = callback(options);
+ if (record) registration.enqueue(record);
+ }
+ }
+ }
+ }
+ var uidCounter = 0;
+ function JsMutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+ }
+ JsMutationObserver.prototype = {
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+ if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
+ throw new SyntaxError();
+ }
+ var registrations = registrationsTable.get(target);
+ if (!registrations) registrationsTable.set(target, registrations = []);
+ var registration;
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ registration.removeListeners();
+ registration.options = options;
+ break;
+ }
+ }
+ if (!registration) {
+ registration = new Registration(this, target, options);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ registration.addListeners();
+ },
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registration.removeListeners();
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = [];
+ this.removedNodes = [];
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+ function copyMutationRecord(original) {
+ var record = new MutationRecord(original.type, original.target);
+ record.addedNodes = original.addedNodes.slice();
+ record.removedNodes = original.removedNodes.slice();
+ record.previousSibling = original.previousSibling;
+ record.nextSibling = original.nextSibling;
+ record.attributeName = original.attributeName;
+ record.attributeNamespace = original.attributeNamespace;
+ record.oldValue = original.oldValue;
+ return record;
+ }
+ var currentRecord, recordWithOldValue;
+ function getRecord(type, target) {
+ return currentRecord = new MutationRecord(type, target);
+ }
+ function getRecordWithOldValue(oldValue) {
+ if (recordWithOldValue) return recordWithOldValue;
+ recordWithOldValue = copyMutationRecord(currentRecord);
+ recordWithOldValue.oldValue = oldValue;
+ return recordWithOldValue;
+ }
+ function clearRecords() {
+ currentRecord = recordWithOldValue = undefined;
+ }
+ function recordRepresentsCurrentMutation(record) {
+ return record === recordWithOldValue || record === currentRecord;
+ }
+ function selectRecord(lastRecord, newRecord) {
+ if (lastRecord === newRecord) return lastRecord;
+ if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
+ return null;
+ }
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+ Registration.prototype = {
+ enqueue: function(record) {
+ var records = this.observer.records_;
+ var length = records.length;
+ if (records.length > 0) {
+ var lastRecord = records[length - 1];
+ var recordToReplaceLast = selectRecord(lastRecord, record);
+ if (recordToReplaceLast) {
+ records[length - 1] = recordToReplaceLast;
+ return;
+ }
+ } else {
+ scheduleCallback(this.observer);
+ }
+ records[length] = record;
+ },
+ addListeners: function() {
+ this.addListeners_(this.target);
+ },
+ addListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
+ },
+ removeListeners: function() {
+ this.removeListeners_(this.target);
+ },
+ removeListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
+ },
+ addTransientObserver: function(node) {
+ if (node === this.target) return;
+ this.addListeners_(node);
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations) registrationsTable.set(node, registrations = []);
+ registrations.push(this);
+ },
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+ transientObservedNodes.forEach(function(node) {
+ this.removeListeners_(node);
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i] === this) {
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ },
+ handleEvent: function(e) {
+ e.stopImmediatePropagation();
+ switch (e.type) {
+ case "DOMAttrModified":
+ var name = e.attrName;
+ var namespace = e.relatedNode.namespaceURI;
+ var target = e.target;
+ var record = new getRecord("attributes", target);
+ record.attributeName = name;
+ record.attributeNamespace = namespace;
+ var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.attributes) return;
+ if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
+ return;
+ }
+ if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMCharacterDataModified":
+ var target = e.target;
+ var record = getRecord("characterData", target);
+ var oldValue = e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.characterData) return;
+ if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMNodeRemoved":
+ this.addTransientObserver(e.target);
+
+ case "DOMNodeInserted":
+ var changedNode = e.target;
+ var addedNodes, removedNodes;
+ if (e.type === "DOMNodeInserted") {
+ addedNodes = [ changedNode ];
+ removedNodes = [];
+ } else {
+ addedNodes = [];
+ removedNodes = [ changedNode ];
+ }
+ var previousSibling = changedNode.previousSibling;
+ var nextSibling = changedNode.nextSibling;
+ var record = getRecord("childList", e.target.parentNode);
+ record.addedNodes = addedNodes;
+ record.removedNodes = removedNodes;
+ record.previousSibling = previousSibling;
+ record.nextSibling = nextSibling;
+ forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
+ if (!options.childList) return;
+ return record;
+ });
+ }
+ clearRecords();
+ }
+ };
+ global.JsMutationObserver = JsMutationObserver;
+ if (!global.MutationObserver) {
+ global.MutationObserver = JsMutationObserver;
+ JsMutationObserver._isPolyfilled = true;
+ }
+})(self);
+
+(function(scope) {
+ "use strict";
+ if (!(window.performance && window.performance.now)) {
+ var start = Date.now();
+ window.performance = {
+ now: function() {
+ return Date.now() - start;
+ }
+ };
+ }
+ if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = function() {
+ var nativeRaf = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
+ return nativeRaf ? function(callback) {
+ return nativeRaf(function() {
+ callback(performance.now());
+ });
+ } : function(callback) {
+ return window.setTimeout(callback, 1e3 / 60);
+ };
+ }();
+ }
+ if (!window.cancelAnimationFrame) {
+ window.cancelAnimationFrame = function() {
+ return window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function(id) {
+ clearTimeout(id);
+ };
+ }();
+ }
+ var workingDefaultPrevented = function() {
+ var e = document.createEvent("Event");
+ e.initEvent("foo", true, true);
+ e.preventDefault();
+ return e.defaultPrevented;
+ }();
+ if (!workingDefaultPrevented) {
+ var origPreventDefault = Event.prototype.preventDefault;
+ Event.prototype.preventDefault = function() {
+ if (!this.cancelable) {
+ return;
+ }
+ origPreventDefault.call(this);
+ Object.defineProperty(this, "defaultPrevented", {
+ get: function() {
+ return true;
+ },
+ configurable: true
+ });
+ };
+ }
+ var isIE = /Trident/.test(navigator.userAgent);
+ if (!window.CustomEvent || isIE && typeof window.CustomEvent !== "function") {
+ window.CustomEvent = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("CustomEvent");
+ e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
+ return e;
+ };
+ window.CustomEvent.prototype = window.Event.prototype;
+ }
+ if (!window.Event || isIE && typeof window.Event !== "function") {
+ var origEvent = window.Event;
+ window.Event = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("Event");
+ e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable));
+ return e;
+ };
+ window.Event.prototype = origEvent.prototype;
+ }
+})(window.WebComponents);
+
+window.CustomElements = window.CustomElements || {
+ flags: {}
+};
+
+(function(scope) {
+ var flags = scope.flags;
+ var modules = [];
+ var addModule = function(module) {
+ modules.push(module);
+ };
+ var initializeModules = function() {
+ modules.forEach(function(module) {
+ module(scope);
+ });
+ };
+ scope.addModule = addModule;
+ scope.initializeModules = initializeModules;
+ scope.hasNative = Boolean(document.registerElement);
+ scope.isIE = /Trident/.test(navigator.userAgent);
+ scope.useNative = !flags.register && scope.hasNative && !window.ShadowDOMPolyfill && (!window.HTMLImports || window.HTMLImports.useNative);
+})(window.CustomElements);
+
+window.CustomElements.addModule(function(scope) {
+ var IMPORT_LINK_TYPE = window.HTMLImports ? window.HTMLImports.IMPORT_LINK_TYPE : "none";
+ function forSubtree(node, cb) {
+ findAllElements(node, function(e) {
+ if (cb(e)) {
+ return true;
+ }
+ forRoots(e, cb);
+ });
+ forRoots(node, cb);
+ }
+ function findAllElements(node, find, data) {
+ var e = node.firstElementChild;
+ if (!e) {
+ e = node.firstChild;
+ while (e && e.nodeType !== Node.ELEMENT_NODE) {
+ e = e.nextSibling;
+ }
+ }
+ while (e) {
+ if (find(e, data) !== true) {
+ findAllElements(e, find, data);
+ }
+ e = e.nextElementSibling;
+ }
+ return null;
+ }
+ function forRoots(node, cb) {
+ var root = node.shadowRoot;
+ while (root) {
+ forSubtree(root, cb);
+ root = root.olderShadowRoot;
+ }
+ }
+ function forDocumentTree(doc, cb) {
+ _forDocumentTree(doc, cb, []);
+ }
+ function _forDocumentTree(doc, cb, processingDocuments) {
+ doc = window.wrap(doc);
+ if (processingDocuments.indexOf(doc) >= 0) {
+ return;
+ }
+ processingDocuments.push(doc);
+ var imports = doc.querySelectorAll("link[rel=" + IMPORT_LINK_TYPE + "]");
+ for (var i = 0, l = imports.length, n; i < l && (n = imports[i]); i++) {
+ if (n.import) {
+ _forDocumentTree(n.import, cb, processingDocuments);
+ }
+ }
+ cb(doc);
+ }
+ scope.forDocumentTree = forDocumentTree;
+ scope.forSubtree = forSubtree;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var flags = scope.flags;
+ var forSubtree = scope.forSubtree;
+ var forDocumentTree = scope.forDocumentTree;
+ function addedNode(node, isAttached) {
+ return added(node, isAttached) || addedSubtree(node, isAttached);
+ }
+ function added(node, isAttached) {
+ if (scope.upgrade(node, isAttached)) {
+ return true;
+ }
+ if (isAttached) {
+ attached(node);
+ }
+ }
+ function addedSubtree(node, isAttached) {
+ forSubtree(node, function(e) {
+ if (added(e, isAttached)) {
+ return true;
+ }
+ });
+ }
+ var hasThrottledAttached = window.MutationObserver._isPolyfilled && flags["throttle-attached"];
+ scope.hasPolyfillMutations = hasThrottledAttached;
+ scope.hasThrottledAttached = hasThrottledAttached;
+ var isPendingMutations = false;
+ var pendingMutations = [];
+ function deferMutation(fn) {
+ pendingMutations.push(fn);
+ if (!isPendingMutations) {
+ isPendingMutations = true;
+ setTimeout(takeMutations);
+ }
+ }
+ function takeMutations() {
+ isPendingMutations = false;
+ var $p = pendingMutations;
+ for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+ p();
+ }
+ pendingMutations = [];
+ }
+ function attached(element) {
+ if (hasThrottledAttached) {
+ deferMutation(function() {
+ _attached(element);
+ });
+ } else {
+ _attached(element);
+ }
+ }
+ function _attached(element) {
+ if (element.__upgraded__ && !element.__attached) {
+ element.__attached = true;
+ if (element.attachedCallback) {
+ element.attachedCallback();
+ }
+ }
+ }
+ function detachedNode(node) {
+ detached(node);
+ forSubtree(node, function(e) {
+ detached(e);
+ });
+ }
+ function detached(element) {
+ if (hasThrottledAttached) {
+ deferMutation(function() {
+ _detached(element);
+ });
+ } else {
+ _detached(element);
+ }
+ }
+ function _detached(element) {
+ if (element.__upgraded__ && element.__attached) {
+ element.__attached = false;
+ if (element.detachedCallback) {
+ element.detachedCallback();
+ }
+ }
+ }
+ function inDocument(element) {
+ var p = element;
+ var doc = window.wrap(document);
+ while (p) {
+ if (p == doc) {
+ return true;
+ }
+ p = p.parentNode || p.nodeType === Node.DOCUMENT_FRAGMENT_NODE && p.host;
+ }
+ }
+ function watchShadow(node) {
+ if (node.shadowRoot && !node.shadowRoot.__watched) {
+ flags.dom && console.log("watching shadow-root for: ", node.localName);
+ var root = node.shadowRoot;
+ while (root) {
+ observe(root);
+ root = root.olderShadowRoot;
+ }
+ }
+ }
+ function handler(root, mutations) {
+ if (flags.dom) {
+ var mx = mutations[0];
+ if (mx && mx.type === "childList" && mx.addedNodes) {
+ if (mx.addedNodes) {
+ var d = mx.addedNodes[0];
+ while (d && d !== document && !d.host) {
+ d = d.parentNode;
+ }
+ var u = d && (d.URL || d._URL || d.host && d.host.localName) || "";
+ u = u.split("/?").shift().split("/").pop();
+ }
+ }
+ console.group("mutations (%d) [%s]", mutations.length, u || "");
+ }
+ var isAttached = inDocument(root);
+ mutations.forEach(function(mx) {
+ if (mx.type === "childList") {
+ forEach(mx.addedNodes, function(n) {
+ if (!n.localName) {
+ return;
+ }
+ addedNode(n, isAttached);
+ });
+ forEach(mx.removedNodes, function(n) {
+ if (!n.localName) {
+ return;
+ }
+ detachedNode(n);
+ });
+ }
+ });
+ flags.dom && console.groupEnd();
+ }
+ function takeRecords(node) {
+ node = window.wrap(node);
+ if (!node) {
+ node = window.wrap(document);
+ }
+ while (node.parentNode) {
+ node = node.parentNode;
+ }
+ var observer = node.__observer;
+ if (observer) {
+ handler(node, observer.takeRecords());
+ takeMutations();
+ }
+ }
+ var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+ function observe(inRoot) {
+ if (inRoot.__observer) {
+ return;
+ }
+ var observer = new MutationObserver(handler.bind(this, inRoot));
+ observer.observe(inRoot, {
+ childList: true,
+ subtree: true
+ });
+ inRoot.__observer = observer;
+ }
+ function upgradeDocument(doc) {
+ doc = window.wrap(doc);
+ flags.dom && console.group("upgradeDocument: ", doc.baseURI.split("/").pop());
+ var isMainDocument = doc === window.wrap(document);
+ addedNode(doc, isMainDocument);
+ observe(doc);
+ flags.dom && console.groupEnd();
+ }
+ function upgradeDocumentTree(doc) {
+ forDocumentTree(doc, upgradeDocument);
+ }
+ var originalCreateShadowRoot = Element.prototype.createShadowRoot;
+ if (originalCreateShadowRoot) {
+ Element.prototype.createShadowRoot = function() {
+ var root = originalCreateShadowRoot.call(this);
+ window.CustomElements.watchShadow(this);
+ return root;
+ };
+ }
+ scope.watchShadow = watchShadow;
+ scope.upgradeDocumentTree = upgradeDocumentTree;
+ scope.upgradeDocument = upgradeDocument;
+ scope.upgradeSubtree = addedSubtree;
+ scope.upgradeAll = addedNode;
+ scope.attached = attached;
+ scope.takeRecords = takeRecords;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var flags = scope.flags;
+ function upgrade(node, isAttached) {
+ if (node.localName === "template") {
+ if (window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
+ HTMLTemplateElement.decorate(node);
+ }
+ }
+ if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) {
+ var is = node.getAttribute("is");
+ var definition = scope.getRegisteredDefinition(node.localName) || scope.getRegisteredDefinition(is);
+ if (definition) {
+ if (is && definition.tag == node.localName || !is && !definition.extends) {
+ return upgradeWithDefinition(node, definition, isAttached);
+ }
+ }
+ }
+ }
+ function upgradeWithDefinition(element, definition, isAttached) {
+ flags.upgrade && console.group("upgrade:", element.localName);
+ if (definition.is) {
+ element.setAttribute("is", definition.is);
+ }
+ implementPrototype(element, definition);
+ element.__upgraded__ = true;
+ created(element);
+ if (isAttached) {
+ scope.attached(element);
+ }
+ scope.upgradeSubtree(element, isAttached);
+ flags.upgrade && console.groupEnd();
+ return element;
+ }
+ function implementPrototype(element, definition) {
+ if (Object.__proto__) {
+ element.__proto__ = definition.prototype;
+ } else {
+ customMixin(element, definition.prototype, definition.native);
+ element.__proto__ = definition.prototype;
+ }
+ }
+ function customMixin(inTarget, inSrc, inNative) {
+ var used = {};
+ var p = inSrc;
+ while (p !== inNative && p !== HTMLElement.prototype) {
+ var keys = Object.getOwnPropertyNames(p);
+ for (var i = 0, k; k = keys[i]; i++) {
+ if (!used[k]) {
+ Object.defineProperty(inTarget, k, Object.getOwnPropertyDescriptor(p, k));
+ used[k] = 1;
+ }
+ }
+ p = Object.getPrototypeOf(p);
+ }
+ }
+ function created(element) {
+ if (element.createdCallback) {
+ element.createdCallback();
+ }
+ }
+ scope.upgrade = upgrade;
+ scope.upgradeWithDefinition = upgradeWithDefinition;
+ scope.implementPrototype = implementPrototype;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var isIE = scope.isIE;
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
+ var upgradeAll = scope.upgradeAll;
+ var upgradeWithDefinition = scope.upgradeWithDefinition;
+ var implementPrototype = scope.implementPrototype;
+ var useNative = scope.useNative;
+ function register(name, options) {
+ var definition = options || {};
+ if (!name) {
+ throw new Error("document.registerElement: first argument `name` must not be empty");
+ }
+ if (name.indexOf("-") < 0) {
+ throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '" + String(name) + "'.");
+ }
+ if (isReservedTag(name)) {
+ throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '" + String(name) + "'. The type name is invalid.");
+ }
+ if (getRegisteredDefinition(name)) {
+ throw new Error("DuplicateDefinitionError: a type with name '" + String(name) + "' is already registered");
+ }
+ if (!definition.prototype) {
+ definition.prototype = Object.create(HTMLElement.prototype);
+ }
+ definition.__name = name.toLowerCase();
+ if (definition.extends) {
+ definition.extends = definition.extends.toLowerCase();
+ }
+ definition.lifecycle = definition.lifecycle || {};
+ definition.ancestry = ancestry(definition.extends);
+ resolveTagName(definition);
+ resolvePrototypeChain(definition);
+ overrideAttributeApi(definition.prototype);
+ registerDefinition(definition.__name, definition);
+ definition.ctor = generateConstructor(definition);
+ definition.ctor.prototype = definition.prototype;
+ definition.prototype.constructor = definition.ctor;
+ if (scope.ready) {
+ upgradeDocumentTree(document);
+ }
+ return definition.ctor;
+ }
+ function overrideAttributeApi(prototype) {
+ if (prototype.setAttribute._polyfilled) {
+ return;
+ }
+ var setAttribute = prototype.setAttribute;
+ prototype.setAttribute = function(name, value) {
+ changeAttribute.call(this, name, value, setAttribute);
+ };
+ var removeAttribute = prototype.removeAttribute;
+ prototype.removeAttribute = function(name) {
+ changeAttribute.call(this, name, null, removeAttribute);
+ };
+ prototype.setAttribute._polyfilled = true;
+ }
+ function changeAttribute(name, value, operation) {
+ name = name.toLowerCase();
+ var oldValue = this.getAttribute(name);
+ operation.apply(this, arguments);
+ var newValue = this.getAttribute(name);
+ if (this.attributeChangedCallback && newValue !== oldValue) {
+ this.attributeChangedCallback(name, oldValue, newValue);
+ }
+ }
+ function isReservedTag(name) {
+ for (var i = 0; i < reservedTagList.length; i++) {
+ if (name === reservedTagList[i]) {
+ return true;
+ }
+ }
+ }
+ var reservedTagList = [ "annotation-xml", "color-profile", "font-face", "font-face-src", "font-face-uri", "font-face-format", "font-face-name", "missing-glyph" ];
+ function ancestry(extnds) {
+ var extendee = getRegisteredDefinition(extnds);
+ if (extendee) {
+ return ancestry(extendee.extends).concat([ extendee ]);
+ }
+ return [];
+ }
+ function resolveTagName(definition) {
+ var baseTag = definition.extends;
+ for (var i = 0, a; a = definition.ancestry[i]; i++) {
+ baseTag = a.is && a.tag;
+ }
+ definition.tag = baseTag || definition.__name;
+ if (baseTag) {
+ definition.is = definition.__name;
+ }
+ }
+ function resolvePrototypeChain(definition) {
+ if (!Object.__proto__) {
+ var nativePrototype = HTMLElement.prototype;
+ if (definition.is) {
+ var inst = document.createElement(definition.tag);
+ nativePrototype = Object.getPrototypeOf(inst);
+ }
+ var proto = definition.prototype, ancestor;
+ var foundPrototype = false;
+ while (proto) {
+ if (proto == nativePrototype) {
+ foundPrototype = true;
+ }
+ ancestor = Object.getPrototypeOf(proto);
+ if (ancestor) {
+ proto.__proto__ = ancestor;
+ }
+ proto = ancestor;
+ }
+ if (!foundPrototype) {
+ console.warn(definition.tag + " prototype not found in prototype chain for " + definition.is);
+ }
+ definition.native = nativePrototype;
+ }
+ }
+ function instantiate(definition) {
+ return upgradeWithDefinition(domCreateElement(definition.tag), definition);
+ }
+ var registry = {};
+ function getRegisteredDefinition(name) {
+ if (name) {
+ return registry[name.toLowerCase()];
+ }
+ }
+ function registerDefinition(name, definition) {
+ registry[name] = definition;
+ }
+ function generateConstructor(definition) {
+ return function() {
+ return instantiate(definition);
+ };
+ }
+ var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+ function createElementNS(namespace, tag, typeExtension) {
+ if (namespace === HTML_NAMESPACE) {
+ return createElement(tag, typeExtension);
+ } else {
+ return domCreateElementNS(namespace, tag);
+ }
+ }
+ function createElement(tag, typeExtension) {
+ if (tag) {
+ tag = tag.toLowerCase();
+ }
+ if (typeExtension) {
+ typeExtension = typeExtension.toLowerCase();
+ }
+ var definition = getRegisteredDefinition(typeExtension || tag);
+ if (definition) {
+ if (tag == definition.tag && typeExtension == definition.is) {
+ return new definition.ctor();
+ }
+ if (!typeExtension && !definition.is) {
+ return new definition.ctor();
+ }
+ }
+ var element;
+ if (typeExtension) {
+ element = createElement(tag);
+ element.setAttribute("is", typeExtension);
+ return element;
+ }
+ element = domCreateElement(tag);
+ if (tag.indexOf("-") >= 0) {
+ implementPrototype(element, HTMLElement);
+ }
+ return element;
+ }
+ var domCreateElement = document.createElement.bind(document);
+ var domCreateElementNS = document.createElementNS.bind(document);
+ var isInstance;
+ if (!Object.__proto__ && !useNative) {
+ isInstance = function(obj, ctor) {
+ if (obj instanceof ctor) {
+ return true;
+ }
+ var p = obj;
+ while (p) {
+ if (p === ctor.prototype) {
+ return true;
+ }
+ p = p.__proto__;
+ }
+ return false;
+ };
+ } else {
+ isInstance = function(obj, base) {
+ return obj instanceof base;
+ };
+ }
+ function wrapDomMethodToForceUpgrade(obj, methodName) {
+ var orig = obj[methodName];
+ obj[methodName] = function() {
+ var n = orig.apply(this, arguments);
+ upgradeAll(n);
+ return n;
+ };
+ }
+ wrapDomMethodToForceUpgrade(Node.prototype, "cloneNode");
+ wrapDomMethodToForceUpgrade(document, "importNode");
+ document.registerElement = register;
+ document.createElement = createElement;
+ document.createElementNS = createElementNS;
+ scope.registry = registry;
+ scope.instanceof = isInstance;
+ scope.reservedTagList = reservedTagList;
+ scope.getRegisteredDefinition = getRegisteredDefinition;
+ document.register = document.registerElement;
+});
+
+(function(scope) {
+ var useNative = scope.useNative;
+ var initializeModules = scope.initializeModules;
+ var isIE = scope.isIE;
+ if (useNative) {
+ var nop = function() {};
+ scope.watchShadow = nop;
+ scope.upgrade = nop;
+ scope.upgradeAll = nop;
+ scope.upgradeDocumentTree = nop;
+ scope.upgradeSubtree = nop;
+ scope.takeRecords = nop;
+ scope.instanceof = function(obj, base) {
+ return obj instanceof base;
+ };
+ } else {
+ initializeModules();
+ }
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
+ var upgradeDocument = scope.upgradeDocument;
+ if (!window.wrap) {
+ if (window.ShadowDOMPolyfill) {
+ window.wrap = window.ShadowDOMPolyfill.wrapIfNeeded;
+ window.unwrap = window.ShadowDOMPolyfill.unwrapIfNeeded;
+ } else {
+ window.wrap = window.unwrap = function(node) {
+ return node;
+ };
+ }
+ }
+ if (window.HTMLImports) {
+ window.HTMLImports.__importsParsingHook = function(elt) {
+ if (elt.import) {
+ upgradeDocument(wrap(elt.import));
+ }
+ };
+ }
+ function bootstrap() {
+ upgradeDocumentTree(window.wrap(document));
+ window.CustomElements.ready = true;
+ var requestAnimationFrame = window.requestAnimationFrame || function(f) {
+ setTimeout(f, 16);
+ };
+ requestAnimationFrame(function() {
+ setTimeout(function() {
+ window.CustomElements.readyTime = Date.now();
+ if (window.HTMLImports) {
+ window.CustomElements.elapsed = window.CustomElements.readyTime - window.HTMLImports.readyTime;
+ }
+ document.dispatchEvent(new CustomEvent("WebComponentsReady", {
+ bubbles: true
+ }));
+ });
+ });
+ }
+ if (document.readyState === "complete" || scope.flags.eager) {
+ bootstrap();
+ } else if (document.readyState === "interactive" && !window.attachEvent && (!window.HTMLImports || window.HTMLImports.ready)) {
+ bootstrap();
+ } else {
+ var loadEvent = window.HTMLImports && !window.HTMLImports.ready ? "HTMLImportsLoaded" : "DOMContentLoaded";
+ window.addEventListener(loadEvent, bootstrap);
+ }
+})(window.CustomElements); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/CustomElements.min.js b/catapult/third_party/polymer/components/webcomponentsjs/CustomElements.min.js
new file mode 100644
index 00000000..839171cf
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/CustomElements.min.js
@@ -0,0 +1,11 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+"undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var o=t[this.name];return o&&o[0]===t?o[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),function(e){function t(e){E.push(e),b||(b=!0,m(o))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function o(){b=!1;var e=E;E=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();r(e),n.length&&(e.callback_(n,e),t=!0)}),t&&o()}function r(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var o=v.get(n);if(o)for(var r=0;r<o.length;r++){var i=o[r],a=i.options;if(n===e||a.subtree){var d=t(a);d&&i.enqueue(d)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++_}function d(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function s(e){var t=new d(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function u(e,t){return y=new d(e,t)}function c(e){return N?N:(N=s(y),N.oldValue=e,N)}function l(){y=N=void 0}function f(e){return e===N||e===y}function p(e,t){return e===t?e:N&&f(e)?N:null}function w(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var m,v=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))m=setTimeout;else if(window.setImmediate)m=window.setImmediate;else{var h=[],g=String(Math.random());window.addEventListener("message",function(e){if(e.data===g){var t=h;h=[],t.forEach(function(e){e()})}}),m=function(e){h.push(e),window.postMessage(g,"*")}}var b=!1,E=[],_=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var o=v.get(e);o||v.set(e,o=[]);for(var r,i=0;i<o.length;i++)if(o[i].observer===this){r=o[i],r.removeListeners(),r.options=t;break}r||(r=new w(this,e,t),o.push(r),this.nodes_.push(e)),r.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=v.get(e),n=0;n<t.length;n++){var o=t[n];if(o.observer===this){o.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var y,N;w.prototype={enqueue:function(e){var n=this.observer.records_,o=n.length;if(n.length>0){var r=n[o-1],i=p(r,e);if(i)return void(n[o-1]=i)}else t(this.observer);n[o]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,o=e.target,r=new u("attributes",o);r.attributeName=t,r.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(o,function(e){if(e.attributes&&(!e.attributeFilter||!e.attributeFilter.length||e.attributeFilter.indexOf(t)!==-1||e.attributeFilter.indexOf(n)!==-1))return e.attributeOldValue?c(a):r});break;case"DOMCharacterDataModified":var o=e.target,r=u("characterData",o),a=e.prevValue;i(o,function(e){if(e.characterData)return e.characterDataOldValue?c(a):r});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var d,s,f=e.target;"DOMNodeInserted"===e.type?(d=[f],s=[]):(d=[],s=[f]);var p=f.previousSibling,w=f.nextSibling,r=u("childList",e.target.parentNode);r.addedNodes=d,r.removedNodes=s,r.previousSibling=p,r.nextSibling=w,i(e.relatedNode,function(e){if(e.childList)return r})}l()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(e){"use strict";if(!window.performance||!window.performance.now){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var o=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(o.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var r=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||r&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||r&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],o=function(e){n.push(e)},r=function(){n.forEach(function(t){t(e)})};e.addModule=o,e.initializeModules=r,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return!!t(e)||void o(e,t)}),o(e,t)}function n(e,t,o){var r=e.firstElementChild;if(!r)for(r=e.firstChild;r&&r.nodeType!==Node.ELEMENT_NODE;)r=r.nextSibling;for(;r;)t(r,o)!==!0&&n(r,t,o),r=r.nextElementSibling;return null}function o(e,n){for(var o=e.shadowRoot;o;)t(o,n),o=o.olderShadowRoot}function r(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var o,r=e.querySelectorAll("link[rel="+a+"]"),d=0,s=r.length;d<s&&(o=r[d]);d++)o["import"]&&i(o["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=r,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||o(e,t)}function n(t,n){return!!e.upgrade(t,n)||void(n&&a(t))}function o(e,t){b(e,function(e){if(n(e,t))return!0})}function r(e){N.push(e),y||(y=!0,setTimeout(i))}function i(){y=!1;for(var e,t=N,n=0,o=t.length;n<o&&(e=t[n]);n++)e();N=[]}function a(e){_?r(function(){d(e)}):d(e)}function d(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function s(e){u(e),b(e,function(e){u(e)})}function u(e){_?r(function(){c(e)}):c(e)}function c(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function l(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function f(e){if(e.shadowRoot&&!e.shadowRoot.__watched){g.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function p(e,n){if(g.dom){var o=n[0];if(o&&"childList"===o.type&&o.addedNodes&&o.addedNodes){for(var r=o.addedNodes[0];r&&r!==document&&!r.host;)r=r.parentNode;var i=r&&(r.URL||r._URL||r.host&&r.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=l(e);n.forEach(function(e){"childList"===e.type&&(M(e.addedNodes,function(e){e.localName&&t(e,a)}),M(e.removedNodes,function(e){e.localName&&s(e)}))}),g.dom&&console.groupEnd()}function w(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(p(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(p.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),g.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document);t(e,n),m(e),g.dom&&console.groupEnd()}function h(e){E(e,v)}var g=e.flags,b=e.forSubtree,E=e.forDocumentTree,_=window.MutationObserver._isPolyfilled&&g["throttle-attached"];e.hasPolyfillMutations=_,e.hasThrottledAttached=_;var y=!1,N=[],M=Array.prototype.forEach.call.bind(Array.prototype.forEach),O=Element.prototype.createShadowRoot;O&&(Element.prototype.createShadowRoot=function(){var e=O.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=f,e.upgradeDocumentTree=h,e.upgradeDocument=v,e.upgradeSubtree=o,e.upgradeAll=t,e.attached=a,e.takeRecords=w}),window.CustomElements.addModule(function(e){function t(t,o){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var r=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(r);if(i&&(r&&i.tag==t.localName||!r&&!i["extends"]))return n(t,i,o)}}function n(t,n,r){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),o(t,n),t.__upgraded__=!0,i(t),r&&e.attached(t),e.upgradeSubtree(t,r),a.upgrade&&console.groupEnd(),t}function o(e,t){Object.__proto__?e.__proto__=t.prototype:(r(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function r(e,t,n){for(var o={},r=t;r!==n&&r!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(r),d=0;i=a[d];d++)o[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(r,i)),o[i]=1);r=Object.getPrototypeOf(r)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=o}),window.CustomElements.addModule(function(e){function t(t,o){var s=o||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(r(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(u(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return s.prototype||(s.prototype=Object.create(HTMLElement.prototype)),s.__name=t.toLowerCase(),s["extends"]&&(s["extends"]=s["extends"].toLowerCase()),s.lifecycle=s.lifecycle||{},s.ancestry=i(s["extends"]),a(s),d(s),n(s.prototype),c(s.__name,s),s.ctor=l(s),s.ctor.prototype=s.prototype,s.prototype.constructor=s.ctor,e.ready&&v(document),s.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){o.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){o.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function o(e,t,n){e=e.toLowerCase();var o=this.getAttribute(e);n.apply(this,arguments);var r=this.getAttribute(e);this.attributeChangedCallback&&r!==o&&this.attributeChangedCallback(e,o,r)}function r(e){for(var t=0;t<_.length;t++)if(e===_[t])return!0}function i(e){var t=u(e);return t?i(t["extends"]).concat([t]):[]}function a(e){for(var t,n=e["extends"],o=0;t=e.ancestry[o];o++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function d(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag);t=Object.getPrototypeOf(n)}for(var o,r=e.prototype,i=!1;r;)r==t&&(i=!0),o=Object.getPrototypeOf(r),o&&(r.__proto__=o),r=o;i||console.warn(e.tag+" prototype not found in prototype chain for "+e.is),e["native"]=t}}function s(e){return g(M(e.tag),e)}function u(e){if(e)return y[e.toLowerCase()]}function c(e,t){y[e]=t}function l(e){return function(){return s(e)}}function f(e,t,n){return e===N?p(t,n):O(e,t)}function p(e,t){e&&(e=e.toLowerCase()),t&&(t=t.toLowerCase());var n=u(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var o;return t?(o=p(e),o.setAttribute("is",t),o):(o=M(e),e.indexOf("-")>=0&&b(o,HTMLElement),o)}function w(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return h(e),e}}var m,v=(e.isIE,e.upgradeDocumentTree),h=e.upgradeAll,g=e.upgradeWithDefinition,b=e.implementPrototype,E=e.useNative,_=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],y={},N="http://www.w3.org/1999/xhtml",M=document.createElement.bind(document),O=document.createElementNS.bind(document);m=Object.__proto__||E?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},w(Node.prototype,"cloneNode"),w(document,"importNode"),document.registerElement=t,document.createElement=p,document.createElementNS=f,e.registry=y,e["instanceof"]=m,e.reservedTagList=_,e.getRegisteredDefinition=u,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,o=e.initializeModules;e.isIE;if(n){var r=function(){};e.watchShadow=r,e.upgrade=r,e.upgradeAll=r,e.upgradeDocumentTree=r,e.upgradeSubtree=r,e.takeRecords=r,e["instanceof"]=function(e,t){return e instanceof t}}else o();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var d=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(d,t)}else t()}(window.CustomElements); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.js b/catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.js
new file mode 100644
index 00000000..882f9437
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.js
@@ -0,0 +1,1163 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+if (typeof WeakMap === "undefined") {
+ (function() {
+ var defineProperty = Object.defineProperty;
+ var counter = Date.now() % 1e9;
+ var WeakMap = function() {
+ this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
+ };
+ WeakMap.prototype = {
+ set: function(key, value) {
+ var entry = key[this.name];
+ if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
+ value: [ key, value ],
+ writable: true
+ });
+ return this;
+ },
+ get: function(key) {
+ var entry;
+ return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
+ },
+ "delete": function(key) {
+ var entry = key[this.name];
+ if (!entry || entry[0] !== key) return false;
+ entry[0] = entry[1] = undefined;
+ return true;
+ },
+ has: function(key) {
+ var entry = key[this.name];
+ if (!entry) return false;
+ return entry[0] === key;
+ }
+ };
+ window.WeakMap = WeakMap;
+ })();
+}
+
+(function(global) {
+ if (global.JsMutationObserver) {
+ return;
+ }
+ var registrationsTable = new WeakMap();
+ var setImmediate;
+ if (/Trident|Edge/.test(navigator.userAgent)) {
+ setImmediate = setTimeout;
+ } else if (window.setImmediate) {
+ setImmediate = window.setImmediate;
+ } else {
+ var setImmediateQueue = [];
+ var sentinel = String(Math.random());
+ window.addEventListener("message", function(e) {
+ if (e.data === sentinel) {
+ var queue = setImmediateQueue;
+ setImmediateQueue = [];
+ queue.forEach(function(func) {
+ func();
+ });
+ }
+ });
+ setImmediate = function(func) {
+ setImmediateQueue.push(func);
+ window.postMessage(sentinel, "*");
+ };
+ }
+ var isScheduled = false;
+ var scheduledObservers = [];
+ function scheduleCallback(observer) {
+ scheduledObservers.push(observer);
+ if (!isScheduled) {
+ isScheduled = true;
+ setImmediate(dispatchCallbacks);
+ }
+ }
+ function wrapIfNeeded(node) {
+ return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
+ }
+ function dispatchCallbacks() {
+ isScheduled = false;
+ var observers = scheduledObservers;
+ scheduledObservers = [];
+ observers.sort(function(o1, o2) {
+ return o1.uid_ - o2.uid_;
+ });
+ var anyNonEmpty = false;
+ observers.forEach(function(observer) {
+ var queue = observer.takeRecords();
+ removeTransientObserversFor(observer);
+ if (queue.length) {
+ observer.callback_(queue, observer);
+ anyNonEmpty = true;
+ }
+ });
+ if (anyNonEmpty) dispatchCallbacks();
+ }
+ function removeTransientObserversFor(observer) {
+ observer.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ if (!registrations) return;
+ registrations.forEach(function(registration) {
+ if (registration.observer === observer) registration.removeTransientObservers();
+ });
+ });
+ }
+ function forEachAncestorAndObserverEnqueueRecord(target, callback) {
+ for (var node = target; node; node = node.parentNode) {
+ var registrations = registrationsTable.get(node);
+ if (registrations) {
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ if (node !== target && !options.subtree) continue;
+ var record = callback(options);
+ if (record) registration.enqueue(record);
+ }
+ }
+ }
+ }
+ var uidCounter = 0;
+ function JsMutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+ }
+ JsMutationObserver.prototype = {
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+ if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
+ throw new SyntaxError();
+ }
+ var registrations = registrationsTable.get(target);
+ if (!registrations) registrationsTable.set(target, registrations = []);
+ var registration;
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ registration.removeListeners();
+ registration.options = options;
+ break;
+ }
+ }
+ if (!registration) {
+ registration = new Registration(this, target, options);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ registration.addListeners();
+ },
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registration.removeListeners();
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = [];
+ this.removedNodes = [];
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+ function copyMutationRecord(original) {
+ var record = new MutationRecord(original.type, original.target);
+ record.addedNodes = original.addedNodes.slice();
+ record.removedNodes = original.removedNodes.slice();
+ record.previousSibling = original.previousSibling;
+ record.nextSibling = original.nextSibling;
+ record.attributeName = original.attributeName;
+ record.attributeNamespace = original.attributeNamespace;
+ record.oldValue = original.oldValue;
+ return record;
+ }
+ var currentRecord, recordWithOldValue;
+ function getRecord(type, target) {
+ return currentRecord = new MutationRecord(type, target);
+ }
+ function getRecordWithOldValue(oldValue) {
+ if (recordWithOldValue) return recordWithOldValue;
+ recordWithOldValue = copyMutationRecord(currentRecord);
+ recordWithOldValue.oldValue = oldValue;
+ return recordWithOldValue;
+ }
+ function clearRecords() {
+ currentRecord = recordWithOldValue = undefined;
+ }
+ function recordRepresentsCurrentMutation(record) {
+ return record === recordWithOldValue || record === currentRecord;
+ }
+ function selectRecord(lastRecord, newRecord) {
+ if (lastRecord === newRecord) return lastRecord;
+ if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
+ return null;
+ }
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+ Registration.prototype = {
+ enqueue: function(record) {
+ var records = this.observer.records_;
+ var length = records.length;
+ if (records.length > 0) {
+ var lastRecord = records[length - 1];
+ var recordToReplaceLast = selectRecord(lastRecord, record);
+ if (recordToReplaceLast) {
+ records[length - 1] = recordToReplaceLast;
+ return;
+ }
+ } else {
+ scheduleCallback(this.observer);
+ }
+ records[length] = record;
+ },
+ addListeners: function() {
+ this.addListeners_(this.target);
+ },
+ addListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
+ },
+ removeListeners: function() {
+ this.removeListeners_(this.target);
+ },
+ removeListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
+ },
+ addTransientObserver: function(node) {
+ if (node === this.target) return;
+ this.addListeners_(node);
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations) registrationsTable.set(node, registrations = []);
+ registrations.push(this);
+ },
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+ transientObservedNodes.forEach(function(node) {
+ this.removeListeners_(node);
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i] === this) {
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ },
+ handleEvent: function(e) {
+ e.stopImmediatePropagation();
+ switch (e.type) {
+ case "DOMAttrModified":
+ var name = e.attrName;
+ var namespace = e.relatedNode.namespaceURI;
+ var target = e.target;
+ var record = new getRecord("attributes", target);
+ record.attributeName = name;
+ record.attributeNamespace = namespace;
+ var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.attributes) return;
+ if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
+ return;
+ }
+ if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMCharacterDataModified":
+ var target = e.target;
+ var record = getRecord("characterData", target);
+ var oldValue = e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.characterData) return;
+ if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMNodeRemoved":
+ this.addTransientObserver(e.target);
+
+ case "DOMNodeInserted":
+ var changedNode = e.target;
+ var addedNodes, removedNodes;
+ if (e.type === "DOMNodeInserted") {
+ addedNodes = [ changedNode ];
+ removedNodes = [];
+ } else {
+ addedNodes = [];
+ removedNodes = [ changedNode ];
+ }
+ var previousSibling = changedNode.previousSibling;
+ var nextSibling = changedNode.nextSibling;
+ var record = getRecord("childList", e.target.parentNode);
+ record.addedNodes = addedNodes;
+ record.removedNodes = removedNodes;
+ record.previousSibling = previousSibling;
+ record.nextSibling = nextSibling;
+ forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
+ if (!options.childList) return;
+ return record;
+ });
+ }
+ clearRecords();
+ }
+ };
+ global.JsMutationObserver = JsMutationObserver;
+ if (!global.MutationObserver) {
+ global.MutationObserver = JsMutationObserver;
+ JsMutationObserver._isPolyfilled = true;
+ }
+})(self);
+
+(function(scope) {
+ "use strict";
+ if (!(window.performance && window.performance.now)) {
+ var start = Date.now();
+ window.performance = {
+ now: function() {
+ return Date.now() - start;
+ }
+ };
+ }
+ if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = function() {
+ var nativeRaf = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
+ return nativeRaf ? function(callback) {
+ return nativeRaf(function() {
+ callback(performance.now());
+ });
+ } : function(callback) {
+ return window.setTimeout(callback, 1e3 / 60);
+ };
+ }();
+ }
+ if (!window.cancelAnimationFrame) {
+ window.cancelAnimationFrame = function() {
+ return window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function(id) {
+ clearTimeout(id);
+ };
+ }();
+ }
+ var workingDefaultPrevented = function() {
+ var e = document.createEvent("Event");
+ e.initEvent("foo", true, true);
+ e.preventDefault();
+ return e.defaultPrevented;
+ }();
+ if (!workingDefaultPrevented) {
+ var origPreventDefault = Event.prototype.preventDefault;
+ Event.prototype.preventDefault = function() {
+ if (!this.cancelable) {
+ return;
+ }
+ origPreventDefault.call(this);
+ Object.defineProperty(this, "defaultPrevented", {
+ get: function() {
+ return true;
+ },
+ configurable: true
+ });
+ };
+ }
+ var isIE = /Trident/.test(navigator.userAgent);
+ if (!window.CustomEvent || isIE && typeof window.CustomEvent !== "function") {
+ window.CustomEvent = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("CustomEvent");
+ e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
+ return e;
+ };
+ window.CustomEvent.prototype = window.Event.prototype;
+ }
+ if (!window.Event || isIE && typeof window.Event !== "function") {
+ var origEvent = window.Event;
+ window.Event = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("Event");
+ e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable));
+ return e;
+ };
+ window.Event.prototype = origEvent.prototype;
+ }
+})(window.WebComponents);
+
+window.HTMLImports = window.HTMLImports || {
+ flags: {}
+};
+
+(function(scope) {
+ var IMPORT_LINK_TYPE = "import";
+ var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement("link"));
+ var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
+ var wrap = function(node) {
+ return hasShadowDOMPolyfill ? window.ShadowDOMPolyfill.wrapIfNeeded(node) : node;
+ };
+ var rootDocument = wrap(document);
+ var currentScriptDescriptor = {
+ get: function() {
+ var script = window.HTMLImports.currentScript || document.currentScript || (document.readyState !== "complete" ? document.scripts[document.scripts.length - 1] : null);
+ return wrap(script);
+ },
+ configurable: true
+ };
+ Object.defineProperty(document, "_currentScript", currentScriptDescriptor);
+ Object.defineProperty(rootDocument, "_currentScript", currentScriptDescriptor);
+ var isIE = /Trident/.test(navigator.userAgent);
+ function whenReady(callback, doc) {
+ doc = doc || rootDocument;
+ whenDocumentReady(function() {
+ watchImportsLoad(callback, doc);
+ }, doc);
+ }
+ var requiredReadyState = isIE ? "complete" : "interactive";
+ var READY_EVENT = "readystatechange";
+ function isDocumentReady(doc) {
+ return doc.readyState === "complete" || doc.readyState === requiredReadyState;
+ }
+ function whenDocumentReady(callback, doc) {
+ if (!isDocumentReady(doc)) {
+ var checkReady = function() {
+ if (doc.readyState === "complete" || doc.readyState === requiredReadyState) {
+ doc.removeEventListener(READY_EVENT, checkReady);
+ whenDocumentReady(callback, doc);
+ }
+ };
+ doc.addEventListener(READY_EVENT, checkReady);
+ } else if (callback) {
+ callback();
+ }
+ }
+ function markTargetLoaded(event) {
+ event.target.__loaded = true;
+ }
+ function watchImportsLoad(callback, doc) {
+ var imports = doc.querySelectorAll("link[rel=import]");
+ var parsedCount = 0, importCount = imports.length, newImports = [], errorImports = [];
+ function checkDone() {
+ if (parsedCount == importCount && callback) {
+ callback({
+ allImports: imports,
+ loadedImports: newImports,
+ errorImports: errorImports
+ });
+ }
+ }
+ function loadedImport(e) {
+ markTargetLoaded(e);
+ newImports.push(this);
+ parsedCount++;
+ checkDone();
+ }
+ function errorLoadingImport(e) {
+ errorImports.push(this);
+ parsedCount++;
+ checkDone();
+ }
+ if (importCount) {
+ for (var i = 0, imp; i < importCount && (imp = imports[i]); i++) {
+ if (isImportLoaded(imp)) {
+ newImports.push(this);
+ parsedCount++;
+ checkDone();
+ } else {
+ imp.addEventListener("load", loadedImport);
+ imp.addEventListener("error", errorLoadingImport);
+ }
+ }
+ } else {
+ checkDone();
+ }
+ }
+ function isImportLoaded(link) {
+ return useNative ? link.__loaded || link.import && link.import.readyState !== "loading" : link.__importParsed;
+ }
+ if (useNative) {
+ new MutationObserver(function(mxns) {
+ for (var i = 0, l = mxns.length, m; i < l && (m = mxns[i]); i++) {
+ if (m.addedNodes) {
+ handleImports(m.addedNodes);
+ }
+ }
+ }).observe(document.head, {
+ childList: true
+ });
+ function handleImports(nodes) {
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (isImport(n)) {
+ handleImport(n);
+ }
+ }
+ }
+ function isImport(element) {
+ return element.localName === "link" && element.rel === "import";
+ }
+ function handleImport(element) {
+ var loaded = element.import;
+ if (loaded) {
+ markTargetLoaded({
+ target: element
+ });
+ } else {
+ element.addEventListener("load", markTargetLoaded);
+ element.addEventListener("error", markTargetLoaded);
+ }
+ }
+ (function() {
+ if (document.readyState === "loading") {
+ var imports = document.querySelectorAll("link[rel=import]");
+ for (var i = 0, l = imports.length, imp; i < l && (imp = imports[i]); i++) {
+ handleImport(imp);
+ }
+ }
+ })();
+ }
+ whenReady(function(detail) {
+ window.HTMLImports.ready = true;
+ window.HTMLImports.readyTime = new Date().getTime();
+ var evt = rootDocument.createEvent("CustomEvent");
+ evt.initCustomEvent("HTMLImportsLoaded", true, true, detail);
+ rootDocument.dispatchEvent(evt);
+ });
+ scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
+ scope.useNative = useNative;
+ scope.rootDocument = rootDocument;
+ scope.whenReady = whenReady;
+ scope.isIE = isIE;
+})(window.HTMLImports);
+
+(function(scope) {
+ var modules = [];
+ var addModule = function(module) {
+ modules.push(module);
+ };
+ var initializeModules = function() {
+ modules.forEach(function(module) {
+ module(scope);
+ });
+ };
+ scope.addModule = addModule;
+ scope.initializeModules = initializeModules;
+})(window.HTMLImports);
+
+window.HTMLImports.addModule(function(scope) {
+ var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
+ var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
+ var path = {
+ resolveUrlsInStyle: function(style, linkUrl) {
+ var doc = style.ownerDocument;
+ var resolver = doc.createElement("a");
+ style.textContent = this.resolveUrlsInCssText(style.textContent, linkUrl, resolver);
+ return style;
+ },
+ resolveUrlsInCssText: function(cssText, linkUrl, urlObj) {
+ var r = this.replaceUrls(cssText, urlObj, linkUrl, CSS_URL_REGEXP);
+ r = this.replaceUrls(r, urlObj, linkUrl, CSS_IMPORT_REGEXP);
+ return r;
+ },
+ replaceUrls: function(text, urlObj, linkUrl, regexp) {
+ return text.replace(regexp, function(m, pre, url, post) {
+ var urlPath = url.replace(/["']/g, "");
+ if (linkUrl) {
+ urlPath = new URL(urlPath, linkUrl).href;
+ }
+ urlObj.href = urlPath;
+ urlPath = urlObj.href;
+ return pre + "'" + urlPath + "'" + post;
+ });
+ }
+ };
+ scope.path = path;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var xhr = {
+ async: true,
+ ok: function(request) {
+ return request.status >= 200 && request.status < 300 || request.status === 304 || request.status === 0;
+ },
+ load: function(url, next, nextContext) {
+ var request = new XMLHttpRequest();
+ if (scope.flags.debug || scope.flags.bust) {
+ url += "?" + Math.random();
+ }
+ request.open("GET", url, xhr.async);
+ request.addEventListener("readystatechange", function(e) {
+ if (request.readyState === 4) {
+ var redirectedUrl = null;
+ try {
+ var locationHeader = request.getResponseHeader("Location");
+ if (locationHeader) {
+ redirectedUrl = locationHeader.substr(0, 1) === "/" ? location.origin + locationHeader : locationHeader;
+ }
+ } catch (e) {
+ console.error(e.message);
+ }
+ next.call(nextContext, !xhr.ok(request) && request, request.response || request.responseText, redirectedUrl);
+ }
+ });
+ request.send();
+ return request;
+ },
+ loadDocument: function(url, next, nextContext) {
+ this.load(url, next, nextContext).responseType = "document";
+ }
+ };
+ scope.xhr = xhr;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var xhr = scope.xhr;
+ var flags = scope.flags;
+ var Loader = function(onLoad, onComplete) {
+ this.cache = {};
+ this.onload = onLoad;
+ this.oncomplete = onComplete;
+ this.inflight = 0;
+ this.pending = {};
+ };
+ Loader.prototype = {
+ addNodes: function(nodes) {
+ this.inflight += nodes.length;
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ this.require(n);
+ }
+ this.checkDone();
+ },
+ addNode: function(node) {
+ this.inflight++;
+ this.require(node);
+ this.checkDone();
+ },
+ require: function(elt) {
+ var url = elt.src || elt.href;
+ elt.__nodeUrl = url;
+ if (!this.dedupe(url, elt)) {
+ this.fetch(url, elt);
+ }
+ },
+ dedupe: function(url, elt) {
+ if (this.pending[url]) {
+ this.pending[url].push(elt);
+ return true;
+ }
+ var resource;
+ if (this.cache[url]) {
+ this.onload(url, elt, this.cache[url]);
+ this.tail();
+ return true;
+ }
+ this.pending[url] = [ elt ];
+ return false;
+ },
+ fetch: function(url, elt) {
+ flags.load && console.log("fetch", url, elt);
+ if (!url) {
+ setTimeout(function() {
+ this.receive(url, elt, {
+ error: "href must be specified"
+ }, null);
+ }.bind(this), 0);
+ } else if (url.match(/^data:/)) {
+ var pieces = url.split(",");
+ var header = pieces[0];
+ var body = pieces[1];
+ if (header.indexOf(";base64") > -1) {
+ body = atob(body);
+ } else {
+ body = decodeURIComponent(body);
+ }
+ setTimeout(function() {
+ this.receive(url, elt, null, body);
+ }.bind(this), 0);
+ } else {
+ var receiveXhr = function(err, resource, redirectedUrl) {
+ this.receive(url, elt, err, resource, redirectedUrl);
+ }.bind(this);
+ xhr.load(url, receiveXhr);
+ }
+ },
+ receive: function(url, elt, err, resource, redirectedUrl) {
+ this.cache[url] = resource;
+ var $p = this.pending[url];
+ for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+ this.onload(url, p, resource, err, redirectedUrl);
+ this.tail();
+ }
+ this.pending[url] = null;
+ },
+ tail: function() {
+ --this.inflight;
+ this.checkDone();
+ },
+ checkDone: function() {
+ if (!this.inflight) {
+ this.oncomplete();
+ }
+ }
+ };
+ scope.Loader = Loader;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var Observer = function(addCallback) {
+ this.addCallback = addCallback;
+ this.mo = new MutationObserver(this.handler.bind(this));
+ };
+ Observer.prototype = {
+ handler: function(mutations) {
+ for (var i = 0, l = mutations.length, m; i < l && (m = mutations[i]); i++) {
+ if (m.type === "childList" && m.addedNodes.length) {
+ this.addedNodes(m.addedNodes);
+ }
+ }
+ },
+ addedNodes: function(nodes) {
+ if (this.addCallback) {
+ this.addCallback(nodes);
+ }
+ for (var i = 0, l = nodes.length, n, loading; i < l && (n = nodes[i]); i++) {
+ if (n.children && n.children.length) {
+ this.addedNodes(n.children);
+ }
+ }
+ },
+ observe: function(root) {
+ this.mo.observe(root, {
+ childList: true,
+ subtree: true
+ });
+ }
+ };
+ scope.Observer = Observer;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var path = scope.path;
+ var rootDocument = scope.rootDocument;
+ var flags = scope.flags;
+ var isIE = scope.isIE;
+ var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+ var IMPORT_SELECTOR = "link[rel=" + IMPORT_LINK_TYPE + "]";
+ var importParser = {
+ documentSelectors: IMPORT_SELECTOR,
+ importsSelectors: [ IMPORT_SELECTOR, "link[rel=stylesheet]:not([type])", "style:not([type])", "script:not([type])", 'script[type="application/javascript"]', 'script[type="text/javascript"]' ].join(","),
+ map: {
+ link: "parseLink",
+ script: "parseScript",
+ style: "parseStyle"
+ },
+ dynamicElements: [],
+ parseNext: function() {
+ var next = this.nextToParse();
+ if (next) {
+ this.parse(next);
+ }
+ },
+ parse: function(elt) {
+ if (this.isParsed(elt)) {
+ flags.parse && console.log("[%s] is already parsed", elt.localName);
+ return;
+ }
+ var fn = this[this.map[elt.localName]];
+ if (fn) {
+ this.markParsing(elt);
+ fn.call(this, elt);
+ }
+ },
+ parseDynamic: function(elt, quiet) {
+ this.dynamicElements.push(elt);
+ if (!quiet) {
+ this.parseNext();
+ }
+ },
+ markParsing: function(elt) {
+ flags.parse && console.log("parsing", elt);
+ this.parsingElement = elt;
+ },
+ markParsingComplete: function(elt) {
+ elt.__importParsed = true;
+ this.markDynamicParsingComplete(elt);
+ if (elt.__importElement) {
+ elt.__importElement.__importParsed = true;
+ this.markDynamicParsingComplete(elt.__importElement);
+ }
+ this.parsingElement = null;
+ flags.parse && console.log("completed", elt);
+ },
+ markDynamicParsingComplete: function(elt) {
+ var i = this.dynamicElements.indexOf(elt);
+ if (i >= 0) {
+ this.dynamicElements.splice(i, 1);
+ }
+ },
+ parseImport: function(elt) {
+ elt.import = elt.__doc;
+ if (window.HTMLImports.__importsParsingHook) {
+ window.HTMLImports.__importsParsingHook(elt);
+ }
+ if (elt.import) {
+ elt.import.__importParsed = true;
+ }
+ this.markParsingComplete(elt);
+ if (elt.__resource && !elt.__error) {
+ elt.dispatchEvent(new CustomEvent("load", {
+ bubbles: false
+ }));
+ } else {
+ elt.dispatchEvent(new CustomEvent("error", {
+ bubbles: false
+ }));
+ }
+ if (elt.__pending) {
+ var fn;
+ while (elt.__pending.length) {
+ fn = elt.__pending.shift();
+ if (fn) {
+ fn({
+ target: elt
+ });
+ }
+ }
+ }
+ this.parseNext();
+ },
+ parseLink: function(linkElt) {
+ if (nodeIsImport(linkElt)) {
+ this.parseImport(linkElt);
+ } else {
+ linkElt.href = linkElt.href;
+ this.parseGeneric(linkElt);
+ }
+ },
+ parseStyle: function(elt) {
+ var src = elt;
+ elt = cloneStyle(elt);
+ src.__appliedElement = elt;
+ elt.__importElement = src;
+ this.parseGeneric(elt);
+ },
+ parseGeneric: function(elt) {
+ this.trackElement(elt);
+ this.addElementToDocument(elt);
+ },
+ rootImportForElement: function(elt) {
+ var n = elt;
+ while (n.ownerDocument.__importLink) {
+ n = n.ownerDocument.__importLink;
+ }
+ return n;
+ },
+ addElementToDocument: function(elt) {
+ var port = this.rootImportForElement(elt.__importElement || elt);
+ port.parentNode.insertBefore(elt, port);
+ },
+ trackElement: function(elt, callback) {
+ var self = this;
+ var done = function(e) {
+ elt.removeEventListener("load", done);
+ elt.removeEventListener("error", done);
+ if (callback) {
+ callback(e);
+ }
+ self.markParsingComplete(elt);
+ self.parseNext();
+ };
+ elt.addEventListener("load", done);
+ elt.addEventListener("error", done);
+ if (isIE && elt.localName === "style") {
+ var fakeLoad = false;
+ if (elt.textContent.indexOf("@import") == -1) {
+ fakeLoad = true;
+ } else if (elt.sheet) {
+ fakeLoad = true;
+ var csr = elt.sheet.cssRules;
+ var len = csr ? csr.length : 0;
+ for (var i = 0, r; i < len && (r = csr[i]); i++) {
+ if (r.type === CSSRule.IMPORT_RULE) {
+ fakeLoad = fakeLoad && Boolean(r.styleSheet);
+ }
+ }
+ }
+ if (fakeLoad) {
+ setTimeout(function() {
+ elt.dispatchEvent(new CustomEvent("load", {
+ bubbles: false
+ }));
+ });
+ }
+ }
+ },
+ parseScript: function(scriptElt) {
+ var script = document.createElement("script");
+ script.__importElement = scriptElt;
+ script.src = scriptElt.src ? scriptElt.src : generateScriptDataUrl(scriptElt);
+ scope.currentScript = scriptElt;
+ this.trackElement(script, function(e) {
+ if (script.parentNode) {
+ script.parentNode.removeChild(script);
+ }
+ scope.currentScript = null;
+ });
+ this.addElementToDocument(script);
+ },
+ nextToParse: function() {
+ this._mayParse = [];
+ return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || this.nextToParseDynamic());
+ },
+ nextToParseInDoc: function(doc, link) {
+ if (doc && this._mayParse.indexOf(doc) < 0) {
+ this._mayParse.push(doc);
+ for (const child of doc.body.children) {
+ if (['link', 'script', 'style'].includes(child.tagName.toLowerCase())) continue;
+ if (child.__importParsed) continue;
+ child.__importParsed = true;
+ document.body.appendChild(child);
+ }
+ var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc));
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (!this.isParsed(n)) {
+ if (this.hasResource(n)) {
+ return nodeIsImport(n) ? this.nextToParseInDoc(n.__doc, n) : n;
+ } else {
+ return;
+ }
+ }
+ }
+ }
+ return link;
+ },
+ nextToParseDynamic: function() {
+ return this.dynamicElements[0];
+ },
+ parseSelectorsForNode: function(node) {
+ var doc = node.ownerDocument || node;
+ return doc === rootDocument ? this.documentSelectors : this.importsSelectors;
+ },
+ isParsed: function(node) {
+ return node.__importParsed;
+ },
+ needsDynamicParsing: function(elt) {
+ return this.dynamicElements.indexOf(elt) >= 0;
+ },
+ hasResource: function(node) {
+ if (nodeIsImport(node) && node.__doc === undefined) {
+ return false;
+ }
+ return true;
+ }
+ };
+ function nodeIsImport(elt) {
+ return elt.localName === "link" && elt.rel === IMPORT_LINK_TYPE;
+ }
+ function generateScriptDataUrl(script) {
+ var scriptContent = generateScriptContent(script);
+ return "data:text/javascript;charset=utf-8," + encodeURIComponent(scriptContent);
+ }
+ function generateScriptContent(script) {
+ return script.textContent + generateSourceMapHint(script);
+ }
+ function generateSourceMapHint(script) {
+ var owner = script.ownerDocument;
+ owner.__importedScripts = owner.__importedScripts || 0;
+ var moniker = script.ownerDocument.baseURI;
+ var num = owner.__importedScripts ? "-" + owner.__importedScripts : "";
+ owner.__importedScripts++;
+ return "\n//# sourceURL=" + moniker + num + ".js\n";
+ }
+ function cloneStyle(style) {
+ var clone = style.ownerDocument.createElement("style");
+ clone.textContent = style.textContent;
+ path.resolveUrlsInStyle(clone);
+ return clone;
+ }
+ scope.parser = importParser;
+ scope.IMPORT_SELECTOR = IMPORT_SELECTOR;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var flags = scope.flags;
+ var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+ var IMPORT_SELECTOR = scope.IMPORT_SELECTOR;
+ var rootDocument = scope.rootDocument;
+ var Loader = scope.Loader;
+ var Observer = scope.Observer;
+ var parser = scope.parser;
+ var importer = {
+ documents: {},
+ documentPreloadSelectors: IMPORT_SELECTOR,
+ importsPreloadSelectors: [ IMPORT_SELECTOR ].join(","),
+ loadNode: function(node) {
+ importLoader.addNode(node);
+ },
+ loadSubtree: function(parent) {
+ var nodes = this.marshalNodes(parent);
+ importLoader.addNodes(nodes);
+ },
+ marshalNodes: function(parent) {
+ return parent.querySelectorAll(this.loadSelectorsForNode(parent));
+ },
+ loadSelectorsForNode: function(node) {
+ var doc = node.ownerDocument || node;
+ return doc === rootDocument ? this.documentPreloadSelectors : this.importsPreloadSelectors;
+ },
+ loaded: function(url, elt, resource, err, redirectedUrl) {
+ flags.load && console.log("loaded", url, elt);
+ elt.__resource = resource;
+ elt.__error = err;
+ if (isImportLink(elt)) {
+ var doc = this.documents[url];
+ if (doc === undefined) {
+ doc = err ? null : makeDocument(resource, redirectedUrl || url);
+ if (doc) {
+ doc.__importLink = elt;
+ this.bootDocument(doc);
+ }
+ this.documents[url] = doc;
+ }
+ elt.__doc = doc;
+ }
+ parser.parseNext();
+ },
+ bootDocument: function(doc) {
+ this.loadSubtree(doc);
+ this.observer.observe(doc);
+ parser.parseNext();
+ },
+ loadedAll: function() {
+ parser.parseNext();
+ }
+ };
+ var importLoader = new Loader(importer.loaded.bind(importer), importer.loadedAll.bind(importer));
+ importer.observer = new Observer();
+ function isImportLink(elt) {
+ return isLinkRel(elt, IMPORT_LINK_TYPE);
+ }
+ function isLinkRel(elt, rel) {
+ return elt.localName === "link" && elt.getAttribute("rel") === rel;
+ }
+ function hasBaseURIAccessor(doc) {
+ return !!Object.getOwnPropertyDescriptor(doc, "baseURI");
+ }
+ function makeDocument(resource, url) {
+ var doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
+ doc._URL = url;
+ var base = doc.createElement("base");
+ base.setAttribute("href", url);
+ if (!doc.baseURI && !hasBaseURIAccessor(doc)) {
+ Object.defineProperty(doc, "baseURI", {
+ value: url
+ });
+ }
+ var meta = doc.createElement("meta");
+ meta.setAttribute("charset", "utf-8");
+ doc.head.appendChild(meta);
+ doc.head.appendChild(base);
+ doc.body.innerHTML = resource;
+ if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
+ HTMLTemplateElement.bootstrap(doc);
+ }
+ return doc;
+ }
+ if (!document.baseURI) {
+ var baseURIDescriptor = {
+ get: function() {
+ var base = document.querySelector("base");
+ return base ? base.href : window.location.href;
+ },
+ configurable: true
+ };
+ Object.defineProperty(document, "baseURI", baseURIDescriptor);
+ Object.defineProperty(rootDocument, "baseURI", baseURIDescriptor);
+ }
+ scope.importer = importer;
+ scope.importLoader = importLoader;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var parser = scope.parser;
+ var importer = scope.importer;
+ var dynamic = {
+ added: function(nodes) {
+ var owner, parsed, loading;
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (!owner) {
+ owner = n.ownerDocument;
+ parsed = parser.isParsed(owner);
+ }
+ loading = this.shouldLoadNode(n);
+ if (loading) {
+ importer.loadNode(n);
+ }
+ if (this.shouldParseNode(n) && parsed) {
+ parser.parseDynamic(n, loading);
+ }
+ }
+ },
+ shouldLoadNode: function(node) {
+ return node.nodeType === 1 && matches.call(node, importer.loadSelectorsForNode(node));
+ },
+ shouldParseNode: function(node) {
+ return node.nodeType === 1 && matches.call(node, parser.parseSelectorsForNode(node));
+ }
+ };
+ importer.observer.addCallback = dynamic.added.bind(dynamic);
+ var matches = HTMLElement.prototype.matches || HTMLElement.prototype.matchesSelector || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector || HTMLElement.prototype.msMatchesSelector;
+});
+
+(function(scope) {
+ var initializeModules = scope.initializeModules;
+ var isIE = scope.isIE;
+ if (scope.useNative) {
+ return;
+ }
+ initializeModules();
+ var rootDocument = scope.rootDocument;
+ function bootstrap() {
+ window.HTMLImports.importer.bootDocument(rootDocument);
+ }
+ if (document.readyState === "complete" || document.readyState === "interactive" && !window.attachEvent) {
+ bootstrap();
+ } else {
+ document.addEventListener("DOMContentLoaded", bootstrap);
+ }
+})(window.HTMLImports);
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.min.js b/catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.min.js
new file mode 100644
index 00000000..e89492c5
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/HTMLImports.min.js
@@ -0,0 +1,11 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+"undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var r=t[this.name];return r&&r[0]===t?r[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),function(e){function t(e){E.push(e),g||(g=!0,f(r))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function r(){g=!1;var e=E;E=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();o(e),n.length&&(e.callback_(n,e),t=!0)}),t&&r()}function o(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var r=v.get(n);if(r)for(var o=0;o<r.length;o++){var i=r[o],a=i.options;if(n===e||a.subtree){var s=t(a);s&&i.enqueue(s)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++_}function s(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function d(e){var t=new s(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function c(e,t){return y=new s(e,t)}function u(e){return L?L:(L=d(y),L.oldValue=e,L)}function l(){y=L=void 0}function h(e){return e===L||e===y}function m(e,t){return e===t?e:L&&h(e)?L:null}function p(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var f,v=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))f=setTimeout;else if(window.setImmediate)f=window.setImmediate;else{var w=[],b=String(Math.random());window.addEventListener("message",function(e){if(e.data===b){var t=w;w=[],t.forEach(function(e){e()})}}),f=function(e){w.push(e),window.postMessage(b,"*")}}var g=!1,E=[],_=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var r=v.get(e);r||v.set(e,r=[]);for(var o,i=0;i<r.length;i++)if(r[i].observer===this){o=r[i],o.removeListeners(),o.options=t;break}o||(o=new p(this,e,t),r.push(o),this.nodes_.push(e)),o.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=v.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){r.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var y,L;p.prototype={enqueue:function(e){var n=this.observer.records_,r=n.length;if(n.length>0){var o=n[r-1],i=m(o,e);if(i)return void(n[r-1]=i)}else t(this.observer);n[r]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,r=e.target,o=new c("attributes",r);o.attributeName=t,o.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(r,function(e){if(e.attributes&&(!e.attributeFilter||!e.attributeFilter.length||e.attributeFilter.indexOf(t)!==-1||e.attributeFilter.indexOf(n)!==-1))return e.attributeOldValue?u(a):o});break;case"DOMCharacterDataModified":var r=e.target,o=c("characterData",r),a=e.prevValue;i(r,function(e){if(e.characterData)return e.characterDataOldValue?u(a):o});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var s,d,h=e.target;"DOMNodeInserted"===e.type?(s=[h],d=[]):(s=[],d=[h]);var m=h.previousSibling,p=h.nextSibling,o=c("childList",e.target.parentNode);o.addedNodes=s,o.removedNodes=d,o.previousSibling=m,o.nextSibling=p,i(e.relatedNode,function(e){if(e.childList)return o})}l()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(e){"use strict";if(!window.performance||!window.performance.now){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var r=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(r.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var o=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||o&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||o&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||p,r(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===w}function r(e,t){if(n(t))e&&e();else{var o=function(){"complete"!==t.readyState&&t.readyState!==w||(t.removeEventListener(b,o),r(e,t))};t.addEventListener(b,o)}}function o(e){e.target.__loaded=!0}function i(e,t){function n(){d==c&&e&&e({allImports:s,loadedImports:u,errorImports:l})}function r(e){o(e),u.push(this),d++,n()}function i(e){l.push(this),d++,n()}var s=t.querySelectorAll("link[rel=import]"),d=0,c=s.length,u=[],l=[];if(c)for(var h,m=0;m<c&&(h=s[m]);m++)a(h)?(u.push(this),d++,n()):(h.addEventListener("load",r),h.addEventListener("error",i));else n()}function a(e){return l?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)d(t)&&c(t)}function d(e){return"link"===e.localName&&"import"===e.rel}function c(e){var t=e["import"];t?o({target:e}):(e.addEventListener("load",o),e.addEventListener("error",o))}var u="import",l=Boolean(u in document.createElement("link")),h=Boolean(window.ShadowDOMPolyfill),m=function(e){return h?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},p=m(document),f={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return m(e)},configurable:!0};Object.defineProperty(document,"_currentScript",f),Object.defineProperty(p,"_currentScript",f);var v=/Trident/.test(navigator.userAgent),w=v?"complete":"interactive",b="readystatechange";l&&(new MutationObserver(function(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,r=t.length;n<r&&(e=t[n]);n++)c(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=p.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),p.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=u,e.useNative=l,e.rootDocument=p,e.whenReady=t,e.isIE=v}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},r=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=r}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,r={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,r=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,r),e},resolveUrlsInCssText:function(e,r,o){var i=this.replaceUrls(e,o,r,t);return i=this.replaceUrls(i,o,r,n)},replaceUrls:function(e,t,n,r){return e.replace(r,function(e,r,o,i){var a=o.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,r+"'"+a+"'"+i})}};e.path=r}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,r,o){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}r.call(o,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,r=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};r.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,r){if(n.load&&console.log("fetch",e,r),e)if(e.match(/^data:/)){var o=e.split(","),i=o[0],a=o[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,r,null,a)}.bind(this),0)}else{var s=function(t,n,o){this.receive(e,r,t,n,o)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,r,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,r,o){this.cache[e]=r;for(var i,a=this.pending[e],s=0,d=a.length;s<d&&(i=a[s]);s++)this.onload(e,i,r,n,o),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=r}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===u}function n(e){var t=r(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function r(e){return e.textContent+o(e)}function o(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,r=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+r+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,d=e.flags,c=e.isIE,u=e.IMPORT_LINK_TYPE,l="link[rel="+u+"]",h={documentSelectors:l,importsSelectors:[l,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(d.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){d.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,d.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,r=function(o){e.removeEventListener("load",r),e.removeEventListener("error",r),t&&t(o),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",r),e.addEventListener("error",r),c&&"style"===e.localName){var o=!1;if(e.textContent.indexOf("@import")==-1)o=!0;else if(e.sheet){o=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,d=0;d<s&&(i=a[d]);d++)i.type===CSSRule.IMPORT_RULE&&(o=o&&Boolean(i.styleSheet))}o&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var r=document.createElement("script");r.__importElement=t,r.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(r,function(t){r.parentNode&&r.parentNode.removeChild(r),e.currentScript=null}),this.addElementToDocument(r)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var r,o=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=o.length;i<a&&(r=o[i]);i++)if(!this.isParsed(r))return this.hasResource(r)?t(r)?this.nextToParseInDoc(r.__doc,r):r:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return!t(e)||void 0!==e.__doc}};e.parser=h,e.IMPORT_SELECTOR=l}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function r(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function o(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var o=n.createElement("base");o.setAttribute("href",t),n.baseURI||r(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(o),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,d=e.rootDocument,c=e.Loader,u=e.Observer,l=e.parser,h={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){m.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);m.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===d?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,r,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=r,n.__error=a,t(n)){var d=this.documents[e];void 0===d&&(d=a?null:o(r,s||e),d&&(d.__importLink=n,this.bootDocument(d)),this.documents[e]=d),n.__doc=d}l.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),l.parseNext()},loadedAll:function(){l.parseNext()}},m=new c(h.loaded.bind(h),h.loadedAll.bind(h));if(h.observer=new u,!document.baseURI){var p={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",p),Object.defineProperty(d,"baseURI",p)}e.importer=h,e.importLoader=m}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,r={added:function(e){for(var r,o,i,a,s=0,d=e.length;s<d&&(a=e[s]);s++)r||(r=a.ownerDocument,o=t.isParsed(r)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&o&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&o.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&o.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=r.added.bind(r);var o=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(r)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var r=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.js b/catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.js
new file mode 100644
index 00000000..204de7d7
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.js
@@ -0,0 +1,350 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+if (typeof WeakMap === "undefined") {
+ (function() {
+ var defineProperty = Object.defineProperty;
+ var counter = Date.now() % 1e9;
+ var WeakMap = function() {
+ this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
+ };
+ WeakMap.prototype = {
+ set: function(key, value) {
+ var entry = key[this.name];
+ if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
+ value: [ key, value ],
+ writable: true
+ });
+ return this;
+ },
+ get: function(key) {
+ var entry;
+ return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
+ },
+ "delete": function(key) {
+ var entry = key[this.name];
+ if (!entry || entry[0] !== key) return false;
+ entry[0] = entry[1] = undefined;
+ return true;
+ },
+ has: function(key) {
+ var entry = key[this.name];
+ if (!entry) return false;
+ return entry[0] === key;
+ }
+ };
+ window.WeakMap = WeakMap;
+ })();
+}
+
+(function(global) {
+ if (global.JsMutationObserver) {
+ return;
+ }
+ var registrationsTable = new WeakMap();
+ var setImmediate;
+ if (/Trident|Edge/.test(navigator.userAgent)) {
+ setImmediate = setTimeout;
+ } else if (window.setImmediate) {
+ setImmediate = window.setImmediate;
+ } else {
+ var setImmediateQueue = [];
+ var sentinel = String(Math.random());
+ window.addEventListener("message", function(e) {
+ if (e.data === sentinel) {
+ var queue = setImmediateQueue;
+ setImmediateQueue = [];
+ queue.forEach(function(func) {
+ func();
+ });
+ }
+ });
+ setImmediate = function(func) {
+ setImmediateQueue.push(func);
+ window.postMessage(sentinel, "*");
+ };
+ }
+ var isScheduled = false;
+ var scheduledObservers = [];
+ function scheduleCallback(observer) {
+ scheduledObservers.push(observer);
+ if (!isScheduled) {
+ isScheduled = true;
+ setImmediate(dispatchCallbacks);
+ }
+ }
+ function wrapIfNeeded(node) {
+ return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
+ }
+ function dispatchCallbacks() {
+ isScheduled = false;
+ var observers = scheduledObservers;
+ scheduledObservers = [];
+ observers.sort(function(o1, o2) {
+ return o1.uid_ - o2.uid_;
+ });
+ var anyNonEmpty = false;
+ observers.forEach(function(observer) {
+ var queue = observer.takeRecords();
+ removeTransientObserversFor(observer);
+ if (queue.length) {
+ observer.callback_(queue, observer);
+ anyNonEmpty = true;
+ }
+ });
+ if (anyNonEmpty) dispatchCallbacks();
+ }
+ function removeTransientObserversFor(observer) {
+ observer.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ if (!registrations) return;
+ registrations.forEach(function(registration) {
+ if (registration.observer === observer) registration.removeTransientObservers();
+ });
+ });
+ }
+ function forEachAncestorAndObserverEnqueueRecord(target, callback) {
+ for (var node = target; node; node = node.parentNode) {
+ var registrations = registrationsTable.get(node);
+ if (registrations) {
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ if (node !== target && !options.subtree) continue;
+ var record = callback(options);
+ if (record) registration.enqueue(record);
+ }
+ }
+ }
+ }
+ var uidCounter = 0;
+ function JsMutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+ }
+ JsMutationObserver.prototype = {
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+ if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
+ throw new SyntaxError();
+ }
+ var registrations = registrationsTable.get(target);
+ if (!registrations) registrationsTable.set(target, registrations = []);
+ var registration;
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ registration.removeListeners();
+ registration.options = options;
+ break;
+ }
+ }
+ if (!registration) {
+ registration = new Registration(this, target, options);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ registration.addListeners();
+ },
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registration.removeListeners();
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = [];
+ this.removedNodes = [];
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+ function copyMutationRecord(original) {
+ var record = new MutationRecord(original.type, original.target);
+ record.addedNodes = original.addedNodes.slice();
+ record.removedNodes = original.removedNodes.slice();
+ record.previousSibling = original.previousSibling;
+ record.nextSibling = original.nextSibling;
+ record.attributeName = original.attributeName;
+ record.attributeNamespace = original.attributeNamespace;
+ record.oldValue = original.oldValue;
+ return record;
+ }
+ var currentRecord, recordWithOldValue;
+ function getRecord(type, target) {
+ return currentRecord = new MutationRecord(type, target);
+ }
+ function getRecordWithOldValue(oldValue) {
+ if (recordWithOldValue) return recordWithOldValue;
+ recordWithOldValue = copyMutationRecord(currentRecord);
+ recordWithOldValue.oldValue = oldValue;
+ return recordWithOldValue;
+ }
+ function clearRecords() {
+ currentRecord = recordWithOldValue = undefined;
+ }
+ function recordRepresentsCurrentMutation(record) {
+ return record === recordWithOldValue || record === currentRecord;
+ }
+ function selectRecord(lastRecord, newRecord) {
+ if (lastRecord === newRecord) return lastRecord;
+ if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
+ return null;
+ }
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+ Registration.prototype = {
+ enqueue: function(record) {
+ var records = this.observer.records_;
+ var length = records.length;
+ if (records.length > 0) {
+ var lastRecord = records[length - 1];
+ var recordToReplaceLast = selectRecord(lastRecord, record);
+ if (recordToReplaceLast) {
+ records[length - 1] = recordToReplaceLast;
+ return;
+ }
+ } else {
+ scheduleCallback(this.observer);
+ }
+ records[length] = record;
+ },
+ addListeners: function() {
+ this.addListeners_(this.target);
+ },
+ addListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
+ },
+ removeListeners: function() {
+ this.removeListeners_(this.target);
+ },
+ removeListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
+ },
+ addTransientObserver: function(node) {
+ if (node === this.target) return;
+ this.addListeners_(node);
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations) registrationsTable.set(node, registrations = []);
+ registrations.push(this);
+ },
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+ transientObservedNodes.forEach(function(node) {
+ this.removeListeners_(node);
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i] === this) {
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ },
+ handleEvent: function(e) {
+ e.stopImmediatePropagation();
+ switch (e.type) {
+ case "DOMAttrModified":
+ var name = e.attrName;
+ var namespace = e.relatedNode.namespaceURI;
+ var target = e.target;
+ var record = new getRecord("attributes", target);
+ record.attributeName = name;
+ record.attributeNamespace = namespace;
+ var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.attributes) return;
+ if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
+ return;
+ }
+ if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMCharacterDataModified":
+ var target = e.target;
+ var record = getRecord("characterData", target);
+ var oldValue = e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.characterData) return;
+ if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMNodeRemoved":
+ this.addTransientObserver(e.target);
+
+ case "DOMNodeInserted":
+ var changedNode = e.target;
+ var addedNodes, removedNodes;
+ if (e.type === "DOMNodeInserted") {
+ addedNodes = [ changedNode ];
+ removedNodes = [];
+ } else {
+ addedNodes = [];
+ removedNodes = [ changedNode ];
+ }
+ var previousSibling = changedNode.previousSibling;
+ var nextSibling = changedNode.nextSibling;
+ var record = getRecord("childList", e.target.parentNode);
+ record.addedNodes = addedNodes;
+ record.removedNodes = removedNodes;
+ record.previousSibling = previousSibling;
+ record.nextSibling = nextSibling;
+ forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
+ if (!options.childList) return;
+ return record;
+ });
+ }
+ clearRecords();
+ }
+ };
+ global.JsMutationObserver = JsMutationObserver;
+ if (!global.MutationObserver) {
+ global.MutationObserver = JsMutationObserver;
+ JsMutationObserver._isPolyfilled = true;
+ }
+})(self); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.min.js b/catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.min.js
new file mode 100644
index 00000000..a97c6244
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/MutationObserver.min.js
@@ -0,0 +1,11 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+"undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,r=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};r.prototype={set:function(t,r){var i=t[this.name];return i&&i[0]===t?i[1]=r:e(t,this.name,{value:[t,r],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=r}(),function(e){function t(e){N.push(e),O||(O=!0,b(i))}function r(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function i(){O=!1;var e=N;N=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var r=e.takeRecords();n(e),r.length&&(e.callback_(r,e),t=!0)}),t&&i()}function n(e){e.nodes_.forEach(function(t){var r=p.get(t);r&&r.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function a(e,t){for(var r=e;r;r=r.parentNode){var i=p.get(r);if(i)for(var n=0;n<i.length;n++){var a=i[n],s=a.options;if(r===e||s.subtree){var o=t(s);o&&a.enqueue(o)}}}}function s(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++M}function o(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function d(e){var t=new o(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function u(e,t){return D=new o(e,t)}function h(e){return w?w:(w=d(D),w.oldValue=e,w)}function c(){D=w=void 0}function v(e){return e===w||e===D}function l(e,t){return e===t?e:w&&v(e)?w:null}function f(e,t,r){this.observer=e,this.target=t,this.options=r,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var b,p=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))b=setTimeout;else if(window.setImmediate)b=window.setImmediate;else{var g=[],m=String(Math.random());window.addEventListener("message",function(e){if(e.data===m){var t=g;g=[],t.forEach(function(e){e()})}}),b=function(e){g.push(e),window.postMessage(m,"*")}}var O=!1,N=[],M=0;s.prototype={observe:function(e,t){if(e=r(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var i=p.get(e);i||p.set(e,i=[]);for(var n,a=0;a<i.length;a++)if(i[a].observer===this){n=i[a],n.removeListeners(),n.options=t;break}n||(n=new f(this,e,t),i.push(n),this.nodes_.push(e)),n.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=p.get(e),r=0;r<t.length;r++){var i=t[r];if(i.observer===this){i.removeListeners(),t.splice(r,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var D,w;f.prototype={enqueue:function(e){var r=this.observer.records_,i=r.length;if(r.length>0){var n=r[i-1],a=l(n,e);if(a)return void(r[i-1]=a)}else t(this.observer);r[i]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=p.get(e);t||p.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=p.get(e),r=0;r<t.length;r++)if(t[r]===this){t.splice(r,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,r=e.relatedNode.namespaceURI,i=e.target,n=new u("attributes",i);n.attributeName=t,n.attributeNamespace=r;var s=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;a(i,function(e){if(e.attributes&&(!e.attributeFilter||!e.attributeFilter.length||e.attributeFilter.indexOf(t)!==-1||e.attributeFilter.indexOf(r)!==-1))return e.attributeOldValue?h(s):n});break;case"DOMCharacterDataModified":var i=e.target,n=u("characterData",i),s=e.prevValue;a(i,function(e){if(e.characterData)return e.characterDataOldValue?h(s):n});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var o,d,v=e.target;"DOMNodeInserted"===e.type?(o=[v],d=[]):(o=[],d=[v]);var l=v.previousSibling,f=v.nextSibling,n=u("childList",e.target.parentNode);n.addedNodes=o,n.removedNodes=d,n.previousSibling=l,n.nextSibling=f,a(e.relatedNode,function(e){if(e.childList)return n})}c()}},e.JsMutationObserver=s,e.MutationObserver||(e.MutationObserver=s,s._isPolyfilled=!0)}}(self); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/README.md b/catapult/third_party/polymer/components/webcomponentsjs/README.md
new file mode 100644
index 00000000..d92ed6fc
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/README.md
@@ -0,0 +1,155 @@
+webcomponents.js
+================
+
+[![Build Status](https://travis-ci.org/webcomponents/webcomponentsjs.svg?branch=master)](https://travis-ci.org/webcomponents/webcomponentsjs)
+
+A suite of polyfills supporting the [Web Components](http://webcomponents.org) specs:
+
+**Custom Elements**: allows authors to define their own custom tags ([spec](https://w3c.github.io/webcomponents/spec/custom/)).
+
+**HTML Imports**: a way to include and reuse HTML documents via other HTML documents ([spec](https://w3c.github.io/webcomponents/spec/imports/)).
+
+**Shadow DOM**: provides encapsulation by hiding DOM subtrees under shadow roots ([spec](https://w3c.github.io/webcomponents/spec/shadow/)).
+
+This also folds in polyfills for `MutationObserver` and `WeakMap`.
+
+
+## Releases
+
+Pre-built (concatenated & minified) versions of the polyfills are maintained in the [tagged versions](https://github.com/webcomponents/webcomponentsjs/releases) of this repo. There are two variants:
+
+`webcomponents.js` includes all of the polyfills.
+
+`webcomponents-lite.js` includes all polyfills except for shadow DOM.
+
+
+## Browser Support
+
+Our polyfills are intended to work in the latest versions of evergreen browsers. See below
+for our complete browser support matrix:
+
+| Polyfill | IE10 | IE11+ | Chrome* | Firefox* | Safari 7+* | Chrome Android* | Mobile Safari* |
+| ---------- |:----:|:-----:|:-------:|:--------:|:----------:|:---------------:|:--------------:|
+| Custom Elements | ~ | ✓ | ✓ | ✓ | ✓ | ✓| ✓ |
+| HTML Imports | ~ | ✓ | ✓ | ✓ | ✓| ✓| ✓ |
+| Shadow DOM | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Templates | ✓ | ✓ | ✓ | ✓| ✓ | ✓ | ✓ |
+
+
+*Indicates the current version of the browser
+
+~Indicates support may be flaky. If using Custom Elements or HTML Imports with Shadow DOM,
+you will get the non-flaky Mutation Observer polyfill that Shadow DOM includes.
+
+The polyfills may work in older browsers, however require additional polyfills (such as classList)
+to be used. We cannot guarantee support for browsers outside of our compatibility matrix.
+
+
+### Manually Building
+
+If you wish to build the polyfills yourself, you'll need `node` and `gulp` on your system:
+
+ * install [node.js](http://nodejs.org/) using the instructions on their website
+ * use `npm` to install [gulp.js](http://gulpjs.com/): `npm install -g gulp`
+
+Now you are ready to build the polyfills with:
+
+ # install dependencies
+ npm install
+ # build
+ gulp build
+
+The builds will be placed into the `dist/` directory.
+
+## Contribute
+
+See the [contributing guide](CONTRIBUTING.md)
+
+## License
+
+Everything in this repository is BSD style license unless otherwise specified.
+
+Copyright (c) 2015 The Polymer Authors. All rights reserved.
+
+## Helper utilities
+
+### `WebComponentsReady`
+
+Under native HTML Imports, `<script>` tags in the main document block the loading of such imports. This is to ensure the imports have loaded and any registered elements in them have been upgraded.
+
+The webcomponents.js and webcomponents-lite.js polyfills parse element definitions and handle their upgrade asynchronously. If prematurely fetching the element from the DOM before it has an opportunity to upgrade, you'll be working with an `HTMLUnknownElement`.
+
+For these situations (or when you need an approximate replacement for the Polymer 0.5 `polymer-ready` behavior), you can use the `WebComponentsReady` event as a signal before interacting with the element. The criteria for this event to fire is all Custom Elements with definitions registered by the time HTML Imports available at load time have loaded have upgraded.
+
+```js
+window.addEventListener('WebComponentsReady', function(e) {
+ // imports are loaded and elements have been registered
+ console.log('Components are ready');
+});
+```
+
+## Known Issues
+
+ * [Limited CSS encapsulation](#encapsulation)
+ * [Element wrapping / unwrapping limitations](#wrapping)
+ * [Custom element's constructor property is unreliable](#constructor)
+ * [Contenteditable elements do not trigger MutationObserver](#contentedit)
+ * [ShadowCSS: :host-context(...):host(...) doesn't work](#hostcontext)
+ * [ShadowCSS: :host(.zot:not(.bar:nth-child(2))) doesn't work](#nestedparens)
+ * [HTML imports: document.currentScript doesn't work as expected](#currentscript)
+ * [execCommand isn't supported under Shadow DOM](#execcommand)
+
+### Limited CSS encapsulation <a id="encapsulation"></a>
+Under native Shadow DOM, CSS selectors cannot cross the shadow boundary. This means document level styles don't apply to shadow roots, and styles defined within a shadow root don't apply outside of that shadow root. [Several selectors](http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/) are provided to be able to deal with the shadow boundary.
+
+The Shadow DOM polyfill can't prevent document styles from leaking into shadow roots. It can, however, encapsulate styles within shadow roots to some extent. This behavior isn't automatically emulated by the Shadow DOM polyfill, but it can be achieved by manually using the included ShadowCSS shim:
+
+```
+WebComponents.ShadowCSS.shimStyling( shadowRoot, scope );
+```
+
+... where `shadowRoot` is the shadow root of a DOM element, and `scope` is the name of the scope used to prefix the selectors. This removes all `<style>` elements from the shadow root, rewrites it rules using the given scope and reinserts the style as a document level stylesheet. Note that the `:host` and `:host-context` pseudo classes are also rewritten.
+
+For a full explanation on the implementation and both the possibilities and the limitations of ShadowCSS please view the documentation in the [ShadowCSS source](src/ShadowCSS/ShadowCSS.js).
+
+### Element wrapping / unwrapping limitations <a id="wrapping"></a>
+The Shadow DOM polyfill is implemented by [wrapping](http://webcomponents.org/polyfills/shadow-dom/#wrappers) DOM elements whenever possible. It does this by wrapping methods like `document.querySelector` to return wrapped DOM elements. This has a few caveats:
+ * Not _everything_ can be wrapped. For example, elements like `document`, `window`, `document.body`, `document.fullscreenElement` and others are non-configurable and thus cannot be overridden.
+ * Wrappers don't support [live NodeLists](https://developer.mozilla.org/en-US/docs/Web/API/NodeList#A_sometimes-live_collection) like `HTMLElement.childNodes` and `HTMLFormElement.elements`. All NodeLists are snapshotted upon read. See [#217](https://github.com/webcomponents/webcomponentsjs/issues/217) for an explanation.
+
+In order to work around these limitations the polyfill provides the `ShadowDOMPolyfill.wrap` and `ShadowDOMPolyfill.unwrap` methods to respectively wrap and unwrap DOM elements manually.
+
+### Custom element's constructor property is unreliable <a id="constructor"></a>
+See [#215](https://github.com/webcomponents/webcomponentsjs/issues/215) for background.
+
+In Safari and IE, instances of Custom Elements have a `constructor` property of `HTMLUnknownElementConstructor` and `HTMLUnknownElement`, respectively. It's unsafe to rely on this property for checking element types.
+
+It's worth noting that `customElement.__proto__.__proto__.constructor` is `HTMLElementPrototype` and that the prototype chain isn't modified by the polyfills(onto `ElementPrototype`, etc.)
+
+### Contenteditable elements do not trigger MutationObserver <a id="contentedit"></a>
+Using the MutationObserver polyfill, it isn't possible to monitor mutations of an element marked `contenteditable`.
+See [the mailing list](https://groups.google.com/forum/#!msg/polymer-dev/LHdtRVXXVsA/v1sGoiTYWUkJ)
+
+### ShadowCSS: :host-context(...):host(...) doesn't work <a id="hostcontext"></a>
+See [#16](https://github.com/webcomponents/webcomponentsjs/issues/16) for background.
+
+Under the shadow DOM polyfill, rules like:
+```
+:host-context(.foo):host(.bar) {...}
+```
+don't work, despite working under native Shadow DOM. The solution is to use `polyfill-next-selector` like:
+
+```
+polyfill-next-selector { content: '.foo :host.bar, :host.foo.bar'; }
+```
+
+### ShadowCSS: :host(.zot:not(.bar:nth-child(2))) doesn't work <a id="nestedparens"></a>
+ShadowCSS `:host()` rules can only have (at most) 1-level of nested parentheses in its argument selector under ShadowCSS. For example, `:host(.zot)` and `:host(.zot:not(.bar))` both work, but `:host(.zot:not(.bar:nth-child(2)))` does not.
+
+### HTML imports: document.currentScript doesn't work as expected <a id="currentscript"></a>
+In native HTML Imports, document.currentScript.ownerDocument references the import document itself. In the polyfill use document._currentScript.ownerDocument (note the underscore).
+
+### execCommand and contenteditable isn't supported under Shadow DOM <a id="execcommand"></a>
+See [#212](https://github.com/webcomponents/webcomponentsjs/issues/212)
+
+`execCommand`, and `contenteditable` aren't supported under the ShadowDOM polyfill, with commands that insert or remove nodes being especially prone to failure.
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.js b/catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.js
new file mode 100644
index 00000000..51989754
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.js
@@ -0,0 +1,4496 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+if (typeof WeakMap === "undefined") {
+ (function() {
+ var defineProperty = Object.defineProperty;
+ var counter = Date.now() % 1e9;
+ var WeakMap = function() {
+ this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
+ };
+ WeakMap.prototype = {
+ set: function(key, value) {
+ var entry = key[this.name];
+ if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
+ value: [ key, value ],
+ writable: true
+ });
+ return this;
+ },
+ get: function(key) {
+ var entry;
+ return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
+ },
+ "delete": function(key) {
+ var entry = key[this.name];
+ if (!entry || entry[0] !== key) return false;
+ entry[0] = entry[1] = undefined;
+ return true;
+ },
+ has: function(key) {
+ var entry = key[this.name];
+ if (!entry) return false;
+ return entry[0] === key;
+ }
+ };
+ window.WeakMap = WeakMap;
+ })();
+}
+
+window.ShadowDOMPolyfill = {};
+
+(function(scope) {
+ "use strict";
+ var constructorTable = new WeakMap();
+ var nativePrototypeTable = new WeakMap();
+ var wrappers = Object.create(null);
+ function detectEval() {
+ if (typeof chrome !== "undefined" && chrome.app && chrome.app.runtime) {
+ return false;
+ }
+ if (navigator.getDeviceStorage) {
+ return false;
+ }
+ try {
+ var f = new Function("return true;");
+ return f();
+ } catch (ex) {
+ return false;
+ }
+ }
+ var hasEval = detectEval();
+ function assert(b) {
+ if (!b) throw new Error("Assertion failed");
+ }
+ var defineProperty = Object.defineProperty;
+ var getOwnPropertyNames = Object.getOwnPropertyNames;
+ var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+ function mixin(to, from) {
+ var names = getOwnPropertyNames(from);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+ }
+ return to;
+ }
+ function mixinStatics(to, from) {
+ var names = getOwnPropertyNames(from);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ switch (name) {
+ case "arguments":
+ case "caller":
+ case "length":
+ case "name":
+ case "prototype":
+ case "toString":
+ continue;
+ }
+ defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+ }
+ return to;
+ }
+ function oneOf(object, propertyNames) {
+ for (var i = 0; i < propertyNames.length; i++) {
+ if (propertyNames[i] in object) return propertyNames[i];
+ }
+ }
+ var nonEnumerableDataDescriptor = {
+ value: undefined,
+ configurable: true,
+ enumerable: false,
+ writable: true
+ };
+ function defineNonEnumerableDataProperty(object, name, value) {
+ nonEnumerableDataDescriptor.value = value;
+ defineProperty(object, name, nonEnumerableDataDescriptor);
+ }
+ getOwnPropertyNames(window);
+ function getWrapperConstructor(node, opt_instance) {
+ var nativePrototype = node.__proto__ || Object.getPrototypeOf(node);
+ if (isFirefox) {
+ try {
+ getOwnPropertyNames(nativePrototype);
+ } catch (error) {
+ nativePrototype = nativePrototype.__proto__;
+ }
+ }
+ var wrapperConstructor = constructorTable.get(nativePrototype);
+ if (wrapperConstructor) return wrapperConstructor;
+ var parentWrapperConstructor = getWrapperConstructor(nativePrototype);
+ var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor);
+ registerInternal(nativePrototype, GeneratedWrapper, opt_instance);
+ return GeneratedWrapper;
+ }
+ function addForwardingProperties(nativePrototype, wrapperPrototype) {
+ installProperty(nativePrototype, wrapperPrototype, true);
+ }
+ function registerInstanceProperties(wrapperPrototype, instanceObject) {
+ installProperty(instanceObject, wrapperPrototype, false);
+ }
+ var isFirefox = /Firefox/.test(navigator.userAgent);
+ var dummyDescriptor = {
+ get: function() {},
+ set: function(v) {},
+ configurable: true,
+ enumerable: true
+ };
+ function isEventHandlerName(name) {
+ return /^on[a-z]+$/.test(name);
+ }
+ function isIdentifierName(name) {
+ return /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(name);
+ }
+ function getGetter(name) {
+ return hasEval && isIdentifierName(name) ? new Function("return this.__impl4cf1e782hg__." + name) : function() {
+ return this.__impl4cf1e782hg__[name];
+ };
+ }
+ function getSetter(name) {
+ return hasEval && isIdentifierName(name) ? new Function("v", "this.__impl4cf1e782hg__." + name + " = v") : function(v) {
+ this.__impl4cf1e782hg__[name] = v;
+ };
+ }
+ function getMethod(name) {
+ return hasEval && isIdentifierName(name) ? new Function("return this.__impl4cf1e782hg__." + name + ".apply(this.__impl4cf1e782hg__, arguments)") : function() {
+ return this.__impl4cf1e782hg__[name].apply(this.__impl4cf1e782hg__, arguments);
+ };
+ }
+ function getDescriptor(source, name) {
+ try {
+ if (source === window && name === "showModalDialog") {
+ return dummyDescriptor;
+ }
+ return Object.getOwnPropertyDescriptor(source, name);
+ } catch (ex) {
+ return dummyDescriptor;
+ }
+ }
+ var isBrokenSafari = function() {
+ var descr = Object.getOwnPropertyDescriptor(Node.prototype, "nodeType");
+ return descr && !descr.get && !descr.set;
+ }();
+ function installProperty(source, target, allowMethod, opt_blacklist) {
+ var names = getOwnPropertyNames(source);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ if (name === "polymerBlackList_") continue;
+ if (name in target) continue;
+ if (source.polymerBlackList_ && source.polymerBlackList_[name]) continue;
+ if (isFirefox) {
+ source.__lookupGetter__(name);
+ }
+ var descriptor = getDescriptor(source, name);
+ var getter, setter;
+ if (typeof descriptor.value === "function") {
+ if (allowMethod) {
+ target[name] = getMethod(name);
+ }
+ continue;
+ }
+ var isEvent = isEventHandlerName(name);
+ if (isEvent) getter = scope.getEventHandlerGetter(name); else getter = getGetter(name);
+ if (descriptor.writable || descriptor.set || isBrokenSafari) {
+ if (isEvent) setter = scope.getEventHandlerSetter(name); else setter = getSetter(name);
+ }
+ var configurable = isBrokenSafari || descriptor.configurable;
+ defineProperty(target, name, {
+ get: getter,
+ set: setter,
+ configurable: configurable,
+ enumerable: descriptor.enumerable
+ });
+ }
+ }
+ function register(nativeConstructor, wrapperConstructor, opt_instance) {
+ if (nativeConstructor == null) {
+ return;
+ }
+ var nativePrototype = nativeConstructor.prototype;
+ registerInternal(nativePrototype, wrapperConstructor, opt_instance);
+ mixinStatics(wrapperConstructor, nativeConstructor);
+ }
+ function registerInternal(nativePrototype, wrapperConstructor, opt_instance) {
+ var wrapperPrototype = wrapperConstructor.prototype;
+ assert(constructorTable.get(nativePrototype) === undefined);
+ constructorTable.set(nativePrototype, wrapperConstructor);
+ nativePrototypeTable.set(wrapperPrototype, nativePrototype);
+ addForwardingProperties(nativePrototype, wrapperPrototype);
+ if (opt_instance) registerInstanceProperties(wrapperPrototype, opt_instance);
+ defineNonEnumerableDataProperty(wrapperPrototype, "constructor", wrapperConstructor);
+ wrapperConstructor.prototype = wrapperPrototype;
+ }
+ function isWrapperFor(wrapperConstructor, nativeConstructor) {
+ return constructorTable.get(nativeConstructor.prototype) === wrapperConstructor;
+ }
+ function registerObject(object) {
+ var nativePrototype = Object.getPrototypeOf(object);
+ var superWrapperConstructor = getWrapperConstructor(nativePrototype);
+ var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor);
+ registerInternal(nativePrototype, GeneratedWrapper, object);
+ return GeneratedWrapper;
+ }
+ function createWrapperConstructor(superWrapperConstructor) {
+ function GeneratedWrapper(node) {
+ superWrapperConstructor.call(this, node);
+ }
+ var p = Object.create(superWrapperConstructor.prototype);
+ p.constructor = GeneratedWrapper;
+ GeneratedWrapper.prototype = p;
+ return GeneratedWrapper;
+ }
+ function isWrapper(object) {
+ return object && object.__impl4cf1e782hg__;
+ }
+ function isNative(object) {
+ return !isWrapper(object);
+ }
+ function wrap(impl) {
+ if (impl === null) return null;
+ assert(isNative(impl));
+ var wrapper = impl.__wrapper8e3dd93a60__;
+ if (wrapper != null) {
+ return wrapper;
+ }
+ return impl.__wrapper8e3dd93a60__ = new (getWrapperConstructor(impl, impl))(impl);
+ }
+ function unwrap(wrapper) {
+ if (wrapper === null) return null;
+ assert(isWrapper(wrapper));
+ return wrapper.__impl4cf1e782hg__;
+ }
+ function unsafeUnwrap(wrapper) {
+ return wrapper.__impl4cf1e782hg__;
+ }
+ function setWrapper(impl, wrapper) {
+ wrapper.__impl4cf1e782hg__ = impl;
+ impl.__wrapper8e3dd93a60__ = wrapper;
+ }
+ function unwrapIfNeeded(object) {
+ return object && isWrapper(object) ? unwrap(object) : object;
+ }
+ function wrapIfNeeded(object) {
+ return object && !isWrapper(object) ? wrap(object) : object;
+ }
+ function rewrap(node, wrapper) {
+ if (wrapper === null) return;
+ assert(isNative(node));
+ assert(wrapper === undefined || isWrapper(wrapper));
+ node.__wrapper8e3dd93a60__ = wrapper;
+ }
+ var getterDescriptor = {
+ get: undefined,
+ configurable: true,
+ enumerable: true
+ };
+ function defineGetter(constructor, name, getter) {
+ getterDescriptor.get = getter;
+ defineProperty(constructor.prototype, name, getterDescriptor);
+ }
+ function defineWrapGetter(constructor, name) {
+ defineGetter(constructor, name, function() {
+ return wrap(this.__impl4cf1e782hg__[name]);
+ });
+ }
+ function forwardMethodsToWrapper(constructors, names) {
+ constructors.forEach(function(constructor) {
+ names.forEach(function(name) {
+ constructor.prototype[name] = function() {
+ var w = wrapIfNeeded(this);
+ return w[name].apply(w, arguments);
+ };
+ });
+ });
+ }
+ scope.addForwardingProperties = addForwardingProperties;
+ scope.assert = assert;
+ scope.constructorTable = constructorTable;
+ scope.defineGetter = defineGetter;
+ scope.defineWrapGetter = defineWrapGetter;
+ scope.forwardMethodsToWrapper = forwardMethodsToWrapper;
+ scope.isIdentifierName = isIdentifierName;
+ scope.isWrapper = isWrapper;
+ scope.isWrapperFor = isWrapperFor;
+ scope.mixin = mixin;
+ scope.nativePrototypeTable = nativePrototypeTable;
+ scope.oneOf = oneOf;
+ scope.registerObject = registerObject;
+ scope.registerWrapper = register;
+ scope.rewrap = rewrap;
+ scope.setWrapper = setWrapper;
+ scope.unsafeUnwrap = unsafeUnwrap;
+ scope.unwrap = unwrap;
+ scope.unwrapIfNeeded = unwrapIfNeeded;
+ scope.wrap = wrap;
+ scope.wrapIfNeeded = wrapIfNeeded;
+ scope.wrappers = wrappers;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ function newSplice(index, removed, addedCount) {
+ return {
+ index: index,
+ removed: removed,
+ addedCount: addedCount
+ };
+ }
+ var EDIT_LEAVE = 0;
+ var EDIT_UPDATE = 1;
+ var EDIT_ADD = 2;
+ var EDIT_DELETE = 3;
+ function ArraySplice() {}
+ ArraySplice.prototype = {
+ calcEditDistances: function(current, currentStart, currentEnd, old, oldStart, oldEnd) {
+ var rowCount = oldEnd - oldStart + 1;
+ var columnCount = currentEnd - currentStart + 1;
+ var distances = new Array(rowCount);
+ for (var i = 0; i < rowCount; i++) {
+ distances[i] = new Array(columnCount);
+ distances[i][0] = i;
+ }
+ for (var j = 0; j < columnCount; j++) distances[0][j] = j;
+ for (var i = 1; i < rowCount; i++) {
+ for (var j = 1; j < columnCount; j++) {
+ if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) distances[i][j] = distances[i - 1][j - 1]; else {
+ var north = distances[i - 1][j] + 1;
+ var west = distances[i][j - 1] + 1;
+ distances[i][j] = north < west ? north : west;
+ }
+ }
+ }
+ return distances;
+ },
+ spliceOperationsFromEditDistances: function(distances) {
+ var i = distances.length - 1;
+ var j = distances[0].length - 1;
+ var current = distances[i][j];
+ var edits = [];
+ while (i > 0 || j > 0) {
+ if (i == 0) {
+ edits.push(EDIT_ADD);
+ j--;
+ continue;
+ }
+ if (j == 0) {
+ edits.push(EDIT_DELETE);
+ i--;
+ continue;
+ }
+ var northWest = distances[i - 1][j - 1];
+ var west = distances[i - 1][j];
+ var north = distances[i][j - 1];
+ var min;
+ if (west < north) min = west < northWest ? west : northWest; else min = north < northWest ? north : northWest;
+ if (min == northWest) {
+ if (northWest == current) {
+ edits.push(EDIT_LEAVE);
+ } else {
+ edits.push(EDIT_UPDATE);
+ current = northWest;
+ }
+ i--;
+ j--;
+ } else if (min == west) {
+ edits.push(EDIT_DELETE);
+ i--;
+ current = west;
+ } else {
+ edits.push(EDIT_ADD);
+ j--;
+ current = north;
+ }
+ }
+ edits.reverse();
+ return edits;
+ },
+ calcSplices: function(current, currentStart, currentEnd, old, oldStart, oldEnd) {
+ var prefixCount = 0;
+ var suffixCount = 0;
+ var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+ if (currentStart == 0 && oldStart == 0) prefixCount = this.sharedPrefix(current, old, minLength);
+ if (currentEnd == current.length && oldEnd == old.length) suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+ currentStart += prefixCount;
+ oldStart += prefixCount;
+ currentEnd -= suffixCount;
+ oldEnd -= suffixCount;
+ if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) return [];
+ if (currentStart == currentEnd) {
+ var splice = newSplice(currentStart, [], 0);
+ while (oldStart < oldEnd) splice.removed.push(old[oldStart++]);
+ return [ splice ];
+ } else if (oldStart == oldEnd) return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+ var ops = this.spliceOperationsFromEditDistances(this.calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
+ var splice = undefined;
+ var splices = [];
+ var index = currentStart;
+ var oldIndex = oldStart;
+ for (var i = 0; i < ops.length; i++) {
+ switch (ops[i]) {
+ case EDIT_LEAVE:
+ if (splice) {
+ splices.push(splice);
+ splice = undefined;
+ }
+ index++;
+ oldIndex++;
+ break;
+
+ case EDIT_UPDATE:
+ if (!splice) splice = newSplice(index, [], 0);
+ splice.addedCount++;
+ index++;
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+
+ case EDIT_ADD:
+ if (!splice) splice = newSplice(index, [], 0);
+ splice.addedCount++;
+ index++;
+ break;
+
+ case EDIT_DELETE:
+ if (!splice) splice = newSplice(index, [], 0);
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ }
+ }
+ if (splice) {
+ splices.push(splice);
+ }
+ return splices;
+ },
+ sharedPrefix: function(current, old, searchLength) {
+ for (var i = 0; i < searchLength; i++) if (!this.equals(current[i], old[i])) return i;
+ return searchLength;
+ },
+ sharedSuffix: function(current, old, searchLength) {
+ var index1 = current.length;
+ var index2 = old.length;
+ var count = 0;
+ while (count < searchLength && this.equals(current[--index1], old[--index2])) count++;
+ return count;
+ },
+ calculateSplices: function(current, previous) {
+ return this.calcSplices(current, 0, current.length, previous, 0, previous.length);
+ },
+ equals: function(currentValue, previousValue) {
+ return currentValue === previousValue;
+ }
+ };
+ scope.ArraySplice = ArraySplice;
+})(window.ShadowDOMPolyfill);
+
+(function(context) {
+ "use strict";
+ var OriginalMutationObserver = window.MutationObserver;
+ var callbacks = [];
+ var pending = false;
+ var timerFunc;
+ function handle() {
+ pending = false;
+ var copies = callbacks.slice(0);
+ callbacks = [];
+ for (var i = 0; i < copies.length; i++) {
+ (0, copies[i])();
+ }
+ }
+ if (OriginalMutationObserver) {
+ var counter = 1;
+ var observer = new OriginalMutationObserver(handle);
+ var textNode = document.createTextNode(counter);
+ observer.observe(textNode, {
+ characterData: true
+ });
+ timerFunc = function() {
+ counter = (counter + 1) % 2;
+ textNode.data = counter;
+ };
+ } else {
+ timerFunc = window.setTimeout;
+ }
+ function setEndOfMicrotask(func) {
+ callbacks.push(func);
+ if (pending) return;
+ pending = true;
+ timerFunc(handle, 0);
+ }
+ context.setEndOfMicrotask = setEndOfMicrotask;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var setEndOfMicrotask = scope.setEndOfMicrotask;
+ var wrapIfNeeded = scope.wrapIfNeeded;
+ var wrappers = scope.wrappers;
+ var registrationsTable = new WeakMap();
+ var globalMutationObservers = [];
+ var isScheduled = false;
+ function scheduleCallback(observer) {
+ if (observer.scheduled_) return;
+ observer.scheduled_ = true;
+ globalMutationObservers.push(observer);
+ if (isScheduled) return;
+ setEndOfMicrotask(notifyObservers);
+ isScheduled = true;
+ }
+ function notifyObservers() {
+ isScheduled = false;
+ while (globalMutationObservers.length) {
+ var notifyList = globalMutationObservers;
+ globalMutationObservers = [];
+ notifyList.sort(function(x, y) {
+ return x.uid_ - y.uid_;
+ });
+ for (var i = 0; i < notifyList.length; i++) {
+ var mo = notifyList[i];
+ mo.scheduled_ = false;
+ var queue = mo.takeRecords();
+ removeTransientObserversFor(mo);
+ if (queue.length) {
+ mo.callback_(queue, mo);
+ }
+ }
+ }
+ }
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = new wrappers.NodeList();
+ this.removedNodes = new wrappers.NodeList();
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+ function registerTransientObservers(ancestor, node) {
+ for (;ancestor; ancestor = ancestor.parentNode) {
+ var registrations = registrationsTable.get(ancestor);
+ if (!registrations) continue;
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.options.subtree) registration.addTransientObserver(node);
+ }
+ }
+ }
+ function removeTransientObserversFor(observer) {
+ for (var i = 0; i < observer.nodes_.length; i++) {
+ var node = observer.nodes_[i];
+ var registrations = registrationsTable.get(node);
+ if (!registrations) return;
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ if (registration.observer === observer) registration.removeTransientObservers();
+ }
+ }
+ }
+ function enqueueMutation(target, type, data) {
+ var interestedObservers = Object.create(null);
+ var associatedStrings = Object.create(null);
+ for (var node = target; node; node = node.parentNode) {
+ var registrations = registrationsTable.get(node);
+ if (!registrations) continue;
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ if (node !== target && !options.subtree) continue;
+ if (type === "attributes" && !options.attributes) continue;
+ if (type === "attributes" && options.attributeFilter && (data.namespace !== null || options.attributeFilter.indexOf(data.name) === -1)) {
+ continue;
+ }
+ if (type === "characterData" && !options.characterData) continue;
+ if (type === "childList" && !options.childList) continue;
+ var observer = registration.observer;
+ interestedObservers[observer.uid_] = observer;
+ if (type === "attributes" && options.attributeOldValue || type === "characterData" && options.characterDataOldValue) {
+ associatedStrings[observer.uid_] = data.oldValue;
+ }
+ }
+ }
+ for (var uid in interestedObservers) {
+ var observer = interestedObservers[uid];
+ var record = new MutationRecord(type, target);
+ if ("name" in data && "namespace" in data) {
+ record.attributeName = data.name;
+ record.attributeNamespace = data.namespace;
+ }
+ if (data.addedNodes) record.addedNodes = data.addedNodes;
+ if (data.removedNodes) record.removedNodes = data.removedNodes;
+ if (data.previousSibling) record.previousSibling = data.previousSibling;
+ if (data.nextSibling) record.nextSibling = data.nextSibling;
+ if (associatedStrings[uid] !== undefined) record.oldValue = associatedStrings[uid];
+ scheduleCallback(observer);
+ observer.records_.push(record);
+ }
+ }
+ var slice = Array.prototype.slice;
+ function MutationObserverOptions(options) {
+ this.childList = !!options.childList;
+ this.subtree = !!options.subtree;
+ if (!("attributes" in options) && ("attributeOldValue" in options || "attributeFilter" in options)) {
+ this.attributes = true;
+ } else {
+ this.attributes = !!options.attributes;
+ }
+ if ("characterDataOldValue" in options && !("characterData" in options)) this.characterData = true; else this.characterData = !!options.characterData;
+ if (!this.attributes && (options.attributeOldValue || "attributeFilter" in options) || !this.characterData && options.characterDataOldValue) {
+ throw new TypeError();
+ }
+ this.characterData = !!options.characterData;
+ this.attributeOldValue = !!options.attributeOldValue;
+ this.characterDataOldValue = !!options.characterDataOldValue;
+ if ("attributeFilter" in options) {
+ if (options.attributeFilter == null || typeof options.attributeFilter !== "object") {
+ throw new TypeError();
+ }
+ this.attributeFilter = slice.call(options.attributeFilter);
+ } else {
+ this.attributeFilter = null;
+ }
+ }
+ var uidCounter = 0;
+ function MutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+ this.scheduled_ = false;
+ }
+ MutationObserver.prototype = {
+ constructor: MutationObserver,
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+ var newOptions = new MutationObserverOptions(options);
+ var registration;
+ var registrations = registrationsTable.get(target);
+ if (!registrations) registrationsTable.set(target, registrations = []);
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ registration.removeTransientObservers();
+ registration.options = newOptions;
+ }
+ }
+ if (!registration) {
+ registration = new Registration(this, target, newOptions);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ },
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+ Registration.prototype = {
+ addTransientObserver: function(node) {
+ if (node === this.target) return;
+ scheduleCallback(this.observer);
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations) registrationsTable.set(node, registrations = []);
+ registrations.push(this);
+ },
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+ for (var i = 0; i < transientObservedNodes.length; i++) {
+ var node = transientObservedNodes[i];
+ var registrations = registrationsTable.get(node);
+ for (var j = 0; j < registrations.length; j++) {
+ if (registrations[j] === this) {
+ registrations.splice(j, 1);
+ break;
+ }
+ }
+ }
+ }
+ };
+ scope.enqueueMutation = enqueueMutation;
+ scope.registerTransientObservers = registerTransientObservers;
+ scope.wrappers.MutationObserver = MutationObserver;
+ scope.wrappers.MutationRecord = MutationRecord;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ function TreeScope(root, parent) {
+ this.root = root;
+ this.parent = parent;
+ }
+ TreeScope.prototype = {
+ get renderer() {
+ if (this.root instanceof scope.wrappers.ShadowRoot) {
+ return scope.getRendererForHost(this.root.host);
+ }
+ return null;
+ },
+ contains: function(treeScope) {
+ for (;treeScope; treeScope = treeScope.parent) {
+ if (treeScope === this) return true;
+ }
+ return false;
+ }
+ };
+ function setTreeScope(node, treeScope) {
+ if (node.treeScope_ !== treeScope) {
+ node.treeScope_ = treeScope;
+ for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) {
+ sr.treeScope_.parent = treeScope;
+ }
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ setTreeScope(child, treeScope);
+ }
+ }
+ }
+ function getTreeScope(node) {
+ if (node instanceof scope.wrappers.Window) {
+ debugger;
+ }
+ if (node.treeScope_) return node.treeScope_;
+ var parent = node.parentNode;
+ var treeScope;
+ if (parent) treeScope = getTreeScope(parent); else treeScope = new TreeScope(node, null);
+ return node.treeScope_ = treeScope;
+ }
+ scope.TreeScope = TreeScope;
+ scope.getTreeScope = getTreeScope;
+ scope.setTreeScope = setTreeScope;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+ var getTreeScope = scope.getTreeScope;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrappers = scope.wrappers;
+ var wrappedFuns = new WeakMap();
+ var listenersTable = new WeakMap();
+ var handledEventsTable = new WeakMap();
+ var currentlyDispatchingEvents = new WeakMap();
+ var targetTable = new WeakMap();
+ var currentTargetTable = new WeakMap();
+ var relatedTargetTable = new WeakMap();
+ var eventPhaseTable = new WeakMap();
+ var stopPropagationTable = new WeakMap();
+ var stopImmediatePropagationTable = new WeakMap();
+ var eventHandlersTable = new WeakMap();
+ var eventPathTable = new WeakMap();
+ function isShadowRoot(node) {
+ return node instanceof wrappers.ShadowRoot;
+ }
+ function rootOfNode(node) {
+ return getTreeScope(node).root;
+ }
+ function getEventPath(node, event) {
+ var path = [];
+ var current = node;
+ path.push(current);
+ while (current) {
+ var destinationInsertionPoints = getDestinationInsertionPoints(current);
+ if (destinationInsertionPoints && destinationInsertionPoints.length > 0) {
+ for (var i = 0; i < destinationInsertionPoints.length; i++) {
+ var insertionPoint = destinationInsertionPoints[i];
+ if (isShadowInsertionPoint(insertionPoint)) {
+ var shadowRoot = rootOfNode(insertionPoint);
+ var olderShadowRoot = shadowRoot.olderShadowRoot;
+ if (olderShadowRoot) path.push(olderShadowRoot);
+ }
+ path.push(insertionPoint);
+ }
+ current = destinationInsertionPoints[destinationInsertionPoints.length - 1];
+ } else {
+ if (isShadowRoot(current)) {
+ if (inSameTree(node, current) && eventMustBeStopped(event)) {
+ break;
+ }
+ current = current.host;
+ path.push(current);
+ } else {
+ current = current.parentNode;
+ if (current) path.push(current);
+ }
+ }
+ }
+ return path;
+ }
+ function eventMustBeStopped(event) {
+ if (!event) return false;
+ switch (event.type) {
+ case "abort":
+ case "error":
+ case "select":
+ case "change":
+ case "load":
+ case "reset":
+ case "resize":
+ case "scroll":
+ case "selectstart":
+ return true;
+ }
+ return false;
+ }
+ function isShadowInsertionPoint(node) {
+ return node instanceof HTMLShadowElement;
+ }
+ function getDestinationInsertionPoints(node) {
+ return scope.getDestinationInsertionPoints(node);
+ }
+ function eventRetargetting(path, currentTarget) {
+ if (path.length === 0) return currentTarget;
+ if (currentTarget instanceof wrappers.Window) currentTarget = currentTarget.document;
+ var currentTargetTree = getTreeScope(currentTarget);
+ var originalTarget = path[0];
+ var originalTargetTree = getTreeScope(originalTarget);
+ var relativeTargetTree = lowestCommonInclusiveAncestor(currentTargetTree, originalTargetTree);
+ for (var i = 0; i < path.length; i++) {
+ var node = path[i];
+ if (getTreeScope(node) === relativeTargetTree) return node;
+ }
+ return path[path.length - 1];
+ }
+ function getTreeScopeAncestors(treeScope) {
+ var ancestors = [];
+ for (;treeScope; treeScope = treeScope.parent) {
+ ancestors.push(treeScope);
+ }
+ return ancestors;
+ }
+ function lowestCommonInclusiveAncestor(tsA, tsB) {
+ var ancestorsA = getTreeScopeAncestors(tsA);
+ var ancestorsB = getTreeScopeAncestors(tsB);
+ var result = null;
+ while (ancestorsA.length > 0 && ancestorsB.length > 0) {
+ var a = ancestorsA.pop();
+ var b = ancestorsB.pop();
+ if (a === b) result = a; else break;
+ }
+ return result;
+ }
+ function getTreeScopeRoot(ts) {
+ if (!ts.parent) return ts;
+ return getTreeScopeRoot(ts.parent);
+ }
+ function relatedTargetResolution(event, currentTarget, relatedTarget) {
+ if (currentTarget instanceof wrappers.Window) currentTarget = currentTarget.document;
+ var currentTargetTree = getTreeScope(currentTarget);
+ var relatedTargetTree = getTreeScope(relatedTarget);
+ var relatedTargetEventPath = getEventPath(relatedTarget, event);
+ var lowestCommonAncestorTree;
+ var lowestCommonAncestorTree = lowestCommonInclusiveAncestor(currentTargetTree, relatedTargetTree);
+ if (!lowestCommonAncestorTree) lowestCommonAncestorTree = relatedTargetTree.root;
+ for (var commonAncestorTree = lowestCommonAncestorTree; commonAncestorTree; commonAncestorTree = commonAncestorTree.parent) {
+ var adjustedRelatedTarget;
+ for (var i = 0; i < relatedTargetEventPath.length; i++) {
+ var node = relatedTargetEventPath[i];
+ if (getTreeScope(node) === commonAncestorTree) return node;
+ }
+ }
+ return null;
+ }
+ function inSameTree(a, b) {
+ return getTreeScope(a) === getTreeScope(b);
+ }
+ var NONE = 0;
+ var CAPTURING_PHASE = 1;
+ var AT_TARGET = 2;
+ var BUBBLING_PHASE = 3;
+ var pendingError;
+ function dispatchOriginalEvent(originalEvent) {
+ if (handledEventsTable.get(originalEvent)) return;
+ handledEventsTable.set(originalEvent, true);
+ dispatchEvent(wrap(originalEvent), wrap(originalEvent.target));
+ if (pendingError) {
+ var err = pendingError;
+ pendingError = null;
+ throw err;
+ }
+ }
+ function isLoadLikeEvent(event) {
+ switch (event.type) {
+ case "load":
+ case "beforeunload":
+ case "unload":
+ return true;
+ }
+ return false;
+ }
+ function dispatchEvent(event, originalWrapperTarget) {
+ if (currentlyDispatchingEvents.get(event)) throw new Error("InvalidStateError");
+ currentlyDispatchingEvents.set(event, true);
+ scope.renderAllPending();
+ var eventPath;
+ var overrideTarget;
+ var win;
+ if (isLoadLikeEvent(event) && !event.bubbles) {
+ var doc = originalWrapperTarget;
+ if (doc instanceof wrappers.Document && (win = doc.defaultView)) {
+ overrideTarget = doc;
+ eventPath = [];
+ }
+ }
+ if (!eventPath) {
+ if (originalWrapperTarget instanceof wrappers.Window) {
+ win = originalWrapperTarget;
+ eventPath = [];
+ } else {
+ eventPath = getEventPath(originalWrapperTarget, event);
+ if (!isLoadLikeEvent(event)) {
+ var doc = eventPath[eventPath.length - 1];
+ if (doc instanceof wrappers.Document) win = doc.defaultView;
+ }
+ }
+ }
+ eventPathTable.set(event, eventPath);
+ if (dispatchCapturing(event, eventPath, win, overrideTarget)) {
+ if (dispatchAtTarget(event, eventPath, win, overrideTarget)) {
+ dispatchBubbling(event, eventPath, win, overrideTarget);
+ }
+ }
+ eventPhaseTable.set(event, NONE);
+ currentTargetTable.delete(event, null);
+ currentlyDispatchingEvents.delete(event);
+ return event.defaultPrevented;
+ }
+ function dispatchCapturing(event, eventPath, win, overrideTarget) {
+ var phase = CAPTURING_PHASE;
+ if (win) {
+ if (!invoke(win, event, phase, eventPath, overrideTarget)) return false;
+ }
+ for (var i = eventPath.length - 1; i > 0; i--) {
+ if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) return false;
+ }
+ return true;
+ }
+ function dispatchAtTarget(event, eventPath, win, overrideTarget) {
+ var phase = AT_TARGET;
+ var currentTarget = eventPath[0] || win;
+ return invoke(currentTarget, event, phase, eventPath, overrideTarget);
+ }
+ function dispatchBubbling(event, eventPath, win, overrideTarget) {
+ var phase = BUBBLING_PHASE;
+ for (var i = 1; i < eventPath.length; i++) {
+ if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) return;
+ }
+ if (win && eventPath.length > 0) {
+ invoke(win, event, phase, eventPath, overrideTarget);
+ }
+ }
+ function invoke(currentTarget, event, phase, eventPath, overrideTarget) {
+ var listeners = listenersTable.get(currentTarget);
+ if (!listeners) return true;
+ var target = overrideTarget || eventRetargetting(eventPath, currentTarget);
+ if (target === currentTarget) {
+ if (phase === CAPTURING_PHASE) return true;
+ if (phase === BUBBLING_PHASE) phase = AT_TARGET;
+ } else if (phase === BUBBLING_PHASE && !event.bubbles) {
+ return true;
+ }
+ if ("relatedTarget" in event) {
+ var originalEvent = unwrap(event);
+ var unwrappedRelatedTarget = originalEvent.relatedTarget;
+ if (unwrappedRelatedTarget) {
+ if (unwrappedRelatedTarget instanceof Object && unwrappedRelatedTarget.addEventListener) {
+ var relatedTarget = wrap(unwrappedRelatedTarget);
+ var adjusted = relatedTargetResolution(event, currentTarget, relatedTarget);
+ if (adjusted === target) return true;
+ } else {
+ adjusted = null;
+ }
+ relatedTargetTable.set(event, adjusted);
+ }
+ }
+ eventPhaseTable.set(event, phase);
+ var type = event.type;
+ var anyRemoved = false;
+ targetTable.set(event, target);
+ currentTargetTable.set(event, currentTarget);
+ listeners.depth++;
+ for (var i = 0, len = listeners.length; i < len; i++) {
+ var listener = listeners[i];
+ if (listener.removed) {
+ anyRemoved = true;
+ continue;
+ }
+ if (listener.type !== type || !listener.capture && phase === CAPTURING_PHASE || listener.capture && phase === BUBBLING_PHASE) {
+ continue;
+ }
+ try {
+ if (typeof listener.handler === "function") listener.handler.call(currentTarget, event); else listener.handler.handleEvent(event);
+ if (stopImmediatePropagationTable.get(event)) return false;
+ } catch (ex) {
+ if (!pendingError) pendingError = ex;
+ }
+ }
+ listeners.depth--;
+ if (anyRemoved && listeners.depth === 0) {
+ var copy = listeners.slice();
+ listeners.length = 0;
+ for (var i = 0; i < copy.length; i++) {
+ if (!copy[i].removed) listeners.push(copy[i]);
+ }
+ }
+ return !stopPropagationTable.get(event);
+ }
+ function Listener(type, handler, capture) {
+ this.type = type;
+ this.handler = handler;
+ this.capture = Boolean(capture);
+ }
+ Listener.prototype = {
+ equals: function(that) {
+ return this.handler === that.handler && this.type === that.type && this.capture === that.capture;
+ },
+ get removed() {
+ return this.handler === null;
+ },
+ remove: function() {
+ this.handler = null;
+ }
+ };
+ var OriginalEvent = window.Event;
+ OriginalEvent.prototype.polymerBlackList_ = {
+ returnValue: true,
+ keyLocation: true
+ };
+ function Event(type, options) {
+ if (type instanceof OriginalEvent) {
+ var impl = type;
+ if (!OriginalBeforeUnloadEvent && impl.type === "beforeunload" && !(this instanceof BeforeUnloadEvent)) {
+ return new BeforeUnloadEvent(impl);
+ }
+ setWrapper(impl, this);
+ } else {
+ return wrap(constructEvent(OriginalEvent, "Event", type, options));
+ }
+ }
+ Event.prototype = {
+ get target() {
+ return targetTable.get(this);
+ },
+ get currentTarget() {
+ return currentTargetTable.get(this);
+ },
+ get eventPhase() {
+ return eventPhaseTable.get(this);
+ },
+ get path() {
+ var eventPath = eventPathTable.get(this);
+ if (!eventPath) return [];
+ return eventPath.slice();
+ },
+ stopPropagation: function() {
+ stopPropagationTable.set(this, true);
+ },
+ stopImmediatePropagation: function() {
+ stopPropagationTable.set(this, true);
+ stopImmediatePropagationTable.set(this, true);
+ }
+ };
+ var supportsDefaultPrevented = function() {
+ var e = document.createEvent("Event");
+ e.initEvent("test", true, true);
+ e.preventDefault();
+ return e.defaultPrevented;
+ }();
+ if (!supportsDefaultPrevented) {
+ Event.prototype.preventDefault = function() {
+ if (!this.cancelable) return;
+ unsafeUnwrap(this).preventDefault();
+ Object.defineProperty(this, "defaultPrevented", {
+ get: function() {
+ return true;
+ },
+ configurable: true
+ });
+ };
+ }
+ registerWrapper(OriginalEvent, Event, document.createEvent("Event"));
+ function unwrapOptions(options) {
+ if (!options || !options.relatedTarget) return options;
+ return Object.create(options, {
+ relatedTarget: {
+ value: unwrap(options.relatedTarget)
+ }
+ });
+ }
+ function registerGenericEvent(name, SuperEvent, prototype) {
+ var OriginalEvent = window[name];
+ var GenericEvent = function(type, options) {
+ if (type instanceof OriginalEvent) setWrapper(type, this); else return wrap(constructEvent(OriginalEvent, name, type, options));
+ };
+ GenericEvent.prototype = Object.create(SuperEvent.prototype);
+ if (prototype) mixin(GenericEvent.prototype, prototype);
+ if (OriginalEvent) {
+ try {
+ registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent("temp"));
+ } catch (ex) {
+ registerWrapper(OriginalEvent, GenericEvent, document.createEvent(name));
+ }
+ }
+ return GenericEvent;
+ }
+ var UIEvent = registerGenericEvent("UIEvent", Event);
+ var CustomEvent = registerGenericEvent("CustomEvent", Event);
+ var relatedTargetProto = {
+ get relatedTarget() {
+ var relatedTarget = relatedTargetTable.get(this);
+ if (relatedTarget !== undefined) return relatedTarget;
+ return wrap(unwrap(this).relatedTarget);
+ }
+ };
+ function getInitFunction(name, relatedTargetIndex) {
+ return function() {
+ arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]);
+ var impl = unwrap(this);
+ impl[name].apply(impl, arguments);
+ };
+ }
+ var mouseEventProto = mixin({
+ initMouseEvent: getInitFunction("initMouseEvent", 14)
+ }, relatedTargetProto);
+ var focusEventProto = mixin({
+ initFocusEvent: getInitFunction("initFocusEvent", 5)
+ }, relatedTargetProto);
+ var MouseEvent = registerGenericEvent("MouseEvent", UIEvent, mouseEventProto);
+ var FocusEvent = registerGenericEvent("FocusEvent", UIEvent, focusEventProto);
+ var defaultInitDicts = Object.create(null);
+ var supportsEventConstructors = function() {
+ try {
+ new window.FocusEvent("focus");
+ } catch (ex) {
+ return false;
+ }
+ return true;
+ }();
+ function constructEvent(OriginalEvent, name, type, options) {
+ if (supportsEventConstructors) return new OriginalEvent(type, unwrapOptions(options));
+ var event = unwrap(document.createEvent(name));
+ var defaultDict = defaultInitDicts[name];
+ var args = [ type ];
+ Object.keys(defaultDict).forEach(function(key) {
+ var v = options != null && key in options ? options[key] : defaultDict[key];
+ if (key === "relatedTarget") v = unwrap(v);
+ args.push(v);
+ });
+ event["init" + name].apply(event, args);
+ return event;
+ }
+ if (!supportsEventConstructors) {
+ var configureEventConstructor = function(name, initDict, superName) {
+ if (superName) {
+ var superDict = defaultInitDicts[superName];
+ initDict = mixin(mixin({}, superDict), initDict);
+ }
+ defaultInitDicts[name] = initDict;
+ };
+ configureEventConstructor("Event", {
+ bubbles: false,
+ cancelable: false
+ });
+ configureEventConstructor("CustomEvent", {
+ detail: null
+ }, "Event");
+ configureEventConstructor("UIEvent", {
+ view: null,
+ detail: 0
+ }, "Event");
+ configureEventConstructor("MouseEvent", {
+ screenX: 0,
+ screenY: 0,
+ clientX: 0,
+ clientY: 0,
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false,
+ button: 0,
+ relatedTarget: null
+ }, "UIEvent");
+ configureEventConstructor("FocusEvent", {
+ relatedTarget: null
+ }, "UIEvent");
+ }
+ var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent;
+ function BeforeUnloadEvent(impl) {
+ Event.call(this, impl);
+ }
+ BeforeUnloadEvent.prototype = Object.create(Event.prototype);
+ mixin(BeforeUnloadEvent.prototype, {
+ get returnValue() {
+ return unsafeUnwrap(this).returnValue;
+ },
+ set returnValue(v) {
+ unsafeUnwrap(this).returnValue = v;
+ }
+ });
+ if (OriginalBeforeUnloadEvent) registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent);
+ function isValidListener(fun) {
+ if (typeof fun === "function") return true;
+ return fun && fun.handleEvent;
+ }
+ function isMutationEvent(type) {
+ switch (type) {
+ case "DOMAttrModified":
+ case "DOMAttributeNameChanged":
+ case "DOMCharacterDataModified":
+ case "DOMElementNameChanged":
+ case "DOMNodeInserted":
+ case "DOMNodeInsertedIntoDocument":
+ case "DOMNodeRemoved":
+ case "DOMNodeRemovedFromDocument":
+ case "DOMSubtreeModified":
+ return true;
+ }
+ return false;
+ }
+ var OriginalEventTarget = window.EventTarget;
+ function EventTarget(impl) {
+ setWrapper(impl, this);
+ }
+ var methodNames = [ "addEventListener", "removeEventListener", "dispatchEvent" ];
+ [ Node, Window ].forEach(function(constructor) {
+ var p = constructor.prototype;
+ methodNames.forEach(function(name) {
+ Object.defineProperty(p, name + "_", {
+ value: p[name]
+ });
+ });
+ });
+ function getTargetToListenAt(wrapper) {
+ if (wrapper instanceof wrappers.ShadowRoot) wrapper = wrapper.host;
+ return unwrap(wrapper);
+ }
+ EventTarget.prototype = {
+ addEventListener: function(type, fun, capture) {
+ if (!isValidListener(fun) || isMutationEvent(type)) return;
+ var listener = new Listener(type, fun, capture);
+ var listeners = listenersTable.get(this);
+ if (!listeners) {
+ listeners = [];
+ listeners.depth = 0;
+ listenersTable.set(this, listeners);
+ } else {
+ for (var i = 0; i < listeners.length; i++) {
+ if (listener.equals(listeners[i])) return;
+ }
+ }
+ listeners.push(listener);
+ var target = getTargetToListenAt(this);
+ target.addEventListener_(type, dispatchOriginalEvent, true);
+ },
+ removeEventListener: function(type, fun, capture) {
+ capture = Boolean(capture);
+ var listeners = listenersTable.get(this);
+ if (!listeners) return;
+ var count = 0, found = false;
+ for (var i = 0; i < listeners.length; i++) {
+ if (listeners[i].type === type && listeners[i].capture === capture) {
+ count++;
+ if (listeners[i].handler === fun) {
+ found = true;
+ listeners[i].remove();
+ }
+ }
+ }
+ if (found && count === 1) {
+ var target = getTargetToListenAt(this);
+ target.removeEventListener_(type, dispatchOriginalEvent, true);
+ }
+ },
+ dispatchEvent: function(event) {
+ var nativeEvent = unwrap(event);
+ var eventType = nativeEvent.type;
+ handledEventsTable.set(nativeEvent, false);
+ scope.renderAllPending();
+ var tempListener;
+ if (!hasListenerInAncestors(this, eventType)) {
+ tempListener = function() {};
+ this.addEventListener(eventType, tempListener, true);
+ }
+ try {
+ return unwrap(this).dispatchEvent_(nativeEvent);
+ } finally {
+ if (tempListener) this.removeEventListener(eventType, tempListener, true);
+ }
+ }
+ };
+ function hasListener(node, type) {
+ var listeners = listenersTable.get(node);
+ if (listeners) {
+ for (var i = 0; i < listeners.length; i++) {
+ if (!listeners[i].removed && listeners[i].type === type) return true;
+ }
+ }
+ return false;
+ }
+ function hasListenerInAncestors(target, type) {
+ for (var node = unwrap(target); node; node = node.parentNode) {
+ if (hasListener(wrap(node), type)) return true;
+ }
+ return false;
+ }
+ if (OriginalEventTarget) registerWrapper(OriginalEventTarget, EventTarget);
+ function wrapEventTargetMethods(constructors) {
+ forwardMethodsToWrapper(constructors, methodNames);
+ }
+ var originalElementFromPoint = document.elementFromPoint;
+ function elementFromPoint(self, document, x, y) {
+ scope.renderAllPending();
+ var element = wrap(originalElementFromPoint.call(unsafeUnwrap(document), x, y));
+ if (!element) return null;
+ var path = getEventPath(element, null);
+ var idx = path.lastIndexOf(self);
+ if (idx == -1) return null; else path = path.slice(0, idx);
+ return eventRetargetting(path, self);
+ }
+ function getEventHandlerGetter(name) {
+ return function() {
+ var inlineEventHandlers = eventHandlersTable.get(this);
+ return inlineEventHandlers && inlineEventHandlers[name] && inlineEventHandlers[name].value || null;
+ };
+ }
+ function getEventHandlerSetter(name) {
+ var eventType = name.slice(2);
+ return function(value) {
+ var inlineEventHandlers = eventHandlersTable.get(this);
+ if (!inlineEventHandlers) {
+ inlineEventHandlers = Object.create(null);
+ eventHandlersTable.set(this, inlineEventHandlers);
+ }
+ var old = inlineEventHandlers[name];
+ if (old) this.removeEventListener(eventType, old.wrapped, false);
+ if (typeof value === "function") {
+ var wrapped = function(e) {
+ var rv = value.call(this, e);
+ if (rv === false) e.preventDefault(); else if (name === "onbeforeunload" && typeof rv === "string") e.returnValue = rv;
+ };
+ this.addEventListener(eventType, wrapped, false);
+ inlineEventHandlers[name] = {
+ value: value,
+ wrapped: wrapped
+ };
+ }
+ };
+ }
+ scope.elementFromPoint = elementFromPoint;
+ scope.getEventHandlerGetter = getEventHandlerGetter;
+ scope.getEventHandlerSetter = getEventHandlerSetter;
+ scope.wrapEventTargetMethods = wrapEventTargetMethods;
+ scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent;
+ scope.wrappers.CustomEvent = CustomEvent;
+ scope.wrappers.Event = Event;
+ scope.wrappers.EventTarget = EventTarget;
+ scope.wrappers.FocusEvent = FocusEvent;
+ scope.wrappers.MouseEvent = MouseEvent;
+ scope.wrappers.UIEvent = UIEvent;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var UIEvent = scope.wrappers.UIEvent;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var OriginalTouchEvent = window.TouchEvent;
+ if (!OriginalTouchEvent) return;
+ var nativeEvent;
+ try {
+ nativeEvent = document.createEvent("TouchEvent");
+ } catch (ex) {
+ return;
+ }
+ var nonEnumDescriptor = {
+ enumerable: false
+ };
+ function nonEnum(obj, prop) {
+ Object.defineProperty(obj, prop, nonEnumDescriptor);
+ }
+ function Touch(impl) {
+ setWrapper(impl, this);
+ }
+ Touch.prototype = {
+ get target() {
+ return wrap(unsafeUnwrap(this).target);
+ }
+ };
+ var descr = {
+ configurable: true,
+ enumerable: true,
+ get: null
+ };
+ [ "clientX", "clientY", "screenX", "screenY", "pageX", "pageY", "identifier", "webkitRadiusX", "webkitRadiusY", "webkitRotationAngle", "webkitForce" ].forEach(function(name) {
+ descr.get = function() {
+ return unsafeUnwrap(this)[name];
+ };
+ Object.defineProperty(Touch.prototype, name, descr);
+ });
+ function TouchList() {
+ this.length = 0;
+ nonEnum(this, "length");
+ }
+ TouchList.prototype = {
+ item: function(index) {
+ return this[index];
+ }
+ };
+ function wrapTouchList(nativeTouchList) {
+ var list = new TouchList();
+ for (var i = 0; i < nativeTouchList.length; i++) {
+ list[i] = new Touch(nativeTouchList[i]);
+ }
+ list.length = i;
+ return list;
+ }
+ function TouchEvent(impl) {
+ UIEvent.call(this, impl);
+ }
+ TouchEvent.prototype = Object.create(UIEvent.prototype);
+ mixin(TouchEvent.prototype, {
+ get touches() {
+ return wrapTouchList(unsafeUnwrap(this).touches);
+ },
+ get targetTouches() {
+ return wrapTouchList(unsafeUnwrap(this).targetTouches);
+ },
+ get changedTouches() {
+ return wrapTouchList(unsafeUnwrap(this).changedTouches);
+ },
+ initTouchEvent: function() {
+ throw new Error("Not implemented");
+ }
+ });
+ registerWrapper(OriginalTouchEvent, TouchEvent, nativeEvent);
+ scope.wrappers.Touch = Touch;
+ scope.wrappers.TouchEvent = TouchEvent;
+ scope.wrappers.TouchList = TouchList;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var nonEnumDescriptor = {
+ enumerable: false
+ };
+ function nonEnum(obj, prop) {
+ Object.defineProperty(obj, prop, nonEnumDescriptor);
+ }
+ function NodeList() {
+ this.length = 0;
+ nonEnum(this, "length");
+ }
+ NodeList.prototype = {
+ item: function(index) {
+ return this[index];
+ }
+ };
+ nonEnum(NodeList.prototype, "item");
+ function wrapNodeList(list) {
+ if (list == null) return list;
+ var wrapperList = new NodeList();
+ for (var i = 0, length = list.length; i < length; i++) {
+ wrapperList[i] = wrap(list[i]);
+ }
+ wrapperList.length = length;
+ return wrapperList;
+ }
+ function addWrapNodeListMethod(wrapperConstructor, name) {
+ wrapperConstructor.prototype[name] = function() {
+ return wrapNodeList(unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments));
+ };
+ }
+ scope.wrappers.NodeList = NodeList;
+ scope.addWrapNodeListMethod = addWrapNodeListMethod;
+ scope.wrapNodeList = wrapNodeList;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ scope.wrapHTMLCollection = scope.wrapNodeList;
+ scope.wrappers.HTMLCollection = scope.wrappers.NodeList;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var EventTarget = scope.wrappers.EventTarget;
+ var NodeList = scope.wrappers.NodeList;
+ var TreeScope = scope.TreeScope;
+ var assert = scope.assert;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var enqueueMutation = scope.enqueueMutation;
+ var getTreeScope = scope.getTreeScope;
+ var isWrapper = scope.isWrapper;
+ var mixin = scope.mixin;
+ var registerTransientObservers = scope.registerTransientObservers;
+ var registerWrapper = scope.registerWrapper;
+ var setTreeScope = scope.setTreeScope;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var wrapIfNeeded = scope.wrapIfNeeded;
+ var wrappers = scope.wrappers;
+ function assertIsNodeWrapper(node) {
+ assert(node instanceof Node);
+ }
+ function createOneElementNodeList(node) {
+ var nodes = new NodeList();
+ nodes[0] = node;
+ nodes.length = 1;
+ return nodes;
+ }
+ var surpressMutations = false;
+ function enqueueRemovalForInsertedNodes(node, parent, nodes) {
+ enqueueMutation(parent, "childList", {
+ removedNodes: nodes,
+ previousSibling: node.previousSibling,
+ nextSibling: node.nextSibling
+ });
+ }
+ function enqueueRemovalForInsertedDocumentFragment(df, nodes) {
+ enqueueMutation(df, "childList", {
+ removedNodes: nodes
+ });
+ }
+ function collectNodes(node, parentNode, previousNode, nextNode) {
+ if (node instanceof DocumentFragment) {
+ var nodes = collectNodesForDocumentFragment(node);
+ surpressMutations = true;
+ for (var i = nodes.length - 1; i >= 0; i--) {
+ node.removeChild(nodes[i]);
+ nodes[i].parentNode_ = parentNode;
+ }
+ surpressMutations = false;
+ for (var i = 0; i < nodes.length; i++) {
+ nodes[i].previousSibling_ = nodes[i - 1] || previousNode;
+ nodes[i].nextSibling_ = nodes[i + 1] || nextNode;
+ }
+ if (previousNode) previousNode.nextSibling_ = nodes[0];
+ if (nextNode) nextNode.previousSibling_ = nodes[nodes.length - 1];
+ return nodes;
+ }
+ var nodes = createOneElementNodeList(node);
+ var oldParent = node.parentNode;
+ if (oldParent) {
+ oldParent.removeChild(node);
+ }
+ node.parentNode_ = parentNode;
+ node.previousSibling_ = previousNode;
+ node.nextSibling_ = nextNode;
+ if (previousNode) previousNode.nextSibling_ = node;
+ if (nextNode) nextNode.previousSibling_ = node;
+ return nodes;
+ }
+ function collectNodesNative(node) {
+ if (node instanceof DocumentFragment) return collectNodesForDocumentFragment(node);
+ var nodes = createOneElementNodeList(node);
+ var oldParent = node.parentNode;
+ if (oldParent) enqueueRemovalForInsertedNodes(node, oldParent, nodes);
+ return nodes;
+ }
+ function collectNodesForDocumentFragment(node) {
+ var nodes = new NodeList();
+ var i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ nodes[i++] = child;
+ }
+ nodes.length = i;
+ enqueueRemovalForInsertedDocumentFragment(node, nodes);
+ return nodes;
+ }
+ function snapshotNodeList(nodeList) {
+ return nodeList;
+ }
+ function nodeWasAdded(node, treeScope) {
+ setTreeScope(node, treeScope);
+ node.nodeIsInserted_();
+ }
+ function nodesWereAdded(nodes, parent) {
+ var treeScope = getTreeScope(parent);
+ for (var i = 0; i < nodes.length; i++) {
+ nodeWasAdded(nodes[i], treeScope);
+ }
+ }
+ function nodeWasRemoved(node) {
+ setTreeScope(node, new TreeScope(node, null));
+ }
+ function nodesWereRemoved(nodes) {
+ for (var i = 0; i < nodes.length; i++) {
+ nodeWasRemoved(nodes[i]);
+ }
+ }
+ function ensureSameOwnerDocument(parent, child) {
+ var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? parent : parent.ownerDocument;
+ if (ownerDoc !== child.ownerDocument) ownerDoc.adoptNode(child);
+ }
+ function adoptNodesIfNeeded(owner, nodes) {
+ if (!nodes.length) return;
+ var ownerDoc = owner.ownerDocument;
+ if (ownerDoc === nodes[0].ownerDocument) return;
+ for (var i = 0; i < nodes.length; i++) {
+ scope.adoptNodeNoRemove(nodes[i], ownerDoc);
+ }
+ }
+ function unwrapNodesForInsertion(owner, nodes) {
+ adoptNodesIfNeeded(owner, nodes);
+ var length = nodes.length;
+ if (length === 1) return unwrap(nodes[0]);
+ var df = unwrap(owner.ownerDocument.createDocumentFragment());
+ for (var i = 0; i < length; i++) {
+ df.appendChild(unwrap(nodes[i]));
+ }
+ return df;
+ }
+ function clearChildNodes(wrapper) {
+ if (wrapper.firstChild_ !== undefined) {
+ var child = wrapper.firstChild_;
+ while (child) {
+ var tmp = child;
+ child = child.nextSibling_;
+ tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined;
+ }
+ }
+ wrapper.firstChild_ = wrapper.lastChild_ = undefined;
+ }
+ function removeAllChildNodes(wrapper) {
+ if (wrapper.invalidateShadowRenderer()) {
+ var childWrapper = wrapper.firstChild;
+ while (childWrapper) {
+ assert(childWrapper.parentNode === wrapper);
+ var nextSibling = childWrapper.nextSibling;
+ var childNode = unwrap(childWrapper);
+ var parentNode = childNode.parentNode;
+ if (parentNode) originalRemoveChild.call(parentNode, childNode);
+ childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.parentNode_ = null;
+ childWrapper = nextSibling;
+ }
+ wrapper.firstChild_ = wrapper.lastChild_ = null;
+ } else {
+ var node = unwrap(wrapper);
+ var child = node.firstChild;
+ var nextSibling;
+ while (child) {
+ nextSibling = child.nextSibling;
+ originalRemoveChild.call(node, child);
+ child = nextSibling;
+ }
+ }
+ }
+ function invalidateParent(node) {
+ var p = node.parentNode;
+ return p && p.invalidateShadowRenderer();
+ }
+ function cleanupNodes(nodes) {
+ for (var i = 0, n; i < nodes.length; i++) {
+ n = nodes[i];
+ n.parentNode.removeChild(n);
+ }
+ }
+ var originalImportNode = document.importNode;
+ var originalCloneNode = window.Node.prototype.cloneNode;
+ function cloneNode(node, deep, opt_doc) {
+ var clone;
+ if (opt_doc) clone = wrap(originalImportNode.call(opt_doc, unsafeUnwrap(node), false)); else clone = wrap(originalCloneNode.call(unsafeUnwrap(node), false));
+ if (deep) {
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ clone.appendChild(cloneNode(child, true, opt_doc));
+ }
+ if (node instanceof wrappers.HTMLTemplateElement) {
+ var cloneContent = clone.content;
+ for (var child = node.content.firstChild; child; child = child.nextSibling) {
+ cloneContent.appendChild(cloneNode(child, true, opt_doc));
+ }
+ }
+ }
+ return clone;
+ }
+ function contains(self, child) {
+ if (!child || getTreeScope(self) !== getTreeScope(child)) return false;
+ for (var node = child; node; node = node.parentNode) {
+ if (node === self) return true;
+ }
+ return false;
+ }
+ var OriginalNode = window.Node;
+ function Node(original) {
+ assert(original instanceof OriginalNode);
+ EventTarget.call(this, original);
+ this.parentNode_ = undefined;
+ this.firstChild_ = undefined;
+ this.lastChild_ = undefined;
+ this.nextSibling_ = undefined;
+ this.previousSibling_ = undefined;
+ this.treeScope_ = undefined;
+ }
+ var OriginalDocumentFragment = window.DocumentFragment;
+ var originalAppendChild = OriginalNode.prototype.appendChild;
+ var originalCompareDocumentPosition = OriginalNode.prototype.compareDocumentPosition;
+ var originalIsEqualNode = OriginalNode.prototype.isEqualNode;
+ var originalInsertBefore = OriginalNode.prototype.insertBefore;
+ var originalRemoveChild = OriginalNode.prototype.removeChild;
+ var originalReplaceChild = OriginalNode.prototype.replaceChild;
+ var isIEOrEdge = /Trident|Edge/.test(navigator.userAgent);
+ var removeChildOriginalHelper = isIEOrEdge ? function(parent, child) {
+ try {
+ originalRemoveChild.call(parent, child);
+ } catch (ex) {
+ if (!(parent instanceof OriginalDocumentFragment)) throw ex;
+ }
+ } : function(parent, child) {
+ originalRemoveChild.call(parent, child);
+ };
+ Node.prototype = Object.create(EventTarget.prototype);
+ mixin(Node.prototype, {
+ appendChild: function(childWrapper) {
+ return this.insertBefore(childWrapper, null);
+ },
+ insertBefore: function(childWrapper, refWrapper) {
+ assertIsNodeWrapper(childWrapper);
+ var refNode;
+ if (refWrapper) {
+ if (isWrapper(refWrapper)) {
+ refNode = unwrap(refWrapper);
+ } else {
+ refNode = refWrapper;
+ refWrapper = wrap(refNode);
+ }
+ } else {
+ refWrapper = null;
+ refNode = null;
+ }
+ refWrapper && assert(refWrapper.parentNode === this);
+ var nodes;
+ var previousNode = refWrapper ? refWrapper.previousSibling : this.lastChild;
+ var useNative = !this.invalidateShadowRenderer() && !invalidateParent(childWrapper);
+ if (useNative) nodes = collectNodesNative(childWrapper); else nodes = collectNodes(childWrapper, this, previousNode, refWrapper);
+ if (useNative) {
+ ensureSameOwnerDocument(this, childWrapper);
+ clearChildNodes(this);
+ originalInsertBefore.call(unsafeUnwrap(this), unwrap(childWrapper), refNode);
+ } else {
+ if (!previousNode) this.firstChild_ = nodes[0];
+ if (!refWrapper) {
+ this.lastChild_ = nodes[nodes.length - 1];
+ if (this.firstChild_ === undefined) this.firstChild_ = this.firstChild;
+ }
+ var parentNode = refNode ? refNode.parentNode : unsafeUnwrap(this);
+ if (parentNode) {
+ originalInsertBefore.call(parentNode, unwrapNodesForInsertion(this, nodes), refNode);
+ } else {
+ adoptNodesIfNeeded(this, nodes);
+ }
+ }
+ enqueueMutation(this, "childList", {
+ addedNodes: nodes,
+ nextSibling: refWrapper,
+ previousSibling: previousNode
+ });
+ nodesWereAdded(nodes, this);
+ return childWrapper;
+ },
+ removeChild: function(childWrapper) {
+ assertIsNodeWrapper(childWrapper);
+ if (childWrapper.parentNode !== this) {
+ var found = false;
+ var childNodes = this.childNodes;
+ for (var ieChild = this.firstChild; ieChild; ieChild = ieChild.nextSibling) {
+ if (ieChild === childWrapper) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new Error("NotFoundError");
+ }
+ }
+ var childNode = unwrap(childWrapper);
+ var childWrapperNextSibling = childWrapper.nextSibling;
+ var childWrapperPreviousSibling = childWrapper.previousSibling;
+ if (this.invalidateShadowRenderer()) {
+ var thisFirstChild = this.firstChild;
+ var thisLastChild = this.lastChild;
+ var parentNode = childNode.parentNode;
+ if (parentNode) removeChildOriginalHelper(parentNode, childNode);
+ if (thisFirstChild === childWrapper) this.firstChild_ = childWrapperNextSibling;
+ if (thisLastChild === childWrapper) this.lastChild_ = childWrapperPreviousSibling;
+ if (childWrapperPreviousSibling) childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling;
+ if (childWrapperNextSibling) {
+ childWrapperNextSibling.previousSibling_ = childWrapperPreviousSibling;
+ }
+ childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.parentNode_ = undefined;
+ } else {
+ clearChildNodes(this);
+ removeChildOriginalHelper(unsafeUnwrap(this), childNode);
+ }
+ if (!surpressMutations) {
+ enqueueMutation(this, "childList", {
+ removedNodes: createOneElementNodeList(childWrapper),
+ nextSibling: childWrapperNextSibling,
+ previousSibling: childWrapperPreviousSibling
+ });
+ }
+ registerTransientObservers(this, childWrapper);
+ return childWrapper;
+ },
+ replaceChild: function(newChildWrapper, oldChildWrapper) {
+ assertIsNodeWrapper(newChildWrapper);
+ var oldChildNode;
+ if (isWrapper(oldChildWrapper)) {
+ oldChildNode = unwrap(oldChildWrapper);
+ } else {
+ oldChildNode = oldChildWrapper;
+ oldChildWrapper = wrap(oldChildNode);
+ }
+ if (oldChildWrapper.parentNode !== this) {
+ throw new Error("NotFoundError");
+ }
+ var nextNode = oldChildWrapper.nextSibling;
+ var previousNode = oldChildWrapper.previousSibling;
+ var nodes;
+ var useNative = !this.invalidateShadowRenderer() && !invalidateParent(newChildWrapper);
+ if (useNative) {
+ nodes = collectNodesNative(newChildWrapper);
+ } else {
+ if (nextNode === newChildWrapper) nextNode = newChildWrapper.nextSibling;
+ nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
+ }
+ if (!useNative) {
+ if (this.firstChild === oldChildWrapper) this.firstChild_ = nodes[0];
+ if (this.lastChild === oldChildWrapper) this.lastChild_ = nodes[nodes.length - 1];
+ oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = oldChildWrapper.parentNode_ = undefined;
+ if (oldChildNode.parentNode) {
+ originalReplaceChild.call(oldChildNode.parentNode, unwrapNodesForInsertion(this, nodes), oldChildNode);
+ }
+ } else {
+ ensureSameOwnerDocument(this, newChildWrapper);
+ clearChildNodes(this);
+ originalReplaceChild.call(unsafeUnwrap(this), unwrap(newChildWrapper), oldChildNode);
+ }
+ enqueueMutation(this, "childList", {
+ addedNodes: nodes,
+ removedNodes: createOneElementNodeList(oldChildWrapper),
+ nextSibling: nextNode,
+ previousSibling: previousNode
+ });
+ nodeWasRemoved(oldChildWrapper);
+ nodesWereAdded(nodes, this);
+ return oldChildWrapper;
+ },
+ nodeIsInserted_: function() {
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ child.nodeIsInserted_();
+ }
+ },
+ hasChildNodes: function() {
+ return this.firstChild !== null;
+ },
+ get parentNode() {
+ return this.parentNode_ !== undefined ? this.parentNode_ : wrap(unsafeUnwrap(this).parentNode);
+ },
+ get firstChild() {
+ return this.firstChild_ !== undefined ? this.firstChild_ : wrap(unsafeUnwrap(this).firstChild);
+ },
+ get lastChild() {
+ return this.lastChild_ !== undefined ? this.lastChild_ : wrap(unsafeUnwrap(this).lastChild);
+ },
+ get nextSibling() {
+ return this.nextSibling_ !== undefined ? this.nextSibling_ : wrap(unsafeUnwrap(this).nextSibling);
+ },
+ get previousSibling() {
+ return this.previousSibling_ !== undefined ? this.previousSibling_ : wrap(unsafeUnwrap(this).previousSibling);
+ },
+ get parentElement() {
+ var p = this.parentNode;
+ while (p && p.nodeType !== Node.ELEMENT_NODE) {
+ p = p.parentNode;
+ }
+ return p;
+ },
+ get textContent() {
+ var s = "";
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType != Node.COMMENT_NODE) {
+ s += child.textContent;
+ }
+ }
+ return s;
+ },
+ set textContent(textContent) {
+ if (textContent == null) textContent = "";
+ var removedNodes = snapshotNodeList(this.childNodes);
+ if (this.invalidateShadowRenderer()) {
+ removeAllChildNodes(this);
+ if (textContent !== "") {
+ var textNode = unsafeUnwrap(this).ownerDocument.createTextNode(textContent);
+ this.appendChild(textNode);
+ }
+ } else {
+ clearChildNodes(this);
+ unsafeUnwrap(this).textContent = textContent;
+ }
+ var addedNodes = snapshotNodeList(this.childNodes);
+ enqueueMutation(this, "childList", {
+ addedNodes: addedNodes,
+ removedNodes: removedNodes
+ });
+ nodesWereRemoved(removedNodes);
+ nodesWereAdded(addedNodes, this);
+ },
+ get childNodes() {
+ var wrapperList = new NodeList();
+ var i = 0;
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ wrapperList[i++] = child;
+ }
+ wrapperList.length = i;
+ return wrapperList;
+ },
+ cloneNode: function(deep) {
+ return cloneNode(this, deep);
+ },
+ contains: function(child) {
+ return contains(this, wrapIfNeeded(child));
+ },
+ compareDocumentPosition: function(otherNode) {
+ return originalCompareDocumentPosition.call(unsafeUnwrap(this), unwrapIfNeeded(otherNode));
+ },
+ isEqualNode: function(otherNode) {
+ return originalIsEqualNode.call(unsafeUnwrap(this), unwrapIfNeeded(otherNode));
+ },
+ normalize: function() {
+ var nodes = snapshotNodeList(this.childNodes);
+ var remNodes = [];
+ var s = "";
+ var modNode;
+ for (var i = 0, n; i < nodes.length; i++) {
+ n = nodes[i];
+ if (n.nodeType === Node.TEXT_NODE) {
+ if (!modNode && !n.data.length) this.removeChild(n); else if (!modNode) modNode = n; else {
+ s += n.data;
+ remNodes.push(n);
+ }
+ } else {
+ if (modNode && remNodes.length) {
+ modNode.data += s;
+ cleanupNodes(remNodes);
+ }
+ remNodes = [];
+ s = "";
+ modNode = null;
+ if (n.childNodes.length) n.normalize();
+ }
+ }
+ if (modNode && remNodes.length) {
+ modNode.data += s;
+ cleanupNodes(remNodes);
+ }
+ }
+ });
+ defineWrapGetter(Node, "ownerDocument");
+ registerWrapper(OriginalNode, Node, document.createDocumentFragment());
+ delete Node.prototype.querySelector;
+ delete Node.prototype.querySelectorAll;
+ Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
+ scope.cloneNode = cloneNode;
+ scope.nodeWasAdded = nodeWasAdded;
+ scope.nodeWasRemoved = nodeWasRemoved;
+ scope.nodesWereAdded = nodesWereAdded;
+ scope.nodesWereRemoved = nodesWereRemoved;
+ scope.originalInsertBefore = originalInsertBefore;
+ scope.originalRemoveChild = originalRemoveChild;
+ scope.snapshotNodeList = snapshotNodeList;
+ scope.wrappers.Node = Node;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLCollection = scope.wrappers.HTMLCollection;
+ var NodeList = scope.wrappers.NodeList;
+ var getTreeScope = scope.getTreeScope;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var originalDocumentQuerySelector = document.querySelector;
+ var originalElementQuerySelector = document.documentElement.querySelector;
+ var originalDocumentQuerySelectorAll = document.querySelectorAll;
+ var originalElementQuerySelectorAll = document.documentElement.querySelectorAll;
+ var originalDocumentGetElementsByTagName = document.getElementsByTagName;
+ var originalElementGetElementsByTagName = document.documentElement.getElementsByTagName;
+ var originalDocumentGetElementsByTagNameNS = document.getElementsByTagNameNS;
+ var originalElementGetElementsByTagNameNS = document.documentElement.getElementsByTagNameNS;
+ var OriginalElement = window.Element;
+ var OriginalDocument = window.HTMLDocument || window.Document;
+ function filterNodeList(list, index, result, deep) {
+ var wrappedItem = null;
+ var root = null;
+ for (var i = 0, length = list.length; i < length; i++) {
+ wrappedItem = wrap(list[i]);
+ if (!deep && (root = getTreeScope(wrappedItem).root)) {
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ continue;
+ }
+ }
+ result[index++] = wrappedItem;
+ }
+ return index;
+ }
+ function shimSelector(selector) {
+ return String(selector).replace(/\/deep\/|::shadow|>>>/g, " ");
+ }
+ function shimMatchesSelector(selector) {
+ return String(selector).replace(/:host\(([^\s]+)\)/g, "$1").replace(/([^\s]):host/g, "$1").replace(":host", "*").replace(/\^|\/shadow\/|\/shadow-deep\/|::shadow|\/deep\/|::content|>>>/g, " ");
+ }
+ function findOne(node, selector) {
+ var m, el = node.firstElementChild;
+ while (el) {
+ if (el.matches(selector)) return el;
+ m = findOne(el, selector);
+ if (m) return m;
+ el = el.nextElementSibling;
+ }
+ return null;
+ }
+ function matchesSelector(el, selector) {
+ return el.matches(selector);
+ }
+ var XHTML_NS = "http://www.w3.org/1999/xhtml";
+ function matchesTagName(el, localName, localNameLowerCase) {
+ var ln = el.localName;
+ return ln === localName || ln === localNameLowerCase && el.namespaceURI === XHTML_NS;
+ }
+ function matchesEveryThing() {
+ return true;
+ }
+ function matchesLocalNameOnly(el, ns, localName) {
+ return el.localName === localName;
+ }
+ function matchesNameSpace(el, ns) {
+ return el.namespaceURI === ns;
+ }
+ function matchesLocalNameNS(el, ns, localName) {
+ return el.namespaceURI === ns && el.localName === localName;
+ }
+ function findElements(node, index, result, p, arg0, arg1) {
+ var el = node.firstElementChild;
+ while (el) {
+ if (p(el, arg0, arg1)) result[index++] = el;
+ index = findElements(el, index, result, p, arg0, arg1);
+ el = el.nextElementSibling;
+ }
+ return index;
+ }
+ function querySelectorAllFiltered(p, index, result, selector, deep) {
+ var target = unsafeUnwrap(this);
+ var list;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findElements(this, index, result, p, selector, null);
+ } else if (target instanceof OriginalElement) {
+ list = originalElementQuerySelectorAll.call(target, selector);
+ } else if (target instanceof OriginalDocument) {
+ list = originalDocumentQuerySelectorAll.call(target, selector);
+ } else {
+ return findElements(this, index, result, p, selector, null);
+ }
+ return filterNodeList(list, index, result, deep);
+ }
+ var SelectorsInterface = {
+ querySelector: function(selector) {
+ var shimmed = shimSelector(selector);
+ var deep = shimmed !== selector;
+ selector = shimmed;
+ var target = unsafeUnwrap(this);
+ var wrappedItem;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findOne(this, selector);
+ } else if (target instanceof OriginalElement) {
+ wrappedItem = wrap(originalElementQuerySelector.call(target, selector));
+ } else if (target instanceof OriginalDocument) {
+ wrappedItem = wrap(originalDocumentQuerySelector.call(target, selector));
+ } else {
+ return findOne(this, selector);
+ }
+ if (!wrappedItem) {
+ return wrappedItem;
+ } else if (!deep && (root = getTreeScope(wrappedItem).root)) {
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findOne(this, selector);
+ }
+ }
+ return wrappedItem;
+ },
+ querySelectorAll: function(selector) {
+ var shimmed = shimSelector(selector);
+ var deep = shimmed !== selector;
+ selector = shimmed;
+ var result = new NodeList();
+ result.length = querySelectorAllFiltered.call(this, matchesSelector, 0, result, selector, deep);
+ return result;
+ }
+ };
+ var MatchesInterface = {
+ matches: function(selector) {
+ selector = shimMatchesSelector(selector);
+ return scope.originalMatches.call(unsafeUnwrap(this), selector);
+ }
+ };
+ function getElementsByTagNameFiltered(p, index, result, localName, lowercase) {
+ var target = unsafeUnwrap(this);
+ var list;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findElements(this, index, result, p, localName, lowercase);
+ } else if (target instanceof OriginalElement) {
+ list = originalElementGetElementsByTagName.call(target, localName, lowercase);
+ } else if (target instanceof OriginalDocument) {
+ list = originalDocumentGetElementsByTagName.call(target, localName, lowercase);
+ } else {
+ return findElements(this, index, result, p, localName, lowercase);
+ }
+ return filterNodeList(list, index, result, false);
+ }
+ function getElementsByTagNameNSFiltered(p, index, result, ns, localName) {
+ var target = unsafeUnwrap(this);
+ var list;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findElements(this, index, result, p, ns, localName);
+ } else if (target instanceof OriginalElement) {
+ list = originalElementGetElementsByTagNameNS.call(target, ns, localName);
+ } else if (target instanceof OriginalDocument) {
+ list = originalDocumentGetElementsByTagNameNS.call(target, ns, localName);
+ } else {
+ return findElements(this, index, result, p, ns, localName);
+ }
+ return filterNodeList(list, index, result, false);
+ }
+ var GetElementsByInterface = {
+ getElementsByTagName: function(localName) {
+ var result = new HTMLCollection();
+ var match = localName === "*" ? matchesEveryThing : matchesTagName;
+ result.length = getElementsByTagNameFiltered.call(this, match, 0, result, localName, localName.toLowerCase());
+ return result;
+ },
+ getElementsByClassName: function(className) {
+ return this.querySelectorAll("." + className);
+ },
+ getElementsByTagNameNS: function(ns, localName) {
+ var result = new HTMLCollection();
+ var match = null;
+ if (ns === "*") {
+ match = localName === "*" ? matchesEveryThing : matchesLocalNameOnly;
+ } else {
+ match = localName === "*" ? matchesNameSpace : matchesLocalNameNS;
+ }
+ result.length = getElementsByTagNameNSFiltered.call(this, match, 0, result, ns || null, localName);
+ return result;
+ }
+ };
+ scope.GetElementsByInterface = GetElementsByInterface;
+ scope.SelectorsInterface = SelectorsInterface;
+ scope.MatchesInterface = MatchesInterface;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var NodeList = scope.wrappers.NodeList;
+ function forwardElement(node) {
+ while (node && node.nodeType !== Node.ELEMENT_NODE) {
+ node = node.nextSibling;
+ }
+ return node;
+ }
+ function backwardsElement(node) {
+ while (node && node.nodeType !== Node.ELEMENT_NODE) {
+ node = node.previousSibling;
+ }
+ return node;
+ }
+ var ParentNodeInterface = {
+ get firstElementChild() {
+ return forwardElement(this.firstChild);
+ },
+ get lastElementChild() {
+ return backwardsElement(this.lastChild);
+ },
+ get childElementCount() {
+ var count = 0;
+ for (var child = this.firstElementChild; child; child = child.nextElementSibling) {
+ count++;
+ }
+ return count;
+ },
+ get children() {
+ var wrapperList = new NodeList();
+ var i = 0;
+ for (var child = this.firstElementChild; child; child = child.nextElementSibling) {
+ wrapperList[i++] = child;
+ }
+ wrapperList.length = i;
+ return wrapperList;
+ },
+ remove: function() {
+ var p = this.parentNode;
+ if (p) p.removeChild(this);
+ }
+ };
+ var ChildNodeInterface = {
+ get nextElementSibling() {
+ return forwardElement(this.nextSibling);
+ },
+ get previousElementSibling() {
+ return backwardsElement(this.previousSibling);
+ }
+ };
+ var NonElementParentNodeInterface = {
+ getElementById: function(id) {
+ if (/[ \t\n\r\f]/.test(id)) return null;
+ return this.querySelector('[id="' + id + '"]');
+ }
+ };
+ scope.ChildNodeInterface = ChildNodeInterface;
+ scope.NonElementParentNodeInterface = NonElementParentNodeInterface;
+ scope.ParentNodeInterface = ParentNodeInterface;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var ChildNodeInterface = scope.ChildNodeInterface;
+ var Node = scope.wrappers.Node;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var OriginalCharacterData = window.CharacterData;
+ function CharacterData(node) {
+ Node.call(this, node);
+ }
+ CharacterData.prototype = Object.create(Node.prototype);
+ mixin(CharacterData.prototype, {
+ get nodeValue() {
+ return this.data;
+ },
+ set nodeValue(data) {
+ this.data = data;
+ },
+ get textContent() {
+ return this.data;
+ },
+ set textContent(value) {
+ this.data = value;
+ },
+ get data() {
+ return unsafeUnwrap(this).data;
+ },
+ set data(value) {
+ var oldValue = unsafeUnwrap(this).data;
+ enqueueMutation(this, "characterData", {
+ oldValue: oldValue
+ });
+ unsafeUnwrap(this).data = value;
+ }
+ });
+ mixin(CharacterData.prototype, ChildNodeInterface);
+ registerWrapper(OriginalCharacterData, CharacterData, document.createTextNode(""));
+ scope.wrappers.CharacterData = CharacterData;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var CharacterData = scope.wrappers.CharacterData;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ function toUInt32(x) {
+ return x >>> 0;
+ }
+ var OriginalText = window.Text;
+ function Text(node) {
+ CharacterData.call(this, node);
+ }
+ Text.prototype = Object.create(CharacterData.prototype);
+ mixin(Text.prototype, {
+ splitText: function(offset) {
+ offset = toUInt32(offset);
+ var s = this.data;
+ if (offset > s.length) throw new Error("IndexSizeError");
+ var head = s.slice(0, offset);
+ var tail = s.slice(offset);
+ this.data = head;
+ var newTextNode = this.ownerDocument.createTextNode(tail);
+ if (this.parentNode) this.parentNode.insertBefore(newTextNode, this.nextSibling);
+ return newTextNode;
+ }
+ });
+ registerWrapper(OriginalText, Text, document.createTextNode(""));
+ scope.wrappers.Text = Text;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ if (!window.DOMTokenList) {
+ console.warn("Missing DOMTokenList prototype, please include a " + "compatible classList polyfill such as http://goo.gl/uTcepH.");
+ return;
+ }
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var enqueueMutation = scope.enqueueMutation;
+ function getClass(el) {
+ return unsafeUnwrap(el).getAttribute("class");
+ }
+ function enqueueClassAttributeChange(el, oldValue) {
+ enqueueMutation(el, "attributes", {
+ name: "class",
+ namespace: null,
+ oldValue: oldValue
+ });
+ }
+ function invalidateClass(el) {
+ scope.invalidateRendererBasedOnAttribute(el, "class");
+ }
+ function changeClass(tokenList, method, args) {
+ var ownerElement = tokenList.ownerElement_;
+ if (ownerElement == null) {
+ return method.apply(tokenList, args);
+ }
+ var oldValue = getClass(ownerElement);
+ var retv = method.apply(tokenList, args);
+ if (getClass(ownerElement) !== oldValue) {
+ enqueueClassAttributeChange(ownerElement, oldValue);
+ invalidateClass(ownerElement);
+ }
+ return retv;
+ }
+ var oldAdd = DOMTokenList.prototype.add;
+ DOMTokenList.prototype.add = function() {
+ changeClass(this, oldAdd, arguments);
+ };
+ var oldRemove = DOMTokenList.prototype.remove;
+ DOMTokenList.prototype.remove = function() {
+ changeClass(this, oldRemove, arguments);
+ };
+ var oldToggle = DOMTokenList.prototype.toggle;
+ DOMTokenList.prototype.toggle = function() {
+ return changeClass(this, oldToggle, arguments);
+ };
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var ChildNodeInterface = scope.ChildNodeInterface;
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var Node = scope.wrappers.Node;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var MatchesInterface = scope.MatchesInterface;
+ var addWrapNodeListMethod = scope.addWrapNodeListMethod;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var oneOf = scope.oneOf;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrappers = scope.wrappers;
+ var OriginalElement = window.Element;
+ var matchesNames = [ "matches", "mozMatchesSelector", "msMatchesSelector", "webkitMatchesSelector" ].filter(function(name) {
+ return OriginalElement.prototype[name];
+ });
+ var matchesName = matchesNames[0];
+ var originalMatches = OriginalElement.prototype[matchesName];
+ function invalidateRendererBasedOnAttribute(element, name) {
+ var p = element.parentNode;
+ if (!p || !p.shadowRoot) return;
+ var renderer = scope.getRendererForHost(p);
+ if (renderer.dependsOnAttribute(name)) renderer.invalidate();
+ }
+ function enqueAttributeChange(element, name, oldValue) {
+ enqueueMutation(element, "attributes", {
+ name: name,
+ namespace: null,
+ oldValue: oldValue
+ });
+ }
+ var classListTable = new WeakMap();
+ function Element(node) {
+ Node.call(this, node);
+ }
+ Element.prototype = Object.create(Node.prototype);
+ mixin(Element.prototype, {
+ createShadowRoot: function() {
+ var newShadowRoot = new wrappers.ShadowRoot(this);
+ unsafeUnwrap(this).polymerShadowRoot_ = newShadowRoot;
+ var renderer = scope.getRendererForHost(this);
+ renderer.invalidate();
+ return newShadowRoot;
+ },
+ get shadowRoot() {
+ return unsafeUnwrap(this).polymerShadowRoot_ || null;
+ },
+ setAttribute: function(name, value) {
+ var oldValue = unsafeUnwrap(this).getAttribute(name);
+ unsafeUnwrap(this).setAttribute(name, value);
+ enqueAttributeChange(this, name, oldValue);
+ invalidateRendererBasedOnAttribute(this, name);
+ },
+ removeAttribute: function(name) {
+ var oldValue = unsafeUnwrap(this).getAttribute(name);
+ unsafeUnwrap(this).removeAttribute(name);
+ enqueAttributeChange(this, name, oldValue);
+ invalidateRendererBasedOnAttribute(this, name);
+ },
+ get classList() {
+ var list = classListTable.get(this);
+ if (!list) {
+ list = unsafeUnwrap(this).classList;
+ if (!list) return;
+ list.ownerElement_ = this;
+ classListTable.set(this, list);
+ }
+ return list;
+ },
+ get className() {
+ return unsafeUnwrap(this).className;
+ },
+ set className(v) {
+ this.setAttribute("class", v);
+ },
+ get id() {
+ return unsafeUnwrap(this).id;
+ },
+ set id(v) {
+ this.setAttribute("id", v);
+ }
+ });
+ matchesNames.forEach(function(name) {
+ if (name !== "matches") {
+ Element.prototype[name] = function(selector) {
+ return this.matches(selector);
+ };
+ }
+ });
+ if (OriginalElement.prototype.webkitCreateShadowRoot) {
+ Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot;
+ }
+ mixin(Element.prototype, ChildNodeInterface);
+ mixin(Element.prototype, GetElementsByInterface);
+ mixin(Element.prototype, ParentNodeInterface);
+ mixin(Element.prototype, SelectorsInterface);
+ mixin(Element.prototype, MatchesInterface);
+ registerWrapper(OriginalElement, Element, document.createElementNS(null, "x"));
+ scope.invalidateRendererBasedOnAttribute = invalidateRendererBasedOnAttribute;
+ scope.matchesNames = matchesNames;
+ scope.originalMatches = originalMatches;
+ scope.wrappers.Element = Element;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var Element = scope.wrappers.Element;
+ var defineGetter = scope.defineGetter;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var nodesWereAdded = scope.nodesWereAdded;
+ var nodesWereRemoved = scope.nodesWereRemoved;
+ var registerWrapper = scope.registerWrapper;
+ var snapshotNodeList = scope.snapshotNodeList;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrappers = scope.wrappers;
+ var escapeAttrRegExp = /[&\u00A0"]/g;
+ var escapeDataRegExp = /[&\u00A0<>]/g;
+ function escapeReplace(c) {
+ switch (c) {
+ case "&":
+ return "&amp;";
+
+ case "<":
+ return "&lt;";
+
+ case ">":
+ return "&gt;";
+
+ case '"':
+ return "&quot;";
+
+ case " ":
+ return "&nbsp;";
+ }
+ }
+ function escapeAttr(s) {
+ return s.replace(escapeAttrRegExp, escapeReplace);
+ }
+ function escapeData(s) {
+ return s.replace(escapeDataRegExp, escapeReplace);
+ }
+ function makeSet(arr) {
+ var set = {};
+ for (var i = 0; i < arr.length; i++) {
+ set[arr[i]] = true;
+ }
+ return set;
+ }
+ var voidElements = makeSet([ "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr" ]);
+ var plaintextParents = makeSet([ "style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript" ]);
+ var XHTML_NS = "http://www.w3.org/1999/xhtml";
+ function needsSelfClosingSlash(node) {
+ if (node.namespaceURI !== XHTML_NS) return true;
+ var doctype = node.ownerDocument.doctype;
+ return doctype && doctype.publicId && doctype.systemId;
+ }
+ function getOuterHTML(node, parentNode) {
+ switch (node.nodeType) {
+ case Node.ELEMENT_NODE:
+ var tagName = node.tagName.toLowerCase();
+ var s = "<" + tagName;
+ var attrs = node.attributes;
+ for (var i = 0, attr; attr = attrs[i]; i++) {
+ s += " " + attr.name + '="' + escapeAttr(attr.value) + '"';
+ }
+ if (voidElements[tagName]) {
+ if (needsSelfClosingSlash(node)) s += "/";
+ return s + ">";
+ }
+ return s + ">" + getInnerHTML(node) + "</" + tagName + ">";
+
+ case Node.TEXT_NODE:
+ var data = node.data;
+ if (parentNode && plaintextParents[parentNode.localName]) return data;
+ return escapeData(data);
+
+ case Node.COMMENT_NODE:
+ return "<!--" + node.data + "-->";
+
+ default:
+ console.error(node);
+ throw new Error("not implemented");
+ }
+ }
+ function getInnerHTML(node) {
+ if (node instanceof wrappers.HTMLTemplateElement) node = node.content;
+ var s = "";
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ s += getOuterHTML(child, node);
+ }
+ return s;
+ }
+ function setInnerHTML(node, value, opt_tagName) {
+ var tagName = opt_tagName || "div";
+ node.textContent = "";
+ var tempElement = unwrap(node.ownerDocument.createElement(tagName));
+ tempElement.innerHTML = value;
+ var firstChild;
+ while (firstChild = tempElement.firstChild) {
+ node.appendChild(wrap(firstChild));
+ }
+ }
+ var oldIe = /MSIE/.test(navigator.userAgent);
+ var OriginalHTMLElement = window.HTMLElement;
+ var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
+ function HTMLElement(node) {
+ Element.call(this, node);
+ }
+ HTMLElement.prototype = Object.create(Element.prototype);
+ mixin(HTMLElement.prototype, {
+ get innerHTML() {
+ return getInnerHTML(this);
+ },
+ set innerHTML(value) {
+ if (oldIe && plaintextParents[this.localName]) {
+ this.textContent = value;
+ return;
+ }
+ var removedNodes = snapshotNodeList(this.childNodes);
+ if (this.invalidateShadowRenderer()) {
+ if (this instanceof wrappers.HTMLTemplateElement) setInnerHTML(this.content, value); else setInnerHTML(this, value, this.tagName);
+ } else if (!OriginalHTMLTemplateElement && this instanceof wrappers.HTMLTemplateElement) {
+ setInnerHTML(this.content, value);
+ } else {
+ unsafeUnwrap(this).innerHTML = value;
+ }
+ var addedNodes = snapshotNodeList(this.childNodes);
+ enqueueMutation(this, "childList", {
+ addedNodes: addedNodes,
+ removedNodes: removedNodes
+ });
+ nodesWereRemoved(removedNodes);
+ nodesWereAdded(addedNodes, this);
+ },
+ get outerHTML() {
+ return getOuterHTML(this, this.parentNode);
+ },
+ set outerHTML(value) {
+ var p = this.parentNode;
+ if (p) {
+ p.invalidateShadowRenderer();
+ var df = frag(p, value);
+ p.replaceChild(df, this);
+ }
+ },
+ insertAdjacentHTML: function(position, text) {
+ var contextElement, refNode;
+ switch (String(position).toLowerCase()) {
+ case "beforebegin":
+ contextElement = this.parentNode;
+ refNode = this;
+ break;
+
+ case "afterend":
+ contextElement = this.parentNode;
+ refNode = this.nextSibling;
+ break;
+
+ case "afterbegin":
+ contextElement = this;
+ refNode = this.firstChild;
+ break;
+
+ case "beforeend":
+ contextElement = this;
+ refNode = null;
+ break;
+
+ default:
+ return;
+ }
+ var df = frag(contextElement, text);
+ contextElement.insertBefore(df, refNode);
+ },
+ get hidden() {
+ return this.hasAttribute("hidden");
+ },
+ set hidden(v) {
+ if (v) {
+ this.setAttribute("hidden", "");
+ } else {
+ this.removeAttribute("hidden");
+ }
+ }
+ });
+ function frag(contextElement, html) {
+ var p = unwrap(contextElement.cloneNode(false));
+ p.innerHTML = html;
+ var df = unwrap(document.createDocumentFragment());
+ var c;
+ while (c = p.firstChild) {
+ df.appendChild(c);
+ }
+ return wrap(df);
+ }
+ function getter(name) {
+ return function() {
+ scope.renderAllPending();
+ return unsafeUnwrap(this)[name];
+ };
+ }
+ function getterRequiresRendering(name) {
+ defineGetter(HTMLElement, name, getter(name));
+ }
+ [ "clientHeight", "clientLeft", "clientTop", "clientWidth", "offsetHeight", "offsetLeft", "offsetTop", "offsetWidth", "scrollHeight", "scrollWidth" ].forEach(getterRequiresRendering);
+ function getterAndSetterRequiresRendering(name) {
+ Object.defineProperty(HTMLElement.prototype, name, {
+ get: getter(name),
+ set: function(v) {
+ scope.renderAllPending();
+ unsafeUnwrap(this)[name] = v;
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+ [ "scrollLeft", "scrollTop" ].forEach(getterAndSetterRequiresRendering);
+ function methodRequiresRendering(name) {
+ Object.defineProperty(HTMLElement.prototype, name, {
+ value: function() {
+ scope.renderAllPending();
+ return unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments);
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+ [ "focus", "getBoundingClientRect", "getClientRects", "scrollIntoView" ].forEach(methodRequiresRendering);
+ registerWrapper(OriginalHTMLElement, HTMLElement, document.createElement("b"));
+ scope.wrappers.HTMLElement = HTMLElement;
+ scope.getInnerHTML = getInnerHTML;
+ scope.setInnerHTML = setInnerHTML;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLCanvasElement = window.HTMLCanvasElement;
+ function HTMLCanvasElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLCanvasElement.prototype, {
+ getContext: function() {
+ var context = unsafeUnwrap(this).getContext.apply(unsafeUnwrap(this), arguments);
+ return context && wrap(context);
+ }
+ });
+ registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement, document.createElement("canvas"));
+ scope.wrappers.HTMLCanvasElement = HTMLCanvasElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLContentElement = window.HTMLContentElement;
+ function HTMLContentElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLContentElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLContentElement.prototype, {
+ constructor: HTMLContentElement,
+ get select() {
+ return this.getAttribute("select");
+ },
+ set select(value) {
+ this.setAttribute("select", value);
+ },
+ setAttribute: function(n, v) {
+ HTMLElement.prototype.setAttribute.call(this, n, v);
+ if (String(n).toLowerCase() === "select") this.invalidateShadowRenderer(true);
+ }
+ });
+ if (OriginalHTMLContentElement) registerWrapper(OriginalHTMLContentElement, HTMLContentElement);
+ scope.wrappers.HTMLContentElement = HTMLContentElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var unwrap = scope.unwrap;
+ var OriginalHTMLFormElement = window.HTMLFormElement;
+ function HTMLFormElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLFormElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLFormElement.prototype, {
+ get elements() {
+ return wrapHTMLCollection(unwrap(this).elements);
+ }
+ });
+ registerWrapper(OriginalHTMLFormElement, HTMLFormElement, document.createElement("form"));
+ scope.wrappers.HTMLFormElement = HTMLFormElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var rewrap = scope.rewrap;
+ var OriginalHTMLImageElement = window.HTMLImageElement;
+ function HTMLImageElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLImageElement.prototype = Object.create(HTMLElement.prototype);
+ registerWrapper(OriginalHTMLImageElement, HTMLImageElement, document.createElement("img"));
+ function Image(width, height) {
+ if (!(this instanceof Image)) {
+ throw new TypeError("DOM object constructor cannot be called as a function.");
+ }
+ var node = unwrap(document.createElement("img"));
+ HTMLElement.call(this, node);
+ rewrap(node, this);
+ if (width !== undefined) node.width = width;
+ if (height !== undefined) node.height = height;
+ }
+ Image.prototype = HTMLImageElement.prototype;
+ scope.wrappers.HTMLImageElement = HTMLImageElement;
+ scope.wrappers.Image = Image;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var NodeList = scope.wrappers.NodeList;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLShadowElement = window.HTMLShadowElement;
+ function HTMLShadowElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLShadowElement.prototype = Object.create(HTMLElement.prototype);
+ HTMLShadowElement.prototype.constructor = HTMLShadowElement;
+ if (OriginalHTMLShadowElement) registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement);
+ scope.wrappers.HTMLShadowElement = HTMLShadowElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var contentTable = new WeakMap();
+ var templateContentsOwnerTable = new WeakMap();
+ function getTemplateContentsOwner(doc) {
+ if (!doc.defaultView) return doc;
+ var d = templateContentsOwnerTable.get(doc);
+ if (!d) {
+ d = doc.implementation.createHTMLDocument("");
+ while (d.lastChild) {
+ d.removeChild(d.lastChild);
+ }
+ templateContentsOwnerTable.set(doc, d);
+ }
+ return d;
+ }
+ function extractContent(templateElement) {
+ var doc = getTemplateContentsOwner(templateElement.ownerDocument);
+ var df = unwrap(doc.createDocumentFragment());
+ var child;
+ while (child = templateElement.firstChild) {
+ df.appendChild(child);
+ }
+ return df;
+ }
+ var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
+ function HTMLTemplateElement(node) {
+ HTMLElement.call(this, node);
+ if (!OriginalHTMLTemplateElement) {
+ var content = extractContent(node);
+ contentTable.set(this, wrap(content));
+ }
+ }
+ HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTemplateElement.prototype, {
+ constructor: HTMLTemplateElement,
+ get content() {
+ if (OriginalHTMLTemplateElement) return wrap(unsafeUnwrap(this).content);
+ return contentTable.get(this);
+ }
+ });
+ if (OriginalHTMLTemplateElement) registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement);
+ scope.wrappers.HTMLTemplateElement = HTMLTemplateElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLMediaElement = window.HTMLMediaElement;
+ if (!OriginalHTMLMediaElement) return;
+ function HTMLMediaElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLMediaElement.prototype = Object.create(HTMLElement.prototype);
+ registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement, document.createElement("audio"));
+ scope.wrappers.HTMLMediaElement = HTMLMediaElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLMediaElement = scope.wrappers.HTMLMediaElement;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var rewrap = scope.rewrap;
+ var OriginalHTMLAudioElement = window.HTMLAudioElement;
+ if (!OriginalHTMLAudioElement) return;
+ function HTMLAudioElement(node) {
+ HTMLMediaElement.call(this, node);
+ }
+ HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype);
+ registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement, document.createElement("audio"));
+ function Audio(src) {
+ if (!(this instanceof Audio)) {
+ throw new TypeError("DOM object constructor cannot be called as a function.");
+ }
+ var node = unwrap(document.createElement("audio"));
+ HTMLMediaElement.call(this, node);
+ rewrap(node, this);
+ node.setAttribute("preload", "auto");
+ if (src !== undefined) node.setAttribute("src", src);
+ }
+ Audio.prototype = HTMLAudioElement.prototype;
+ scope.wrappers.HTMLAudioElement = HTMLAudioElement;
+ scope.wrappers.Audio = Audio;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var rewrap = scope.rewrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLOptionElement = window.HTMLOptionElement;
+ function trimText(s) {
+ return s.replace(/\s+/g, " ").trim();
+ }
+ function HTMLOptionElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLOptionElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLOptionElement.prototype, {
+ get text() {
+ return trimText(this.textContent);
+ },
+ set text(value) {
+ this.textContent = trimText(String(value));
+ },
+ get form() {
+ return wrap(unwrap(this).form);
+ }
+ });
+ registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement, document.createElement("option"));
+ function Option(text, value, defaultSelected, selected) {
+ if (!(this instanceof Option)) {
+ throw new TypeError("DOM object constructor cannot be called as a function.");
+ }
+ var node = unwrap(document.createElement("option"));
+ HTMLElement.call(this, node);
+ rewrap(node, this);
+ if (text !== undefined) node.text = text;
+ if (value !== undefined) node.setAttribute("value", value);
+ if (defaultSelected === true) node.setAttribute("selected", "");
+ node.selected = selected === true;
+ }
+ Option.prototype = HTMLOptionElement.prototype;
+ scope.wrappers.HTMLOptionElement = HTMLOptionElement;
+ scope.wrappers.Option = Option;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLSelectElement = window.HTMLSelectElement;
+ function HTMLSelectElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLSelectElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLSelectElement.prototype, {
+ add: function(element, before) {
+ if (typeof before === "object") before = unwrap(before);
+ unwrap(this).add(unwrap(element), before);
+ },
+ remove: function(indexOrNode) {
+ if (indexOrNode === undefined) {
+ HTMLElement.prototype.remove.call(this);
+ return;
+ }
+ if (typeof indexOrNode === "object") indexOrNode = unwrap(indexOrNode);
+ unwrap(this).remove(indexOrNode);
+ },
+ get form() {
+ return wrap(unwrap(this).form);
+ }
+ });
+ registerWrapper(OriginalHTMLSelectElement, HTMLSelectElement, document.createElement("select"));
+ scope.wrappers.HTMLSelectElement = HTMLSelectElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var OriginalHTMLTableElement = window.HTMLTableElement;
+ function HTMLTableElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLTableElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTableElement.prototype, {
+ get caption() {
+ return wrap(unwrap(this).caption);
+ },
+ createCaption: function() {
+ return wrap(unwrap(this).createCaption());
+ },
+ get tHead() {
+ return wrap(unwrap(this).tHead);
+ },
+ createTHead: function() {
+ return wrap(unwrap(this).createTHead());
+ },
+ createTFoot: function() {
+ return wrap(unwrap(this).createTFoot());
+ },
+ get tFoot() {
+ return wrap(unwrap(this).tFoot);
+ },
+ get tBodies() {
+ return wrapHTMLCollection(unwrap(this).tBodies);
+ },
+ createTBody: function() {
+ return wrap(unwrap(this).createTBody());
+ },
+ get rows() {
+ return wrapHTMLCollection(unwrap(this).rows);
+ },
+ insertRow: function(index) {
+ return wrap(unwrap(this).insertRow(index));
+ }
+ });
+ registerWrapper(OriginalHTMLTableElement, HTMLTableElement, document.createElement("table"));
+ scope.wrappers.HTMLTableElement = HTMLTableElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLTableSectionElement = window.HTMLTableSectionElement;
+ function HTMLTableSectionElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLTableSectionElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTableSectionElement.prototype, {
+ constructor: HTMLTableSectionElement,
+ get rows() {
+ return wrapHTMLCollection(unwrap(this).rows);
+ },
+ insertRow: function(index) {
+ return wrap(unwrap(this).insertRow(index));
+ }
+ });
+ registerWrapper(OriginalHTMLTableSectionElement, HTMLTableSectionElement, document.createElement("thead"));
+ scope.wrappers.HTMLTableSectionElement = HTMLTableSectionElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLTableRowElement = window.HTMLTableRowElement;
+ function HTMLTableRowElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLTableRowElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTableRowElement.prototype, {
+ get cells() {
+ return wrapHTMLCollection(unwrap(this).cells);
+ },
+ insertCell: function(index) {
+ return wrap(unwrap(this).insertCell(index));
+ }
+ });
+ registerWrapper(OriginalHTMLTableRowElement, HTMLTableRowElement, document.createElement("tr"));
+ scope.wrappers.HTMLTableRowElement = HTMLTableRowElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLContentElement = scope.wrappers.HTMLContentElement;
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+ var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLUnknownElement = window.HTMLUnknownElement;
+ function HTMLUnknownElement(node) {
+ switch (node.localName) {
+ case "content":
+ return new HTMLContentElement(node);
+
+ case "shadow":
+ return new HTMLShadowElement(node);
+
+ case "template":
+ return new HTMLTemplateElement(node);
+ }
+ HTMLElement.call(this, node);
+ }
+ HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype);
+ registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement);
+ scope.wrappers.HTMLUnknownElement = HTMLUnknownElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var Element = scope.wrappers.Element;
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var mixin = scope.mixin;
+ var SVG_NS = "http://www.w3.org/2000/svg";
+ var OriginalSVGElement = window.SVGElement;
+ var svgTitleElement = document.createElementNS(SVG_NS, "title");
+ if (!("classList" in svgTitleElement)) {
+ var descr = Object.getOwnPropertyDescriptor(Element.prototype, "classList");
+ Object.defineProperty(HTMLElement.prototype, "classList", descr);
+ delete Element.prototype.classList;
+ }
+ function SVGElement(node) {
+ Element.call(this, node);
+ }
+ SVGElement.prototype = Object.create(Element.prototype);
+ mixin(SVGElement.prototype, {
+ get ownerSVGElement() {
+ return wrap(unsafeUnwrap(this).ownerSVGElement);
+ }
+ });
+ registerWrapper(OriginalSVGElement, SVGElement, document.createElementNS(SVG_NS, "title"));
+ scope.wrappers.SVGElement = SVGElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalSVGUseElement = window.SVGUseElement;
+ var SVG_NS = "http://www.w3.org/2000/svg";
+ var gWrapper = wrap(document.createElementNS(SVG_NS, "g"));
+ var useElement = document.createElementNS(SVG_NS, "use");
+ var SVGGElement = gWrapper.constructor;
+ var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype);
+ var parentInterface = parentInterfacePrototype.constructor;
+ function SVGUseElement(impl) {
+ parentInterface.call(this, impl);
+ }
+ SVGUseElement.prototype = Object.create(parentInterfacePrototype);
+ if ("instanceRoot" in useElement) {
+ mixin(SVGUseElement.prototype, {
+ get instanceRoot() {
+ return wrap(unwrap(this).instanceRoot);
+ },
+ get animatedInstanceRoot() {
+ return wrap(unwrap(this).animatedInstanceRoot);
+ }
+ });
+ }
+ registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement);
+ scope.wrappers.SVGUseElement = SVGUseElement;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var EventTarget = scope.wrappers.EventTarget;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var OriginalSVGElementInstance = window.SVGElementInstance;
+ if (!OriginalSVGElementInstance) return;
+ function SVGElementInstance(impl) {
+ EventTarget.call(this, impl);
+ }
+ SVGElementInstance.prototype = Object.create(EventTarget.prototype);
+ mixin(SVGElementInstance.prototype, {
+ get correspondingElement() {
+ return wrap(unsafeUnwrap(this).correspondingElement);
+ },
+ get correspondingUseElement() {
+ return wrap(unsafeUnwrap(this).correspondingUseElement);
+ },
+ get parentNode() {
+ return wrap(unsafeUnwrap(this).parentNode);
+ },
+ get childNodes() {
+ throw new Error("Not implemented");
+ },
+ get firstChild() {
+ return wrap(unsafeUnwrap(this).firstChild);
+ },
+ get lastChild() {
+ return wrap(unsafeUnwrap(this).lastChild);
+ },
+ get previousSibling() {
+ return wrap(unsafeUnwrap(this).previousSibling);
+ },
+ get nextSibling() {
+ return wrap(unsafeUnwrap(this).nextSibling);
+ }
+ });
+ registerWrapper(OriginalSVGElementInstance, SVGElementInstance);
+ scope.wrappers.SVGElementInstance = SVGElementInstance;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
+ function CanvasRenderingContext2D(impl) {
+ setWrapper(impl, this);
+ }
+ mixin(CanvasRenderingContext2D.prototype, {
+ get canvas() {
+ return wrap(unsafeUnwrap(this).canvas);
+ },
+ drawImage: function() {
+ arguments[0] = unwrapIfNeeded(arguments[0]);
+ unsafeUnwrap(this).drawImage.apply(unsafeUnwrap(this), arguments);
+ },
+ createPattern: function() {
+ arguments[0] = unwrap(arguments[0]);
+ return unsafeUnwrap(this).createPattern.apply(unsafeUnwrap(this), arguments);
+ }
+ });
+ registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D, document.createElement("canvas").getContext("2d"));
+ scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var addForwardingProperties = scope.addForwardingProperties;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
+ if (!OriginalWebGLRenderingContext) return;
+ function WebGLRenderingContext(impl) {
+ setWrapper(impl, this);
+ }
+ mixin(WebGLRenderingContext.prototype, {
+ get canvas() {
+ return wrap(unsafeUnwrap(this).canvas);
+ },
+ texImage2D: function() {
+ arguments[5] = unwrapIfNeeded(arguments[5]);
+ unsafeUnwrap(this).texImage2D.apply(unsafeUnwrap(this), arguments);
+ },
+ texSubImage2D: function() {
+ arguments[6] = unwrapIfNeeded(arguments[6]);
+ unsafeUnwrap(this).texSubImage2D.apply(unsafeUnwrap(this), arguments);
+ }
+ });
+ var OriginalWebGLRenderingContextBase = Object.getPrototypeOf(OriginalWebGLRenderingContext.prototype);
+ if (OriginalWebGLRenderingContextBase !== Object.prototype) {
+ addForwardingProperties(OriginalWebGLRenderingContextBase, WebGLRenderingContext.prototype);
+ }
+ var instanceProperties = /WebKit/.test(navigator.userAgent) ? {
+ drawingBufferHeight: null,
+ drawingBufferWidth: null
+ } : {};
+ registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext, instanceProperties);
+ scope.wrappers.WebGLRenderingContext = WebGLRenderingContext;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var Node = scope.wrappers.Node;
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var NonElementParentNodeInterface = scope.NonElementParentNodeInterface;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var mixin = scope.mixin;
+ var registerObject = scope.registerObject;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalDocumentFragment = window.DocumentFragment;
+ function DocumentFragment(node) {
+ Node.call(this, node);
+ }
+ DocumentFragment.prototype = Object.create(Node.prototype);
+ mixin(DocumentFragment.prototype, ParentNodeInterface);
+ mixin(DocumentFragment.prototype, SelectorsInterface);
+ mixin(DocumentFragment.prototype, GetElementsByInterface);
+ mixin(DocumentFragment.prototype, NonElementParentNodeInterface);
+ registerWrapper(OriginalDocumentFragment, DocumentFragment, document.createDocumentFragment());
+ scope.wrappers.DocumentFragment = DocumentFragment;
+ var Comment = registerObject(document.createComment(""));
+ scope.wrappers.Comment = Comment;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var DocumentFragment = scope.wrappers.DocumentFragment;
+ var TreeScope = scope.TreeScope;
+ var elementFromPoint = scope.elementFromPoint;
+ var getInnerHTML = scope.getInnerHTML;
+ var getTreeScope = scope.getTreeScope;
+ var mixin = scope.mixin;
+ var rewrap = scope.rewrap;
+ var setInnerHTML = scope.setInnerHTML;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var shadowHostTable = new WeakMap();
+ var nextOlderShadowTreeTable = new WeakMap();
+ function ShadowRoot(hostWrapper) {
+ var node = unwrap(unsafeUnwrap(hostWrapper).ownerDocument.createDocumentFragment());
+ DocumentFragment.call(this, node);
+ rewrap(node, this);
+ var oldShadowRoot = hostWrapper.shadowRoot;
+ nextOlderShadowTreeTable.set(this, oldShadowRoot);
+ this.treeScope_ = new TreeScope(this, getTreeScope(oldShadowRoot || hostWrapper));
+ shadowHostTable.set(this, hostWrapper);
+ }
+ ShadowRoot.prototype = Object.create(DocumentFragment.prototype);
+ mixin(ShadowRoot.prototype, {
+ constructor: ShadowRoot,
+ get innerHTML() {
+ return getInnerHTML(this);
+ },
+ set innerHTML(value) {
+ setInnerHTML(this, value);
+ this.invalidateShadowRenderer();
+ },
+ get olderShadowRoot() {
+ return nextOlderShadowTreeTable.get(this) || null;
+ },
+ get host() {
+ return shadowHostTable.get(this) || null;
+ },
+ invalidateShadowRenderer: function() {
+ return shadowHostTable.get(this).invalidateShadowRenderer();
+ },
+ elementFromPoint: function(x, y) {
+ return elementFromPoint(this, this.ownerDocument, x, y);
+ },
+ getSelection: function() {
+ return document.getSelection();
+ },
+ get activeElement() {
+ var unwrappedActiveElement = unwrap(this).ownerDocument.activeElement;
+ if (!unwrappedActiveElement || !unwrappedActiveElement.nodeType) return null;
+ var activeElement = wrap(unwrappedActiveElement);
+ while (!this.contains(activeElement)) {
+ while (activeElement.parentNode) {
+ activeElement = activeElement.parentNode;
+ }
+ if (activeElement.host) {
+ activeElement = activeElement.host;
+ } else {
+ return null;
+ }
+ }
+ return activeElement;
+ }
+ });
+ scope.wrappers.ShadowRoot = ShadowRoot;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var getTreeScope = scope.getTreeScope;
+ var OriginalRange = window.Range;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ function getHost(node) {
+ var root = getTreeScope(node).root;
+ if (root instanceof ShadowRoot) {
+ return root.host;
+ }
+ return null;
+ }
+ function hostNodeToShadowNode(refNode, offset) {
+ if (refNode.shadowRoot) {
+ offset = Math.min(refNode.childNodes.length - 1, offset);
+ var child = refNode.childNodes[offset];
+ if (child) {
+ var insertionPoint = scope.getDestinationInsertionPoints(child);
+ if (insertionPoint.length > 0) {
+ var parentNode = insertionPoint[0].parentNode;
+ if (parentNode.nodeType == Node.ELEMENT_NODE) {
+ refNode = parentNode;
+ }
+ }
+ }
+ }
+ return refNode;
+ }
+ function shadowNodeToHostNode(node) {
+ node = wrap(node);
+ return getHost(node) || node;
+ }
+ function Range(impl) {
+ setWrapper(impl, this);
+ }
+ Range.prototype = {
+ get startContainer() {
+ return shadowNodeToHostNode(unsafeUnwrap(this).startContainer);
+ },
+ get endContainer() {
+ return shadowNodeToHostNode(unsafeUnwrap(this).endContainer);
+ },
+ get commonAncestorContainer() {
+ return shadowNodeToHostNode(unsafeUnwrap(this).commonAncestorContainer);
+ },
+ setStart: function(refNode, offset) {
+ refNode = hostNodeToShadowNode(refNode, offset);
+ unsafeUnwrap(this).setStart(unwrapIfNeeded(refNode), offset);
+ },
+ setEnd: function(refNode, offset) {
+ refNode = hostNodeToShadowNode(refNode, offset);
+ unsafeUnwrap(this).setEnd(unwrapIfNeeded(refNode), offset);
+ },
+ setStartBefore: function(refNode) {
+ unsafeUnwrap(this).setStartBefore(unwrapIfNeeded(refNode));
+ },
+ setStartAfter: function(refNode) {
+ unsafeUnwrap(this).setStartAfter(unwrapIfNeeded(refNode));
+ },
+ setEndBefore: function(refNode) {
+ unsafeUnwrap(this).setEndBefore(unwrapIfNeeded(refNode));
+ },
+ setEndAfter: function(refNode) {
+ unsafeUnwrap(this).setEndAfter(unwrapIfNeeded(refNode));
+ },
+ selectNode: function(refNode) {
+ unsafeUnwrap(this).selectNode(unwrapIfNeeded(refNode));
+ },
+ selectNodeContents: function(refNode) {
+ unsafeUnwrap(this).selectNodeContents(unwrapIfNeeded(refNode));
+ },
+ compareBoundaryPoints: function(how, sourceRange) {
+ return unsafeUnwrap(this).compareBoundaryPoints(how, unwrap(sourceRange));
+ },
+ extractContents: function() {
+ return wrap(unsafeUnwrap(this).extractContents());
+ },
+ cloneContents: function() {
+ return wrap(unsafeUnwrap(this).cloneContents());
+ },
+ insertNode: function(node) {
+ unsafeUnwrap(this).insertNode(unwrapIfNeeded(node));
+ },
+ surroundContents: function(newParent) {
+ unsafeUnwrap(this).surroundContents(unwrapIfNeeded(newParent));
+ },
+ cloneRange: function() {
+ return wrap(unsafeUnwrap(this).cloneRange());
+ },
+ isPointInRange: function(node, offset) {
+ return unsafeUnwrap(this).isPointInRange(unwrapIfNeeded(node), offset);
+ },
+ comparePoint: function(node, offset) {
+ return unsafeUnwrap(this).comparePoint(unwrapIfNeeded(node), offset);
+ },
+ intersectsNode: function(node) {
+ return unsafeUnwrap(this).intersectsNode(unwrapIfNeeded(node));
+ },
+ toString: function() {
+ return unsafeUnwrap(this).toString();
+ }
+ };
+ if (OriginalRange.prototype.createContextualFragment) {
+ Range.prototype.createContextualFragment = function(html) {
+ return wrap(unsafeUnwrap(this).createContextualFragment(html));
+ };
+ }
+ registerWrapper(window.Range, Range, document.createRange());
+ scope.wrappers.Range = Range;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var Element = scope.wrappers.Element;
+ var HTMLContentElement = scope.wrappers.HTMLContentElement;
+ var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+ var Node = scope.wrappers.Node;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ var assert = scope.assert;
+ var getTreeScope = scope.getTreeScope;
+ var mixin = scope.mixin;
+ var oneOf = scope.oneOf;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var ArraySplice = scope.ArraySplice;
+ function updateWrapperUpAndSideways(wrapper) {
+ wrapper.previousSibling_ = wrapper.previousSibling;
+ wrapper.nextSibling_ = wrapper.nextSibling;
+ wrapper.parentNode_ = wrapper.parentNode;
+ }
+ function updateWrapperDown(wrapper) {
+ wrapper.firstChild_ = wrapper.firstChild;
+ wrapper.lastChild_ = wrapper.lastChild;
+ }
+ function updateAllChildNodes(parentNodeWrapper) {
+ assert(parentNodeWrapper instanceof Node);
+ for (var childWrapper = parentNodeWrapper.firstChild; childWrapper; childWrapper = childWrapper.nextSibling) {
+ updateWrapperUpAndSideways(childWrapper);
+ }
+ updateWrapperDown(parentNodeWrapper);
+ }
+ function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
+ var parentNode = unwrap(parentNodeWrapper);
+ var newChild = unwrap(newChildWrapper);
+ var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
+ remove(newChildWrapper);
+ updateWrapperUpAndSideways(newChildWrapper);
+ if (!refChildWrapper) {
+ parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
+ if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
+ var lastChildWrapper = wrap(parentNode.lastChild);
+ if (lastChildWrapper) lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
+ } else {
+ if (parentNodeWrapper.firstChild === refChildWrapper) parentNodeWrapper.firstChild_ = refChildWrapper;
+ refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
+ }
+ scope.originalInsertBefore.call(parentNode, newChild, refChild);
+ }
+ function remove(nodeWrapper) {
+ var node = unwrap(nodeWrapper);
+ var parentNode = node.parentNode;
+ if (!parentNode) return;
+ var parentNodeWrapper = wrap(parentNode);
+ updateWrapperUpAndSideways(nodeWrapper);
+ if (nodeWrapper.previousSibling) nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
+ if (nodeWrapper.nextSibling) nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
+ if (parentNodeWrapper.lastChild === nodeWrapper) parentNodeWrapper.lastChild_ = nodeWrapper;
+ if (parentNodeWrapper.firstChild === nodeWrapper) parentNodeWrapper.firstChild_ = nodeWrapper;
+ scope.originalRemoveChild.call(parentNode, node);
+ }
+ var distributedNodesTable = new WeakMap();
+ var destinationInsertionPointsTable = new WeakMap();
+ var rendererForHostTable = new WeakMap();
+ function resetDistributedNodes(insertionPoint) {
+ distributedNodesTable.set(insertionPoint, []);
+ }
+ function getDistributedNodes(insertionPoint) {
+ var rv = distributedNodesTable.get(insertionPoint);
+ if (!rv) distributedNodesTable.set(insertionPoint, rv = []);
+ return rv;
+ }
+ function getChildNodesSnapshot(node) {
+ var result = [], i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ result[i++] = child;
+ }
+ return result;
+ }
+ var request = oneOf(window, [ "requestAnimationFrame", "mozRequestAnimationFrame", "webkitRequestAnimationFrame", "setTimeout" ]);
+ var pendingDirtyRenderers = [];
+ var renderTimer;
+ function renderAllPending() {
+ for (var i = 0; i < pendingDirtyRenderers.length; i++) {
+ var renderer = pendingDirtyRenderers[i];
+ var parentRenderer = renderer.parentRenderer;
+ if (parentRenderer && parentRenderer.dirty) continue;
+ renderer.render();
+ }
+ pendingDirtyRenderers = [];
+ }
+ function handleRequestAnimationFrame() {
+ renderTimer = null;
+ renderAllPending();
+ }
+ function getRendererForHost(host) {
+ var renderer = rendererForHostTable.get(host);
+ if (!renderer) {
+ renderer = new ShadowRenderer(host);
+ rendererForHostTable.set(host, renderer);
+ }
+ return renderer;
+ }
+ function getShadowRootAncestor(node) {
+ var root = getTreeScope(node).root;
+ if (root instanceof ShadowRoot) return root;
+ return null;
+ }
+ function getRendererForShadowRoot(shadowRoot) {
+ return getRendererForHost(shadowRoot.host);
+ }
+ var spliceDiff = new ArraySplice();
+ spliceDiff.equals = function(renderNode, rawNode) {
+ return unwrap(renderNode.node) === rawNode;
+ };
+ function RenderNode(node) {
+ this.skip = false;
+ this.node = node;
+ this.childNodes = [];
+ }
+ RenderNode.prototype = {
+ append: function(node) {
+ var rv = new RenderNode(node);
+ this.childNodes.push(rv);
+ return rv;
+ },
+ sync: function(opt_added) {
+ if (this.skip) return;
+ var nodeWrapper = this.node;
+ var newChildren = this.childNodes;
+ var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
+ var added = opt_added || new WeakMap();
+ var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
+ var newIndex = 0, oldIndex = 0;
+ var lastIndex = 0;
+ for (var i = 0; i < splices.length; i++) {
+ var splice = splices[i];
+ for (;lastIndex < splice.index; lastIndex++) {
+ oldIndex++;
+ newChildren[newIndex++].sync(added);
+ }
+ var removedCount = splice.removed.length;
+ for (var j = 0; j < removedCount; j++) {
+ var wrapper = wrap(oldChildren[oldIndex++]);
+ if (!added.get(wrapper)) remove(wrapper);
+ }
+ var addedCount = splice.addedCount;
+ var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
+ for (var j = 0; j < addedCount; j++) {
+ var newChildRenderNode = newChildren[newIndex++];
+ var newChildWrapper = newChildRenderNode.node;
+ insertBefore(nodeWrapper, newChildWrapper, refNode);
+ added.set(newChildWrapper, true);
+ newChildRenderNode.sync(added);
+ }
+ lastIndex += addedCount;
+ }
+ for (var i = lastIndex; i < newChildren.length; i++) {
+ newChildren[i].sync(added);
+ }
+ }
+ };
+ function ShadowRenderer(host) {
+ this.host = host;
+ this.dirty = false;
+ this.invalidateAttributes();
+ this.associateNode(host);
+ }
+ ShadowRenderer.prototype = {
+ render: function(opt_renderNode) {
+ if (!this.dirty) return;
+ this.invalidateAttributes();
+ var host = this.host;
+ this.distribution(host);
+ var renderNode = opt_renderNode || new RenderNode(host);
+ this.buildRenderTree(renderNode, host);
+ var topMostRenderer = !opt_renderNode;
+ if (topMostRenderer) renderNode.sync();
+ this.dirty = false;
+ },
+ get parentRenderer() {
+ return getTreeScope(this.host).renderer;
+ },
+ invalidate: function() {
+ if (!this.dirty) {
+ this.dirty = true;
+ var parentRenderer = this.parentRenderer;
+ if (parentRenderer) parentRenderer.invalidate();
+ pendingDirtyRenderers.push(this);
+ if (renderTimer) return;
+ renderTimer = window[request](handleRequestAnimationFrame, 0);
+ }
+ },
+ distribution: function(root) {
+ this.resetAllSubtrees(root);
+ this.distributionResolution(root);
+ },
+ resetAll: function(node) {
+ if (isInsertionPoint(node)) resetDistributedNodes(node); else resetDestinationInsertionPoints(node);
+ this.resetAllSubtrees(node);
+ },
+ resetAllSubtrees: function(node) {
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.resetAll(child);
+ }
+ if (node.shadowRoot) this.resetAll(node.shadowRoot);
+ if (node.olderShadowRoot) this.resetAll(node.olderShadowRoot);
+ },
+ distributionResolution: function(node) {
+ if (isShadowHost(node)) {
+ var shadowHost = node;
+ var pool = poolPopulation(shadowHost);
+ var shadowTrees = getShadowTrees(shadowHost);
+ for (var i = 0; i < shadowTrees.length; i++) {
+ this.poolDistribution(shadowTrees[i], pool);
+ }
+ for (var i = shadowTrees.length - 1; i >= 0; i--) {
+ var shadowTree = shadowTrees[i];
+ var shadow = getShadowInsertionPoint(shadowTree);
+ if (shadow) {
+ var olderShadowRoot = shadowTree.olderShadowRoot;
+ if (olderShadowRoot) {
+ pool = poolPopulation(olderShadowRoot);
+ }
+ for (var j = 0; j < pool.length; j++) {
+ destributeNodeInto(pool[j], shadow);
+ }
+ }
+ this.distributionResolution(shadowTree);
+ }
+ }
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.distributionResolution(child);
+ }
+ },
+ poolDistribution: function(node, pool) {
+ if (node instanceof HTMLShadowElement) return;
+ if (node instanceof HTMLContentElement) {
+ var content = node;
+ this.updateDependentAttributes(content.getAttribute("select"));
+ var anyDistributed = false;
+ for (var i = 0; i < pool.length; i++) {
+ var node = pool[i];
+ if (!node) continue;
+ if (matches(node, content)) {
+ destributeNodeInto(node, content);
+ pool[i] = undefined;
+ anyDistributed = true;
+ }
+ }
+ if (!anyDistributed) {
+ for (var child = content.firstChild; child; child = child.nextSibling) {
+ destributeNodeInto(child, content);
+ }
+ }
+ return;
+ }
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.poolDistribution(child, pool);
+ }
+ },
+ buildRenderTree: function(renderNode, node) {
+ var children = this.compose(node);
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ var childRenderNode = renderNode.append(child);
+ this.buildRenderTree(childRenderNode, child);
+ }
+ if (isShadowHost(node)) {
+ var renderer = getRendererForHost(node);
+ renderer.dirty = false;
+ }
+ },
+ compose: function(node) {
+ var children = [];
+ var p = node.shadowRoot || node;
+ for (var child = p.firstChild; child; child = child.nextSibling) {
+ if (isInsertionPoint(child)) {
+ this.associateNode(p);
+ var distributedNodes = getDistributedNodes(child);
+ for (var j = 0; j < distributedNodes.length; j++) {
+ var distributedNode = distributedNodes[j];
+ if (isFinalDestination(child, distributedNode)) children.push(distributedNode);
+ }
+ } else {
+ children.push(child);
+ }
+ }
+ return children;
+ },
+ invalidateAttributes: function() {
+ this.attributes = Object.create(null);
+ },
+ updateDependentAttributes: function(selector) {
+ if (!selector) return;
+ var attributes = this.attributes;
+ if (/\.\w+/.test(selector)) attributes["class"] = true;
+ if (/#\w+/.test(selector)) attributes["id"] = true;
+ selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) {
+ attributes[name] = true;
+ });
+ },
+ dependsOnAttribute: function(name) {
+ return this.attributes[name];
+ },
+ associateNode: function(node) {
+ unsafeUnwrap(node).polymerShadowRenderer_ = this;
+ }
+ };
+ function poolPopulation(node) {
+ var pool = [];
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ if (isInsertionPoint(child)) {
+ pool.push.apply(pool, getDistributedNodes(child));
+ } else {
+ pool.push(child);
+ }
+ }
+ return pool;
+ }
+ function getShadowInsertionPoint(node) {
+ if (node instanceof HTMLShadowElement) return node;
+ if (node instanceof HTMLContentElement) return null;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ var res = getShadowInsertionPoint(child);
+ if (res) return res;
+ }
+ return null;
+ }
+ function destributeNodeInto(child, insertionPoint) {
+ getDistributedNodes(insertionPoint).push(child);
+ var points = destinationInsertionPointsTable.get(child);
+ if (!points) destinationInsertionPointsTable.set(child, [ insertionPoint ]); else points.push(insertionPoint);
+ }
+ function getDestinationInsertionPoints(node) {
+ return destinationInsertionPointsTable.get(node);
+ }
+ function resetDestinationInsertionPoints(node) {
+ destinationInsertionPointsTable.set(node, undefined);
+ }
+ var selectorStartCharRe = /^(:not\()?[*.#[a-zA-Z_|]/;
+ function matches(node, contentElement) {
+ var select = contentElement.getAttribute("select");
+ if (!select) return true;
+ select = select.trim();
+ if (!select) return true;
+ if (!(node instanceof Element)) return false;
+ if (!selectorStartCharRe.test(select)) return false;
+ try {
+ return node.matches(select);
+ } catch (ex) {
+ return false;
+ }
+ }
+ function isFinalDestination(insertionPoint, node) {
+ var points = getDestinationInsertionPoints(node);
+ return points && points[points.length - 1] === insertionPoint;
+ }
+ function isInsertionPoint(node) {
+ return node instanceof HTMLContentElement || node instanceof HTMLShadowElement;
+ }
+ function isShadowHost(shadowHost) {
+ return shadowHost.shadowRoot;
+ }
+ function getShadowTrees(host) {
+ var trees = [];
+ for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) {
+ trees.push(tree);
+ }
+ return trees;
+ }
+ function render(host) {
+ new ShadowRenderer(host).render();
+ }
+ Node.prototype.invalidateShadowRenderer = function(force) {
+ var renderer = unsafeUnwrap(this).polymerShadowRenderer_;
+ if (renderer) {
+ renderer.invalidate();
+ return true;
+ }
+ return false;
+ };
+ HTMLContentElement.prototype.getDistributedNodes = HTMLShadowElement.prototype.getDistributedNodes = function() {
+ renderAllPending();
+ return getDistributedNodes(this);
+ };
+ Element.prototype.getDestinationInsertionPoints = function() {
+ renderAllPending();
+ return getDestinationInsertionPoints(this) || [];
+ };
+ HTMLContentElement.prototype.nodeIsInserted_ = HTMLShadowElement.prototype.nodeIsInserted_ = function() {
+ this.invalidateShadowRenderer();
+ var shadowRoot = getShadowRootAncestor(this);
+ var renderer;
+ if (shadowRoot) renderer = getRendererForShadowRoot(shadowRoot);
+ unsafeUnwrap(this).polymerShadowRenderer_ = renderer;
+ if (renderer) renderer.invalidate();
+ };
+ scope.getRendererForHost = getRendererForHost;
+ scope.getShadowTrees = getShadowTrees;
+ scope.renderAllPending = renderAllPending;
+ scope.getDestinationInsertionPoints = getDestinationInsertionPoints;
+ scope.visual = {
+ insertBefore: insertBefore,
+ remove: remove
+ };
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var assert = scope.assert;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var elementsWithFormProperty = [ "HTMLButtonElement", "HTMLFieldSetElement", "HTMLInputElement", "HTMLKeygenElement", "HTMLLabelElement", "HTMLLegendElement", "HTMLObjectElement", "HTMLOutputElement", "HTMLTextAreaElement" ];
+ function createWrapperConstructor(name) {
+ if (!window[name]) return;
+ assert(!scope.wrappers[name]);
+ var GeneratedWrapper = function(node) {
+ HTMLElement.call(this, node);
+ };
+ GeneratedWrapper.prototype = Object.create(HTMLElement.prototype);
+ mixin(GeneratedWrapper.prototype, {
+ get form() {
+ return wrap(unwrap(this).form);
+ }
+ });
+ registerWrapper(window[name], GeneratedWrapper, document.createElement(name.slice(4, -7)));
+ scope.wrappers[name] = GeneratedWrapper;
+ }
+ elementsWithFormProperty.forEach(createWrapperConstructor);
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalSelection = window.Selection;
+ function Selection(impl) {
+ setWrapper(impl, this);
+ }
+ Selection.prototype = {
+ get anchorNode() {
+ return wrap(unsafeUnwrap(this).anchorNode);
+ },
+ get focusNode() {
+ return wrap(unsafeUnwrap(this).focusNode);
+ },
+ addRange: function(range) {
+ unsafeUnwrap(this).addRange(unwrapIfNeeded(range));
+ },
+ collapse: function(node, index) {
+ unsafeUnwrap(this).collapse(unwrapIfNeeded(node), index);
+ },
+ containsNode: function(node, allowPartial) {
+ return unsafeUnwrap(this).containsNode(unwrapIfNeeded(node), allowPartial);
+ },
+ getRangeAt: function(index) {
+ return wrap(unsafeUnwrap(this).getRangeAt(index));
+ },
+ removeRange: function(range) {
+ unsafeUnwrap(this).removeRange(unwrap(range));
+ },
+ selectAllChildren: function(node) {
+ unsafeUnwrap(this).selectAllChildren(node instanceof ShadowRoot ? unsafeUnwrap(node.host) : unwrapIfNeeded(node));
+ },
+ toString: function() {
+ return unsafeUnwrap(this).toString();
+ }
+ };
+ if (OriginalSelection.prototype.extend) {
+ Selection.prototype.extend = function(node, offset) {
+ unsafeUnwrap(this).extend(unwrapIfNeeded(node), offset);
+ };
+ }
+ registerWrapper(window.Selection, Selection, window.getSelection());
+ scope.wrappers.Selection = Selection;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalTreeWalker = window.TreeWalker;
+ function TreeWalker(impl) {
+ setWrapper(impl, this);
+ }
+ TreeWalker.prototype = {
+ get root() {
+ return wrap(unsafeUnwrap(this).root);
+ },
+ get currentNode() {
+ return wrap(unsafeUnwrap(this).currentNode);
+ },
+ set currentNode(node) {
+ unsafeUnwrap(this).currentNode = unwrapIfNeeded(node);
+ },
+ get filter() {
+ return unsafeUnwrap(this).filter;
+ },
+ parentNode: function() {
+ return wrap(unsafeUnwrap(this).parentNode());
+ },
+ firstChild: function() {
+ return wrap(unsafeUnwrap(this).firstChild());
+ },
+ lastChild: function() {
+ return wrap(unsafeUnwrap(this).lastChild());
+ },
+ previousSibling: function() {
+ return wrap(unsafeUnwrap(this).previousSibling());
+ },
+ previousNode: function() {
+ return wrap(unsafeUnwrap(this).previousNode());
+ },
+ nextNode: function() {
+ return wrap(unsafeUnwrap(this).nextNode());
+ }
+ };
+ registerWrapper(OriginalTreeWalker, TreeWalker);
+ scope.wrappers.TreeWalker = TreeWalker;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var Node = scope.wrappers.Node;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var NonElementParentNodeInterface = scope.NonElementParentNodeInterface;
+ var Selection = scope.wrappers.Selection;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ var TreeScope = scope.TreeScope;
+ var cloneNode = scope.cloneNode;
+ var defineGetter = scope.defineGetter;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var elementFromPoint = scope.elementFromPoint;
+ var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+ var matchesNames = scope.matchesNames;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var renderAllPending = scope.renderAllPending;
+ var rewrap = scope.rewrap;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrapEventTargetMethods = scope.wrapEventTargetMethods;
+ var wrapNodeList = scope.wrapNodeList;
+ var implementationTable = new WeakMap();
+ function Document(node) {
+ Node.call(this, node);
+ this.treeScope_ = new TreeScope(this, null);
+ }
+ Document.prototype = Object.create(Node.prototype);
+ defineWrapGetter(Document, "documentElement");
+ defineWrapGetter(Document, "body");
+ defineWrapGetter(Document, "head");
+ defineGetter(Document, "activeElement", function() {
+ var unwrappedActiveElement = unwrap(this).activeElement;
+ if (!unwrappedActiveElement || !unwrappedActiveElement.nodeType) return null;
+ var activeElement = wrap(unwrappedActiveElement);
+ while (!this.contains(activeElement)) {
+ while (activeElement.parentNode) {
+ activeElement = activeElement.parentNode;
+ }
+ if (activeElement.host) {
+ activeElement = activeElement.host;
+ } else {
+ return null;
+ }
+ }
+ return activeElement;
+ });
+ function wrapMethod(name) {
+ var original = document[name];
+ Document.prototype[name] = function() {
+ return wrap(original.apply(unsafeUnwrap(this), arguments));
+ };
+ }
+ [ "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEvent", "createEventNS", "createRange", "createTextNode" ].forEach(wrapMethod);
+ var originalAdoptNode = document.adoptNode;
+ function adoptNodeNoRemove(node, doc) {
+ originalAdoptNode.call(unsafeUnwrap(doc), unwrap(node));
+ adoptSubtree(node, doc);
+ }
+ function adoptSubtree(node, doc) {
+ if (node.shadowRoot) doc.adoptNode(node.shadowRoot);
+ if (node instanceof ShadowRoot) adoptOlderShadowRoots(node, doc);
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ adoptSubtree(child, doc);
+ }
+ }
+ function adoptOlderShadowRoots(shadowRoot, doc) {
+ var oldShadowRoot = shadowRoot.olderShadowRoot;
+ if (oldShadowRoot) doc.adoptNode(oldShadowRoot);
+ }
+ var originalGetSelection = document.getSelection;
+ mixin(Document.prototype, {
+ adoptNode: function(node) {
+ if (node.parentNode) node.parentNode.removeChild(node);
+ adoptNodeNoRemove(node, this);
+ return node;
+ },
+ elementFromPoint: function(x, y) {
+ return elementFromPoint(this, this, x, y);
+ },
+ importNode: function(node, deep) {
+ return cloneNode(node, deep, unsafeUnwrap(this));
+ },
+ getSelection: function() {
+ renderAllPending();
+ return new Selection(originalGetSelection.call(unwrap(this)));
+ },
+ getElementsByName: function(name) {
+ return SelectorsInterface.querySelectorAll.call(this, "[name=" + JSON.stringify(String(name)) + "]");
+ }
+ });
+ var originalCreateTreeWalker = document.createTreeWalker;
+ var TreeWalkerWrapper = scope.wrappers.TreeWalker;
+ Document.prototype.createTreeWalker = function(root, whatToShow, filter, expandEntityReferences) {
+ var newFilter = null;
+ if (filter) {
+ if (filter.acceptNode && typeof filter.acceptNode === "function") {
+ newFilter = {
+ acceptNode: function(node) {
+ return filter.acceptNode(wrap(node));
+ }
+ };
+ } else if (typeof filter === "function") {
+ newFilter = function(node) {
+ return filter(wrap(node));
+ };
+ }
+ }
+ return new TreeWalkerWrapper(originalCreateTreeWalker.call(unwrap(this), unwrap(root), whatToShow, newFilter, expandEntityReferences));
+ };
+ if (document.registerElement) {
+ var originalRegisterElement = document.registerElement;
+ Document.prototype.registerElement = function(tagName, object) {
+ var prototype, extendsOption;
+ if (object !== undefined) {
+ prototype = object.prototype;
+ extendsOption = object.extends;
+ }
+ if (!prototype) prototype = Object.create(HTMLElement.prototype);
+ if (scope.nativePrototypeTable.get(prototype)) {
+ throw new Error("NotSupportedError");
+ }
+ var proto = Object.getPrototypeOf(prototype);
+ var nativePrototype;
+ var prototypes = [];
+ while (proto) {
+ nativePrototype = scope.nativePrototypeTable.get(proto);
+ if (nativePrototype) break;
+ prototypes.push(proto);
+ proto = Object.getPrototypeOf(proto);
+ }
+ if (!nativePrototype) {
+ throw new Error("NotSupportedError");
+ }
+ var newPrototype = Object.create(nativePrototype);
+ for (var i = prototypes.length - 1; i >= 0; i--) {
+ newPrototype = Object.create(newPrototype);
+ }
+ [ "createdCallback", "attachedCallback", "detachedCallback", "attributeChangedCallback" ].forEach(function(name) {
+ var f = prototype[name];
+ if (!f) return;
+ newPrototype[name] = function() {
+ if (!(wrap(this) instanceof CustomElementConstructor)) {
+ rewrap(this);
+ }
+ f.apply(wrap(this), arguments);
+ };
+ });
+ var p = {
+ prototype: newPrototype
+ };
+ if (extendsOption) p.extends = extendsOption;
+ function CustomElementConstructor(node) {
+ if (!node) {
+ if (extendsOption) {
+ return document.createElement(extendsOption, tagName);
+ } else {
+ return document.createElement(tagName);
+ }
+ }
+ setWrapper(node, this);
+ }
+ CustomElementConstructor.prototype = prototype;
+ CustomElementConstructor.prototype.constructor = CustomElementConstructor;
+ scope.constructorTable.set(newPrototype, CustomElementConstructor);
+ scope.nativePrototypeTable.set(prototype, newPrototype);
+ var nativeConstructor = originalRegisterElement.call(unwrap(this), tagName, p);
+ return CustomElementConstructor;
+ };
+ forwardMethodsToWrapper([ window.HTMLDocument || window.Document ], [ "registerElement" ]);
+ }
+ forwardMethodsToWrapper([ window.HTMLBodyElement, window.HTMLDocument || window.Document, window.HTMLHeadElement, window.HTMLHtmlElement ], [ "appendChild", "compareDocumentPosition", "contains", "getElementsByClassName", "getElementsByTagName", "getElementsByTagNameNS", "insertBefore", "querySelector", "querySelectorAll", "removeChild", "replaceChild" ]);
+ forwardMethodsToWrapper([ window.HTMLBodyElement, window.HTMLHeadElement, window.HTMLHtmlElement ], matchesNames);
+ forwardMethodsToWrapper([ window.HTMLDocument || window.Document ], [ "adoptNode", "importNode", "contains", "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEvent", "createEventNS", "createRange", "createTextNode", "createTreeWalker", "elementFromPoint", "getElementById", "getElementsByName", "getSelection" ]);
+ mixin(Document.prototype, GetElementsByInterface);
+ mixin(Document.prototype, ParentNodeInterface);
+ mixin(Document.prototype, SelectorsInterface);
+ mixin(Document.prototype, NonElementParentNodeInterface);
+ mixin(Document.prototype, {
+ get implementation() {
+ var implementation = implementationTable.get(this);
+ if (implementation) return implementation;
+ implementation = new DOMImplementation(unwrap(this).implementation);
+ implementationTable.set(this, implementation);
+ return implementation;
+ },
+ get defaultView() {
+ return wrap(unwrap(this).defaultView);
+ }
+ });
+ registerWrapper(window.Document, Document, document.implementation.createHTMLDocument(""));
+ if (window.HTMLDocument) registerWrapper(window.HTMLDocument, Document);
+ wrapEventTargetMethods([ window.HTMLBodyElement, window.HTMLDocument || window.Document, window.HTMLHeadElement ]);
+ function DOMImplementation(impl) {
+ setWrapper(impl, this);
+ }
+ var originalCreateDocument = document.implementation.createDocument;
+ DOMImplementation.prototype.createDocument = function() {
+ arguments[2] = unwrap(arguments[2]);
+ return wrap(originalCreateDocument.apply(unsafeUnwrap(this), arguments));
+ };
+ function wrapImplMethod(constructor, name) {
+ var original = document.implementation[name];
+ constructor.prototype[name] = function() {
+ return wrap(original.apply(unsafeUnwrap(this), arguments));
+ };
+ }
+ function forwardImplMethod(constructor, name) {
+ var original = document.implementation[name];
+ constructor.prototype[name] = function() {
+ return original.apply(unsafeUnwrap(this), arguments);
+ };
+ }
+ wrapImplMethod(DOMImplementation, "createDocumentType");
+ wrapImplMethod(DOMImplementation, "createHTMLDocument");
+ forwardImplMethod(DOMImplementation, "hasFeature");
+ registerWrapper(window.DOMImplementation, DOMImplementation);
+ forwardMethodsToWrapper([ window.DOMImplementation ], [ "createDocument", "createDocumentType", "createHTMLDocument", "hasFeature" ]);
+ scope.adoptNodeNoRemove = adoptNodeNoRemove;
+ scope.wrappers.DOMImplementation = DOMImplementation;
+ scope.wrappers.Document = Document;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var EventTarget = scope.wrappers.EventTarget;
+ var Selection = scope.wrappers.Selection;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var renderAllPending = scope.renderAllPending;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalWindow = window.Window;
+ var originalGetComputedStyle = window.getComputedStyle;
+ var originalGetDefaultComputedStyle = window.getDefaultComputedStyle;
+ var originalGetSelection = window.getSelection;
+ function Window(impl) {
+ EventTarget.call(this, impl);
+ }
+ Window.prototype = Object.create(EventTarget.prototype);
+ OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
+ return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo);
+ };
+ if (originalGetDefaultComputedStyle) {
+ OriginalWindow.prototype.getDefaultComputedStyle = function(el, pseudo) {
+ return wrap(this || window).getDefaultComputedStyle(unwrapIfNeeded(el), pseudo);
+ };
+ }
+ OriginalWindow.prototype.getSelection = function() {
+ return wrap(this || window).getSelection();
+ };
+ delete window.getComputedStyle;
+ delete window.getDefaultComputedStyle;
+ delete window.getSelection;
+ [ "addEventListener", "removeEventListener", "dispatchEvent" ].forEach(function(name) {
+ OriginalWindow.prototype[name] = function() {
+ var w = wrap(this || window);
+ return w[name].apply(w, arguments);
+ };
+ delete window[name];
+ });
+ mixin(Window.prototype, {
+ getComputedStyle: function(el, pseudo) {
+ renderAllPending();
+ return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), pseudo);
+ },
+ getSelection: function() {
+ renderAllPending();
+ return new Selection(originalGetSelection.call(unwrap(this)));
+ },
+ get document() {
+ return wrap(unwrap(this).document);
+ }
+ });
+ if (originalGetDefaultComputedStyle) {
+ Window.prototype.getDefaultComputedStyle = function(el, pseudo) {
+ renderAllPending();
+ return originalGetDefaultComputedStyle.call(unwrap(this), unwrapIfNeeded(el), pseudo);
+ };
+ }
+ registerWrapper(OriginalWindow, Window, window);
+ scope.wrappers.Window = Window;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var unwrap = scope.unwrap;
+ var OriginalDataTransfer = window.DataTransfer || window.Clipboard;
+ var OriginalDataTransferSetDragImage = OriginalDataTransfer.prototype.setDragImage;
+ if (OriginalDataTransferSetDragImage) {
+ OriginalDataTransfer.prototype.setDragImage = function(image, x, y) {
+ OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y);
+ };
+ }
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unwrap = scope.unwrap;
+ var OriginalFormData = window.FormData;
+ if (!OriginalFormData) return;
+ function FormData(formElement) {
+ var impl;
+ if (formElement instanceof OriginalFormData) {
+ impl = formElement;
+ } else {
+ impl = new OriginalFormData(formElement && unwrap(formElement));
+ }
+ setWrapper(impl, this);
+ }
+ registerWrapper(OriginalFormData, FormData, new OriginalFormData());
+ scope.wrappers.FormData = FormData;
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var originalSend = XMLHttpRequest.prototype.send;
+ XMLHttpRequest.prototype.send = function(obj) {
+ return originalSend.call(this, unwrapIfNeeded(obj));
+ };
+})(window.ShadowDOMPolyfill);
+
+(function(scope) {
+ "use strict";
+ var isWrapperFor = scope.isWrapperFor;
+ var elements = {
+ a: "HTMLAnchorElement",
+ area: "HTMLAreaElement",
+ audio: "HTMLAudioElement",
+ base: "HTMLBaseElement",
+ body: "HTMLBodyElement",
+ br: "HTMLBRElement",
+ button: "HTMLButtonElement",
+ canvas: "HTMLCanvasElement",
+ caption: "HTMLTableCaptionElement",
+ col: "HTMLTableColElement",
+ content: "HTMLContentElement",
+ data: "HTMLDataElement",
+ datalist: "HTMLDataListElement",
+ del: "HTMLModElement",
+ dir: "HTMLDirectoryElement",
+ div: "HTMLDivElement",
+ dl: "HTMLDListElement",
+ embed: "HTMLEmbedElement",
+ fieldset: "HTMLFieldSetElement",
+ font: "HTMLFontElement",
+ form: "HTMLFormElement",
+ frame: "HTMLFrameElement",
+ frameset: "HTMLFrameSetElement",
+ h1: "HTMLHeadingElement",
+ head: "HTMLHeadElement",
+ hr: "HTMLHRElement",
+ html: "HTMLHtmlElement",
+ iframe: "HTMLIFrameElement",
+ img: "HTMLImageElement",
+ input: "HTMLInputElement",
+ keygen: "HTMLKeygenElement",
+ label: "HTMLLabelElement",
+ legend: "HTMLLegendElement",
+ li: "HTMLLIElement",
+ link: "HTMLLinkElement",
+ map: "HTMLMapElement",
+ marquee: "HTMLMarqueeElement",
+ menu: "HTMLMenuElement",
+ menuitem: "HTMLMenuItemElement",
+ meta: "HTMLMetaElement",
+ meter: "HTMLMeterElement",
+ object: "HTMLObjectElement",
+ ol: "HTMLOListElement",
+ optgroup: "HTMLOptGroupElement",
+ option: "HTMLOptionElement",
+ output: "HTMLOutputElement",
+ p: "HTMLParagraphElement",
+ param: "HTMLParamElement",
+ pre: "HTMLPreElement",
+ progress: "HTMLProgressElement",
+ q: "HTMLQuoteElement",
+ script: "HTMLScriptElement",
+ select: "HTMLSelectElement",
+ shadow: "HTMLShadowElement",
+ source: "HTMLSourceElement",
+ span: "HTMLSpanElement",
+ style: "HTMLStyleElement",
+ table: "HTMLTableElement",
+ tbody: "HTMLTableSectionElement",
+ template: "HTMLTemplateElement",
+ textarea: "HTMLTextAreaElement",
+ thead: "HTMLTableSectionElement",
+ time: "HTMLTimeElement",
+ title: "HTMLTitleElement",
+ tr: "HTMLTableRowElement",
+ track: "HTMLTrackElement",
+ ul: "HTMLUListElement",
+ video: "HTMLVideoElement"
+ };
+ function overrideConstructor(tagName) {
+ var nativeConstructorName = elements[tagName];
+ var nativeConstructor = window[nativeConstructorName];
+ if (!nativeConstructor) return;
+ var element = document.createElement(tagName);
+ var wrapperConstructor = element.constructor;
+ window[nativeConstructorName] = wrapperConstructor;
+ }
+ Object.keys(elements).forEach(overrideConstructor);
+ Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) {
+ window[name] = scope.wrappers[name];
+ });
+})(window.ShadowDOMPolyfill); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.min.js b/catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.min.js
new file mode 100644
index 00000000..fdbb8645
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/ShadowDOM.min.js
@@ -0,0 +1,13 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+"undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var r=t[this.name];return r&&r[0]===t?r[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),window.ShadowDOMPolyfill={},function(e){"use strict";function t(){if("undefined"!=typeof chrome&&chrome.app&&chrome.app.runtime)return!1;if(navigator.getDeviceStorage)return!1;try{var e=new Function("return true;");return e()}catch(t){return!1}}function n(e){if(!e)throw new Error("Assertion failed")}function r(e,t){for(var n=k(t),r=0;r<n.length;r++){var o=n[r];A(e,o,F(t,o))}return e}function o(e,t){for(var n=k(t),r=0;r<n.length;r++){var o=n[r];switch(o){case"arguments":case"caller":case"length":case"name":case"prototype":case"toString":continue}A(e,o,F(t,o))}return e}function i(e,t){for(var n=0;n<t.length;n++)if(t[n]in e)return t[n]}function a(e,t,n){B.value=n,A(e,t,B)}function s(e,t){var n=e.__proto__||Object.getPrototypeOf(e);if(U)try{k(n)}catch(r){n=n.__proto__}var o=R.get(n);if(o)return o;var i=s(n),a=E(i);return g(n,a,t),a}function c(e,t){m(e,t,!0)}function u(e,t){m(t,e,!1)}function l(e){return/^on[a-z]+$/.test(e)}function p(e){return/^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(e)}function d(e){return I&&p(e)?new Function("return this.__impl4cf1e782hg__."+e):function(){return this.__impl4cf1e782hg__[e]}}function f(e){return I&&p(e)?new Function("v","this.__impl4cf1e782hg__."+e+" = v"):function(t){this.__impl4cf1e782hg__[e]=t}}function h(e){return I&&p(e)?new Function("return this.__impl4cf1e782hg__."+e+".apply(this.__impl4cf1e782hg__, arguments)"):function(){return this.__impl4cf1e782hg__[e].apply(this.__impl4cf1e782hg__,arguments)}}function w(e,t){try{return e===window&&"showModalDialog"===t?q:Object.getOwnPropertyDescriptor(e,t)}catch(n){return q}}function m(t,n,r,o){for(var i=k(t),a=0;a<i.length;a++){var s=i[a];if("polymerBlackList_"!==s&&!(s in n||t.polymerBlackList_&&t.polymerBlackList_[s])){U&&t.__lookupGetter__(s);var c,u,p=w(t,s);if("function"!=typeof p.value){var m=l(s);c=m?e.getEventHandlerGetter(s):d(s),(p.writable||p.set||V)&&(u=m?e.getEventHandlerSetter(s):f(s));var v=V||p.configurable;A(n,s,{get:c,set:u,configurable:v,enumerable:p.enumerable})}else r&&(n[s]=h(s))}}}function v(e,t,n){if(null!=e){var r=e.prototype;g(r,t,n),o(t,e)}}function g(e,t,r){var o=t.prototype;n(void 0===R.get(e)),R.set(e,t),P.set(o,e),c(e,o),r&&u(o,r),a(o,"constructor",t),t.prototype=o}function b(e,t){return R.get(t.prototype)===e}function y(e){var t=Object.getPrototypeOf(e),n=s(t),r=E(n);return g(t,r,e),r}function E(e){function t(t){e.call(this,t)}var n=Object.create(e.prototype);return n.constructor=t,t.prototype=n,t}function S(e){return e&&e.__impl4cf1e782hg__}function M(e){return!S(e)}function T(e){if(null===e)return null;n(M(e));var t=e.__wrapper8e3dd93a60__;return null!=t?t:e.__wrapper8e3dd93a60__=new(s(e,e))(e)}function O(e){return null===e?null:(n(S(e)),e.__impl4cf1e782hg__)}function N(e){return e.__impl4cf1e782hg__}function j(e,t){t.__impl4cf1e782hg__=e,e.__wrapper8e3dd93a60__=t}function L(e){return e&&S(e)?O(e):e}function _(e){return e&&!S(e)?T(e):e}function D(e,t){null!==t&&(n(M(e)),n(void 0===t||S(t)),e.__wrapper8e3dd93a60__=t)}function C(e,t,n){G.get=n,A(e.prototype,t,G)}function H(e,t){C(e,t,function(){return T(this.__impl4cf1e782hg__[t])})}function x(e,t){e.forEach(function(e){t.forEach(function(t){e.prototype[t]=function(){var e=_(this);return e[t].apply(e,arguments)}})})}var R=new WeakMap,P=new WeakMap,W=Object.create(null),I=t(),A=Object.defineProperty,k=Object.getOwnPropertyNames,F=Object.getOwnPropertyDescriptor,B={value:void 0,configurable:!0,enumerable:!1,writable:!0};k(window);var U=/Firefox/.test(navigator.userAgent),q={get:function(){},set:function(e){},configurable:!0,enumerable:!0},V=function(){var e=Object.getOwnPropertyDescriptor(Node.prototype,"nodeType");return e&&!e.get&&!e.set}(),G={get:void 0,configurable:!0,enumerable:!0};e.addForwardingProperties=c,e.assert=n,e.constructorTable=R,e.defineGetter=C,e.defineWrapGetter=H,e.forwardMethodsToWrapper=x,e.isIdentifierName=p,e.isWrapper=S,e.isWrapperFor=b,e.mixin=r,e.nativePrototypeTable=P,e.oneOf=i,e.registerObject=y,e.registerWrapper=v,e.rewrap=D,e.setWrapper=j,e.unsafeUnwrap=N,e.unwrap=O,e.unwrapIfNeeded=L,e.wrap=T,e.wrapIfNeeded=_,e.wrappers=W}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t,n){return{index:e,removed:t,addedCount:n}}function n(){}var r=0,o=1,i=2,a=3;n.prototype={calcEditDistances:function(e,t,n,r,o,i){for(var a=i-o+1,s=n-t+1,c=new Array(a),u=0;u<a;u++)c[u]=new Array(s),c[u][0]=u;for(var l=0;l<s;l++)c[0][l]=l;for(var u=1;u<a;u++)for(var l=1;l<s;l++)if(this.equals(e[t+l-1],r[o+u-1]))c[u][l]=c[u-1][l-1];else{var p=c[u-1][l]+1,d=c[u][l-1]+1;c[u][l]=p<d?p:d}return c},spliceOperationsFromEditDistances:function(e){for(var t=e.length-1,n=e[0].length-1,s=e[t][n],c=[];t>0||n>0;)if(0!=t)if(0!=n){var u,l=e[t-1][n-1],p=e[t-1][n],d=e[t][n-1];u=p<d?p<l?p:l:d<l?d:l,u==l?(l==s?c.push(r):(c.push(o),s=l),t--,n--):u==p?(c.push(a),t--,s=p):(c.push(i),n--,s=d)}else c.push(a),t--;else c.push(i),n--;return c.reverse(),c},calcSplices:function(e,n,s,c,u,l){var p=0,d=0,f=Math.min(s-n,l-u);if(0==n&&0==u&&(p=this.sharedPrefix(e,c,f)),s==e.length&&l==c.length&&(d=this.sharedSuffix(e,c,f-p)),n+=p,u+=p,s-=d,l-=d,s-n==0&&l-u==0)return[];if(n==s){for(var h=t(n,[],0);u<l;)h.removed.push(c[u++]);return[h]}if(u==l)return[t(n,[],s-n)];for(var w=this.spliceOperationsFromEditDistances(this.calcEditDistances(e,n,s,c,u,l)),h=void 0,m=[],v=n,g=u,b=0;b<w.length;b++)switch(w[b]){case r:h&&(m.push(h),h=void 0),v++,g++;break;case o:h||(h=t(v,[],0)),h.addedCount++,v++,h.removed.push(c[g]),g++;break;case i:h||(h=t(v,[],0)),h.addedCount++,v++;break;case a:h||(h=t(v,[],0)),h.removed.push(c[g]),g++}return h&&m.push(h),m},sharedPrefix:function(e,t,n){for(var r=0;r<n;r++)if(!this.equals(e[r],t[r]))return r;return n},sharedSuffix:function(e,t,n){for(var r=e.length,o=t.length,i=0;i<n&&this.equals(e[--r],t[--o]);)i++;return i},calculateSplices:function(e,t){return this.calcSplices(e,0,e.length,t,0,t.length)},equals:function(e,t){return e===t}},e.ArraySplice=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(){a=!1;var e=i.slice(0);i=[];for(var t=0;t<e.length;t++)(0,e[t])()}function n(e){i.push(e),a||(a=!0,r(t,0))}var r,o=window.MutationObserver,i=[],a=!1;if(o){var s=1,c=new o(t),u=document.createTextNode(s);c.observe(u,{characterData:!0}),r=function(){s=(s+1)%2,u.data=s}}else r=window.setTimeout;e.setEndOfMicrotask=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.scheduled_||(e.scheduled_=!0,h.push(e),w||(l(n),w=!0))}function n(){for(w=!1;h.length;){var e=h;h=[],e.sort(function(e,t){return e.uid_-t.uid_});for(var t=0;t<e.length;t++){var n=e[t];n.scheduled_=!1;var r=n.takeRecords();i(n),r.length&&n.callback_(r,n)}}}function r(e,t){this.type=e,this.target=t,this.addedNodes=new d.NodeList,this.removedNodes=new d.NodeList,this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function o(e,t){for(;e;e=e.parentNode){var n=f.get(e);if(n)for(var r=0;r<n.length;r++){var o=n[r];o.options.subtree&&o.addTransientObserver(t)}}}function i(e){for(var t=0;t<e.nodes_.length;t++){var n=e.nodes_[t],r=f.get(n);if(!r)return;for(var o=0;o<r.length;o++){var i=r[o];i.observer===e&&i.removeTransientObservers()}}}function a(e,n,o){for(var i=Object.create(null),a=Object.create(null),s=e;s;s=s.parentNode){var c=f.get(s);if(c)for(var u=0;u<c.length;u++){var l=c[u],p=l.options;if((s===e||p.subtree)&&("attributes"!==n||p.attributes)&&("attributes"!==n||!p.attributeFilter||null===o.namespace&&p.attributeFilter.indexOf(o.name)!==-1)&&("characterData"!==n||p.characterData)&&("childList"!==n||p.childList)){var d=l.observer;i[d.uid_]=d,("attributes"===n&&p.attributeOldValue||"characterData"===n&&p.characterDataOldValue)&&(a[d.uid_]=o.oldValue)}}}for(var h in i){var d=i[h],w=new r(n,e);"name"in o&&"namespace"in o&&(w.attributeName=o.name,w.attributeNamespace=o.namespace),o.addedNodes&&(w.addedNodes=o.addedNodes),o.removedNodes&&(w.removedNodes=o.removedNodes),o.previousSibling&&(w.previousSibling=o.previousSibling),o.nextSibling&&(w.nextSibling=o.nextSibling),void 0!==a[h]&&(w.oldValue=a[h]),t(d),d.records_.push(w)}}function s(e){if(this.childList=!!e.childList,this.subtree=!!e.subtree,"attributes"in e||!("attributeOldValue"in e||"attributeFilter"in e)?this.attributes=!!e.attributes:this.attributes=!0,"characterDataOldValue"in e&&!("characterData"in e)?this.characterData=!0:this.characterData=!!e.characterData,!this.attributes&&(e.attributeOldValue||"attributeFilter"in e)||!this.characterData&&e.characterDataOldValue)throw new TypeError;if(this.characterData=!!e.characterData,this.attributeOldValue=!!e.attributeOldValue,this.characterDataOldValue=!!e.characterDataOldValue,"attributeFilter"in e){if(null==e.attributeFilter||"object"!=typeof e.attributeFilter)throw new TypeError;this.attributeFilter=m.call(e.attributeFilter)}else this.attributeFilter=null}function c(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++v,this.scheduled_=!1}function u(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}var l=e.setEndOfMicrotask,p=e.wrapIfNeeded,d=e.wrappers,f=new WeakMap,h=[],w=!1,m=Array.prototype.slice,v=0;c.prototype={constructor:c,observe:function(e,t){e=p(e);var n,r=new s(t),o=f.get(e);o||f.set(e,o=[]);for(var i=0;i<o.length;i++)o[i].observer===this&&(n=o[i],n.removeTransientObservers(),n.options=r);n||(n=new u(this,e,r),o.push(n),this.nodes_.push(e))},disconnect:function(){this.nodes_.forEach(function(e){for(var t=f.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}},u.prototype={addTransientObserver:function(e){if(e!==this.target){t(this.observer),this.transientObservedNodes.push(e);var n=f.get(e);n||f.set(e,n=[]),n.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[];for(var t=0;t<e.length;t++)for(var n=e[t],r=f.get(n),o=0;o<r.length;o++)if(r[o]===this){r.splice(o,1);break}}},e.enqueueMutation=a,e.registerTransientObservers=o,e.wrappers.MutationObserver=c,e.wrappers.MutationRecord=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){this.root=e,this.parent=t}function n(e,t){if(e.treeScope_!==t){e.treeScope_=t;for(var r=e.shadowRoot;r;r=r.olderShadowRoot)r.treeScope_.parent=t;for(var o=e.firstChild;o;o=o.nextSibling)n(o,t)}}function r(n){if(n instanceof e.wrappers.Window,n.treeScope_)return n.treeScope_;var o,i=n.parentNode;return o=i?r(i):new t(n,null),n.treeScope_=o}t.prototype={get renderer(){return this.root instanceof e.wrappers.ShadowRoot?e.getRendererForHost(this.root.host):null},contains:function(e){for(;e;e=e.parent)if(e===this)return!0;return!1}},e.TreeScope=t,e.getTreeScope=r,e.setTreeScope=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e instanceof G.ShadowRoot}function n(e){return A(e).root}function r(e,r){var s=[],c=e;for(s.push(c);c;){var u=a(c);if(u&&u.length>0){for(var l=0;l<u.length;l++){var d=u[l];if(i(d)){var f=n(d),h=f.olderShadowRoot;h&&s.push(h)}s.push(d)}c=u[u.length-1]}else if(t(c)){if(p(e,c)&&o(r))break;c=c.host,s.push(c)}else c=c.parentNode,c&&s.push(c)}return s}function o(e){if(!e)return!1;switch(e.type){case"abort":case"error":case"select":case"change":case"load":case"reset":case"resize":case"scroll":case"selectstart":return!0}return!1}function i(e){return e instanceof HTMLShadowElement}function a(t){return e.getDestinationInsertionPoints(t)}function s(e,t){if(0===e.length)return t;t instanceof G.Window&&(t=t.document);for(var n=A(t),r=e[0],o=A(r),i=u(n,o),a=0;a<e.length;a++){var s=e[a];if(A(s)===i)return s}return e[e.length-1]}function c(e){for(var t=[];e;e=e.parent)t.push(e);return t}function u(e,t){for(var n=c(e),r=c(t),o=null;n.length>0&&r.length>0;){var i=n.pop(),a=r.pop();if(i!==a)break;o=i}return o}function l(e,t,n){t instanceof G.Window&&(t=t.document);var o,i=A(t),a=A(n),s=r(n,e),o=u(i,a);o||(o=a.root);for(var c=o;c;c=c.parent)for(var l=0;l<s.length;l++){var p=s[l];if(A(p)===c)return p}return null}function p(e,t){return A(e)===A(t)}function d(e){if(!X.get(e)&&(X.set(e,!0),h(V(e),V(e.target)),W)){var t=W;throw W=null,t}}function f(e){switch(e.type){case"load":case"beforeunload":case"unload":return!0}return!1}function h(t,n){if(K.get(t))throw new Error("InvalidStateError");K.set(t,!0),e.renderAllPending();var o,i,a;if(f(t)&&!t.bubbles){var s=n;s instanceof G.Document&&(a=s.defaultView)&&(i=s,o=[])}if(!o)if(n instanceof G.Window)a=n,o=[];else if(o=r(n,t),!f(t)){var s=o[o.length-1];s instanceof G.Document&&(a=s.defaultView)}return ne.set(t,o),w(t,o,a,i)&&m(t,o,a,i)&&v(t,o,a,i),J.set(t,re),$["delete"](t,null),K["delete"](t),t.defaultPrevented}function w(e,t,n,r){var o=oe;if(n&&!g(n,e,o,t,r))return!1;for(var i=t.length-1;i>0;i--)if(!g(t[i],e,o,t,r))return!1;return!0}function m(e,t,n,r){var o=ie,i=t[0]||n;return g(i,e,o,t,r)}function v(e,t,n,r){for(var o=ae,i=1;i<t.length;i++)if(!g(t[i],e,o,t,r))return;n&&t.length>0&&g(n,e,o,t,r)}function g(e,t,n,r,o){var i=z.get(e);if(!i)return!0;var a=o||s(r,e);if(a===e){if(n===oe)return!0;n===ae&&(n=ie)}else if(n===ae&&!t.bubbles)return!0;if("relatedTarget"in t){var c=q(t),u=c.relatedTarget;if(u){if(u instanceof Object&&u.addEventListener){var p=V(u),d=l(t,e,p);if(d===a)return!0}else d=null;Z.set(t,d)}}J.set(t,n);var f=t.type,h=!1;Y.set(t,a),$.set(t,e),i.depth++;for(var w=0,m=i.length;w<m;w++){var v=i[w];if(v.removed)h=!0;else if(!(v.type!==f||!v.capture&&n===oe||v.capture&&n===ae))try{if("function"==typeof v.handler?v.handler.call(e,t):v.handler.handleEvent(t),ee.get(t))return!1}catch(g){W||(W=g)}}if(i.depth--,h&&0===i.depth){var b=i.slice();i.length=0;for(var w=0;w<b.length;w++)b[w].removed||i.push(b[w])}return!Q.get(t)}function b(e,t,n){this.type=e,this.handler=t,this.capture=Boolean(n)}function y(e,t){if(!(e instanceof se))return V(T(se,"Event",e,t));var n=e;return be||"beforeunload"!==n.type||this instanceof O?void B(n,this):new O(n)}function E(e){return e&&e.relatedTarget?Object.create(e,{relatedTarget:{value:q(e.relatedTarget)}}):e}function S(e,t,n){var r=window[e],o=function(t,n){return t instanceof r?void B(t,this):V(T(r,e,t,n))};if(o.prototype=Object.create(t.prototype),n&&k(o.prototype,n),r)try{F(r,o,new r("temp"))}catch(i){F(r,o,document.createEvent(e))}return o}function M(e,t){return function(){arguments[t]=q(arguments[t]);var n=q(this);n[e].apply(n,arguments)}}function T(e,t,n,r){if(ve)return new e(n,E(r));var o=q(document.createEvent(t)),i=me[t],a=[n];return Object.keys(i).forEach(function(e){var t=null!=r&&e in r?r[e]:i[e];"relatedTarget"===e&&(t=q(t)),a.push(t)}),o["init"+t].apply(o,a),o}function O(e){y.call(this,e)}function N(e){return"function"==typeof e||e&&e.handleEvent}function j(e){switch(e){case"DOMAttrModified":case"DOMAttributeNameChanged":case"DOMCharacterDataModified":case"DOMElementNameChanged":case"DOMNodeInserted":case"DOMNodeInsertedIntoDocument":case"DOMNodeRemoved":case"DOMNodeRemovedFromDocument":case"DOMSubtreeModified":return!0}return!1}function L(e){B(e,this)}function _(e){return e instanceof G.ShadowRoot&&(e=e.host),q(e)}function D(e,t){var n=z.get(e);if(n)for(var r=0;r<n.length;r++)if(!n[r].removed&&n[r].type===t)return!0;return!1}function C(e,t){for(var n=q(e);n;n=n.parentNode)if(D(V(n),t))return!0;return!1}function H(e){I(e,Ee)}function x(t,n,o,i){e.renderAllPending();var a=V(Se.call(U(n),o,i));if(!a)return null;var c=r(a,null),u=c.lastIndexOf(t);return u==-1?null:(c=c.slice(0,u),s(c,t))}function R(e){return function(){var t=te.get(this);return t&&t[e]&&t[e].value||null}}function P(e){var t=e.slice(2);return function(n){var r=te.get(this);r||(r=Object.create(null),te.set(this,r));var o=r[e];if(o&&this.removeEventListener(t,o.wrapped,!1),"function"==typeof n){var i=function(t){var r=n.call(this,t);r===!1?t.preventDefault():"onbeforeunload"===e&&"string"==typeof r&&(t.returnValue=r)};this.addEventListener(t,i,!1),r[e]={value:n,wrapped:i}}}}var W,I=e.forwardMethodsToWrapper,A=e.getTreeScope,k=e.mixin,F=e.registerWrapper,B=e.setWrapper,U=e.unsafeUnwrap,q=e.unwrap,V=e.wrap,G=e.wrappers,z=(new WeakMap,new WeakMap),X=new WeakMap,K=new WeakMap,Y=new WeakMap,$=new WeakMap,Z=new WeakMap,J=new WeakMap,Q=new WeakMap,ee=new WeakMap,te=new WeakMap,ne=new WeakMap,re=0,oe=1,ie=2,ae=3;b.prototype={equals:function(e){return this.handler===e.handler&&this.type===e.type&&this.capture===e.capture},get removed(){return null===this.handler},remove:function(){this.handler=null}};var se=window.Event;se.prototype.polymerBlackList_={returnValue:!0,keyLocation:!0},y.prototype={get target(){return Y.get(this)},get currentTarget(){return $.get(this)},get eventPhase(){return J.get(this)},get path(){var e=ne.get(this);return e?e.slice():[]},stopPropagation:function(){Q.set(this,!0)},stopImmediatePropagation:function(){Q.set(this,!0),ee.set(this,!0)}};var ce=function(){var e=document.createEvent("Event");return e.initEvent("test",!0,!0),e.preventDefault(),e.defaultPrevented}();ce||(y.prototype.preventDefault=function(){this.cancelable&&(U(this).preventDefault(),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}),F(se,y,document.createEvent("Event"));var ue=S("UIEvent",y),le=S("CustomEvent",y),pe={get relatedTarget(){var e=Z.get(this);return void 0!==e?e:V(q(this).relatedTarget)}},de=k({initMouseEvent:M("initMouseEvent",14)},pe),fe=k({initFocusEvent:M("initFocusEvent",5)},pe),he=S("MouseEvent",ue,de),we=S("FocusEvent",ue,fe),me=Object.create(null),ve=function(){try{new window.FocusEvent("focus")}catch(e){return!1}return!0}();if(!ve){var ge=function(e,t,n){if(n){var r=me[n];t=k(k({},r),t)}me[e]=t};ge("Event",{bubbles:!1,cancelable:!1}),ge("CustomEvent",{detail:null},"Event"),ge("UIEvent",{view:null,detail:0},"Event"),ge("MouseEvent",{screenX:0,screenY:0,clientX:0,clientY:0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:null},"UIEvent"),ge("FocusEvent",{relatedTarget:null},"UIEvent")}var be=window.BeforeUnloadEvent;O.prototype=Object.create(y.prototype),k(O.prototype,{get returnValue(){return U(this).returnValue},set returnValue(e){U(this).returnValue=e}}),be&&F(be,O);var ye=window.EventTarget,Ee=["addEventListener","removeEventListener","dispatchEvent"];[Node,Window].forEach(function(e){var t=e.prototype;Ee.forEach(function(e){Object.defineProperty(t,e+"_",{value:t[e]})})}),L.prototype={addEventListener:function(e,t,n){if(N(t)&&!j(e)){var r=new b(e,t,n),o=z.get(this);if(o){for(var i=0;i<o.length;i++)if(r.equals(o[i]))return}else o=[],o.depth=0,z.set(this,o);o.push(r);var a=_(this);a.addEventListener_(e,d,!0)}},removeEventListener:function(e,t,n){n=Boolean(n);var r=z.get(this);if(r){for(var o=0,i=!1,a=0;a<r.length;a++)r[a].type===e&&r[a].capture===n&&(o++,r[a].handler===t&&(i=!0,r[a].remove()));if(i&&1===o){var s=_(this);s.removeEventListener_(e,d,!0)}}},dispatchEvent:function(t){var n=q(t),r=n.type;X.set(n,!1),e.renderAllPending();var o;C(this,r)||(o=function(){},this.addEventListener(r,o,!0));try{return q(this).dispatchEvent_(n)}finally{o&&this.removeEventListener(r,o,!0)}}},ye&&F(ye,L);var Se=document.elementFromPoint;e.elementFromPoint=x,e.getEventHandlerGetter=R,e.getEventHandlerSetter=P,e.wrapEventTargetMethods=H,e.wrappers.BeforeUnloadEvent=O,e.wrappers.CustomEvent=le,e.wrappers.Event=y,e.wrappers.EventTarget=L,e.wrappers.FocusEvent=we,e.wrappers.MouseEvent=he,e.wrappers.UIEvent=ue}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,w)}function n(e){u(e,this)}function r(){this.length=0,t(this,"length")}function o(e){for(var t=new r,o=0;o<e.length;o++)t[o]=new n(e[o]);return t.length=o,t}function i(e){a.call(this,e)}var a=e.wrappers.UIEvent,s=e.mixin,c=e.registerWrapper,u=e.setWrapper,l=e.unsafeUnwrap,p=e.wrap,d=window.TouchEvent;if(d){var f;try{f=document.createEvent("TouchEvent")}catch(h){return}var w={enumerable:!1};n.prototype={get target(){return p(l(this).target)}};var m={configurable:!0,enumerable:!0,get:null};["clientX","clientY","screenX","screenY","pageX","pageY","identifier","webkitRadiusX","webkitRadiusY","webkitRotationAngle","webkitForce"].forEach(function(e){m.get=function(){return l(this)[e]},Object.defineProperty(n.prototype,e,m)}),r.prototype={item:function(e){return this[e]}},i.prototype=Object.create(a.prototype),s(i.prototype,{get touches(){return o(l(this).touches)},get targetTouches(){return o(l(this).targetTouches)},get changedTouches(){return o(l(this).changedTouches)},initTouchEvent:function(){throw new Error("Not implemented")}}),c(d,i,f),e.wrappers.Touch=n,e.wrappers.TouchEvent=i,e.wrappers.TouchList=r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,s)}function n(){this.length=0,t(this,"length")}function r(e){if(null==e)return e;for(var t=new n,r=0,o=e.length;r<o;r++)t[r]=a(e[r]);return t.length=o,t}function o(e,t){e.prototype[t]=function(){return r(i(this)[t].apply(i(this),arguments))}}var i=e.unsafeUnwrap,a=e.wrap,s={enumerable:!1};n.prototype={item:function(e){return this[e]}},t(n.prototype,"item"),e.wrappers.NodeList=n,e.addWrapNodeListMethod=o,e.wrapNodeList=r}(window.ShadowDOMPolyfill),function(e){"use strict";e.wrapHTMLCollection=e.wrapNodeList,e.wrappers.HTMLCollection=e.wrappers.NodeList}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){N(e instanceof S)}function n(e){var t=new T;return t[0]=e,t.length=1,t}function r(e,t,n){L(t,"childList",{removedNodes:n,previousSibling:e.previousSibling,nextSibling:e.nextSibling})}function o(e,t){L(e,"childList",{removedNodes:t})}function i(e,t,r,o){if(e instanceof DocumentFragment){var i=s(e);B=!0;for(var a=i.length-1;a>=0;a--)e.removeChild(i[a]),i[a].parentNode_=t;B=!1;for(var a=0;a<i.length;a++)i[a].previousSibling_=i[a-1]||r,i[a].nextSibling_=i[a+1]||o;return r&&(r.nextSibling_=i[0]),o&&(o.previousSibling_=i[i.length-1]),i}var i=n(e),c=e.parentNode;return c&&c.removeChild(e),e.parentNode_=t,e.previousSibling_=r,e.nextSibling_=o,r&&(r.nextSibling_=e),o&&(o.previousSibling_=e),i}function a(e){if(e instanceof DocumentFragment)return s(e);var t=n(e),o=e.parentNode;return o&&r(e,o,t),t}function s(e){for(var t=new T,n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t.length=n,o(e,t),t}function c(e){return e}function u(e,t){R(e,t),e.nodeIsInserted_()}function l(e,t){for(var n=_(t),r=0;r<e.length;r++)u(e[r],n)}function p(e){R(e,new O(e,null))}function d(e){for(var t=0;t<e.length;t++)p(e[t])}function f(e,t){var n=e.nodeType===S.DOCUMENT_NODE?e:e.ownerDocument;n!==t.ownerDocument&&n.adoptNode(t)}function h(t,n){if(n.length){var r=t.ownerDocument;if(r!==n[0].ownerDocument)for(var o=0;o<n.length;o++)e.adoptNodeNoRemove(n[o],r)}}function w(e,t){h(e,t);var n=t.length;if(1===n)return W(t[0]);for(var r=W(e.ownerDocument.createDocumentFragment()),o=0;o<n;o++)r.appendChild(W(t[o]));return r}function m(e){if(void 0!==e.firstChild_)for(var t=e.firstChild_;t;){var n=t;t=t.nextSibling_,n.parentNode_=n.previousSibling_=n.nextSibling_=void 0}e.firstChild_=e.lastChild_=void 0}function v(e){if(e.invalidateShadowRenderer()){for(var t=e.firstChild;t;){N(t.parentNode===e);var n=t.nextSibling,r=W(t),o=r.parentNode;o&&Y.call(o,r),t.previousSibling_=t.nextSibling_=t.parentNode_=null,t=n}e.firstChild_=e.lastChild_=null}else for(var n,i=W(e),a=i.firstChild;a;)n=a.nextSibling,Y.call(i,a),a=n}function g(e){var t=e.parentNode;return t&&t.invalidateShadowRenderer()}function b(e){for(var t,n=0;n<e.length;n++)t=e[n],t.parentNode.removeChild(t)}function y(e,t,n){var r;if(r=A(n?U.call(n,P(e),!1):q.call(P(e),!1)),t){for(var o=e.firstChild;o;o=o.nextSibling)r.appendChild(y(o,!0,n));if(e instanceof F.HTMLTemplateElement)for(var i=r.content,o=e.content.firstChild;o;o=o.nextSibling)i.appendChild(y(o,!0,n))}return r}function E(e,t){if(!t||_(e)!==_(t))return!1;for(var n=t;n;n=n.parentNode)if(n===e)return!0;return!1}function S(e){N(e instanceof V),M.call(this,e),this.parentNode_=void 0,this.firstChild_=void 0,this.lastChild_=void 0,this.nextSibling_=void 0,this.previousSibling_=void 0,this.treeScope_=void 0}var M=e.wrappers.EventTarget,T=e.wrappers.NodeList,O=e.TreeScope,N=e.assert,j=e.defineWrapGetter,L=e.enqueueMutation,_=e.getTreeScope,D=e.isWrapper,C=e.mixin,H=e.registerTransientObservers,x=e.registerWrapper,R=e.setTreeScope,P=e.unsafeUnwrap,W=e.unwrap,I=e.unwrapIfNeeded,A=e.wrap,k=e.wrapIfNeeded,F=e.wrappers,B=!1,U=document.importNode,q=window.Node.prototype.cloneNode,V=window.Node,G=window.DocumentFragment,z=(V.prototype.appendChild,V.prototype.compareDocumentPosition),X=V.prototype.isEqualNode,K=V.prototype.insertBefore,Y=V.prototype.removeChild,$=V.prototype.replaceChild,Z=/Trident|Edge/.test(navigator.userAgent),J=Z?function(e,t){try{Y.call(e,t)}catch(n){if(!(e instanceof G))throw n}}:function(e,t){Y.call(e,t)};S.prototype=Object.create(M.prototype),C(S.prototype,{appendChild:function(e){return this.insertBefore(e,null)},insertBefore:function(e,n){t(e);var r;n?D(n)?r=W(n):(r=n,n=A(r)):(n=null,r=null),n&&N(n.parentNode===this);var o,s=n?n.previousSibling:this.lastChild,c=!this.invalidateShadowRenderer()&&!g(e);if(o=c?a(e):i(e,this,s,n),c)f(this,e),m(this),K.call(P(this),W(e),r);else{s||(this.firstChild_=o[0]),n||(this.lastChild_=o[o.length-1],void 0===this.firstChild_&&(this.firstChild_=this.firstChild));var u=r?r.parentNode:P(this);u?K.call(u,w(this,o),r):h(this,o)}return L(this,"childList",{addedNodes:o,nextSibling:n,previousSibling:s}),l(o,this),e},removeChild:function(e){if(t(e),e.parentNode!==this){for(var r=!1,o=(this.childNodes,this.firstChild);o;o=o.nextSibling)if(o===e){r=!0;break}if(!r)throw new Error("NotFoundError")}var i=W(e),a=e.nextSibling,s=e.previousSibling;if(this.invalidateShadowRenderer()){var c=this.firstChild,u=this.lastChild,l=i.parentNode;l&&J(l,i),c===e&&(this.firstChild_=a),u===e&&(this.lastChild_=s),s&&(s.nextSibling_=a),a&&(a.previousSibling_=s),e.previousSibling_=e.nextSibling_=e.parentNode_=void 0}else m(this),J(P(this),i);return B||L(this,"childList",{removedNodes:n(e),nextSibling:a,previousSibling:s}),H(this,e),e},replaceChild:function(e,r){t(e);var o;if(D(r)?o=W(r):(o=r,r=A(o)),r.parentNode!==this)throw new Error("NotFoundError");var s,c=r.nextSibling,u=r.previousSibling,d=!this.invalidateShadowRenderer()&&!g(e);return d?s=a(e):(c===e&&(c=e.nextSibling),s=i(e,this,u,c)),d?(f(this,e),m(this),$.call(P(this),W(e),o)):(this.firstChild===r&&(this.firstChild_=s[0]),this.lastChild===r&&(this.lastChild_=s[s.length-1]),r.previousSibling_=r.nextSibling_=r.parentNode_=void 0,o.parentNode&&$.call(o.parentNode,w(this,s),o)),L(this,"childList",{addedNodes:s,removedNodes:n(r),nextSibling:c,previousSibling:u}),p(r),l(s,this),r},nodeIsInserted_:function(){for(var e=this.firstChild;e;e=e.nextSibling)e.nodeIsInserted_()},hasChildNodes:function(){return null!==this.firstChild},get parentNode(){return void 0!==this.parentNode_?this.parentNode_:A(P(this).parentNode)},get firstChild(){return void 0!==this.firstChild_?this.firstChild_:A(P(this).firstChild)},get lastChild(){return void 0!==this.lastChild_?this.lastChild_:A(P(this).lastChild)},get nextSibling(){return void 0!==this.nextSibling_?this.nextSibling_:A(P(this).nextSibling)},get previousSibling(){return void 0!==this.previousSibling_?this.previousSibling_:A(P(this).previousSibling)},get parentElement(){for(var e=this.parentNode;e&&e.nodeType!==S.ELEMENT_NODE;)e=e.parentNode;return e},get textContent(){for(var e="",t=this.firstChild;t;t=t.nextSibling)t.nodeType!=S.COMMENT_NODE&&(e+=t.textContent);return e},set textContent(e){null==e&&(e="");var t=c(this.childNodes);if(this.invalidateShadowRenderer()){if(v(this),""!==e){var n=P(this).ownerDocument.createTextNode(e);this.appendChild(n)}}else m(this),P(this).textContent=e;var r=c(this.childNodes);L(this,"childList",{addedNodes:r,removedNodes:t}),d(t),l(r,this)},get childNodes(){for(var e=new T,t=0,n=this.firstChild;n;n=n.nextSibling)e[t++]=n;return e.length=t,e},cloneNode:function(e){return y(this,e)},contains:function(e){return E(this,k(e))},compareDocumentPosition:function(e){return z.call(P(this),I(e))},isEqualNode:function(e){return X.call(P(this),I(e))},normalize:function(){for(var e,t,n=c(this.childNodes),r=[],o="",i=0;i<n.length;i++)t=n[i],t.nodeType===S.TEXT_NODE?e||t.data.length?e?(o+=t.data,r.push(t)):e=t:this.removeChild(t):(e&&r.length&&(e.data+=o,b(r)),r=[],o="",e=null,t.childNodes.length&&t.normalize());e&&r.length&&(e.data+=o,b(r))}}),j(S,"ownerDocument"),x(V,S,document.createDocumentFragment()),delete S.prototype.querySelector,delete S.prototype.querySelectorAll,S.prototype=C(Object.create(M.prototype),S.prototype),e.cloneNode=y,e.nodeWasAdded=u,e.nodeWasRemoved=p,e.nodesWereAdded=l,e.nodesWereRemoved=d,e.originalInsertBefore=K,e.originalRemoveChild=Y,e.snapshotNodeList=c,e.wrappers.Node=S}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n,r,o){for(var i=null,a=null,s=0,c=t.length;s<c;s++)i=b(t[s]),!o&&(a=v(i).root)&&a instanceof e.wrappers.ShadowRoot||(r[n++]=i);return n}function n(e){return String(e).replace(/\/deep\/|::shadow|>>>/g," ")}function r(e){return String(e).replace(/:host\(([^\s]+)\)/g,"$1").replace(/([^\s]):host/g,"$1").replace(":host","*").replace(/\^|\/shadow\/|\/shadow-deep\/|::shadow|\/deep\/|::content|>>>/g," ")}function o(e,t){for(var n,r=e.firstElementChild;r;){if(r.matches(t))return r;if(n=o(r,t))return n;r=r.nextElementSibling}return null}function i(e,t){return e.matches(t)}function a(e,t,n){var r=e.localName;return r===t||r===n&&e.namespaceURI===D}function s(){return!0}function c(e,t,n){return e.localName===n}function u(e,t){return e.namespaceURI===t}function l(e,t,n){return e.namespaceURI===t&&e.localName===n}function p(e,t,n,r,o,i){for(var a=e.firstElementChild;a;)r(a,o,i)&&(n[t++]=a),t=p(a,t,n,r,o,i),a=a.nextElementSibling;return t}function d(n,r,o,i,a){var s,c=g(this),u=v(this).root;if(u instanceof e.wrappers.ShadowRoot)return p(this,r,o,n,i,null);if(c instanceof L)s=M.call(c,i);else{if(!(c instanceof _))return p(this,r,o,n,i,null);s=S.call(c,i)}return t(s,r,o,a)}function f(n,r,o,i,a){var s,c=g(this),u=v(this).root;if(u instanceof e.wrappers.ShadowRoot)return p(this,r,o,n,i,a);if(c instanceof L)s=O.call(c,i,a);else{if(!(c instanceof _))return p(this,r,o,n,i,a);s=T.call(c,i,a)}return t(s,r,o,!1)}function h(n,r,o,i,a){var s,c=g(this),u=v(this).root;if(u instanceof e.wrappers.ShadowRoot)return p(this,r,o,n,i,a);if(c instanceof L)s=j.call(c,i,a);else{if(!(c instanceof _))return p(this,r,o,n,i,a);s=N.call(c,i,a)}return t(s,r,o,!1)}var w=e.wrappers.HTMLCollection,m=e.wrappers.NodeList,v=e.getTreeScope,g=e.unsafeUnwrap,b=e.wrap,y=document.querySelector,E=document.documentElement.querySelector,S=document.querySelectorAll,M=document.documentElement.querySelectorAll,T=document.getElementsByTagName,O=document.documentElement.getElementsByTagName,N=document.getElementsByTagNameNS,j=document.documentElement.getElementsByTagNameNS,L=window.Element,_=window.HTMLDocument||window.Document,D="http://www.w3.org/1999/xhtml",C={querySelector:function(t){var r=n(t),i=r!==t;t=r;var a,s=g(this),c=v(this).root;if(c instanceof e.wrappers.ShadowRoot)return o(this,t);if(s instanceof L)a=b(E.call(s,t));else{if(!(s instanceof _))return o(this,t);a=b(y.call(s,t))}return a&&!i&&(c=v(a).root)&&c instanceof e.wrappers.ShadowRoot?o(this,t):a},querySelectorAll:function(e){var t=n(e),r=t!==e;e=t;var o=new m;return o.length=d.call(this,i,0,o,e,r),o}},H={matches:function(t){return t=r(t),e.originalMatches.call(g(this),t)}},x={getElementsByTagName:function(e){var t=new w,n="*"===e?s:a;return t.length=f.call(this,n,0,t,e,e.toLowerCase()),
+t},getElementsByClassName:function(e){return this.querySelectorAll("."+e)},getElementsByTagNameNS:function(e,t){var n=new w,r=null;return r="*"===e?"*"===t?s:c:"*"===t?u:l,n.length=h.call(this,r,0,n,e||null,t),n}};e.GetElementsByInterface=x,e.SelectorsInterface=C,e.MatchesInterface=H}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.nextSibling;return e}function n(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.previousSibling;return e}var r=e.wrappers.NodeList,o={get firstElementChild(){return t(this.firstChild)},get lastElementChild(){return n(this.lastChild)},get childElementCount(){for(var e=0,t=this.firstElementChild;t;t=t.nextElementSibling)e++;return e},get children(){for(var e=new r,t=0,n=this.firstElementChild;n;n=n.nextElementSibling)e[t++]=n;return e.length=t,e},remove:function(){var e=this.parentNode;e&&e.removeChild(this)}},i={get nextElementSibling(){return t(this.nextSibling)},get previousElementSibling(){return n(this.previousSibling)}},a={getElementById:function(e){return/[ \t\n\r\f]/.test(e)?null:this.querySelector('[id="'+e+'"]')}};e.ChildNodeInterface=i,e.NonElementParentNodeInterface=a,e.ParentNodeInterface=o}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}var n=e.ChildNodeInterface,r=e.wrappers.Node,o=e.enqueueMutation,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=window.CharacterData;t.prototype=Object.create(r.prototype),i(t.prototype,{get nodeValue(){return this.data},set nodeValue(e){this.data=e},get textContent(){return this.data},set textContent(e){this.data=e},get data(){return s(this).data},set data(e){var t=s(this).data;o(this,"characterData",{oldValue:t}),s(this).data=e}}),i(t.prototype,n),a(c,t,document.createTextNode("")),e.wrappers.CharacterData=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e>>>0}function n(e){r.call(this,e)}var r=e.wrappers.CharacterData,o=(e.enqueueMutation,e.mixin),i=e.registerWrapper,a=window.Text;n.prototype=Object.create(r.prototype),o(n.prototype,{splitText:function(e){e=t(e);var n=this.data;if(e>n.length)throw new Error("IndexSizeError");var r=n.slice(0,e),o=n.slice(e);this.data=r;var i=this.ownerDocument.createTextNode(o);return this.parentNode&&this.parentNode.insertBefore(i,this.nextSibling),i}}),i(a,n,document.createTextNode("")),e.wrappers.Text=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return i(e).getAttribute("class")}function n(e,t){a(e,"attributes",{name:"class",namespace:null,oldValue:t})}function r(t){e.invalidateRendererBasedOnAttribute(t,"class")}function o(e,o,i){var a=e.ownerElement_;if(null==a)return o.apply(e,i);var s=t(a),c=o.apply(e,i);return t(a)!==s&&(n(a,s),r(a)),c}if(!window.DOMTokenList)return void console.warn("Missing DOMTokenList prototype, please include a compatible classList polyfill such as http://goo.gl/uTcepH.");var i=e.unsafeUnwrap,a=e.enqueueMutation,s=DOMTokenList.prototype.add;DOMTokenList.prototype.add=function(){o(this,s,arguments)};var c=DOMTokenList.prototype.remove;DOMTokenList.prototype.remove=function(){o(this,c,arguments)};var u=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(){return o(this,u,arguments)}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n){var r=t.parentNode;if(r&&r.shadowRoot){var o=e.getRendererForHost(r);o.dependsOnAttribute(n)&&o.invalidate()}}function n(e,t,n){l(e,"attributes",{name:t,namespace:null,oldValue:n})}function r(e){a.call(this,e)}var o=e.ChildNodeInterface,i=e.GetElementsByInterface,a=e.wrappers.Node,s=e.ParentNodeInterface,c=e.SelectorsInterface,u=e.MatchesInterface,l=(e.addWrapNodeListMethod,e.enqueueMutation),p=e.mixin,d=(e.oneOf,e.registerWrapper),f=e.unsafeUnwrap,h=e.wrappers,w=window.Element,m=["matches","mozMatchesSelector","msMatchesSelector","webkitMatchesSelector"].filter(function(e){return w.prototype[e]}),v=m[0],g=w.prototype[v],b=new WeakMap;r.prototype=Object.create(a.prototype),p(r.prototype,{createShadowRoot:function(){var t=new h.ShadowRoot(this);f(this).polymerShadowRoot_=t;var n=e.getRendererForHost(this);return n.invalidate(),t},get shadowRoot(){return f(this).polymerShadowRoot_||null},setAttribute:function(e,r){var o=f(this).getAttribute(e);f(this).setAttribute(e,r),n(this,e,o),t(this,e)},removeAttribute:function(e){var r=f(this).getAttribute(e);f(this).removeAttribute(e),n(this,e,r),t(this,e)},get classList(){var e=b.get(this);if(!e){if(e=f(this).classList,!e)return;e.ownerElement_=this,b.set(this,e)}return e},get className(){return f(this).className},set className(e){this.setAttribute("class",e)},get id(){return f(this).id},set id(e){this.setAttribute("id",e)}}),m.forEach(function(e){"matches"!==e&&(r.prototype[e]=function(e){return this.matches(e)})}),w.prototype.webkitCreateShadowRoot&&(r.prototype.webkitCreateShadowRoot=r.prototype.createShadowRoot),p(r.prototype,o),p(r.prototype,i),p(r.prototype,s),p(r.prototype,c),p(r.prototype,u),d(w,r,document.createElementNS(null,"x")),e.invalidateRendererBasedOnAttribute=t,e.matchesNames=m,e.originalMatches=g,e.wrappers.Element=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";case" ":return"&nbsp;"}}function n(e){return e.replace(j,t)}function r(e){return e.replace(L,t)}function o(e){for(var t={},n=0;n<e.length;n++)t[e[n]]=!0;return t}function i(e){if(e.namespaceURI!==C)return!0;var t=e.ownerDocument.doctype;return t&&t.publicId&&t.systemId}function a(e,t){switch(e.nodeType){case Node.ELEMENT_NODE:for(var o,a=e.tagName.toLowerCase(),c="<"+a,u=e.attributes,l=0;o=u[l];l++)c+=" "+o.name+'="'+n(o.value)+'"';return _[a]?(i(e)&&(c+="/"),c+">"):c+">"+s(e)+"</"+a+">";case Node.TEXT_NODE:var p=e.data;return t&&D[t.localName]?p:r(p);case Node.COMMENT_NODE:return"<!--"+e.data+"-->";default:throw console.error(e),new Error("not implemented")}}function s(e){e instanceof N.HTMLTemplateElement&&(e=e.content);for(var t="",n=e.firstChild;n;n=n.nextSibling)t+=a(n,e);return t}function c(e,t,n){var r=n||"div";e.textContent="";var o=T(e.ownerDocument.createElement(r));o.innerHTML=t;for(var i;i=o.firstChild;)e.appendChild(O(i))}function u(e){w.call(this,e)}function l(e,t){var n=T(e.cloneNode(!1));n.innerHTML=t;for(var r,o=T(document.createDocumentFragment());r=n.firstChild;)o.appendChild(r);return O(o)}function p(t){return function(){return e.renderAllPending(),M(this)[t]}}function d(e){m(u,e,p(e))}function f(t){Object.defineProperty(u.prototype,t,{get:p(t),set:function(n){e.renderAllPending(),M(this)[t]=n},configurable:!0,enumerable:!0})}function h(t){Object.defineProperty(u.prototype,t,{value:function(){return e.renderAllPending(),M(this)[t].apply(M(this),arguments)},configurable:!0,enumerable:!0})}var w=e.wrappers.Element,m=e.defineGetter,v=e.enqueueMutation,g=e.mixin,b=e.nodesWereAdded,y=e.nodesWereRemoved,E=e.registerWrapper,S=e.snapshotNodeList,M=e.unsafeUnwrap,T=e.unwrap,O=e.wrap,N=e.wrappers,j=/[&\u00A0"]/g,L=/[&\u00A0<>]/g,_=o(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),D=o(["style","script","xmp","iframe","noembed","noframes","plaintext","noscript"]),C="http://www.w3.org/1999/xhtml",H=/MSIE/.test(navigator.userAgent),x=window.HTMLElement,R=window.HTMLTemplateElement;u.prototype=Object.create(w.prototype),g(u.prototype,{get innerHTML(){return s(this)},set innerHTML(e){if(H&&D[this.localName])return void(this.textContent=e);var t=S(this.childNodes);this.invalidateShadowRenderer()?this instanceof N.HTMLTemplateElement?c(this.content,e):c(this,e,this.tagName):!R&&this instanceof N.HTMLTemplateElement?c(this.content,e):M(this).innerHTML=e;var n=S(this.childNodes);v(this,"childList",{addedNodes:n,removedNodes:t}),y(t),b(n,this)},get outerHTML(){return a(this,this.parentNode)},set outerHTML(e){var t=this.parentNode;if(t){t.invalidateShadowRenderer();var n=l(t,e);t.replaceChild(n,this)}},insertAdjacentHTML:function(e,t){var n,r;switch(String(e).toLowerCase()){case"beforebegin":n=this.parentNode,r=this;break;case"afterend":n=this.parentNode,r=this.nextSibling;break;case"afterbegin":n=this,r=this.firstChild;break;case"beforeend":n=this,r=null;break;default:return}var o=l(n,t);n.insertBefore(o,r)},get hidden(){return this.hasAttribute("hidden")},set hidden(e){e?this.setAttribute("hidden",""):this.removeAttribute("hidden")}}),["clientHeight","clientLeft","clientTop","clientWidth","offsetHeight","offsetLeft","offsetTop","offsetWidth","scrollHeight","scrollWidth"].forEach(d),["scrollLeft","scrollTop"].forEach(f),["focus","getBoundingClientRect","getClientRects","scrollIntoView"].forEach(h),E(x,u,document.createElement("b")),e.wrappers.HTMLElement=u,e.getInnerHTML=s,e.setInnerHTML=c}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.HTMLCanvasElement;t.prototype=Object.create(n.prototype),r(t.prototype,{getContext:function(){var e=i(this).getContext.apply(i(this),arguments);return e&&a(e)}}),o(s,t,document.createElement("canvas")),e.wrappers.HTMLCanvasElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=window.HTMLContentElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get select(){return this.getAttribute("select")},set select(e){this.setAttribute("select",e)},setAttribute:function(e,t){n.prototype.setAttribute.call(this,e,t),"select"===String(e).toLowerCase()&&this.invalidateShadowRenderer(!0)}}),i&&o(i,t),e.wrappers.HTMLContentElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=window.HTMLFormElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get elements(){return i(a(this).elements)}}),o(s,t,document.createElement("form")),e.wrappers.HTMLFormElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e,t){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var o=i(document.createElement("img"));r.call(this,o),a(o,this),void 0!==e&&(o.width=e),void 0!==t&&(o.height=t)}var r=e.wrappers.HTMLElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLImageElement;t.prototype=Object.create(r.prototype),o(s,t,document.createElement("img")),n.prototype=t.prototype,e.wrappers.HTMLImageElement=t,e.wrappers.Image=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=(e.mixin,e.wrappers.NodeList,e.registerWrapper),o=window.HTMLShadowElement;t.prototype=Object.create(n.prototype),t.prototype.constructor=t,o&&r(o,t),e.wrappers.HTMLShadowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){if(!e.defaultView)return e;var t=p.get(e);if(!t){for(t=e.implementation.createHTMLDocument("");t.lastChild;)t.removeChild(t.lastChild);p.set(e,t)}return t}function n(e){for(var n,r=t(e.ownerDocument),o=c(r.createDocumentFragment());n=e.firstChild;)o.appendChild(n);return o}function r(e){if(o.call(this,e),!d){var t=n(e);l.set(this,u(t))}}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=e.unwrap,u=e.wrap,l=new WeakMap,p=new WeakMap,d=window.HTMLTemplateElement;r.prototype=Object.create(o.prototype),i(r.prototype,{constructor:r,get content(){return d?u(s(this).content):l.get(this)}}),d&&a(d,r),e.wrappers.HTMLTemplateElement=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.registerWrapper,o=window.HTMLMediaElement;o&&(t.prototype=Object.create(n.prototype),r(o,t,document.createElement("audio")),e.wrappers.HTMLMediaElement=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var t=i(document.createElement("audio"));r.call(this,t),a(t,this),t.setAttribute("preload","auto"),void 0!==e&&t.setAttribute("src",e)}var r=e.wrappers.HTMLMediaElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLAudioElement;s&&(t.prototype=Object.create(r.prototype),o(s,t,document.createElement("audio")),n.prototype=t.prototype,e.wrappers.HTMLAudioElement=t,e.wrappers.Audio=n)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e.replace(/\s+/g," ").trim()}function n(e){o.call(this,e)}function r(e,t,n,i){if(!(this instanceof r))throw new TypeError("DOM object constructor cannot be called as a function.");var a=c(document.createElement("option"));o.call(this,a),s(a,this),void 0!==e&&(a.text=e),void 0!==t&&a.setAttribute("value",t),n===!0&&a.setAttribute("selected",""),a.selected=i===!0}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.rewrap,c=e.unwrap,u=e.wrap,l=window.HTMLOptionElement;n.prototype=Object.create(o.prototype),i(n.prototype,{get text(){return t(this.textContent)},set text(e){this.textContent=t(String(e))},get form(){return u(c(this).form)}}),a(l,n,document.createElement("option")),r.prototype=n.prototype,e.wrappers.HTMLOptionElement=n,e.wrappers.Option=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=window.HTMLSelectElement;t.prototype=Object.create(n.prototype),r(t.prototype,{add:function(e,t){"object"==typeof t&&(t=i(t)),i(this).add(i(e),t)},remove:function(e){return void 0===e?void n.prototype.remove.call(this):("object"==typeof e&&(e=i(e)),void i(this).remove(e))},get form(){return a(i(this).form)}}),o(s,t,document.createElement("select")),e.wrappers.HTMLSelectElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=e.wrapHTMLCollection,c=window.HTMLTableElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get caption(){return a(i(this).caption)},createCaption:function(){return a(i(this).createCaption())},get tHead(){return a(i(this).tHead)},createTHead:function(){return a(i(this).createTHead())},createTFoot:function(){return a(i(this).createTFoot())},get tFoot(){return a(i(this).tFoot)},get tBodies(){return s(i(this).tBodies)},createTBody:function(){return a(i(this).createTBody())},get rows(){return s(i(this).rows)},insertRow:function(e){return a(i(this).insertRow(e))}}),o(c,t,document.createElement("table")),e.wrappers.HTMLTableElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableSectionElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get rows(){return i(a(this).rows)},insertRow:function(e){return s(a(this).insertRow(e))}}),o(c,t,document.createElement("thead")),e.wrappers.HTMLTableSectionElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableRowElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get cells(){return i(a(this).cells)},insertCell:function(e){return s(a(this).insertCell(e))}}),o(c,t,document.createElement("tr")),e.wrappers.HTMLTableRowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e.localName){case"content":return new n(e);case"shadow":return new o(e);case"template":return new i(e)}r.call(this,e)}var n=e.wrappers.HTMLContentElement,r=e.wrappers.HTMLElement,o=e.wrappers.HTMLShadowElement,i=e.wrappers.HTMLTemplateElement,a=(e.mixin,e.registerWrapper),s=window.HTMLUnknownElement;t.prototype=Object.create(r.prototype),a(s,t),e.wrappers.HTMLUnknownElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.Element,r=e.wrappers.HTMLElement,o=e.registerWrapper,i=(e.defineWrapGetter,e.unsafeUnwrap),a=e.wrap,s=e.mixin,c="http://www.w3.org/2000/svg",u=window.SVGElement,l=document.createElementNS(c,"title");if(!("classList"in l)){var p=Object.getOwnPropertyDescriptor(n.prototype,"classList");Object.defineProperty(r.prototype,"classList",p),delete n.prototype.classList}t.prototype=Object.create(n.prototype),s(t.prototype,{get ownerSVGElement(){return a(i(this).ownerSVGElement)}}),o(u,t,document.createElementNS(c,"title")),e.wrappers.SVGElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){d.call(this,e)}var n=e.mixin,r=e.registerWrapper,o=e.unwrap,i=e.wrap,a=window.SVGUseElement,s="http://www.w3.org/2000/svg",c=i(document.createElementNS(s,"g")),u=document.createElementNS(s,"use"),l=c.constructor,p=Object.getPrototypeOf(l.prototype),d=p.constructor;t.prototype=Object.create(p),"instanceRoot"in u&&n(t.prototype,{get instanceRoot(){return i(o(this).instanceRoot)},get animatedInstanceRoot(){return i(o(this).animatedInstanceRoot)}}),r(a,t,u),e.wrappers.SVGUseElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.SVGElementInstance;s&&(t.prototype=Object.create(n.prototype),r(t.prototype,{get correspondingElement(){return a(i(this).correspondingElement)},get correspondingUseElement(){return a(i(this).correspondingUseElement)},get parentNode(){return a(i(this).parentNode)},get childNodes(){throw new Error("Not implemented")},get firstChild(){return a(i(this).firstChild)},get lastChild(){return a(i(this).lastChild)},get previousSibling(){return a(i(this).previousSibling)},get nextSibling(){return a(i(this).nextSibling)}}),o(s,t),e.wrappers.SVGElementInstance=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){o(e,this)}var n=e.mixin,r=e.registerWrapper,o=e.setWrapper,i=e.unsafeUnwrap,a=e.unwrap,s=e.unwrapIfNeeded,c=e.wrap,u=window.CanvasRenderingContext2D;n(t.prototype,{get canvas(){return c(i(this).canvas)},drawImage:function(){arguments[0]=s(arguments[0]),i(this).drawImage.apply(i(this),arguments)},createPattern:function(){return arguments[0]=a(arguments[0]),i(this).createPattern.apply(i(this),arguments)}}),r(u,t,document.createElement("canvas").getContext("2d")),e.wrappers.CanvasRenderingContext2D=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){i(e,this)}var n=e.addForwardingProperties,r=e.mixin,o=e.registerWrapper,i=e.setWrapper,a=e.unsafeUnwrap,s=e.unwrapIfNeeded,c=e.wrap,u=window.WebGLRenderingContext;if(u){r(t.prototype,{get canvas(){return c(a(this).canvas)},texImage2D:function(){arguments[5]=s(arguments[5]),a(this).texImage2D.apply(a(this),arguments)},texSubImage2D:function(){arguments[6]=s(arguments[6]),a(this).texSubImage2D.apply(a(this),arguments)}});var l=Object.getPrototypeOf(u.prototype);l!==Object.prototype&&n(l,t.prototype);var p=/WebKit/.test(navigator.userAgent)?{drawingBufferHeight:null,drawingBufferWidth:null}:{};o(u,t,p),e.wrappers.WebGLRenderingContext=t}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.Node,r=e.GetElementsByInterface,o=e.NonElementParentNodeInterface,i=e.ParentNodeInterface,a=e.SelectorsInterface,s=e.mixin,c=e.registerObject,u=e.registerWrapper,l=window.DocumentFragment;t.prototype=Object.create(n.prototype),s(t.prototype,i),s(t.prototype,a),s(t.prototype,r),s(t.prototype,o),u(l,t,document.createDocumentFragment()),e.wrappers.DocumentFragment=t;var p=c(document.createComment(""));e.wrappers.Comment=p}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=p(l(e).ownerDocument.createDocumentFragment());n.call(this,t),c(t,this);var o=e.shadowRoot;h.set(this,o),this.treeScope_=new r(this,a(o||e)),f.set(this,e)}var n=e.wrappers.DocumentFragment,r=e.TreeScope,o=e.elementFromPoint,i=e.getInnerHTML,a=e.getTreeScope,s=e.mixin,c=e.rewrap,u=e.setInnerHTML,l=e.unsafeUnwrap,p=e.unwrap,d=e.wrap,f=new WeakMap,h=new WeakMap;t.prototype=Object.create(n.prototype),s(t.prototype,{constructor:t,get innerHTML(){return i(this)},set innerHTML(e){u(this,e),this.invalidateShadowRenderer()},get olderShadowRoot(){return h.get(this)||null},get host(){return f.get(this)||null},invalidateShadowRenderer:function(){return f.get(this).invalidateShadowRenderer()},elementFromPoint:function(e,t){return o(this,this.ownerDocument,e,t)},getSelection:function(){return document.getSelection()},get activeElement(){var e=p(this).ownerDocument.activeElement;if(!e||!e.nodeType)return null;for(var t=d(e);!this.contains(t);){for(;t.parentNode;)t=t.parentNode;if(!t.host)return null;t=t.host}return t}}),e.wrappers.ShadowRoot=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=p(e).root;return t instanceof f?t.host:null}function n(t,n){if(t.shadowRoot){n=Math.min(t.childNodes.length-1,n);var r=t.childNodes[n];if(r){var o=e.getDestinationInsertionPoints(r);if(o.length>0){var i=o[0].parentNode;i.nodeType==Node.ELEMENT_NODE&&(t=i)}}}return t}function r(e){return e=l(e),t(e)||e}function o(e){a(e,this)}var i=e.registerWrapper,a=e.setWrapper,s=e.unsafeUnwrap,c=e.unwrap,u=e.unwrapIfNeeded,l=e.wrap,p=e.getTreeScope,d=window.Range,f=e.wrappers.ShadowRoot;o.prototype={get startContainer(){return r(s(this).startContainer)},get endContainer(){return r(s(this).endContainer)},get commonAncestorContainer(){return r(s(this).commonAncestorContainer)},setStart:function(e,t){e=n(e,t),s(this).setStart(u(e),t)},setEnd:function(e,t){e=n(e,t),s(this).setEnd(u(e),t)},setStartBefore:function(e){s(this).setStartBefore(u(e))},setStartAfter:function(e){s(this).setStartAfter(u(e))},setEndBefore:function(e){s(this).setEndBefore(u(e))},setEndAfter:function(e){s(this).setEndAfter(u(e))},selectNode:function(e){s(this).selectNode(u(e))},selectNodeContents:function(e){s(this).selectNodeContents(u(e))},compareBoundaryPoints:function(e,t){return s(this).compareBoundaryPoints(e,c(t))},extractContents:function(){return l(s(this).extractContents())},cloneContents:function(){return l(s(this).cloneContents())},insertNode:function(e){s(this).insertNode(u(e))},surroundContents:function(e){s(this).surroundContents(u(e))},cloneRange:function(){return l(s(this).cloneRange())},isPointInRange:function(e,t){return s(this).isPointInRange(u(e),t)},comparePoint:function(e,t){return s(this).comparePoint(u(e),t)},intersectsNode:function(e){return s(this).intersectsNode(u(e))},toString:function(){return s(this).toString()}},d.prototype.createContextualFragment&&(o.prototype.createContextualFragment=function(e){return l(s(this).createContextualFragment(e))}),i(window.Range,o,document.createRange()),e.wrappers.Range=o}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.previousSibling_=e.previousSibling,e.nextSibling_=e.nextSibling,e.parentNode_=e.parentNode}function n(n,o,i){var a=x(n),s=x(o),c=i?x(i):null;if(r(o),t(o),i)n.firstChild===i&&(n.firstChild_=i),i.previousSibling_=i.previousSibling;else{n.lastChild_=n.lastChild,n.lastChild===n.firstChild&&(n.firstChild_=n.firstChild);var u=R(a.lastChild);u&&(u.nextSibling_=u.nextSibling)}e.originalInsertBefore.call(a,s,c)}function r(n){var r=x(n),o=r.parentNode;if(o){var i=R(o);t(n),n.previousSibling&&(n.previousSibling.nextSibling_=n),n.nextSibling&&(n.nextSibling.previousSibling_=n),i.lastChild===n&&(i.lastChild_=n),i.firstChild===n&&(i.firstChild_=n),e.originalRemoveChild.call(o,r)}}function o(e){W.set(e,[])}function i(e){var t=W.get(e);return t||W.set(e,t=[]),t}function a(e){for(var t=[],n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t}function s(){for(var e=0;e<F.length;e++){var t=F[e],n=t.parentRenderer;n&&n.dirty||t.render()}F=[]}function c(){T=null,s()}function u(e){var t=A.get(e);return t||(t=new f(e),A.set(e,t)),t}function l(e){var t=D(e).root;return t instanceof _?t:null}function p(e){return u(e.host)}function d(e){this.skip=!1,this.node=e,this.childNodes=[]}function f(e){this.host=e,this.dirty=!1,this.invalidateAttributes(),this.associateNode(e)}function h(e){for(var t=[],n=e.firstChild;n;n=n.nextSibling)E(n)?t.push.apply(t,i(n)):t.push(n);return t}function w(e){if(e instanceof j)return e;if(e instanceof N)return null;for(var t=e.firstChild;t;t=t.nextSibling){var n=w(t);if(n)return n}return null}function m(e,t){i(t).push(e);var n=I.get(e);n?n.push(t):I.set(e,[t])}function v(e){return I.get(e)}function g(e){I.set(e,void 0)}function b(e,t){var n=t.getAttribute("select");if(!n)return!0;if(n=n.trim(),!n)return!0;if(!(e instanceof O))return!1;if(!U.test(n))return!1;try{return e.matches(n)}catch(r){return!1}}function y(e,t){var n=v(t);return n&&n[n.length-1]===e}function E(e){return e instanceof N||e instanceof j}function S(e){return e.shadowRoot}function M(e){for(var t=[],n=e.shadowRoot;n;n=n.olderShadowRoot)t.push(n);return t}var T,O=e.wrappers.Element,N=e.wrappers.HTMLContentElement,j=e.wrappers.HTMLShadowElement,L=e.wrappers.Node,_=e.wrappers.ShadowRoot,D=(e.assert,e.getTreeScope),C=(e.mixin,e.oneOf),H=e.unsafeUnwrap,x=e.unwrap,R=e.wrap,P=e.ArraySplice,W=new WeakMap,I=new WeakMap,A=new WeakMap,k=C(window,["requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","setTimeout"]),F=[],B=new P;B.equals=function(e,t){return x(e.node)===t},d.prototype={append:function(e){var t=new d(e);return this.childNodes.push(t),t},sync:function(e){if(!this.skip){for(var t=this.node,o=this.childNodes,i=a(x(t)),s=e||new WeakMap,c=B.calculateSplices(o,i),u=0,l=0,p=0,d=0;d<c.length;d++){for(var f=c[d];p<f.index;p++)l++,o[u++].sync(s);for(var h=f.removed.length,w=0;w<h;w++){var m=R(i[l++]);s.get(m)||r(m)}for(var v=f.addedCount,g=i[l]&&R(i[l]),w=0;w<v;w++){var b=o[u++],y=b.node;n(t,y,g),s.set(y,!0),b.sync(s)}p+=v}for(var d=p;d<o.length;d++)o[d].sync(s)}}},f.prototype={render:function(e){if(this.dirty){this.invalidateAttributes();var t=this.host;this.distribution(t);var n=e||new d(t);this.buildRenderTree(n,t);var r=!e;r&&n.sync(),this.dirty=!1}},get parentRenderer(){return D(this.host).renderer},invalidate:function(){if(!this.dirty){this.dirty=!0;var e=this.parentRenderer;if(e&&e.invalidate(),F.push(this),T)return;T=window[k](c,0)}},distribution:function(e){this.resetAllSubtrees(e),this.distributionResolution(e)},resetAll:function(e){E(e)?o(e):g(e),this.resetAllSubtrees(e)},resetAllSubtrees:function(e){for(var t=e.firstChild;t;t=t.nextSibling)this.resetAll(t);e.shadowRoot&&this.resetAll(e.shadowRoot),e.olderShadowRoot&&this.resetAll(e.olderShadowRoot)},distributionResolution:function(e){if(S(e)){for(var t=e,n=h(t),r=M(t),o=0;o<r.length;o++)this.poolDistribution(r[o],n);for(var o=r.length-1;o>=0;o--){var i=r[o],a=w(i);if(a){var s=i.olderShadowRoot;s&&(n=h(s));for(var c=0;c<n.length;c++)m(n[c],a)}this.distributionResolution(i)}}for(var u=e.firstChild;u;u=u.nextSibling)this.distributionResolution(u)},poolDistribution:function(e,t){if(!(e instanceof j))if(e instanceof N){var n=e;this.updateDependentAttributes(n.getAttribute("select"));for(var r=!1,o=0;o<t.length;o++){var e=t[o];e&&b(e,n)&&(m(e,n),t[o]=void 0,r=!0)}if(!r)for(var i=n.firstChild;i;i=i.nextSibling)m(i,n)}else for(var i=e.firstChild;i;i=i.nextSibling)this.poolDistribution(i,t)},buildRenderTree:function(e,t){for(var n=this.compose(t),r=0;r<n.length;r++){var o=n[r],i=e.append(o);this.buildRenderTree(i,o)}if(S(t)){var a=u(t);a.dirty=!1}},compose:function(e){for(var t=[],n=e.shadowRoot||e,r=n.firstChild;r;r=r.nextSibling)if(E(r)){this.associateNode(n);for(var o=i(r),a=0;a<o.length;a++){var s=o[a];y(r,s)&&t.push(s)}}else t.push(r);return t},invalidateAttributes:function(){this.attributes=Object.create(null)},updateDependentAttributes:function(e){if(e){var t=this.attributes;/\.\w+/.test(e)&&(t["class"]=!0),/#\w+/.test(e)&&(t.id=!0),e.replace(/\[\s*([^\s=\|~\]]+)/g,function(e,n){t[n]=!0})}},dependsOnAttribute:function(e){return this.attributes[e]},associateNode:function(e){H(e).polymerShadowRenderer_=this}};var U=/^(:not\()?[*.#[a-zA-Z_|]/;L.prototype.invalidateShadowRenderer=function(e){var t=H(this).polymerShadowRenderer_;return!!t&&(t.invalidate(),!0)},N.prototype.getDistributedNodes=j.prototype.getDistributedNodes=function(){return s(),i(this)},O.prototype.getDestinationInsertionPoints=function(){return s(),v(this)||[]},N.prototype.nodeIsInserted_=j.prototype.nodeIsInserted_=function(){this.invalidateShadowRenderer();var e,t=l(this);t&&(e=p(t)),H(this).polymerShadowRenderer_=e,e&&e.invalidate()},e.getRendererForHost=u,e.getShadowTrees=M,e.renderAllPending=s,e.getDestinationInsertionPoints=v,e.visual={insertBefore:n,remove:r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t){if(window[t]){r(!e.wrappers[t]);var c=function(e){n.call(this,e)};c.prototype=Object.create(n.prototype),o(c.prototype,{get form(){return s(a(this).form)}}),i(window[t],c,document.createElement(t.slice(4,-7))),e.wrappers[t]=c}}var n=e.wrappers.HTMLElement,r=e.assert,o=e.mixin,i=e.registerWrapper,a=e.unwrap,s=e.wrap,c=["HTMLButtonElement","HTMLFieldSetElement","HTMLInputElement","HTMLKeygenElement","HTMLLabelElement","HTMLLegendElement","HTMLObjectElement","HTMLOutputElement","HTMLTextAreaElement"];c.forEach(t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrap,a=e.unwrapIfNeeded,s=e.wrap,c=window.Selection;t.prototype={get anchorNode(){return s(o(this).anchorNode)},get focusNode(){return s(o(this).focusNode)},addRange:function(e){o(this).addRange(a(e))},collapse:function(e,t){o(this).collapse(a(e),t)},containsNode:function(e,t){return o(this).containsNode(a(e),t)},getRangeAt:function(e){return s(o(this).getRangeAt(e))},removeRange:function(e){o(this).removeRange(i(e))},selectAllChildren:function(e){o(this).selectAllChildren(e instanceof ShadowRoot?o(e.host):a(e))},toString:function(){return o(this).toString()}},c.prototype.extend&&(t.prototype.extend=function(e,t){o(this).extend(a(e),t)}),n(window.Selection,t,window.getSelection()),e.wrappers.Selection=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrapIfNeeded,a=e.wrap,s=window.TreeWalker;t.prototype={get root(){return a(o(this).root)},get currentNode(){return a(o(this).currentNode)},set currentNode(e){o(this).currentNode=i(e)},get filter(){return o(this).filter},parentNode:function(){return a(o(this).parentNode())},firstChild:function(){return a(o(this).firstChild())},lastChild:function(){return a(o(this).lastChild())},previousSibling:function(){return a(o(this).previousSibling())},previousNode:function(){return a(o(this).previousNode())},nextNode:function(){return a(o(this).nextNode())}},n(s,t),e.wrappers.TreeWalker=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){l.call(this,e),this.treeScope_=new m(this,null)}function n(e){var n=document[e];t.prototype[e]=function(){return D(n.apply(L(this),arguments))}}function r(e,t){x.call(L(t),_(e)),o(e,t)}function o(e,t){e.shadowRoot&&t.adoptNode(e.shadowRoot),e instanceof w&&i(e,t);for(var n=e.firstChild;n;n=n.nextSibling)o(n,t)}function i(e,t){var n=e.olderShadowRoot;n&&t.adoptNode(n)}function a(e){j(e,this)}function s(e,t){var n=document.implementation[t];e.prototype[t]=function(){return D(n.apply(L(this),arguments))}}function c(e,t){var n=document.implementation[t];e.prototype[t]=function(){return n.apply(L(this),arguments)}}var u=e.GetElementsByInterface,l=e.wrappers.Node,p=e.ParentNodeInterface,d=e.NonElementParentNodeInterface,f=e.wrappers.Selection,h=e.SelectorsInterface,w=e.wrappers.ShadowRoot,m=e.TreeScope,v=e.cloneNode,g=e.defineGetter,b=e.defineWrapGetter,y=e.elementFromPoint,E=e.forwardMethodsToWrapper,S=e.matchesNames,M=e.mixin,T=e.registerWrapper,O=e.renderAllPending,N=e.rewrap,j=e.setWrapper,L=e.unsafeUnwrap,_=e.unwrap,D=e.wrap,C=e.wrapEventTargetMethods,H=(e.wrapNodeList,
+new WeakMap);t.prototype=Object.create(l.prototype),b(t,"documentElement"),b(t,"body"),b(t,"head"),g(t,"activeElement",function(){var e=_(this).activeElement;if(!e||!e.nodeType)return null;for(var t=D(e);!this.contains(t);){for(;t.parentNode;)t=t.parentNode;if(!t.host)return null;t=t.host}return t}),["createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode"].forEach(n);var x=document.adoptNode,R=document.getSelection;M(t.prototype,{adoptNode:function(e){return e.parentNode&&e.parentNode.removeChild(e),r(e,this),e},elementFromPoint:function(e,t){return y(this,this,e,t)},importNode:function(e,t){return v(e,t,L(this))},getSelection:function(){return O(),new f(R.call(_(this)))},getElementsByName:function(e){return h.querySelectorAll.call(this,"[name="+JSON.stringify(String(e))+"]")}});var P=document.createTreeWalker,W=e.wrappers.TreeWalker;if(t.prototype.createTreeWalker=function(e,t,n,r){var o=null;return n&&(n.acceptNode&&"function"==typeof n.acceptNode?o={acceptNode:function(e){return n.acceptNode(D(e))}}:"function"==typeof n&&(o=function(e){return n(D(e))})),new W(P.call(_(this),_(e),t,o,r))},document.registerElement){var I=document.registerElement;t.prototype.registerElement=function(t,n){function r(e){return e?void j(e,this):i?document.createElement(i,t):document.createElement(t)}var o,i;if(void 0!==n&&(o=n.prototype,i=n["extends"]),o||(o=Object.create(HTMLElement.prototype)),e.nativePrototypeTable.get(o))throw new Error("NotSupportedError");for(var a,s=Object.getPrototypeOf(o),c=[];s&&!(a=e.nativePrototypeTable.get(s));)c.push(s),s=Object.getPrototypeOf(s);if(!a)throw new Error("NotSupportedError");for(var u=Object.create(a),l=c.length-1;l>=0;l--)u=Object.create(u);["createdCallback","attachedCallback","detachedCallback","attributeChangedCallback"].forEach(function(e){var t=o[e];t&&(u[e]=function(){D(this)instanceof r||N(this),t.apply(D(this),arguments)})});var p={prototype:u};i&&(p["extends"]=i),r.prototype=o,r.prototype.constructor=r,e.constructorTable.set(u,r),e.nativePrototypeTable.set(o,u);I.call(_(this),t,p);return r},E([window.HTMLDocument||window.Document],["registerElement"])}E([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement,window.HTMLHtmlElement],["appendChild","compareDocumentPosition","contains","getElementsByClassName","getElementsByTagName","getElementsByTagNameNS","insertBefore","querySelector","querySelectorAll","removeChild","replaceChild"]),E([window.HTMLBodyElement,window.HTMLHeadElement,window.HTMLHtmlElement],S),E([window.HTMLDocument||window.Document],["adoptNode","importNode","contains","createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","createTreeWalker","elementFromPoint","getElementById","getElementsByName","getSelection"]),M(t.prototype,u),M(t.prototype,p),M(t.prototype,h),M(t.prototype,d),M(t.prototype,{get implementation(){var e=H.get(this);return e?e:(e=new a(_(this).implementation),H.set(this,e),e)},get defaultView(){return D(_(this).defaultView)}}),T(window.Document,t,document.implementation.createHTMLDocument("")),window.HTMLDocument&&T(window.HTMLDocument,t),C([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement]);var A=document.implementation.createDocument;a.prototype.createDocument=function(){return arguments[2]=_(arguments[2]),D(A.apply(L(this),arguments))},s(a,"createDocumentType"),s(a,"createHTMLDocument"),c(a,"hasFeature"),T(window.DOMImplementation,a),E([window.DOMImplementation],["createDocument","createDocumentType","createHTMLDocument","hasFeature"]),e.adoptNodeNoRemove=r,e.wrappers.DOMImplementation=a,e.wrappers.Document=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.wrappers.Selection,o=e.mixin,i=e.registerWrapper,a=e.renderAllPending,s=e.unwrap,c=e.unwrapIfNeeded,u=e.wrap,l=window.Window,p=window.getComputedStyle,d=window.getDefaultComputedStyle,f=window.getSelection;t.prototype=Object.create(n.prototype),l.prototype.getComputedStyle=function(e,t){return u(this||window).getComputedStyle(c(e),t)},d&&(l.prototype.getDefaultComputedStyle=function(e,t){return u(this||window).getDefaultComputedStyle(c(e),t)}),l.prototype.getSelection=function(){return u(this||window).getSelection()},delete window.getComputedStyle,delete window.getDefaultComputedStyle,delete window.getSelection,["addEventListener","removeEventListener","dispatchEvent"].forEach(function(e){l.prototype[e]=function(){var t=u(this||window);return t[e].apply(t,arguments)},delete window[e]}),o(t.prototype,{getComputedStyle:function(e,t){return a(),p.call(s(this),c(e),t)},getSelection:function(){return a(),new r(f.call(s(this)))},get document(){return u(s(this).document)}}),d&&(t.prototype.getDefaultComputedStyle=function(e,t){return a(),d.call(s(this),c(e),t)}),i(l,t,window),e.wrappers.Window=t}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrap,n=window.DataTransfer||window.Clipboard,r=n.prototype.setDragImage;r&&(n.prototype.setDragImage=function(e,n,o){r.call(this,t(e),n,o)})}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t;t=e instanceof i?e:new i(e&&o(e)),r(t,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unwrap,i=window.FormData;i&&(n(i,t,new i),e.wrappers.FormData=t)}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrapIfNeeded,n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(e){return n.call(this,t(e))}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=n[e],r=window[t];if(r){var o=document.createElement(e),i=o.constructor;window[t]=i}}var n=(e.isWrapperFor,{a:"HTMLAnchorElement",area:"HTMLAreaElement",audio:"HTMLAudioElement",base:"HTMLBaseElement",body:"HTMLBodyElement",br:"HTMLBRElement",button:"HTMLButtonElement",canvas:"HTMLCanvasElement",caption:"HTMLTableCaptionElement",col:"HTMLTableColElement",content:"HTMLContentElement",data:"HTMLDataElement",datalist:"HTMLDataListElement",del:"HTMLModElement",dir:"HTMLDirectoryElement",div:"HTMLDivElement",dl:"HTMLDListElement",embed:"HTMLEmbedElement",fieldset:"HTMLFieldSetElement",font:"HTMLFontElement",form:"HTMLFormElement",frame:"HTMLFrameElement",frameset:"HTMLFrameSetElement",h1:"HTMLHeadingElement",head:"HTMLHeadElement",hr:"HTMLHRElement",html:"HTMLHtmlElement",iframe:"HTMLIFrameElement",img:"HTMLImageElement",input:"HTMLInputElement",keygen:"HTMLKeygenElement",label:"HTMLLabelElement",legend:"HTMLLegendElement",li:"HTMLLIElement",link:"HTMLLinkElement",map:"HTMLMapElement",marquee:"HTMLMarqueeElement",menu:"HTMLMenuElement",menuitem:"HTMLMenuItemElement",meta:"HTMLMetaElement",meter:"HTMLMeterElement",object:"HTMLObjectElement",ol:"HTMLOListElement",optgroup:"HTMLOptGroupElement",option:"HTMLOptionElement",output:"HTMLOutputElement",p:"HTMLParagraphElement",param:"HTMLParamElement",pre:"HTMLPreElement",progress:"HTMLProgressElement",q:"HTMLQuoteElement",script:"HTMLScriptElement",select:"HTMLSelectElement",shadow:"HTMLShadowElement",source:"HTMLSourceElement",span:"HTMLSpanElement",style:"HTMLStyleElement",table:"HTMLTableElement",tbody:"HTMLTableSectionElement",template:"HTMLTemplateElement",textarea:"HTMLTextAreaElement",thead:"HTMLTableSectionElement",time:"HTMLTimeElement",title:"HTMLTitleElement",tr:"HTMLTableRowElement",track:"HTMLTrackElement",ul:"HTMLUListElement",video:"HTMLVideoElement"});Object.keys(n).forEach(t),Object.getOwnPropertyNames(e.wrappers).forEach(function(t){window[t]=e.wrappers[t]})}(window.ShadowDOMPolyfill); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/bower.json b/catapult/third_party/polymer/components/webcomponentsjs/bower.json
new file mode 100644
index 00000000..b96995e0
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/bower.json
@@ -0,0 +1,21 @@
+{
+ "name": "webcomponentsjs",
+ "main": "webcomponents.js",
+ "version": "0.7.24",
+ "homepage": "http://webcomponents.org",
+ "authors": [
+ "The Polymer Authors"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/webcomponents/webcomponentsjs.git"
+ },
+ "keywords": [
+ "webcomponents"
+ ],
+ "license": "BSD",
+ "ignore": [],
+ "devDependencies": {
+ "web-component-tester": "^4.0.1"
+ }
+}
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/build.log b/catapult/third_party/polymer/components/webcomponentsjs/build.log
new file mode 100644
index 00000000..3f1d6bb0
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/build.log
@@ -0,0 +1,507 @@
+BUILD LOG
+---------
+Build Time: 2017-02-06T15:35:26-0800
+
+NODEJS INFORMATION
+==================
+nodejs: v6.9.3
+accepts: 1.3.3
+accessibility-developer-tools: 2.11.0
+adm-zip: 0.4.7
+after: 0.8.2
+agent-base: 2.0.1
+align-text: 0.1.4
+ansi-regex: 2.1.1
+ansi-styles: 2.2.1
+archiver: 0.14.4
+append-field: 0.1.0
+archy: 1.0.0
+arr-diff: 2.0.0
+arr-flatten: 1.0.1
+array-differ: 1.0.0
+array-flatten: 1.1.1
+array-uniq: 1.0.3
+array-unique: 0.2.1
+asap: 2.0.5
+asn1: 0.1.11
+arraybuffer.slice: 0.0.6
+assert-plus: 0.1.5
+assertion-error: 1.0.2
+asynckit: 0.4.0
+async: 0.9.2
+aws-sign2: 0.6.0
+aws4: 1.5.0
+backo2: 1.0.2
+backoff: 2.5.0
+balanced-match: 0.4.2
+base64-arraybuffer: 0.1.5
+base64-js: 1.1.2
+base64id: 1.0.0
+bcrypt-pbkdf: 1.0.1
+beeper: 1.1.1
+better-assert: 1.0.2
+bl: 1.2.0
+blob: 0.0.4
+bluebird: 2.11.0
+body-parser: 1.16.0
+boom: 2.10.1
+boxen: 0.3.1
+brace-expansion: 1.1.6
+braces: 1.8.5
+browserstack: 1.5.0
+buffer-crc32: 0.2.13
+bunyan: 1.8.5
+buffer-shims: 1.0.0
+busboy: 0.2.14
+bytes: 2.4.0
+callsite: 1.0.0
+camelcase: 1.2.1
+capture-stack-trace: 1.0.0
+chai: 3.5.0
+caseless: 0.11.0
+center-align: 0.1.3
+chalk: 1.1.3
+cleankill: 1.0.3
+cliui: 2.1.0
+clone: 1.0.2
+clone-buffer: 1.0.0
+clone-stats: 0.0.1
+cloneable-readable: 1.0.0
+code-point-at: 1.1.0
+combined-stream: 1.0.5
+commander: 2.3.0
+component-bind: 1.0.0
+component-emitter: 1.1.2
+component-inherit: 0.0.3
+compress-commons: 0.2.9
+concat-map: 0.0.1
+concat-stream: 1.6.0
+concat-with-sourcemaps: 1.0.4
+configstore: 2.1.0
+content-disposition: 0.5.2
+content-type: 1.0.2
+cookie: 0.3.1
+cookie-signature: 1.0.6
+core-util-is: 1.0.2
+crc: 3.2.1
+crc32-stream: 0.3.4
+create-error-class: 3.0.2
+cryptiles: 2.0.5
+csv-generate: 0.0.6
+csv: 0.4.6
+csv-parse: 1.2.0
+ctype: 0.5.3
+csv-stringify: 0.0.8
+dashdash: 1.14.1
+dateformat: 2.0.0
+deap: 1.0.0
+debug: 2.6.0
+debuglog: 1.0.1
+decamelize: 1.2.0
+defaults: 1.0.3
+deep-eql: 0.1.3
+deep-extend: 0.4.1
+delayed-stream: 1.0.0
+depd: 1.1.0
+deprecated: 0.0.1
+detect-file: 0.1.0
+destroy: 1.0.4
+dezalgo: 1.0.3
+dicer: 0.2.5
+diff: 1.4.0
+dot-prop: 3.0.0
+dtrace-provider: 0.6.0
+duplexer2: 0.0.2
+ee-first: 1.1.1
+ecc-jsbn: 0.1.1
+encodeurl: 1.0.1
+end-of-stream: 0.1.5
+engine.io: 1.8.2
+engine.io-client: 1.8.2
+engine.io-parser: 1.3.2
+error-ex: 1.3.0
+escape-html: 1.0.3
+escape-regexp-component: 1.0.2
+etag: 1.7.0
+escape-string-regexp: 1.0.5
+expand-brackets: 0.1.5
+expand-range: 1.8.2
+expand-tilde: 1.2.2
+express: 4.14.1
+extend: 3.0.0
+extglob: 0.3.2
+extsprintf: 1.2.0
+fancy-log: 1.3.0
+fd-slicer: 1.0.1
+filename-regex: 2.0.0
+fill-range: 2.2.3
+filled-array: 1.1.0
+finalhandler: 0.5.1
+find-index: 0.1.1
+findup-sync: 0.4.3
+fined: 1.0.2
+first-chunk-stream: 1.0.0
+flagged-respawn: 0.3.2
+for-in: 0.1.6
+for-own: 0.1.4
+forever-agent: 0.6.1
+form-data: 2.1.2
+formatio: 1.1.1
+formidable: 1.1.1
+forwarded: 0.1.0
+fresh: 0.3.0
+freeport: 1.0.5
+fs-exists-sync: 0.1.0
+fs.realpath: 1.0.0
+gaze: 0.5.2
+generate-function: 2.0.0
+generate-object-property: 1.2.0
+getpass: 0.1.6
+github-url-from-git: 1.5.0
+github-url-from-username-repo: 1.0.2
+glob: 4.5.3
+glob-base: 0.3.0
+glob-parent: 2.0.0
+glob-stream: 3.1.18
+glob2base: 0.0.12
+glob-watcher: 0.0.6
+global-prefix: 0.1.5
+global-modules: 0.2.3
+globule: 0.1.0
+glogg: 1.0.0
+got: 5.7.1
+graceful-fs: 3.0.11
+growl: 1.9.2
+graceful-readlink: 1.0.1
+gulp: 3.9.1
+gulp-audit: 1.0.0
+gulp-concat: 2.6.1
+gulp-header: 1.8.8
+gulp-uglify: 1.5.4
+gulplog: 1.0.0
+gulp-util: 3.0.8
+handle-thing: 1.2.5
+har-validator: 2.0.6
+has-binary: 0.1.7
+has-ansi: 2.0.0
+has-color: 0.1.7
+has-cors: 1.1.0
+has-gulplog: 0.1.0
+hawk: 3.1.3
+hoek: 2.16.3
+hpack.js: 2.1.6
+homedir-polyfill: 1.0.1
+http-deceiver: 1.2.7
+http-errors: 1.5.1
+http-signature: 0.11.0
+https-proxy-agent: 1.0.0
+imurmurhash: 0.1.4
+indexof: 0.0.1
+inflight: 1.0.6
+iconv-lite: 0.4.15
+inherits: 2.0.3
+ini: 1.3.4
+interpret: 1.0.1
+ipaddr.js: 1.2.0
+is-absolute: 0.2.6
+is-arrayish: 0.2.1
+is-buffer: 1.1.4
+is-dotfile: 1.0.2
+is-equal-shallow: 0.1.3
+is-extglob: 1.0.0
+is-extendable: 0.1.1
+is-finite: 1.0.2
+is-fullwidth-code-point: 1.0.0
+is-glob: 2.0.1
+is-my-json-valid: 2.15.0
+is-npm: 1.0.0
+is-number: 2.1.0
+is-posix-bracket: 0.1.1
+is-obj: 1.0.1
+is-primitive: 2.0.0
+is-property: 1.0.2
+is-redirect: 1.0.0
+is-relative: 0.2.1
+is-retry-allowed: 1.1.0
+is-typedarray: 1.0.0
+is-stream: 1.1.0
+is-unc-path: 0.1.2
+is-utf8: 0.2.1
+is-windows: 0.2.0
+isarray: 0.0.1
+isexe: 1.1.2
+isobject: 2.1.0
+isstream: 0.1.2
+jju: 1.3.0
+jade: 0.26.3
+jodid25519: 1.0.2
+json-parse-helpfulerror: 1.0.3
+json-schema: 0.2.3
+jsbn: 0.1.0
+json-stringify-safe: 5.0.1
+json3: 3.3.2
+jsonpointer: 4.0.1
+keep-alive-agent: 0.0.1
+jsprim: 1.3.1
+kind-of: 3.1.0
+latest-version: 2.0.0
+lazy-cache: 1.0.4
+lazystream: 0.1.0
+launchpad: 0.5.4
+liftoff: 2.3.0
+lodash: 1.0.2
+lodash._basetostring: 3.0.1
+lodash._basevalues: 3.0.0
+lodash._basecopy: 3.0.1
+lodash._getnative: 3.9.1
+lodash._isiterateecall: 3.0.9
+lodash._reescape: 3.0.0
+lodash._reevaluate: 3.0.0
+lodash._reinterpolate: 3.0.0
+lodash._root: 3.0.1
+lodash.assignwith: 4.2.0
+lodash.escape: 3.2.0
+lodash.isarguments: 3.1.0
+lodash.isarray: 3.0.4
+lodash.isplainobject: 4.0.6
+lodash.isempty: 4.4.0
+lodash.isstring: 4.0.1
+lodash.keys: 3.1.2
+lodash.mapvalues: 4.6.0
+lodash.pick: 4.4.0
+lodash.template: 3.6.2
+lodash.templatesettings: 3.1.1
+lodash.restparam: 3.6.1
+lolex: 1.3.2
+longest: 1.0.1
+lru-cache: 2.7.3
+lowercase-keys: 1.0.0
+media-typer: 0.3.0
+map-cache: 0.2.2
+methods: 1.1.2
+merge-descriptors: 1.0.1
+micromatch: 2.3.11
+mime: 1.3.4
+mime-db: 1.26.0
+mime-types: 2.1.14
+minimalistic-assert: 1.0.0
+minimatch: 2.0.10
+minimist: 1.2.0
+mkdirp: 0.5.1
+mocha: 2.5.3
+moment: 2.17.1
+ms: 0.7.2
+multer: 1.3.0
+multipipe: 0.1.2
+mv: 2.1.1
+nan: 2.5.1
+ncp: 2.0.0
+negotiator: 0.6.1
+natives: 1.1.0
+node-int64: 0.3.3
+node-status-codes: 1.0.0
+node-uuid: 1.4.7
+nodegit-promise: 4.0.0
+nomnom: 1.8.1
+normalize-package-data: 1.0.3
+normalize-path: 2.0.1
+number-is-nan: 1.0.1
+oauth-sign: 0.8.2
+object-assign: 3.0.0
+object-component: 0.0.3
+object.omit: 2.0.1
+obuf: 1.1.1
+on-finished: 2.3.0
+once: 1.3.3
+options: 0.0.6
+orchestrator: 0.3.8
+ordered-read-streams: 0.1.0
+os-homedir: 1.0.2
+os-tmpdir: 1.0.2
+osenv: 0.1.4
+parse-filepath: 1.0.1
+package-json: 2.4.0
+parse-glob: 3.0.4
+parse-json: 2.2.0
+parse-passwd: 1.0.0
+parsejson: 0.0.3
+parseqs: 0.0.5
+parseuri: 0.0.5
+parseurl: 1.3.1
+path-is-absolute: 1.0.1
+path-root: 0.1.1
+path-root-regex: 0.1.2
+path-to-regexp: 0.1.7
+pend: 1.2.0
+pinkie: 2.0.4
+pinkie-promise: 2.0.1
+plist: 2.0.1
+precond: 0.2.3
+prepend-http: 1.0.4
+preserve: 0.2.0
+pretty-hrtime: 1.0.3
+process-nextick-args: 1.0.7
+progress: 1.1.8
+promisify-node: 0.4.0
+proxy-addr: 1.1.3
+pseudomap: 1.0.2
+q: 1.4.1
+punycode: 1.4.1
+qs: 6.2.1
+randomatic: 1.1.6
+range-parser: 1.2.0
+raw-body: 2.2.0
+rc: 1.1.6
+read-all-stream: 3.1.0
+read-installed: 3.1.5
+read-package-json: 1.3.3
+readable-stream: 1.1.14
+readdir-scoped-modules: 1.0.2
+rechoir: 0.6.2
+regex-cache: 0.4.3
+registry-auth-token: 3.1.0
+registry-url: 3.1.0
+remove-trailing-separator: 1.0.1
+repeat-element: 1.1.2
+repeating: 2.0.1
+repeat-string: 1.6.1
+request: 2.79.0
+replace-ext: 0.0.1
+resolve: 1.2.0
+resolve-dir: 0.1.1
+restify: 4.3.0
+right-align: 0.1.3
+rimraf: 2.4.5
+run-sequence: 1.2.2
+safe-json-stringify: 1.0.3
+samsam: 1.1.2
+sauce-connect-launcher: 1.2.0
+selenium-standalone: 5.11.2
+select-hose: 2.0.0
+semver-diff: 2.1.0
+semver: 4.3.6
+send: 0.11.1
+sequencify: 0.0.7
+serve-static: 1.11.2
+serve-waterfall: 1.1.1
+server-destroy: 1.0.1
+setprototypeof: 1.0.2
+sigmund: 1.0.1
+sinon: 1.17.7
+sinon-chai: 2.8.0
+slide: 1.1.6
+sntp: 1.0.9
+socket.io: 1.7.2
+socket.io-adapter: 0.5.0
+socket.io-client: 1.7.2
+source-map: 0.5.6
+socket.io-parser: 2.3.1
+sparkles: 1.0.0
+spdy: 3.4.4
+spdy-transport: 2.0.18
+sshpk: 1.10.2
+stacky: 1.3.1
+statuses: 1.3.1
+stream-consume: 0.1.0
+stream-transform: 0.1.1
+streamsearch: 0.1.2
+string-width: 1.0.2
+string_decoder: 0.10.31
+stringstream: 0.0.5
+strip-ansi: 3.0.1
+strip-bom: 1.0.0
+strip-json-comments: 1.0.4
+supports-color: 2.0.0
+tar-stream: 1.5.2
+temp: 0.8.3
+test-fixture: 2.0.1
+through2: 2.0.3
+tildify: 1.2.0
+time-stamp: 1.0.1
+timed-out: 3.1.3
+to-iso-string: 0.0.2
+tough-cookie: 2.3.2
+to-array: 0.1.4
+tweetnacl: 0.14.5
+tunnel-agent: 0.4.3
+type-detect: 1.0.0
+type-is: 1.6.14
+typedarray: 0.0.6
+uglify-js: 2.6.4
+uglify-save-license: 0.4.1
+uglify-to-browserify: 1.0.2
+ultron: 1.0.2
+unc-path-regex: 0.1.2
+underscore: 1.6.0
+underscore.string: 3.0.3
+unique-stream: 1.0.0
+unpipe: 1.0.0
+unzip-response: 1.0.2
+update-notifier: 0.6.3
+urijs: 1.16.1
+url-parse-lax: 1.0.0
+util: 0.10.3
+user-home: 1.1.1
+util-deprecate: 1.0.2
+util-extend: 1.0.3
+utils-merge: 1.0.0
+uuid: 2.0.3
+v8flags: 2.0.11
+vargs: 0.1.0
+vary: 1.1.0
+verror: 1.9.0
+vasync: 1.6.3
+vinyl: 0.5.3
+vinyl-fs: 0.3.14
+vinyl-sourcemaps-apply: 0.2.1
+wbuf: 1.7.2
+wct-local: 2.0.14
+wct-sauce: 1.8.6
+wd: 0.3.12
+which: 1.2.12
+web-component-tester: 4.3.6
+widest-line: 1.0.0
+window-size: 0.1.0
+wrappy: 1.0.2
+wordwrap: 0.0.2
+write-file-atomic: 1.3.1
+ws: 1.1.1
+wtf-8: 1.0.0
+xmlbuilder: 8.2.2
+xdg-basedir: 2.0.0
+xmldom: 0.1.27
+xmlhttprequest-ssl: 1.5.3
+xtend: 4.0.1
+yargs: 3.10.0
+yallist: 2.0.0
+yauzl: 2.7.0
+yeast: 0.1.2
+zip-stream: 0.5.2
+@types/chalk: 0.4.31
+@types/express-serve-static-core: 4.0.40
+@types/express: 4.0.35
+@types/freeport: 1.0.21
+@types/launchpad: 0.0.4
+@types/node: 6.0.62
+@types/mime: 0.0.29
+@types/serve-static: 1.7.31
+@types/which: 1.0.28
+
+REPO REVISIONS
+==============
+webcomponentsjs: 0fbebea7bb0d9c310fdc328493bb9f53c600b366
+
+BUILD HASHES
+============
+CustomElements.js: f33eb6e0a617ddfdc1508e1cb913442f6599c7f7
+CustomElements.min.js: a0e1b31e5a0bf95cfb0ff7f0cd12c354879cad8a
+HTMLImports.js: 3723342f206868f1a57001d7348159a3525d087f
+HTMLImports.min.js: 2dfac33d31d9a3b247868c85782197c9a24b70c6
+MutationObserver.js: 40566c0e3aec84650b43f74f51bdad7218a3a10a
+MutationObserver.min.js: 9838a8deeb8ae110aa1c19d6e5d88c52cfbb7924
+ShadowDOM.js: 9d56dd89199114170bc56ce0c759d0112d9895d6
+ShadowDOM.min.js: 6bca1a02e3ecb6933c27f24036ff40f83a581f82
+webcomponents-lite.js: 8ac8296830abcc2eecd9bfa4cea05edf9c5d4bc8
+webcomponents-lite.min.js: 6c4edd8efa046627216a625bf8f8903d41a9fe87
+webcomponents.js: 10940fc011b02665c826cde432601297fc0f1179
+webcomponents.min.js: 42404bdd62ebb7b6bb63d15d8e1bccf070de700b \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/package.json b/catapult/third_party/polymer/components/webcomponentsjs/package.json
new file mode 100644
index 00000000..10dc3111
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "webcomponents.js",
+ "version": "0.7.24",
+ "description": "webcomponents.js",
+ "main": "webcomponents.js",
+ "directories": {
+ "test": "tests"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/webcomponents/webcomponentsjs.git"
+ },
+ "author": "The Polymer Authors",
+ "license": "BSD-3-Clause",
+ "bugs": {
+ "url": "https://github.com/webcomponents/webcomponentsjs/issues"
+ },
+ "scripts": {
+ "test": "wct"
+ },
+ "homepage": "http://webcomponents.org",
+ "devDependencies": {
+ "gulp": "^3.8.8",
+ "gulp-audit": "^1.0.0",
+ "gulp-concat": "^2.4.1",
+ "gulp-header": "^1.1.1",
+ "gulp-uglify": "^1.0.1",
+ "run-sequence": "^1.0.1",
+ "web-component-tester": "^4.0.1"
+ }
+}
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.js b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.js
new file mode 100644
index 00000000..957bd569
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.js
@@ -0,0 +1,2505 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+(function() {
+ window.WebComponents = window.WebComponents || {
+ flags: {}
+ };
+ var file = "webcomponents-lite.js";
+ var script = document.querySelector('script[src*="' + file + '"]');
+ var flags = {};
+ if (!flags.noOpts) {
+ location.search.slice(1).split("&").forEach(function(option) {
+ var parts = option.split("=");
+ var match;
+ if (parts[0] && (match = parts[0].match(/wc-(.+)/))) {
+ flags[match[1]] = parts[1] || true;
+ }
+ });
+ if (script) {
+ for (var i = 0, a; a = script.attributes[i]; i++) {
+ if (a.name !== "src") {
+ flags[a.name] = a.value || true;
+ }
+ }
+ }
+ if (flags.log && flags.log.split) {
+ var parts = flags.log.split(",");
+ flags.log = {};
+ parts.forEach(function(f) {
+ flags.log[f] = true;
+ });
+ } else {
+ flags.log = {};
+ }
+ }
+ if (flags.register) {
+ window.CustomElements = window.CustomElements || {
+ flags: {}
+ };
+ window.CustomElements.flags.register = flags.register;
+ }
+ WebComponents.flags = flags;
+})();
+
+(function(scope) {
+ "use strict";
+ var hasWorkingUrl = false;
+ if (!scope.forceJURL) {
+ try {
+ var u = new URL("b", "http://a");
+ u.pathname = "c%20d";
+ hasWorkingUrl = u.href === "http://a/c%20d";
+ } catch (e) {}
+ }
+ if (hasWorkingUrl) return;
+ var relative = Object.create(null);
+ relative["ftp"] = 21;
+ relative["file"] = 0;
+ relative["gopher"] = 70;
+ relative["http"] = 80;
+ relative["https"] = 443;
+ relative["ws"] = 80;
+ relative["wss"] = 443;
+ var relativePathDotMapping = Object.create(null);
+ relativePathDotMapping["%2e"] = ".";
+ relativePathDotMapping[".%2e"] = "..";
+ relativePathDotMapping["%2e."] = "..";
+ relativePathDotMapping["%2e%2e"] = "..";
+ function isRelativeScheme(scheme) {
+ return relative[scheme] !== undefined;
+ }
+ function invalid() {
+ clear.call(this);
+ this._isInvalid = true;
+ }
+ function IDNAToASCII(h) {
+ if ("" == h) {
+ invalid.call(this);
+ }
+ return h.toLowerCase();
+ }
+ function percentEscape(c) {
+ var unicode = c.charCodeAt(0);
+ if (unicode > 32 && unicode < 127 && [ 34, 35, 60, 62, 63, 96 ].indexOf(unicode) == -1) {
+ return c;
+ }
+ return encodeURIComponent(c);
+ }
+ function percentEscapeQuery(c) {
+ var unicode = c.charCodeAt(0);
+ if (unicode > 32 && unicode < 127 && [ 34, 35, 60, 62, 96 ].indexOf(unicode) == -1) {
+ return c;
+ }
+ return encodeURIComponent(c);
+ }
+ var EOF = undefined, ALPHA = /[a-zA-Z]/, ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
+ function parse(input, stateOverride, base) {
+ function err(message) {
+ errors.push(message);
+ }
+ var state = stateOverride || "scheme start", cursor = 0, buffer = "", seenAt = false, seenBracket = false, errors = [];
+ loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
+ var c = input[cursor];
+ switch (state) {
+ case "scheme start":
+ if (c && ALPHA.test(c)) {
+ buffer += c.toLowerCase();
+ state = "scheme";
+ } else if (!stateOverride) {
+ buffer = "";
+ state = "no scheme";
+ continue;
+ } else {
+ err("Invalid scheme.");
+ break loop;
+ }
+ break;
+
+ case "scheme":
+ if (c && ALPHANUMERIC.test(c)) {
+ buffer += c.toLowerCase();
+ } else if (":" == c) {
+ this._scheme = buffer;
+ buffer = "";
+ if (stateOverride) {
+ break loop;
+ }
+ if (isRelativeScheme(this._scheme)) {
+ this._isRelative = true;
+ }
+ if ("file" == this._scheme) {
+ state = "relative";
+ } else if (this._isRelative && base && base._scheme == this._scheme) {
+ state = "relative or authority";
+ } else if (this._isRelative) {
+ state = "authority first slash";
+ } else {
+ state = "scheme data";
+ }
+ } else if (!stateOverride) {
+ buffer = "";
+ cursor = 0;
+ state = "no scheme";
+ continue;
+ } else if (EOF == c) {
+ break loop;
+ } else {
+ err("Code point not allowed in scheme: " + c);
+ break loop;
+ }
+ break;
+
+ case "scheme data":
+ if ("?" == c) {
+ this._query = "?";
+ state = "query";
+ } else if ("#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ } else {
+ if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._schemeData += percentEscape(c);
+ }
+ }
+ break;
+
+ case "no scheme":
+ if (!base || !isRelativeScheme(base._scheme)) {
+ err("Missing scheme.");
+ invalid.call(this);
+ } else {
+ state = "relative";
+ continue;
+ }
+ break;
+
+ case "relative or authority":
+ if ("/" == c && "/" == input[cursor + 1]) {
+ state = "authority ignore slashes";
+ } else {
+ err("Expected /, got: " + c);
+ state = "relative";
+ continue;
+ }
+ break;
+
+ case "relative":
+ this._isRelative = true;
+ if ("file" != this._scheme) this._scheme = base._scheme;
+ if (EOF == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = base._query;
+ this._username = base._username;
+ this._password = base._password;
+ break loop;
+ } else if ("/" == c || "\\" == c) {
+ if ("\\" == c) err("\\ is an invalid code point.");
+ state = "relative slash";
+ } else if ("?" == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = "?";
+ this._username = base._username;
+ this._password = base._password;
+ state = "query";
+ } else if ("#" == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = base._query;
+ this._fragment = "#";
+ this._username = base._username;
+ this._password = base._password;
+ state = "fragment";
+ } else {
+ var nextC = input[cursor + 1];
+ var nextNextC = input[cursor + 2];
+ if ("file" != this._scheme || !ALPHA.test(c) || nextC != ":" && nextC != "|" || EOF != nextNextC && "/" != nextNextC && "\\" != nextNextC && "?" != nextNextC && "#" != nextNextC) {
+ this._host = base._host;
+ this._port = base._port;
+ this._username = base._username;
+ this._password = base._password;
+ this._path = base._path.slice();
+ this._path.pop();
+ }
+ state = "relative path";
+ continue;
+ }
+ break;
+
+ case "relative slash":
+ if ("/" == c || "\\" == c) {
+ if ("\\" == c) {
+ err("\\ is an invalid code point.");
+ }
+ if ("file" == this._scheme) {
+ state = "file host";
+ } else {
+ state = "authority ignore slashes";
+ }
+ } else {
+ if ("file" != this._scheme) {
+ this._host = base._host;
+ this._port = base._port;
+ this._username = base._username;
+ this._password = base._password;
+ }
+ state = "relative path";
+ continue;
+ }
+ break;
+
+ case "authority first slash":
+ if ("/" == c) {
+ state = "authority second slash";
+ } else {
+ err("Expected '/', got: " + c);
+ state = "authority ignore slashes";
+ continue;
+ }
+ break;
+
+ case "authority second slash":
+ state = "authority ignore slashes";
+ if ("/" != c) {
+ err("Expected '/', got: " + c);
+ continue;
+ }
+ break;
+
+ case "authority ignore slashes":
+ if ("/" != c && "\\" != c) {
+ state = "authority";
+ continue;
+ } else {
+ err("Expected authority, got: " + c);
+ }
+ break;
+
+ case "authority":
+ if ("@" == c) {
+ if (seenAt) {
+ err("@ already seen.");
+ buffer += "%40";
+ }
+ seenAt = true;
+ for (var i = 0; i < buffer.length; i++) {
+ var cp = buffer[i];
+ if ("\t" == cp || "\n" == cp || "\r" == cp) {
+ err("Invalid whitespace in authority.");
+ continue;
+ }
+ if (":" == cp && null === this._password) {
+ this._password = "";
+ continue;
+ }
+ var tempC = percentEscape(cp);
+ null !== this._password ? this._password += tempC : this._username += tempC;
+ }
+ buffer = "";
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ cursor -= buffer.length;
+ buffer = "";
+ state = "host";
+ continue;
+ } else {
+ buffer += c;
+ }
+ break;
+
+ case "file host":
+ if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ":" || buffer[1] == "|")) {
+ state = "relative path";
+ } else if (buffer.length == 0) {
+ state = "relative path start";
+ } else {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "relative path start";
+ }
+ continue;
+ } else if ("\t" == c || "\n" == c || "\r" == c) {
+ err("Invalid whitespace in file host.");
+ } else {
+ buffer += c;
+ }
+ break;
+
+ case "host":
+ case "hostname":
+ if (":" == c && !seenBracket) {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "port";
+ if ("hostname" == stateOverride) {
+ break loop;
+ }
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "relative path start";
+ if (stateOverride) {
+ break loop;
+ }
+ continue;
+ } else if ("\t" != c && "\n" != c && "\r" != c) {
+ if ("[" == c) {
+ seenBracket = true;
+ } else if ("]" == c) {
+ seenBracket = false;
+ }
+ buffer += c;
+ } else {
+ err("Invalid code point in host/hostname: " + c);
+ }
+ break;
+
+ case "port":
+ if (/[0-9]/.test(c)) {
+ buffer += c;
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c || stateOverride) {
+ if ("" != buffer) {
+ var temp = parseInt(buffer, 10);
+ if (temp != relative[this._scheme]) {
+ this._port = temp + "";
+ }
+ buffer = "";
+ }
+ if (stateOverride) {
+ break loop;
+ }
+ state = "relative path start";
+ continue;
+ } else if ("\t" == c || "\n" == c || "\r" == c) {
+ err("Invalid code point in port: " + c);
+ } else {
+ invalid.call(this);
+ }
+ break;
+
+ case "relative path start":
+ if ("\\" == c) err("'\\' not allowed in path.");
+ state = "relative path";
+ if ("/" != c && "\\" != c) {
+ continue;
+ }
+ break;
+
+ case "relative path":
+ if (EOF == c || "/" == c || "\\" == c || !stateOverride && ("?" == c || "#" == c)) {
+ if ("\\" == c) {
+ err("\\ not allowed in relative path.");
+ }
+ var tmp;
+ if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
+ buffer = tmp;
+ }
+ if (".." == buffer) {
+ this._path.pop();
+ if ("/" != c && "\\" != c) {
+ this._path.push("");
+ }
+ } else if ("." == buffer && "/" != c && "\\" != c) {
+ this._path.push("");
+ } else if ("." != buffer) {
+ if ("file" == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == "|") {
+ buffer = buffer[0] + ":";
+ }
+ this._path.push(buffer);
+ }
+ buffer = "";
+ if ("?" == c) {
+ this._query = "?";
+ state = "query";
+ } else if ("#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ }
+ } else if ("\t" != c && "\n" != c && "\r" != c) {
+ buffer += percentEscape(c);
+ }
+ break;
+
+ case "query":
+ if (!stateOverride && "#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ } else if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._query += percentEscapeQuery(c);
+ }
+ break;
+
+ case "fragment":
+ if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._fragment += c;
+ }
+ break;
+ }
+ cursor++;
+ }
+ }
+ function clear() {
+ this._scheme = "";
+ this._schemeData = "";
+ this._username = "";
+ this._password = null;
+ this._host = "";
+ this._port = "";
+ this._path = [];
+ this._query = "";
+ this._fragment = "";
+ this._isInvalid = false;
+ this._isRelative = false;
+ }
+ function jURL(url, base) {
+ if (base !== undefined && !(base instanceof jURL)) base = new jURL(String(base));
+ this._url = url;
+ clear.call(this);
+ var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, "");
+ parse.call(this, input, null, base);
+ }
+ jURL.prototype = {
+ toString: function() {
+ return this.href;
+ },
+ get href() {
+ if (this._isInvalid) return this._url;
+ var authority = "";
+ if ("" != this._username || null != this._password) {
+ authority = this._username + (null != this._password ? ":" + this._password : "") + "@";
+ }
+ return this.protocol + (this._isRelative ? "//" + authority + this.host : "") + this.pathname + this._query + this._fragment;
+ },
+ set href(href) {
+ clear.call(this);
+ parse.call(this, href);
+ },
+ get protocol() {
+ return this._scheme + ":";
+ },
+ set protocol(protocol) {
+ if (this._isInvalid) return;
+ parse.call(this, protocol + ":", "scheme start");
+ },
+ get host() {
+ return this._isInvalid ? "" : this._port ? this._host + ":" + this._port : this._host;
+ },
+ set host(host) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, host, "host");
+ },
+ get hostname() {
+ return this._host;
+ },
+ set hostname(hostname) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, hostname, "hostname");
+ },
+ get port() {
+ return this._port;
+ },
+ set port(port) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, port, "port");
+ },
+ get pathname() {
+ return this._isInvalid ? "" : this._isRelative ? "/" + this._path.join("/") : this._schemeData;
+ },
+ set pathname(pathname) {
+ if (this._isInvalid || !this._isRelative) return;
+ this._path = [];
+ parse.call(this, pathname, "relative path start");
+ },
+ get search() {
+ return this._isInvalid || !this._query || "?" == this._query ? "" : this._query;
+ },
+ set search(search) {
+ if (this._isInvalid || !this._isRelative) return;
+ this._query = "?";
+ if ("?" == search[0]) search = search.slice(1);
+ parse.call(this, search, "query");
+ },
+ get hash() {
+ return this._isInvalid || !this._fragment || "#" == this._fragment ? "" : this._fragment;
+ },
+ set hash(hash) {
+ if (this._isInvalid) return;
+ this._fragment = "#";
+ if ("#" == hash[0]) hash = hash.slice(1);
+ parse.call(this, hash, "fragment");
+ },
+ get origin() {
+ var host;
+ if (this._isInvalid || !this._scheme) {
+ return "";
+ }
+ switch (this._scheme) {
+ case "data":
+ case "file":
+ case "javascript":
+ case "mailto":
+ return "null";
+ }
+ host = this.host;
+ if (!host) {
+ return "";
+ }
+ return this._scheme + "://" + host;
+ }
+ };
+ var OriginalURL = scope.URL;
+ if (OriginalURL) {
+ jURL.createObjectURL = function(blob) {
+ return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
+ };
+ jURL.revokeObjectURL = function(url) {
+ OriginalURL.revokeObjectURL(url);
+ };
+ }
+ scope.URL = jURL;
+})(self);
+
+if (typeof WeakMap === "undefined") {
+ (function() {
+ var defineProperty = Object.defineProperty;
+ var counter = Date.now() % 1e9;
+ var WeakMap = function() {
+ this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
+ };
+ WeakMap.prototype = {
+ set: function(key, value) {
+ var entry = key[this.name];
+ if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
+ value: [ key, value ],
+ writable: true
+ });
+ return this;
+ },
+ get: function(key) {
+ var entry;
+ return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
+ },
+ "delete": function(key) {
+ var entry = key[this.name];
+ if (!entry || entry[0] !== key) return false;
+ entry[0] = entry[1] = undefined;
+ return true;
+ },
+ has: function(key) {
+ var entry = key[this.name];
+ if (!entry) return false;
+ return entry[0] === key;
+ }
+ };
+ window.WeakMap = WeakMap;
+ })();
+}
+
+(function(global) {
+ if (global.JsMutationObserver) {
+ return;
+ }
+ var registrationsTable = new WeakMap();
+ var setImmediate;
+ if (/Trident|Edge/.test(navigator.userAgent)) {
+ setImmediate = setTimeout;
+ } else if (window.setImmediate) {
+ setImmediate = window.setImmediate;
+ } else {
+ var setImmediateQueue = [];
+ var sentinel = String(Math.random());
+ window.addEventListener("message", function(e) {
+ if (e.data === sentinel) {
+ var queue = setImmediateQueue;
+ setImmediateQueue = [];
+ queue.forEach(function(func) {
+ func();
+ });
+ }
+ });
+ setImmediate = function(func) {
+ setImmediateQueue.push(func);
+ window.postMessage(sentinel, "*");
+ };
+ }
+ var isScheduled = false;
+ var scheduledObservers = [];
+ function scheduleCallback(observer) {
+ scheduledObservers.push(observer);
+ if (!isScheduled) {
+ isScheduled = true;
+ setImmediate(dispatchCallbacks);
+ }
+ }
+ function wrapIfNeeded(node) {
+ return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
+ }
+ function dispatchCallbacks() {
+ isScheduled = false;
+ var observers = scheduledObservers;
+ scheduledObservers = [];
+ observers.sort(function(o1, o2) {
+ return o1.uid_ - o2.uid_;
+ });
+ var anyNonEmpty = false;
+ observers.forEach(function(observer) {
+ var queue = observer.takeRecords();
+ removeTransientObserversFor(observer);
+ if (queue.length) {
+ observer.callback_(queue, observer);
+ anyNonEmpty = true;
+ }
+ });
+ if (anyNonEmpty) dispatchCallbacks();
+ }
+ function removeTransientObserversFor(observer) {
+ observer.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ if (!registrations) return;
+ registrations.forEach(function(registration) {
+ if (registration.observer === observer) registration.removeTransientObservers();
+ });
+ });
+ }
+ function forEachAncestorAndObserverEnqueueRecord(target, callback) {
+ for (var node = target; node; node = node.parentNode) {
+ var registrations = registrationsTable.get(node);
+ if (registrations) {
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ if (node !== target && !options.subtree) continue;
+ var record = callback(options);
+ if (record) registration.enqueue(record);
+ }
+ }
+ }
+ }
+ var uidCounter = 0;
+ function JsMutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+ }
+ JsMutationObserver.prototype = {
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+ if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
+ throw new SyntaxError();
+ }
+ var registrations = registrationsTable.get(target);
+ if (!registrations) registrationsTable.set(target, registrations = []);
+ var registration;
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ registration.removeListeners();
+ registration.options = options;
+ break;
+ }
+ }
+ if (!registration) {
+ registration = new Registration(this, target, options);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ registration.addListeners();
+ },
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registration.removeListeners();
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = [];
+ this.removedNodes = [];
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+ function copyMutationRecord(original) {
+ var record = new MutationRecord(original.type, original.target);
+ record.addedNodes = original.addedNodes.slice();
+ record.removedNodes = original.removedNodes.slice();
+ record.previousSibling = original.previousSibling;
+ record.nextSibling = original.nextSibling;
+ record.attributeName = original.attributeName;
+ record.attributeNamespace = original.attributeNamespace;
+ record.oldValue = original.oldValue;
+ return record;
+ }
+ var currentRecord, recordWithOldValue;
+ function getRecord(type, target) {
+ return currentRecord = new MutationRecord(type, target);
+ }
+ function getRecordWithOldValue(oldValue) {
+ if (recordWithOldValue) return recordWithOldValue;
+ recordWithOldValue = copyMutationRecord(currentRecord);
+ recordWithOldValue.oldValue = oldValue;
+ return recordWithOldValue;
+ }
+ function clearRecords() {
+ currentRecord = recordWithOldValue = undefined;
+ }
+ function recordRepresentsCurrentMutation(record) {
+ return record === recordWithOldValue || record === currentRecord;
+ }
+ function selectRecord(lastRecord, newRecord) {
+ if (lastRecord === newRecord) return lastRecord;
+ if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
+ return null;
+ }
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+ Registration.prototype = {
+ enqueue: function(record) {
+ var records = this.observer.records_;
+ var length = records.length;
+ if (records.length > 0) {
+ var lastRecord = records[length - 1];
+ var recordToReplaceLast = selectRecord(lastRecord, record);
+ if (recordToReplaceLast) {
+ records[length - 1] = recordToReplaceLast;
+ return;
+ }
+ } else {
+ scheduleCallback(this.observer);
+ }
+ records[length] = record;
+ },
+ addListeners: function() {
+ this.addListeners_(this.target);
+ },
+ addListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
+ },
+ removeListeners: function() {
+ this.removeListeners_(this.target);
+ },
+ removeListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
+ },
+ addTransientObserver: function(node) {
+ if (node === this.target) return;
+ this.addListeners_(node);
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations) registrationsTable.set(node, registrations = []);
+ registrations.push(this);
+ },
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+ transientObservedNodes.forEach(function(node) {
+ this.removeListeners_(node);
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i] === this) {
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ },
+ handleEvent: function(e) {
+ e.stopImmediatePropagation();
+ switch (e.type) {
+ case "DOMAttrModified":
+ var name = e.attrName;
+ var namespace = e.relatedNode.namespaceURI;
+ var target = e.target;
+ var record = new getRecord("attributes", target);
+ record.attributeName = name;
+ record.attributeNamespace = namespace;
+ var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.attributes) return;
+ if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
+ return;
+ }
+ if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMCharacterDataModified":
+ var target = e.target;
+ var record = getRecord("characterData", target);
+ var oldValue = e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.characterData) return;
+ if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMNodeRemoved":
+ this.addTransientObserver(e.target);
+
+ case "DOMNodeInserted":
+ var changedNode = e.target;
+ var addedNodes, removedNodes;
+ if (e.type === "DOMNodeInserted") {
+ addedNodes = [ changedNode ];
+ removedNodes = [];
+ } else {
+ addedNodes = [];
+ removedNodes = [ changedNode ];
+ }
+ var previousSibling = changedNode.previousSibling;
+ var nextSibling = changedNode.nextSibling;
+ var record = getRecord("childList", e.target.parentNode);
+ record.addedNodes = addedNodes;
+ record.removedNodes = removedNodes;
+ record.previousSibling = previousSibling;
+ record.nextSibling = nextSibling;
+ forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
+ if (!options.childList) return;
+ return record;
+ });
+ }
+ clearRecords();
+ }
+ };
+ global.JsMutationObserver = JsMutationObserver;
+ if (!global.MutationObserver) {
+ global.MutationObserver = JsMutationObserver;
+ JsMutationObserver._isPolyfilled = true;
+ }
+})(self);
+
+(function() {
+ var needsTemplate = typeof HTMLTemplateElement === "undefined";
+ if (/Trident/.test(navigator.userAgent)) {
+ (function() {
+ var importNode = document.importNode;
+ document.importNode = function() {
+ var n = importNode.apply(document, arguments);
+ if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+ var f = document.createDocumentFragment();
+ f.appendChild(n);
+ return f;
+ } else {
+ return n;
+ }
+ };
+ })();
+ }
+ var needsCloning = function() {
+ if (!needsTemplate) {
+ var t = document.createElement("template");
+ var t2 = document.createElement("template");
+ t2.content.appendChild(document.createElement("div"));
+ t.content.appendChild(t2);
+ var clone = t.cloneNode(true);
+ return clone.content.childNodes.length === 0 || clone.content.firstChild.content.childNodes.length === 0;
+ }
+ }();
+ var TEMPLATE_TAG = "template";
+ var TemplateImpl = function() {};
+ if (needsTemplate) {
+ var contentDoc = document.implementation.createHTMLDocument("template");
+ var canDecorate = true;
+ var templateStyle = document.createElement("style");
+ templateStyle.textContent = TEMPLATE_TAG + "{display:none;}";
+ var head = document.head;
+ head.insertBefore(templateStyle, head.firstElementChild);
+ TemplateImpl.prototype = Object.create(HTMLElement.prototype);
+ TemplateImpl.decorate = function(template) {
+ if (template.content) {
+ return;
+ }
+ template.content = contentDoc.createDocumentFragment();
+ var child;
+ while (child = template.firstChild) {
+ template.content.appendChild(child);
+ }
+ template.cloneNode = function(deep) {
+ return TemplateImpl.cloneNode(this, deep);
+ };
+ if (canDecorate) {
+ try {
+ Object.defineProperty(template, "innerHTML", {
+ get: function() {
+ var o = "";
+ for (var e = this.content.firstChild; e; e = e.nextSibling) {
+ o += e.outerHTML || escapeData(e.data);
+ }
+ return o;
+ },
+ set: function(text) {
+ contentDoc.body.innerHTML = text;
+ TemplateImpl.bootstrap(contentDoc);
+ while (this.content.firstChild) {
+ this.content.removeChild(this.content.firstChild);
+ }
+ while (contentDoc.body.firstChild) {
+ this.content.appendChild(contentDoc.body.firstChild);
+ }
+ },
+ configurable: true
+ });
+ } catch (err) {
+ canDecorate = false;
+ }
+ }
+ TemplateImpl.bootstrap(template.content);
+ };
+ TemplateImpl.bootstrap = function(doc) {
+ var templates = doc.querySelectorAll(TEMPLATE_TAG);
+ for (var i = 0, l = templates.length, t; i < l && (t = templates[i]); i++) {
+ TemplateImpl.decorate(t);
+ }
+ };
+ document.addEventListener("DOMContentLoaded", function() {
+ TemplateImpl.bootstrap(document);
+ });
+ var createElement = document.createElement;
+ document.createElement = function() {
+ "use strict";
+ var el = createElement.apply(document, arguments);
+ if (el.localName === "template") {
+ TemplateImpl.decorate(el);
+ }
+ return el;
+ };
+ var escapeDataRegExp = /[&\u00A0<>]/g;
+ function escapeReplace(c) {
+ switch (c) {
+ case "&":
+ return "&amp;";
+
+ case "<":
+ return "&lt;";
+
+ case ">":
+ return "&gt;";
+
+ case " ":
+ return "&nbsp;";
+ }
+ }
+ function escapeData(s) {
+ return s.replace(escapeDataRegExp, escapeReplace);
+ }
+ }
+ if (needsTemplate || needsCloning) {
+ var nativeCloneNode = Node.prototype.cloneNode;
+ TemplateImpl.cloneNode = function(template, deep) {
+ var clone = nativeCloneNode.call(template, false);
+ if (this.decorate) {
+ this.decorate(clone);
+ }
+ if (deep) {
+ clone.content.appendChild(nativeCloneNode.call(template.content, true));
+ this.fixClonedDom(clone.content, template.content);
+ }
+ return clone;
+ };
+ TemplateImpl.fixClonedDom = function(clone, source) {
+ if (!source.querySelectorAll) return;
+ var s$ = source.querySelectorAll(TEMPLATE_TAG);
+ var t$ = clone.querySelectorAll(TEMPLATE_TAG);
+ for (var i = 0, l = t$.length, t, s; i < l; i++) {
+ s = s$[i];
+ t = t$[i];
+ if (this.decorate) {
+ this.decorate(s);
+ }
+ t.parentNode.replaceChild(s.cloneNode(true), t);
+ }
+ };
+ var originalImportNode = document.importNode;
+ Node.prototype.cloneNode = function(deep) {
+ var dom = nativeCloneNode.call(this, deep);
+ if (deep) {
+ TemplateImpl.fixClonedDom(dom, this);
+ }
+ return dom;
+ };
+ document.importNode = function(element, deep) {
+ if (element.localName === TEMPLATE_TAG) {
+ return TemplateImpl.cloneNode(element, deep);
+ } else {
+ var dom = originalImportNode.call(document, element, deep);
+ if (deep) {
+ TemplateImpl.fixClonedDom(dom, element);
+ }
+ return dom;
+ }
+ };
+ if (needsCloning) {
+ HTMLTemplateElement.prototype.cloneNode = function(deep) {
+ return TemplateImpl.cloneNode(this, deep);
+ };
+ }
+ }
+ if (needsTemplate) {
+ window.HTMLTemplateElement = TemplateImpl;
+ }
+})();
+
+(function(scope) {
+ "use strict";
+ if (!(window.performance && window.performance.now)) {
+ var start = Date.now();
+ window.performance = {
+ now: function() {
+ return Date.now() - start;
+ }
+ };
+ }
+ if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = function() {
+ var nativeRaf = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
+ return nativeRaf ? function(callback) {
+ return nativeRaf(function() {
+ callback(performance.now());
+ });
+ } : function(callback) {
+ return window.setTimeout(callback, 1e3 / 60);
+ };
+ }();
+ }
+ if (!window.cancelAnimationFrame) {
+ window.cancelAnimationFrame = function() {
+ return window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function(id) {
+ clearTimeout(id);
+ };
+ }();
+ }
+ var workingDefaultPrevented = function() {
+ var e = document.createEvent("Event");
+ e.initEvent("foo", true, true);
+ e.preventDefault();
+ return e.defaultPrevented;
+ }();
+ if (!workingDefaultPrevented) {
+ var origPreventDefault = Event.prototype.preventDefault;
+ Event.prototype.preventDefault = function() {
+ if (!this.cancelable) {
+ return;
+ }
+ origPreventDefault.call(this);
+ Object.defineProperty(this, "defaultPrevented", {
+ get: function() {
+ return true;
+ },
+ configurable: true
+ });
+ };
+ }
+ var isIE = /Trident/.test(navigator.userAgent);
+ if (!window.CustomEvent || isIE && typeof window.CustomEvent !== "function") {
+ window.CustomEvent = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("CustomEvent");
+ e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
+ return e;
+ };
+ window.CustomEvent.prototype = window.Event.prototype;
+ }
+ if (!window.Event || isIE && typeof window.Event !== "function") {
+ var origEvent = window.Event;
+ window.Event = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("Event");
+ e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable));
+ return e;
+ };
+ window.Event.prototype = origEvent.prototype;
+ }
+})(window.WebComponents);
+
+window.HTMLImports = window.HTMLImports || {
+ flags: {}
+};
+
+(function(scope) {
+ var IMPORT_LINK_TYPE = "import";
+ var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement("link"));
+ var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
+ var wrap = function(node) {
+ return hasShadowDOMPolyfill ? window.ShadowDOMPolyfill.wrapIfNeeded(node) : node;
+ };
+ var rootDocument = wrap(document);
+ var currentScriptDescriptor = {
+ get: function() {
+ var script = window.HTMLImports.currentScript || document.currentScript || (document.readyState !== "complete" ? document.scripts[document.scripts.length - 1] : null);
+ return wrap(script);
+ },
+ configurable: true
+ };
+ Object.defineProperty(document, "_currentScript", currentScriptDescriptor);
+ Object.defineProperty(rootDocument, "_currentScript", currentScriptDescriptor);
+ var isIE = /Trident/.test(navigator.userAgent);
+ function whenReady(callback, doc) {
+ doc = doc || rootDocument;
+ whenDocumentReady(function() {
+ watchImportsLoad(callback, doc);
+ }, doc);
+ }
+ var requiredReadyState = isIE ? "complete" : "interactive";
+ var READY_EVENT = "readystatechange";
+ function isDocumentReady(doc) {
+ return doc.readyState === "complete" || doc.readyState === requiredReadyState;
+ }
+ function whenDocumentReady(callback, doc) {
+ if (!isDocumentReady(doc)) {
+ var checkReady = function() {
+ if (doc.readyState === "complete" || doc.readyState === requiredReadyState) {
+ doc.removeEventListener(READY_EVENT, checkReady);
+ whenDocumentReady(callback, doc);
+ }
+ };
+ doc.addEventListener(READY_EVENT, checkReady);
+ } else if (callback) {
+ callback();
+ }
+ }
+ function markTargetLoaded(event) {
+ event.target.__loaded = true;
+ }
+ function watchImportsLoad(callback, doc) {
+ var imports = doc.querySelectorAll("link[rel=import]");
+ var parsedCount = 0, importCount = imports.length, newImports = [], errorImports = [];
+ function checkDone() {
+ if (parsedCount == importCount && callback) {
+ callback({
+ allImports: imports,
+ loadedImports: newImports,
+ errorImports: errorImports
+ });
+ }
+ }
+ function loadedImport(e) {
+ markTargetLoaded(e);
+ newImports.push(this);
+ parsedCount++;
+ checkDone();
+ }
+ function errorLoadingImport(e) {
+ errorImports.push(this);
+ parsedCount++;
+ checkDone();
+ }
+ if (importCount) {
+ for (var i = 0, imp; i < importCount && (imp = imports[i]); i++) {
+ if (isImportLoaded(imp)) {
+ newImports.push(this);
+ parsedCount++;
+ checkDone();
+ } else {
+ imp.addEventListener("load", loadedImport);
+ imp.addEventListener("error", errorLoadingImport);
+ }
+ }
+ } else {
+ checkDone();
+ }
+ }
+ function isImportLoaded(link) {
+ return useNative ? link.__loaded || link.import && link.import.readyState !== "loading" : link.__importParsed;
+ }
+ if (useNative) {
+ new MutationObserver(function(mxns) {
+ for (var i = 0, l = mxns.length, m; i < l && (m = mxns[i]); i++) {
+ if (m.addedNodes) {
+ handleImports(m.addedNodes);
+ }
+ }
+ }).observe(document.head, {
+ childList: true
+ });
+ function handleImports(nodes) {
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (isImport(n)) {
+ handleImport(n);
+ }
+ }
+ }
+ function isImport(element) {
+ return element.localName === "link" && element.rel === "import";
+ }
+ function handleImport(element) {
+ var loaded = element.import;
+ if (loaded) {
+ markTargetLoaded({
+ target: element
+ });
+ } else {
+ element.addEventListener("load", markTargetLoaded);
+ element.addEventListener("error", markTargetLoaded);
+ }
+ }
+ (function() {
+ if (document.readyState === "loading") {
+ var imports = document.querySelectorAll("link[rel=import]");
+ for (var i = 0, l = imports.length, imp; i < l && (imp = imports[i]); i++) {
+ handleImport(imp);
+ }
+ }
+ })();
+ }
+ whenReady(function(detail) {
+ window.HTMLImports.ready = true;
+ window.HTMLImports.readyTime = new Date().getTime();
+ var evt = rootDocument.createEvent("CustomEvent");
+ evt.initCustomEvent("HTMLImportsLoaded", true, true, detail);
+ rootDocument.dispatchEvent(evt);
+ });
+ scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
+ scope.useNative = useNative;
+ scope.rootDocument = rootDocument;
+ scope.whenReady = whenReady;
+ scope.isIE = isIE;
+})(window.HTMLImports);
+
+(function(scope) {
+ var modules = [];
+ var addModule = function(module) {
+ modules.push(module);
+ };
+ var initializeModules = function() {
+ modules.forEach(function(module) {
+ module(scope);
+ });
+ };
+ scope.addModule = addModule;
+ scope.initializeModules = initializeModules;
+})(window.HTMLImports);
+
+window.HTMLImports.addModule(function(scope) {
+ var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
+ var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
+ var path = {
+ resolveUrlsInStyle: function(style, linkUrl) {
+ var doc = style.ownerDocument;
+ var resolver = doc.createElement("a");
+ style.textContent = this.resolveUrlsInCssText(style.textContent, linkUrl, resolver);
+ return style;
+ },
+ resolveUrlsInCssText: function(cssText, linkUrl, urlObj) {
+ var r = this.replaceUrls(cssText, urlObj, linkUrl, CSS_URL_REGEXP);
+ r = this.replaceUrls(r, urlObj, linkUrl, CSS_IMPORT_REGEXP);
+ return r;
+ },
+ replaceUrls: function(text, urlObj, linkUrl, regexp) {
+ return text.replace(regexp, function(m, pre, url, post) {
+ var urlPath = url.replace(/["']/g, "");
+ if (linkUrl) {
+ urlPath = new URL(urlPath, linkUrl).href;
+ }
+ urlObj.href = urlPath;
+ urlPath = urlObj.href;
+ return pre + "'" + urlPath + "'" + post;
+ });
+ }
+ };
+ scope.path = path;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var xhr = {
+ async: true,
+ ok: function(request) {
+ return request.status >= 200 && request.status < 300 || request.status === 304 || request.status === 0;
+ },
+ load: function(url, next, nextContext) {
+ var request = new XMLHttpRequest();
+ if (scope.flags.debug || scope.flags.bust) {
+ url += "?" + Math.random();
+ }
+ request.open("GET", url, xhr.async);
+ request.addEventListener("readystatechange", function(e) {
+ if (request.readyState === 4) {
+ var redirectedUrl = null;
+ try {
+ var locationHeader = request.getResponseHeader("Location");
+ if (locationHeader) {
+ redirectedUrl = locationHeader.substr(0, 1) === "/" ? location.origin + locationHeader : locationHeader;
+ }
+ } catch (e) {
+ console.error(e.message);
+ }
+ next.call(nextContext, !xhr.ok(request) && request, request.response || request.responseText, redirectedUrl);
+ }
+ });
+ request.send();
+ return request;
+ },
+ loadDocument: function(url, next, nextContext) {
+ this.load(url, next, nextContext).responseType = "document";
+ }
+ };
+ scope.xhr = xhr;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var xhr = scope.xhr;
+ var flags = scope.flags;
+ var Loader = function(onLoad, onComplete) {
+ this.cache = {};
+ this.onload = onLoad;
+ this.oncomplete = onComplete;
+ this.inflight = 0;
+ this.pending = {};
+ };
+ Loader.prototype = {
+ addNodes: function(nodes) {
+ this.inflight += nodes.length;
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ this.require(n);
+ }
+ this.checkDone();
+ },
+ addNode: function(node) {
+ this.inflight++;
+ this.require(node);
+ this.checkDone();
+ },
+ require: function(elt) {
+ var url = elt.src || elt.href;
+ elt.__nodeUrl = url;
+ if (!this.dedupe(url, elt)) {
+ this.fetch(url, elt);
+ }
+ },
+ dedupe: function(url, elt) {
+ if (this.pending[url]) {
+ this.pending[url].push(elt);
+ return true;
+ }
+ var resource;
+ if (this.cache[url]) {
+ this.onload(url, elt, this.cache[url]);
+ this.tail();
+ return true;
+ }
+ this.pending[url] = [ elt ];
+ return false;
+ },
+ fetch: function(url, elt) {
+ flags.load && console.log("fetch", url, elt);
+ if (!url) {
+ setTimeout(function() {
+ this.receive(url, elt, {
+ error: "href must be specified"
+ }, null);
+ }.bind(this), 0);
+ } else if (url.match(/^data:/)) {
+ var pieces = url.split(",");
+ var header = pieces[0];
+ var body = pieces[1];
+ if (header.indexOf(";base64") > -1) {
+ body = atob(body);
+ } else {
+ body = decodeURIComponent(body);
+ }
+ setTimeout(function() {
+ this.receive(url, elt, null, body);
+ }.bind(this), 0);
+ } else {
+ var receiveXhr = function(err, resource, redirectedUrl) {
+ this.receive(url, elt, err, resource, redirectedUrl);
+ }.bind(this);
+ xhr.load(url, receiveXhr);
+ }
+ },
+ receive: function(url, elt, err, resource, redirectedUrl) {
+ this.cache[url] = resource;
+ var $p = this.pending[url];
+ for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+ this.onload(url, p, resource, err, redirectedUrl);
+ this.tail();
+ }
+ this.pending[url] = null;
+ },
+ tail: function() {
+ --this.inflight;
+ this.checkDone();
+ },
+ checkDone: function() {
+ if (!this.inflight) {
+ this.oncomplete();
+ }
+ }
+ };
+ scope.Loader = Loader;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var Observer = function(addCallback) {
+ this.addCallback = addCallback;
+ this.mo = new MutationObserver(this.handler.bind(this));
+ };
+ Observer.prototype = {
+ handler: function(mutations) {
+ for (var i = 0, l = mutations.length, m; i < l && (m = mutations[i]); i++) {
+ if (m.type === "childList" && m.addedNodes.length) {
+ this.addedNodes(m.addedNodes);
+ }
+ }
+ },
+ addedNodes: function(nodes) {
+ if (this.addCallback) {
+ this.addCallback(nodes);
+ }
+ for (var i = 0, l = nodes.length, n, loading; i < l && (n = nodes[i]); i++) {
+ if (n.children && n.children.length) {
+ this.addedNodes(n.children);
+ }
+ }
+ },
+ observe: function(root) {
+ this.mo.observe(root, {
+ childList: true,
+ subtree: true
+ });
+ }
+ };
+ scope.Observer = Observer;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var path = scope.path;
+ var rootDocument = scope.rootDocument;
+ var flags = scope.flags;
+ var isIE = scope.isIE;
+ var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+ var IMPORT_SELECTOR = "link[rel=" + IMPORT_LINK_TYPE + "]";
+ var importParser = {
+ documentSelectors: IMPORT_SELECTOR,
+ importsSelectors: [ IMPORT_SELECTOR, "link[rel=stylesheet]:not([type])", "style:not([type])", "script:not([type])", 'script[type="application/javascript"]', 'script[type="text/javascript"]' ].join(","),
+ map: {
+ link: "parseLink",
+ script: "parseScript",
+ style: "parseStyle"
+ },
+ dynamicElements: [],
+ parseNext: function() {
+ var next = this.nextToParse();
+ if (next) {
+ this.parse(next);
+ }
+ },
+ parse: function(elt) {
+ if (this.isParsed(elt)) {
+ flags.parse && console.log("[%s] is already parsed", elt.localName);
+ return;
+ }
+ var fn = this[this.map[elt.localName]];
+ if (fn) {
+ this.markParsing(elt);
+ fn.call(this, elt);
+ }
+ },
+ parseDynamic: function(elt, quiet) {
+ this.dynamicElements.push(elt);
+ if (!quiet) {
+ this.parseNext();
+ }
+ },
+ markParsing: function(elt) {
+ flags.parse && console.log("parsing", elt);
+ this.parsingElement = elt;
+ },
+ markParsingComplete: function(elt) {
+ elt.__importParsed = true;
+ this.markDynamicParsingComplete(elt);
+ if (elt.__importElement) {
+ elt.__importElement.__importParsed = true;
+ this.markDynamicParsingComplete(elt.__importElement);
+ }
+ this.parsingElement = null;
+ flags.parse && console.log("completed", elt);
+ },
+ markDynamicParsingComplete: function(elt) {
+ var i = this.dynamicElements.indexOf(elt);
+ if (i >= 0) {
+ this.dynamicElements.splice(i, 1);
+ }
+ },
+ parseImport: function(elt) {
+ elt.import = elt.__doc;
+ if (window.HTMLImports.__importsParsingHook) {
+ window.HTMLImports.__importsParsingHook(elt);
+ }
+ if (elt.import) {
+ elt.import.__importParsed = true;
+ }
+ this.markParsingComplete(elt);
+ if (elt.__resource && !elt.__error) {
+ elt.dispatchEvent(new CustomEvent("load", {
+ bubbles: false
+ }));
+ } else {
+ elt.dispatchEvent(new CustomEvent("error", {
+ bubbles: false
+ }));
+ }
+ if (elt.__pending) {
+ var fn;
+ while (elt.__pending.length) {
+ fn = elt.__pending.shift();
+ if (fn) {
+ fn({
+ target: elt
+ });
+ }
+ }
+ }
+ this.parseNext();
+ },
+ parseLink: function(linkElt) {
+ if (nodeIsImport(linkElt)) {
+ this.parseImport(linkElt);
+ } else {
+ linkElt.href = linkElt.href;
+ this.parseGeneric(linkElt);
+ }
+ },
+ parseStyle: function(elt) {
+ var src = elt;
+ elt = cloneStyle(elt);
+ src.__appliedElement = elt;
+ elt.__importElement = src;
+ this.parseGeneric(elt);
+ },
+ parseGeneric: function(elt) {
+ this.trackElement(elt);
+ this.addElementToDocument(elt);
+ },
+ rootImportForElement: function(elt) {
+ var n = elt;
+ while (n.ownerDocument.__importLink) {
+ n = n.ownerDocument.__importLink;
+ }
+ return n;
+ },
+ addElementToDocument: function(elt) {
+ var port = this.rootImportForElement(elt.__importElement || elt);
+ port.parentNode.insertBefore(elt, port);
+ },
+ trackElement: function(elt, callback) {
+ var self = this;
+ var done = function(e) {
+ elt.removeEventListener("load", done);
+ elt.removeEventListener("error", done);
+ if (callback) {
+ callback(e);
+ }
+ self.markParsingComplete(elt);
+ self.parseNext();
+ };
+ elt.addEventListener("load", done);
+ elt.addEventListener("error", done);
+ if (isIE && elt.localName === "style") {
+ var fakeLoad = false;
+ if (elt.textContent.indexOf("@import") == -1) {
+ fakeLoad = true;
+ } else if (elt.sheet) {
+ fakeLoad = true;
+ var csr = elt.sheet.cssRules;
+ var len = csr ? csr.length : 0;
+ for (var i = 0, r; i < len && (r = csr[i]); i++) {
+ if (r.type === CSSRule.IMPORT_RULE) {
+ fakeLoad = fakeLoad && Boolean(r.styleSheet);
+ }
+ }
+ }
+ if (fakeLoad) {
+ setTimeout(function() {
+ elt.dispatchEvent(new CustomEvent("load", {
+ bubbles: false
+ }));
+ });
+ }
+ }
+ },
+ parseScript: function(scriptElt) {
+ var script = document.createElement("script");
+ script.__importElement = scriptElt;
+ script.src = scriptElt.src ? scriptElt.src : generateScriptDataUrl(scriptElt);
+ scope.currentScript = scriptElt;
+ this.trackElement(script, function(e) {
+ if (script.parentNode) {
+ script.parentNode.removeChild(script);
+ }
+ scope.currentScript = null;
+ });
+ this.addElementToDocument(script);
+ },
+ nextToParse: function() {
+ this._mayParse = [];
+ return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || this.nextToParseDynamic());
+ },
+ nextToParseInDoc: function(doc, link) {
+ if (doc && this._mayParse.indexOf(doc) < 0) {
+ this._mayParse.push(doc);
+ var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc));
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (!this.isParsed(n)) {
+ if (this.hasResource(n)) {
+ return nodeIsImport(n) ? this.nextToParseInDoc(n.__doc, n) : n;
+ } else {
+ return;
+ }
+ }
+ }
+ }
+ return link;
+ },
+ nextToParseDynamic: function() {
+ return this.dynamicElements[0];
+ },
+ parseSelectorsForNode: function(node) {
+ var doc = node.ownerDocument || node;
+ return doc === rootDocument ? this.documentSelectors : this.importsSelectors;
+ },
+ isParsed: function(node) {
+ return node.__importParsed;
+ },
+ needsDynamicParsing: function(elt) {
+ return this.dynamicElements.indexOf(elt) >= 0;
+ },
+ hasResource: function(node) {
+ if (nodeIsImport(node) && node.__doc === undefined) {
+ return false;
+ }
+ return true;
+ }
+ };
+ function nodeIsImport(elt) {
+ return elt.localName === "link" && elt.rel === IMPORT_LINK_TYPE;
+ }
+ function generateScriptDataUrl(script) {
+ var scriptContent = generateScriptContent(script);
+ return "data:text/javascript;charset=utf-8," + encodeURIComponent(scriptContent);
+ }
+ function generateScriptContent(script) {
+ return script.textContent + generateSourceMapHint(script);
+ }
+ function generateSourceMapHint(script) {
+ var owner = script.ownerDocument;
+ owner.__importedScripts = owner.__importedScripts || 0;
+ var moniker = script.ownerDocument.baseURI;
+ var num = owner.__importedScripts ? "-" + owner.__importedScripts : "";
+ owner.__importedScripts++;
+ return "\n//# sourceURL=" + moniker + num + ".js\n";
+ }
+ function cloneStyle(style) {
+ var clone = style.ownerDocument.createElement("style");
+ clone.textContent = style.textContent;
+ path.resolveUrlsInStyle(clone);
+ return clone;
+ }
+ scope.parser = importParser;
+ scope.IMPORT_SELECTOR = IMPORT_SELECTOR;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var flags = scope.flags;
+ var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+ var IMPORT_SELECTOR = scope.IMPORT_SELECTOR;
+ var rootDocument = scope.rootDocument;
+ var Loader = scope.Loader;
+ var Observer = scope.Observer;
+ var parser = scope.parser;
+ var importer = {
+ documents: {},
+ documentPreloadSelectors: IMPORT_SELECTOR,
+ importsPreloadSelectors: [ IMPORT_SELECTOR ].join(","),
+ loadNode: function(node) {
+ importLoader.addNode(node);
+ },
+ loadSubtree: function(parent) {
+ var nodes = this.marshalNodes(parent);
+ importLoader.addNodes(nodes);
+ },
+ marshalNodes: function(parent) {
+ return parent.querySelectorAll(this.loadSelectorsForNode(parent));
+ },
+ loadSelectorsForNode: function(node) {
+ var doc = node.ownerDocument || node;
+ return doc === rootDocument ? this.documentPreloadSelectors : this.importsPreloadSelectors;
+ },
+ loaded: function(url, elt, resource, err, redirectedUrl) {
+ flags.load && console.log("loaded", url, elt);
+ elt.__resource = resource;
+ elt.__error = err;
+ if (isImportLink(elt)) {
+ var doc = this.documents[url];
+ if (doc === undefined) {
+ doc = err ? null : makeDocument(resource, redirectedUrl || url);
+ if (doc) {
+ doc.__importLink = elt;
+ this.bootDocument(doc);
+ }
+ this.documents[url] = doc;
+ }
+ elt.__doc = doc;
+ }
+ parser.parseNext();
+ },
+ bootDocument: function(doc) {
+ this.loadSubtree(doc);
+ this.observer.observe(doc);
+ parser.parseNext();
+ },
+ loadedAll: function() {
+ parser.parseNext();
+ }
+ };
+ var importLoader = new Loader(importer.loaded.bind(importer), importer.loadedAll.bind(importer));
+ importer.observer = new Observer();
+ function isImportLink(elt) {
+ return isLinkRel(elt, IMPORT_LINK_TYPE);
+ }
+ function isLinkRel(elt, rel) {
+ return elt.localName === "link" && elt.getAttribute("rel") === rel;
+ }
+ function hasBaseURIAccessor(doc) {
+ return !!Object.getOwnPropertyDescriptor(doc, "baseURI");
+ }
+ function makeDocument(resource, url) {
+ var doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
+ doc._URL = url;
+ var base = doc.createElement("base");
+ base.setAttribute("href", url);
+ if (!doc.baseURI && !hasBaseURIAccessor(doc)) {
+ Object.defineProperty(doc, "baseURI", {
+ value: url
+ });
+ }
+ var meta = doc.createElement("meta");
+ meta.setAttribute("charset", "utf-8");
+ doc.head.appendChild(meta);
+ doc.head.appendChild(base);
+ doc.body.innerHTML = resource;
+ if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
+ HTMLTemplateElement.bootstrap(doc);
+ }
+ return doc;
+ }
+ if (!document.baseURI) {
+ var baseURIDescriptor = {
+ get: function() {
+ var base = document.querySelector("base");
+ return base ? base.href : window.location.href;
+ },
+ configurable: true
+ };
+ Object.defineProperty(document, "baseURI", baseURIDescriptor);
+ Object.defineProperty(rootDocument, "baseURI", baseURIDescriptor);
+ }
+ scope.importer = importer;
+ scope.importLoader = importLoader;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var parser = scope.parser;
+ var importer = scope.importer;
+ var dynamic = {
+ added: function(nodes) {
+ var owner, parsed, loading;
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (!owner) {
+ owner = n.ownerDocument;
+ parsed = parser.isParsed(owner);
+ }
+ loading = this.shouldLoadNode(n);
+ if (loading) {
+ importer.loadNode(n);
+ }
+ if (this.shouldParseNode(n) && parsed) {
+ parser.parseDynamic(n, loading);
+ }
+ }
+ },
+ shouldLoadNode: function(node) {
+ return node.nodeType === 1 && matches.call(node, importer.loadSelectorsForNode(node));
+ },
+ shouldParseNode: function(node) {
+ return node.nodeType === 1 && matches.call(node, parser.parseSelectorsForNode(node));
+ }
+ };
+ importer.observer.addCallback = dynamic.added.bind(dynamic);
+ var matches = HTMLElement.prototype.matches || HTMLElement.prototype.matchesSelector || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector || HTMLElement.prototype.msMatchesSelector;
+});
+
+(function(scope) {
+ var initializeModules = scope.initializeModules;
+ var isIE = scope.isIE;
+ if (scope.useNative) {
+ return;
+ }
+ initializeModules();
+ var rootDocument = scope.rootDocument;
+ function bootstrap() {
+ window.HTMLImports.importer.bootDocument(rootDocument);
+ }
+ if (document.readyState === "complete" || document.readyState === "interactive" && !window.attachEvent) {
+ bootstrap();
+ } else {
+ document.addEventListener("DOMContentLoaded", bootstrap);
+ }
+})(window.HTMLImports);
+
+window.CustomElements = window.CustomElements || {
+ flags: {}
+};
+
+(function(scope) {
+ var flags = scope.flags;
+ var modules = [];
+ var addModule = function(module) {
+ modules.push(module);
+ };
+ var initializeModules = function() {
+ modules.forEach(function(module) {
+ module(scope);
+ });
+ };
+ scope.addModule = addModule;
+ scope.initializeModules = initializeModules;
+ scope.hasNative = Boolean(document.registerElement);
+ scope.isIE = /Trident/.test(navigator.userAgent);
+ scope.useNative = !flags.register && scope.hasNative && !window.ShadowDOMPolyfill && (!window.HTMLImports || window.HTMLImports.useNative);
+})(window.CustomElements);
+
+window.CustomElements.addModule(function(scope) {
+ var IMPORT_LINK_TYPE = window.HTMLImports ? window.HTMLImports.IMPORT_LINK_TYPE : "none";
+ function forSubtree(node, cb) {
+ findAllElements(node, function(e) {
+ if (cb(e)) {
+ return true;
+ }
+ forRoots(e, cb);
+ });
+ forRoots(node, cb);
+ }
+ function findAllElements(node, find, data) {
+ var e = node.firstElementChild;
+ if (!e) {
+ e = node.firstChild;
+ while (e && e.nodeType !== Node.ELEMENT_NODE) {
+ e = e.nextSibling;
+ }
+ }
+ while (e) {
+ if (find(e, data) !== true) {
+ findAllElements(e, find, data);
+ }
+ e = e.nextElementSibling;
+ }
+ return null;
+ }
+ function forRoots(node, cb) {
+ var root = node.shadowRoot;
+ while (root) {
+ forSubtree(root, cb);
+ root = root.olderShadowRoot;
+ }
+ }
+ function forDocumentTree(doc, cb) {
+ _forDocumentTree(doc, cb, []);
+ }
+ function _forDocumentTree(doc, cb, processingDocuments) {
+ doc = window.wrap(doc);
+ if (processingDocuments.indexOf(doc) >= 0) {
+ return;
+ }
+ processingDocuments.push(doc);
+ var imports = doc.querySelectorAll("link[rel=" + IMPORT_LINK_TYPE + "]");
+ for (var i = 0, l = imports.length, n; i < l && (n = imports[i]); i++) {
+ if (n.import) {
+ _forDocumentTree(n.import, cb, processingDocuments);
+ }
+ }
+ cb(doc);
+ }
+ scope.forDocumentTree = forDocumentTree;
+ scope.forSubtree = forSubtree;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var flags = scope.flags;
+ var forSubtree = scope.forSubtree;
+ var forDocumentTree = scope.forDocumentTree;
+ function addedNode(node, isAttached) {
+ return added(node, isAttached) || addedSubtree(node, isAttached);
+ }
+ function added(node, isAttached) {
+ if (scope.upgrade(node, isAttached)) {
+ return true;
+ }
+ if (isAttached) {
+ attached(node);
+ }
+ }
+ function addedSubtree(node, isAttached) {
+ forSubtree(node, function(e) {
+ if (added(e, isAttached)) {
+ return true;
+ }
+ });
+ }
+ var hasThrottledAttached = window.MutationObserver._isPolyfilled && flags["throttle-attached"];
+ scope.hasPolyfillMutations = hasThrottledAttached;
+ scope.hasThrottledAttached = hasThrottledAttached;
+ var isPendingMutations = false;
+ var pendingMutations = [];
+ function deferMutation(fn) {
+ pendingMutations.push(fn);
+ if (!isPendingMutations) {
+ isPendingMutations = true;
+ setTimeout(takeMutations);
+ }
+ }
+ function takeMutations() {
+ isPendingMutations = false;
+ var $p = pendingMutations;
+ for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+ p();
+ }
+ pendingMutations = [];
+ }
+ function attached(element) {
+ if (hasThrottledAttached) {
+ deferMutation(function() {
+ _attached(element);
+ });
+ } else {
+ _attached(element);
+ }
+ }
+ function _attached(element) {
+ if (element.__upgraded__ && !element.__attached) {
+ element.__attached = true;
+ if (element.attachedCallback) {
+ element.attachedCallback();
+ }
+ }
+ }
+ function detachedNode(node) {
+ detached(node);
+ forSubtree(node, function(e) {
+ detached(e);
+ });
+ }
+ function detached(element) {
+ if (hasThrottledAttached) {
+ deferMutation(function() {
+ _detached(element);
+ });
+ } else {
+ _detached(element);
+ }
+ }
+ function _detached(element) {
+ if (element.__upgraded__ && element.__attached) {
+ element.__attached = false;
+ if (element.detachedCallback) {
+ element.detachedCallback();
+ }
+ }
+ }
+ function inDocument(element) {
+ var p = element;
+ var doc = window.wrap(document);
+ while (p) {
+ if (p == doc) {
+ return true;
+ }
+ p = p.parentNode || p.nodeType === Node.DOCUMENT_FRAGMENT_NODE && p.host;
+ }
+ }
+ function watchShadow(node) {
+ if (node.shadowRoot && !node.shadowRoot.__watched) {
+ flags.dom && console.log("watching shadow-root for: ", node.localName);
+ var root = node.shadowRoot;
+ while (root) {
+ observe(root);
+ root = root.olderShadowRoot;
+ }
+ }
+ }
+ function handler(root, mutations) {
+ if (flags.dom) {
+ var mx = mutations[0];
+ if (mx && mx.type === "childList" && mx.addedNodes) {
+ if (mx.addedNodes) {
+ var d = mx.addedNodes[0];
+ while (d && d !== document && !d.host) {
+ d = d.parentNode;
+ }
+ var u = d && (d.URL || d._URL || d.host && d.host.localName) || "";
+ u = u.split("/?").shift().split("/").pop();
+ }
+ }
+ console.group("mutations (%d) [%s]", mutations.length, u || "");
+ }
+ var isAttached = inDocument(root);
+ mutations.forEach(function(mx) {
+ if (mx.type === "childList") {
+ forEach(mx.addedNodes, function(n) {
+ if (!n.localName) {
+ return;
+ }
+ addedNode(n, isAttached);
+ });
+ forEach(mx.removedNodes, function(n) {
+ if (!n.localName) {
+ return;
+ }
+ detachedNode(n);
+ });
+ }
+ });
+ flags.dom && console.groupEnd();
+ }
+ function takeRecords(node) {
+ node = window.wrap(node);
+ if (!node) {
+ node = window.wrap(document);
+ }
+ while (node.parentNode) {
+ node = node.parentNode;
+ }
+ var observer = node.__observer;
+ if (observer) {
+ handler(node, observer.takeRecords());
+ takeMutations();
+ }
+ }
+ var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+ function observe(inRoot) {
+ if (inRoot.__observer) {
+ return;
+ }
+ var observer = new MutationObserver(handler.bind(this, inRoot));
+ observer.observe(inRoot, {
+ childList: true,
+ subtree: true
+ });
+ inRoot.__observer = observer;
+ }
+ function upgradeDocument(doc) {
+ doc = window.wrap(doc);
+ flags.dom && console.group("upgradeDocument: ", doc.baseURI.split("/").pop());
+ var isMainDocument = doc === window.wrap(document);
+ addedNode(doc, isMainDocument);
+ observe(doc);
+ flags.dom && console.groupEnd();
+ }
+ function upgradeDocumentTree(doc) {
+ forDocumentTree(doc, upgradeDocument);
+ }
+ var originalCreateShadowRoot = Element.prototype.createShadowRoot;
+ if (originalCreateShadowRoot) {
+ Element.prototype.createShadowRoot = function() {
+ var root = originalCreateShadowRoot.call(this);
+ window.CustomElements.watchShadow(this);
+ return root;
+ };
+ }
+ scope.watchShadow = watchShadow;
+ scope.upgradeDocumentTree = upgradeDocumentTree;
+ scope.upgradeDocument = upgradeDocument;
+ scope.upgradeSubtree = addedSubtree;
+ scope.upgradeAll = addedNode;
+ scope.attached = attached;
+ scope.takeRecords = takeRecords;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var flags = scope.flags;
+ function upgrade(node, isAttached) {
+ if (node.localName === "template") {
+ if (window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
+ HTMLTemplateElement.decorate(node);
+ }
+ }
+ if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) {
+ var is = node.getAttribute("is");
+ var definition = scope.getRegisteredDefinition(node.localName) || scope.getRegisteredDefinition(is);
+ if (definition) {
+ if (is && definition.tag == node.localName || !is && !definition.extends) {
+ return upgradeWithDefinition(node, definition, isAttached);
+ }
+ }
+ }
+ }
+ function upgradeWithDefinition(element, definition, isAttached) {
+ flags.upgrade && console.group("upgrade:", element.localName);
+ if (definition.is) {
+ element.setAttribute("is", definition.is);
+ }
+ implementPrototype(element, definition);
+ element.__upgraded__ = true;
+ created(element);
+ if (isAttached) {
+ scope.attached(element);
+ }
+ scope.upgradeSubtree(element, isAttached);
+ flags.upgrade && console.groupEnd();
+ return element;
+ }
+ function implementPrototype(element, definition) {
+ if (Object.__proto__) {
+ element.__proto__ = definition.prototype;
+ } else {
+ customMixin(element, definition.prototype, definition.native);
+ element.__proto__ = definition.prototype;
+ }
+ }
+ function customMixin(inTarget, inSrc, inNative) {
+ var used = {};
+ var p = inSrc;
+ while (p !== inNative && p !== HTMLElement.prototype) {
+ var keys = Object.getOwnPropertyNames(p);
+ for (var i = 0, k; k = keys[i]; i++) {
+ if (!used[k]) {
+ Object.defineProperty(inTarget, k, Object.getOwnPropertyDescriptor(p, k));
+ used[k] = 1;
+ }
+ }
+ p = Object.getPrototypeOf(p);
+ }
+ }
+ function created(element) {
+ if (element.createdCallback) {
+ element.createdCallback();
+ }
+ }
+ scope.upgrade = upgrade;
+ scope.upgradeWithDefinition = upgradeWithDefinition;
+ scope.implementPrototype = implementPrototype;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var isIE = scope.isIE;
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
+ var upgradeAll = scope.upgradeAll;
+ var upgradeWithDefinition = scope.upgradeWithDefinition;
+ var implementPrototype = scope.implementPrototype;
+ var useNative = scope.useNative;
+ function register(name, options) {
+ var definition = options || {};
+ if (!name) {
+ throw new Error("document.registerElement: first argument `name` must not be empty");
+ }
+ if (name.indexOf("-") < 0) {
+ throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '" + String(name) + "'.");
+ }
+ if (isReservedTag(name)) {
+ throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '" + String(name) + "'. The type name is invalid.");
+ }
+ if (getRegisteredDefinition(name)) {
+ throw new Error("DuplicateDefinitionError: a type with name '" + String(name) + "' is already registered");
+ }
+ if (!definition.prototype) {
+ definition.prototype = Object.create(HTMLElement.prototype);
+ }
+ definition.__name = name.toLowerCase();
+ if (definition.extends) {
+ definition.extends = definition.extends.toLowerCase();
+ }
+ definition.lifecycle = definition.lifecycle || {};
+ definition.ancestry = ancestry(definition.extends);
+ resolveTagName(definition);
+ resolvePrototypeChain(definition);
+ overrideAttributeApi(definition.prototype);
+ registerDefinition(definition.__name, definition);
+ definition.ctor = generateConstructor(definition);
+ definition.ctor.prototype = definition.prototype;
+ definition.prototype.constructor = definition.ctor;
+ if (scope.ready) {
+ upgradeDocumentTree(document);
+ }
+ return definition.ctor;
+ }
+ function overrideAttributeApi(prototype) {
+ if (prototype.setAttribute._polyfilled) {
+ return;
+ }
+ var setAttribute = prototype.setAttribute;
+ prototype.setAttribute = function(name, value) {
+ changeAttribute.call(this, name, value, setAttribute);
+ };
+ var removeAttribute = prototype.removeAttribute;
+ prototype.removeAttribute = function(name) {
+ changeAttribute.call(this, name, null, removeAttribute);
+ };
+ prototype.setAttribute._polyfilled = true;
+ }
+ function changeAttribute(name, value, operation) {
+ name = name.toLowerCase();
+ var oldValue = this.getAttribute(name);
+ operation.apply(this, arguments);
+ var newValue = this.getAttribute(name);
+ if (this.attributeChangedCallback && newValue !== oldValue) {
+ this.attributeChangedCallback(name, oldValue, newValue);
+ }
+ }
+ function isReservedTag(name) {
+ for (var i = 0; i < reservedTagList.length; i++) {
+ if (name === reservedTagList[i]) {
+ return true;
+ }
+ }
+ }
+ var reservedTagList = [ "annotation-xml", "color-profile", "font-face", "font-face-src", "font-face-uri", "font-face-format", "font-face-name", "missing-glyph" ];
+ function ancestry(extnds) {
+ var extendee = getRegisteredDefinition(extnds);
+ if (extendee) {
+ return ancestry(extendee.extends).concat([ extendee ]);
+ }
+ return [];
+ }
+ function resolveTagName(definition) {
+ var baseTag = definition.extends;
+ for (var i = 0, a; a = definition.ancestry[i]; i++) {
+ baseTag = a.is && a.tag;
+ }
+ definition.tag = baseTag || definition.__name;
+ if (baseTag) {
+ definition.is = definition.__name;
+ }
+ }
+ function resolvePrototypeChain(definition) {
+ if (!Object.__proto__) {
+ var nativePrototype = HTMLElement.prototype;
+ if (definition.is) {
+ var inst = document.createElement(definition.tag);
+ nativePrototype = Object.getPrototypeOf(inst);
+ }
+ var proto = definition.prototype, ancestor;
+ var foundPrototype = false;
+ while (proto) {
+ if (proto == nativePrototype) {
+ foundPrototype = true;
+ }
+ ancestor = Object.getPrototypeOf(proto);
+ if (ancestor) {
+ proto.__proto__ = ancestor;
+ }
+ proto = ancestor;
+ }
+ if (!foundPrototype) {
+ console.warn(definition.tag + " prototype not found in prototype chain for " + definition.is);
+ }
+ definition.native = nativePrototype;
+ }
+ }
+ function instantiate(definition) {
+ return upgradeWithDefinition(domCreateElement(definition.tag), definition);
+ }
+ var registry = {};
+ function getRegisteredDefinition(name) {
+ if (name) {
+ return registry[name.toLowerCase()];
+ }
+ }
+ function registerDefinition(name, definition) {
+ registry[name] = definition;
+ }
+ function generateConstructor(definition) {
+ return function() {
+ return instantiate(definition);
+ };
+ }
+ var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+ function createElementNS(namespace, tag, typeExtension) {
+ if (namespace === HTML_NAMESPACE) {
+ return createElement(tag, typeExtension);
+ } else {
+ return domCreateElementNS(namespace, tag);
+ }
+ }
+ function createElement(tag, typeExtension) {
+ if (tag) {
+ tag = tag.toLowerCase();
+ }
+ if (typeExtension) {
+ typeExtension = typeExtension.toLowerCase();
+ }
+ var definition = getRegisteredDefinition(typeExtension || tag);
+ if (definition) {
+ if (tag == definition.tag && typeExtension == definition.is) {
+ return new definition.ctor();
+ }
+ if (!typeExtension && !definition.is) {
+ return new definition.ctor();
+ }
+ }
+ var element;
+ if (typeExtension) {
+ element = createElement(tag);
+ element.setAttribute("is", typeExtension);
+ return element;
+ }
+ element = domCreateElement(tag);
+ if (tag.indexOf("-") >= 0) {
+ implementPrototype(element, HTMLElement);
+ }
+ return element;
+ }
+ var domCreateElement = document.createElement.bind(document);
+ var domCreateElementNS = document.createElementNS.bind(document);
+ var isInstance;
+ if (!Object.__proto__ && !useNative) {
+ isInstance = function(obj, ctor) {
+ if (obj instanceof ctor) {
+ return true;
+ }
+ var p = obj;
+ while (p) {
+ if (p === ctor.prototype) {
+ return true;
+ }
+ p = p.__proto__;
+ }
+ return false;
+ };
+ } else {
+ isInstance = function(obj, base) {
+ return obj instanceof base;
+ };
+ }
+ function wrapDomMethodToForceUpgrade(obj, methodName) {
+ var orig = obj[methodName];
+ obj[methodName] = function() {
+ var n = orig.apply(this, arguments);
+ upgradeAll(n);
+ return n;
+ };
+ }
+ wrapDomMethodToForceUpgrade(Node.prototype, "cloneNode");
+ wrapDomMethodToForceUpgrade(document, "importNode");
+ document.registerElement = register;
+ document.createElement = createElement;
+ document.createElementNS = createElementNS;
+ scope.registry = registry;
+ scope.instanceof = isInstance;
+ scope.reservedTagList = reservedTagList;
+ scope.getRegisteredDefinition = getRegisteredDefinition;
+ document.register = document.registerElement;
+});
+
+(function(scope) {
+ var useNative = scope.useNative;
+ var initializeModules = scope.initializeModules;
+ var isIE = scope.isIE;
+ if (useNative) {
+ var nop = function() {};
+ scope.watchShadow = nop;
+ scope.upgrade = nop;
+ scope.upgradeAll = nop;
+ scope.upgradeDocumentTree = nop;
+ scope.upgradeSubtree = nop;
+ scope.takeRecords = nop;
+ scope.instanceof = function(obj, base) {
+ return obj instanceof base;
+ };
+ } else {
+ initializeModules();
+ }
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
+ var upgradeDocument = scope.upgradeDocument;
+ if (!window.wrap) {
+ if (window.ShadowDOMPolyfill) {
+ window.wrap = window.ShadowDOMPolyfill.wrapIfNeeded;
+ window.unwrap = window.ShadowDOMPolyfill.unwrapIfNeeded;
+ } else {
+ window.wrap = window.unwrap = function(node) {
+ return node;
+ };
+ }
+ }
+ if (window.HTMLImports) {
+ window.HTMLImports.__importsParsingHook = function(elt) {
+ if (elt.import) {
+ upgradeDocument(wrap(elt.import));
+ }
+ };
+ }
+ function bootstrap() {
+ upgradeDocumentTree(window.wrap(document));
+ window.CustomElements.ready = true;
+ var requestAnimationFrame = window.requestAnimationFrame || function(f) {
+ setTimeout(f, 16);
+ };
+ requestAnimationFrame(function() {
+ setTimeout(function() {
+ window.CustomElements.readyTime = Date.now();
+ if (window.HTMLImports) {
+ window.CustomElements.elapsed = window.CustomElements.readyTime - window.HTMLImports.readyTime;
+ }
+ document.dispatchEvent(new CustomEvent("WebComponentsReady", {
+ bubbles: true
+ }));
+ });
+ });
+ }
+ if (document.readyState === "complete" || scope.flags.eager) {
+ bootstrap();
+ } else if (document.readyState === "interactive" && !window.attachEvent && (!window.HTMLImports || window.HTMLImports.ready)) {
+ bootstrap();
+ } else {
+ var loadEvent = window.HTMLImports && !window.HTMLImports.ready ? "HTMLImportsLoaded" : "DOMContentLoaded";
+ window.addEventListener(loadEvent, bootstrap);
+ }
+})(window.CustomElements);
+
+(function(scope) {
+ var style = document.createElement("style");
+ style.textContent = "" + "body {" + "transition: opacity ease-in 0.2s;" + " } \n" + "body[unresolved] {" + "opacity: 0; display: block; overflow: hidden; position: relative;" + " } \n";
+ var head = document.querySelector("head");
+ head.insertBefore(style, head.firstChild);
+})(window.WebComponents); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.min.js b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.min.js
new file mode 100644
index 00000000..fb9d0102
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents-lite.min.js
@@ -0,0 +1,12 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+!function(){window.WebComponents=window.WebComponents||{flags:{}};var e="webcomponents-lite.js",t=document.querySelector('script[src*="'+e+'"]'),n={};if(!n.noOpts){if(location.search.slice(1).split("&").forEach(function(e){var t,o=e.split("=");o[0]&&(t=o[0].match(/wc-(.+)/))&&(n[t[1]]=o[1]||!0)}),t)for(var o,r=0;o=t.attributes[r];r++)"src"!==o.name&&(n[o.name]=o.value||!0);if(n.log&&n.log.split){var i=n.log.split(",");n.log={},i.forEach(function(e){n.log[e]=!0})}else n.log={}}n.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=n.register),WebComponents.flags=n}(),function(e){"use strict";function t(e){return void 0!==h[e]}function n(){s.call(this),this._isInvalid=!0}function o(e){return""==e&&n.call(this),e.toLowerCase()}function r(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,63,96].indexOf(t)==-1?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,96].indexOf(t)==-1?e:encodeURIComponent(e)}function a(e,a,s){function c(e){g.push(e)}var d=a||"scheme start",l=0,u="",w=!1,_=!1,g=[];e:for(;(e[l-1]!=p||0==l)&&!this._isInvalid;){var b=e[l];switch(d){case"scheme start":if(!b||!m.test(b)){if(a){c("Invalid scheme.");break e}u="",d="no scheme";continue}u+=b.toLowerCase(),d="scheme";break;case"scheme":if(b&&v.test(b))u+=b.toLowerCase();else{if(":"!=b){if(a){if(p==b)break e;c("Code point not allowed in scheme: "+b);break e}u="",l=0,d="no scheme";continue}if(this._scheme=u,u="",a)break e;t(this._scheme)&&(this._isRelative=!0),d="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==b?(this._query="?",d="query"):"#"==b?(this._fragment="#",d="fragment"):p!=b&&"\t"!=b&&"\n"!=b&&"\r"!=b&&(this._schemeData+=r(b));break;case"no scheme":if(s&&t(s._scheme)){d="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=b||"/"!=e[l+1]){c("Expected /, got: "+b),d="relative";continue}d="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),p==b){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==b||"\\"==b)"\\"==b&&c("\\ is an invalid code point."),d="relative slash";else if("?"==b)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,d="query";else{if("#"!=b){var y=e[l+1],E=e[l+2];("file"!=this._scheme||!m.test(b)||":"!=y&&"|"!=y||p!=E&&"/"!=E&&"\\"!=E&&"?"!=E&&"#"!=E)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),d="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,d="fragment"}break;case"relative slash":if("/"!=b&&"\\"!=b){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),d="relative path";continue}"\\"==b&&c("\\ is an invalid code point."),d="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=b){c("Expected '/', got: "+b),d="authority ignore slashes";continue}d="authority second slash";break;case"authority second slash":if(d="authority ignore slashes","/"!=b){c("Expected '/', got: "+b);continue}break;case"authority ignore slashes":if("/"!=b&&"\\"!=b){d="authority";continue}c("Expected authority, got: "+b);break;case"authority":if("@"==b){w&&(c("@ already seen."),u+="%40"),w=!0;for(var L=0;L<u.length;L++){var N=u[L];if("\t"!=N&&"\n"!=N&&"\r"!=N)if(":"!=N||null!==this._password){var M=r(N);null!==this._password?this._password+=M:this._username+=M}else this._password="";else c("Invalid whitespace in authority.")}u=""}else{if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b){l-=u.length,u="",d="host";continue}u+=b}break;case"file host":if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b){2!=u.length||!m.test(u[0])||":"!=u[1]&&"|"!=u[1]?0==u.length?d="relative path start":(this._host=o.call(this,u),u="",d="relative path start"):d="relative path";continue}"\t"==b||"\n"==b||"\r"==b?c("Invalid whitespace in file host."):u+=b;break;case"host":case"hostname":if(":"!=b||_){if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b){if(this._host=o.call(this,u),u="",d="relative path start",a)break e;continue}"\t"!=b&&"\n"!=b&&"\r"!=b?("["==b?_=!0:"]"==b&&(_=!1),u+=b):c("Invalid code point in host/hostname: "+b)}else if(this._host=o.call(this,u),u="",d="port","hostname"==a)break e;break;case"port":if(/[0-9]/.test(b))u+=b;else{if(p==b||"/"==b||"\\"==b||"?"==b||"#"==b||a){if(""!=u){var T=parseInt(u,10);T!=h[this._scheme]&&(this._port=T+""),u=""}if(a)break e;d="relative path start";continue}"\t"==b||"\n"==b||"\r"==b?c("Invalid code point in port: "+b):n.call(this)}break;case"relative path start":if("\\"==b&&c("'\\' not allowed in path."),d="relative path","/"!=b&&"\\"!=b)continue;break;case"relative path":if(p!=b&&"/"!=b&&"\\"!=b&&(a||"?"!=b&&"#"!=b))"\t"!=b&&"\n"!=b&&"\r"!=b&&(u+=r(b));else{"\\"==b&&c("\\ not allowed in relative path.");var O;(O=f[u.toLowerCase()])&&(u=O),".."==u?(this._path.pop(),"/"!=b&&"\\"!=b&&this._path.push("")):"."==u&&"/"!=b&&"\\"!=b?this._path.push(""):"."!=u&&("file"==this._scheme&&0==this._path.length&&2==u.length&&m.test(u[0])&&"|"==u[1]&&(u=u[0]+":"),this._path.push(u)),u="","?"==b?(this._query="?",d="query"):"#"==b&&(this._fragment="#",d="fragment")}break;case"query":a||"#"!=b?p!=b&&"\t"!=b&&"\n"!=b&&"\r"!=b&&(this._query+=i(b)):(this._fragment="#",d="fragment");break;case"fragment":p!=b&&"\t"!=b&&"\n"!=b&&"\r"!=b&&(this._fragment+=b)}l++}}function s(){this._scheme="",this._schemeData="",this._username="",this._password=null,this._host="",this._port="",this._path=[],this._query="",this._fragment="",this._isInvalid=!1,this._isRelative=!1}function c(e,t){void 0===t||t instanceof c||(t=new c(String(t))),this._url=e,s.call(this);var n=e.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g,"");a.call(this,n,null,t)}var d=!1;if(!e.forceJURL)try{var l=new URL("b","http://a");l.pathname="c%20d",d="http://a/c%20d"===l.href}catch(u){}if(!d){var h=Object.create(null);h.ftp=21,h.file=0,h.gopher=70,h.http=80,h.https=443,h.ws=80,h.wss=443;var f=Object.create(null);f["%2e"]=".",f[".%2e"]="..",f["%2e."]="..",f["%2e%2e"]="..";var p=void 0,m=/[a-zA-Z]/,v=/[a-zA-Z0-9\+\-\.]/;c.prototype={toString:function(){return this.href},get href(){if(this._isInvalid)return this._url;var e="";return""==this._username&&null==this._password||(e=this._username+(null!=this._password?":"+this._password:"")+"@"),this.protocol+(this._isRelative?"//"+e+this.host:"")+this.pathname+this._query+this._fragment},set href(e){s.call(this),a.call(this,e)},get protocol(){return this._scheme+":"},set protocol(e){this._isInvalid||a.call(this,e+":","scheme start")},get host(){return this._isInvalid?"":this._port?this._host+":"+this._port:this._host},set host(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"host")},get hostname(){return this._host},set hostname(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"hostname")},get port(){return this._port},set port(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"port")},get pathname(){return this._isInvalid?"":this._isRelative?"/"+this._path.join("/"):this._schemeData},set pathname(e){!this._isInvalid&&this._isRelative&&(this._path=[],a.call(this,e,"relative path start"))},get search(){return this._isInvalid||!this._query||"?"==this._query?"":this._query},set search(e){!this._isInvalid&&this._isRelative&&(this._query="?","?"==e[0]&&(e=e.slice(1)),a.call(this,e,"query"))},get hash(){return this._isInvalid||!this._fragment||"#"==this._fragment?"":this._fragment},set hash(e){this._isInvalid||(this._fragment="#","#"==e[0]&&(e=e.slice(1)),a.call(this,e,"fragment"))},get origin(){var e;if(this._isInvalid||!this._scheme)return"";switch(this._scheme){case"data":case"file":case"javascript":case"mailto":return"null"}return e=this.host,e?this._scheme+"://"+e:""}};var w=e.URL;w&&(c.createObjectURL=function(e){return w.createObjectURL.apply(w,arguments)},c.revokeObjectURL=function(e){w.revokeObjectURL(e)}),e.URL=c}}(self),"undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var o=t[this.name];return o&&o[0]===t?o[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),function(e){function t(e){b.push(e),g||(g=!0,m(o))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function o(){g=!1;var e=b;b=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();r(e),n.length&&(e.callback_(n,e),t=!0)}),t&&o()}function r(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var o=v.get(n);if(o)for(var r=0;r<o.length;r++){var i=o[r],a=i.options;if(n===e||a.subtree){var s=t(a);s&&i.enqueue(s)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++y}function s(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function c(e){var t=new s(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function d(e,t){return E=new s(e,t)}function l(e){return L?L:(L=c(E),L.oldValue=e,L)}function u(){E=L=void 0}function h(e){return e===L||e===E}function f(e,t){return e===t?e:L&&h(e)?L:null}function p(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var m,v=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))m=setTimeout;else if(window.setImmediate)m=window.setImmediate;else{var w=[],_=String(Math.random());window.addEventListener("message",function(e){if(e.data===_){var t=w;w=[],t.forEach(function(e){e()})}}),m=function(e){w.push(e),window.postMessage(_,"*")}}var g=!1,b=[],y=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var o=v.get(e);o||v.set(e,o=[]);for(var r,i=0;i<o.length;i++)if(o[i].observer===this){r=o[i],r.removeListeners(),r.options=t;break}r||(r=new p(this,e,t),o.push(r),this.nodes_.push(e)),r.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=v.get(e),n=0;n<t.length;n++){var o=t[n];if(o.observer===this){o.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var E,L;p.prototype={enqueue:function(e){var n=this.observer.records_,o=n.length;if(n.length>0){var r=n[o-1],i=f(r,e);if(i)return void(n[o-1]=i)}else t(this.observer);n[o]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,o=e.target,r=new d("attributes",o);r.attributeName=t,r.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(o,function(e){if(e.attributes&&(!e.attributeFilter||!e.attributeFilter.length||e.attributeFilter.indexOf(t)!==-1||e.attributeFilter.indexOf(n)!==-1))return e.attributeOldValue?l(a):r});break;case"DOMCharacterDataModified":var o=e.target,r=d("characterData",o),a=e.prevValue;i(o,function(e){if(e.characterData)return e.characterDataOldValue?l(a):r});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var s,c,h=e.target;"DOMNodeInserted"===e.type?(s=[h],c=[]):(s=[],c=[h]);var f=h.previousSibling,p=h.nextSibling,r=d("childList",e.target.parentNode);r.addedNodes=s,r.removedNodes=c,r.previousSibling=f,r.nextSibling=p,i(e.relatedNode,function(e){if(e.childList)return r})}u()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(){function e(e){switch(e){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case" ":return"&nbsp;"}}function t(t){return t.replace(u,e)}var n="undefined"==typeof HTMLTemplateElement;/Trident/.test(navigator.userAgent)&&!function(){var e=document.importNode;document.importNode=function(){var t=e.apply(document,arguments);if(t.nodeType===Node.DOCUMENT_FRAGMENT_NODE){var n=document.createDocumentFragment();return n.appendChild(t),n}return t}}();var o=function(){if(!n){var e=document.createElement("template"),t=document.createElement("template");t.content.appendChild(document.createElement("div")),e.content.appendChild(t);var o=e.cloneNode(!0);return 0===o.content.childNodes.length||0===o.content.firstChild.content.childNodes.length}}(),r="template",i=function(){};if(n){var a=document.implementation.createHTMLDocument("template"),s=!0,c=document.createElement("style");c.textContent=r+"{display:none;}";var d=document.head;d.insertBefore(c,d.firstElementChild),i.prototype=Object.create(HTMLElement.prototype),i.decorate=function(e){if(!e.content){e.content=a.createDocumentFragment();for(var n;n=e.firstChild;)e.content.appendChild(n);if(e.cloneNode=function(e){return i.cloneNode(this,e)},s)try{Object.defineProperty(e,"innerHTML",{get:function(){for(var e="",n=this.content.firstChild;n;n=n.nextSibling)e+=n.outerHTML||t(n.data);return e},set:function(e){for(a.body.innerHTML=e,i.bootstrap(a);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;a.body.firstChild;)this.content.appendChild(a.body.firstChild)},configurable:!0})}catch(o){s=!1}i.bootstrap(e.content)}},i.bootstrap=function(e){for(var t,n=e.querySelectorAll(r),o=0,a=n.length;o<a&&(t=n[o]);o++)i.decorate(t)},document.addEventListener("DOMContentLoaded",function(){i.bootstrap(document)});var l=document.createElement;document.createElement=function(){"use strict";var e=l.apply(document,arguments);return"template"===e.localName&&i.decorate(e),e};var u=/[&\u00A0<>]/g}if(n||o){var h=Node.prototype.cloneNode;i.cloneNode=function(e,t){var n=h.call(e,!1);return this.decorate&&this.decorate(n),t&&(n.content.appendChild(h.call(e.content,!0)),this.fixClonedDom(n.content,e.content)),n},i.fixClonedDom=function(e,t){if(t.querySelectorAll)for(var n,o,i=t.querySelectorAll(r),a=e.querySelectorAll(r),s=0,c=a.length;s<c;s++)o=i[s],n=a[s],this.decorate&&this.decorate(o),n.parentNode.replaceChild(o.cloneNode(!0),n)};var f=document.importNode;Node.prototype.cloneNode=function(e){var t=h.call(this,e);return e&&i.fixClonedDom(t,this),t},document.importNode=function(e,t){if(e.localName===r)return i.cloneNode(e,t);var n=f.call(document,e,t);return t&&i.fixClonedDom(n,e),n},o&&(HTMLTemplateElement.prototype.cloneNode=function(e){return i.cloneNode(this,e)})}n&&(window.HTMLTemplateElement=i)}(),function(e){"use strict";if(!window.performance||!window.performance.now){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var o=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(o.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var r=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||r&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||r&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||p,o(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===w}function o(e,t){if(n(t))e&&e();else{var r=function(){"complete"!==t.readyState&&t.readyState!==w||(t.removeEventListener(_,r),o(e,t))};t.addEventListener(_,r)}}function r(e){e.target.__loaded=!0}function i(e,t){function n(){c==d&&e&&e({allImports:s,loadedImports:l,errorImports:u})}function o(e){r(e),l.push(this),c++,n()}function i(e){u.push(this),c++,n()}var s=t.querySelectorAll("link[rel=import]"),c=0,d=s.length,l=[],u=[];if(d)for(var h,f=0;f<d&&(h=s[f]);f++)a(h)?(l.push(this),c++,n()):(h.addEventListener("load",o),h.addEventListener("error",i));else n()}function a(e){return u?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)c(t)&&d(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function d(e){var t=e["import"];t?r({target:e}):(e.addEventListener("load",r),e.addEventListener("error",r))}var l="import",u=Boolean(l in document.createElement("link")),h=Boolean(window.ShadowDOMPolyfill),f=function(e){return h?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},p=f(document),m={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return f(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(p,"_currentScript",m);var v=/Trident/.test(navigator.userAgent),w=v?"complete":"interactive",_="readystatechange";u&&(new MutationObserver(function(e){for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,o=t.length;n<o&&(e=t[n]);n++)d(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=p.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),p.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=l,e.useNative=u,e.rootDocument=p,e.whenReady=t,e.isIE=v}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},o=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=o}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,o={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,o=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,o),e},resolveUrlsInCssText:function(e,o,r){var i=this.replaceUrls(e,r,o,t);return i=this.replaceUrls(i,r,o,n)},replaceUrls:function(e,t,n,o){return e.replace(o,function(e,o,r,i){var a=r.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,o+"'"+a+"'"+i})}};e.path=o}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,o,r){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}o.call(r,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,o=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};o.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,o){if(n.load&&console.log("fetch",e,o),e)if(e.match(/^data:/)){var r=e.split(","),i=r[0],a=r[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,o,null,a)}.bind(this),0)}else{var s=function(t,n,r){this.receive(e,o,t,n,r)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,o,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,o,r){this.cache[e]=o;for(var i,a=this.pending[e],s=0,c=a.length;s<c&&(i=a[s]);s++)this.onload(e,i,o,n,r),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=o}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,o=e.length;n<o&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===l}function n(e){var t=o(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function o(e){return e.textContent+r(e)}function r(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,o=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+o+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,d=e.isIE,l=e.IMPORT_LINK_TYPE,u="link[rel="+l+"]",h={documentSelectors:u,importsSelectors:[u,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,o=function(r){e.removeEventListener("load",o),e.removeEventListener("error",o),t&&t(r),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",o),e.addEventListener("error",o),d&&"style"===e.localName){var r=!1;if(e.textContent.indexOf("@import")==-1)r=!0;else if(e.sheet){r=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;c<s&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(r=r&&Boolean(i.styleSheet))}r&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var o=document.createElement("script");o.__importElement=t,o.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(o,function(t){o.parentNode&&o.parentNode.removeChild(o),e.currentScript=null}),this.addElementToDocument(o)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var o,r=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=r.length;i<a&&(o=r[i]);i++)if(!this.isParsed(o))return this.hasResource(o)?t(o)?this.nextToParseInDoc(o.__doc,o):o:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return!t(e)||void 0!==e.__doc}};e.parser=h,e.IMPORT_SELECTOR=u}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function o(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function r(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var r=n.createElement("base");r.setAttribute("href",t),n.baseURI||o(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(r),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,d=e.Loader,l=e.Observer,u=e.parser,h={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){f.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);f.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,o,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=o,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:r(o,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}u.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),u.parseNext()},loadedAll:function(){u.parseNext()}},f=new d(h.loaded.bind(h),h.loadedAll.bind(h));if(h.observer=new l,!document.baseURI){var p={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",p),Object.defineProperty(c,"baseURI",p)}e.importer=h,e.importLoader=f}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,o={added:function(e){for(var o,r,i,a,s=0,c=e.length;s<c&&(a=e[s]);s++)o||(o=a.ownerDocument,r=t.isParsed(o)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&r&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&r.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&r.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=o.added.bind(o);var r=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(o)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var o=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],o=function(e){n.push(e)},r=function(){n.forEach(function(t){t(e)})};e.addModule=o,e.initializeModules=r,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return!!t(e)||void o(e,t)}),o(e,t)}function n(e,t,o){var r=e.firstElementChild;if(!r)for(r=e.firstChild;r&&r.nodeType!==Node.ELEMENT_NODE;)r=r.nextSibling;for(;r;)t(r,o)!==!0&&n(r,t,o),r=r.nextElementSibling;return null}function o(e,n){for(var o=e.shadowRoot;o;)t(o,n),o=o.olderShadowRoot}function r(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var o,r=e.querySelectorAll("link[rel="+a+"]"),s=0,c=r.length;s<c&&(o=r[s]);s++)o["import"]&&i(o["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=r,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||o(e,t)}function n(t,n){return!!e.upgrade(t,n)||void(n&&a(t))}function o(e,t){g(e,function(e){if(n(e,t))return!0})}function r(e){L.push(e),E||(E=!0,setTimeout(i))}function i(){E=!1;for(var e,t=L,n=0,o=t.length;n<o&&(e=t[n]);n++)e();L=[]}function a(e){y?r(function(){s(e);
+}):s(e)}function s(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function c(e){d(e),g(e,function(e){d(e)})}function d(e){y?r(function(){l(e)}):l(e)}function l(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function u(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function h(e){if(e.shadowRoot&&!e.shadowRoot.__watched){_.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function f(e,n){if(_.dom){var o=n[0];if(o&&"childList"===o.type&&o.addedNodes&&o.addedNodes){for(var r=o.addedNodes[0];r&&r!==document&&!r.host;)r=r.parentNode;var i=r&&(r.URL||r._URL||r.host&&r.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=u(e);n.forEach(function(e){"childList"===e.type&&(N(e.addedNodes,function(e){e.localName&&t(e,a)}),N(e.removedNodes,function(e){e.localName&&c(e)}))}),_.dom&&console.groupEnd()}function p(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(f(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(f.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),_.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document);t(e,n),m(e),_.dom&&console.groupEnd()}function w(e){b(e,v)}var _=e.flags,g=e.forSubtree,b=e.forDocumentTree,y=window.MutationObserver._isPolyfilled&&_["throttle-attached"];e.hasPolyfillMutations=y,e.hasThrottledAttached=y;var E=!1,L=[],N=Array.prototype.forEach.call.bind(Array.prototype.forEach),M=Element.prototype.createShadowRoot;M&&(Element.prototype.createShadowRoot=function(){var e=M.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=h,e.upgradeDocumentTree=w,e.upgradeDocument=v,e.upgradeSubtree=o,e.upgradeAll=t,e.attached=a,e.takeRecords=p}),window.CustomElements.addModule(function(e){function t(t,o){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var r=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(r);if(i&&(r&&i.tag==t.localName||!r&&!i["extends"]))return n(t,i,o)}}function n(t,n,r){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),o(t,n),t.__upgraded__=!0,i(t),r&&e.attached(t),e.upgradeSubtree(t,r),a.upgrade&&console.groupEnd(),t}function o(e,t){Object.__proto__?e.__proto__=t.prototype:(r(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function r(e,t,n){for(var o={},r=t;r!==n&&r!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(r),s=0;i=a[s];s++)o[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(r,i)),o[i]=1);r=Object.getPrototypeOf(r)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=o}),window.CustomElements.addModule(function(e){function t(t,o){var c=o||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(r(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(d(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c["extends"]&&(c["extends"]=c["extends"].toLowerCase()),c.lifecycle=c.lifecycle||{},c.ancestry=i(c["extends"]),a(c),s(c),n(c.prototype),l(c.__name,c),c.ctor=u(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&v(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){o.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){o.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function o(e,t,n){e=e.toLowerCase();var o=this.getAttribute(e);n.apply(this,arguments);var r=this.getAttribute(e);this.attributeChangedCallback&&r!==o&&this.attributeChangedCallback(e,o,r)}function r(e){for(var t=0;t<y.length;t++)if(e===y[t])return!0}function i(e){var t=d(e);return t?i(t["extends"]).concat([t]):[]}function a(e){for(var t,n=e["extends"],o=0;t=e.ancestry[o];o++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function s(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag);t=Object.getPrototypeOf(n)}for(var o,r=e.prototype,i=!1;r;)r==t&&(i=!0),o=Object.getPrototypeOf(r),o&&(r.__proto__=o),r=o;i||console.warn(e.tag+" prototype not found in prototype chain for "+e.is),e["native"]=t}}function c(e){return _(N(e.tag),e)}function d(e){if(e)return E[e.toLowerCase()]}function l(e,t){E[e]=t}function u(e){return function(){return c(e)}}function h(e,t,n){return e===L?f(t,n):M(e,t)}function f(e,t){e&&(e=e.toLowerCase()),t&&(t=t.toLowerCase());var n=d(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var o;return t?(o=f(e),o.setAttribute("is",t),o):(o=N(e),e.indexOf("-")>=0&&g(o,HTMLElement),o)}function p(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return w(e),e}}var m,v=(e.isIE,e.upgradeDocumentTree),w=e.upgradeAll,_=e.upgradeWithDefinition,g=e.implementPrototype,b=e.useNative,y=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],E={},L="http://www.w3.org/1999/xhtml",N=document.createElement.bind(document),M=document.createElementNS.bind(document);m=Object.__proto__||b?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},p(Node.prototype,"cloneNode"),p(document,"importNode"),document.registerElement=t,document.createElement=f,document.createElementNS=h,e.registry=E,e["instanceof"]=m,e.reservedTagList=y,e.getRegisteredDefinition=d,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,o=e.initializeModules;e.isIE;if(n){var r=function(){};e.watchShadow=r,e.upgrade=r,e.upgradeAll=r,e.upgradeDocumentTree=r,e.upgradeSubtree=r,e.takeRecords=r,e["instanceof"]=function(e,t){return e instanceof t}}else o();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/webcomponents.js b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents.js
new file mode 100644
index 00000000..daf5c22d
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents.js
@@ -0,0 +1,7209 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+(function() {
+ window.WebComponents = window.WebComponents || {
+ flags: {}
+ };
+ var file = "webcomponents.js";
+ var script = document.querySelector('script[src*="' + file + '"]');
+ var flags = {};
+ if (!flags.noOpts) {
+ location.search.slice(1).split("&").forEach(function(option) {
+ var parts = option.split("=");
+ var match;
+ if (parts[0] && (match = parts[0].match(/wc-(.+)/))) {
+ flags[match[1]] = parts[1] || true;
+ }
+ });
+ if (script) {
+ for (var i = 0, a; a = script.attributes[i]; i++) {
+ if (a.name !== "src") {
+ flags[a.name] = a.value || true;
+ }
+ }
+ }
+ if (flags.log && flags.log.split) {
+ var parts = flags.log.split(",");
+ flags.log = {};
+ parts.forEach(function(f) {
+ flags.log[f] = true;
+ });
+ } else {
+ flags.log = {};
+ }
+ }
+ flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill;
+ if (flags.shadow === "native") {
+ flags.shadow = false;
+ } else {
+ flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot;
+ }
+ if (flags.register) {
+ window.CustomElements = window.CustomElements || {
+ flags: {}
+ };
+ window.CustomElements.flags.register = flags.register;
+ }
+ WebComponents.flags = flags;
+})();
+
+if (WebComponents.flags.shadow) {
+ if (typeof WeakMap === "undefined") {
+ (function() {
+ var defineProperty = Object.defineProperty;
+ var counter = Date.now() % 1e9;
+ var WeakMap = function() {
+ this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
+ };
+ WeakMap.prototype = {
+ set: function(key, value) {
+ var entry = key[this.name];
+ if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
+ value: [ key, value ],
+ writable: true
+ });
+ return this;
+ },
+ get: function(key) {
+ var entry;
+ return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
+ },
+ "delete": function(key) {
+ var entry = key[this.name];
+ if (!entry || entry[0] !== key) return false;
+ entry[0] = entry[1] = undefined;
+ return true;
+ },
+ has: function(key) {
+ var entry = key[this.name];
+ if (!entry) return false;
+ return entry[0] === key;
+ }
+ };
+ window.WeakMap = WeakMap;
+ })();
+ }
+ window.ShadowDOMPolyfill = {};
+ (function(scope) {
+ "use strict";
+ var constructorTable = new WeakMap();
+ var nativePrototypeTable = new WeakMap();
+ var wrappers = Object.create(null);
+ function detectEval() {
+ if (typeof chrome !== "undefined" && chrome.app && chrome.app.runtime) {
+ return false;
+ }
+ if (navigator.getDeviceStorage) {
+ return false;
+ }
+ try {
+ var f = new Function("return true;");
+ return f();
+ } catch (ex) {
+ return false;
+ }
+ }
+ var hasEval = detectEval();
+ function assert(b) {
+ if (!b) throw new Error("Assertion failed");
+ }
+ var defineProperty = Object.defineProperty;
+ var getOwnPropertyNames = Object.getOwnPropertyNames;
+ var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+ function mixin(to, from) {
+ var names = getOwnPropertyNames(from);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+ }
+ return to;
+ }
+ function mixinStatics(to, from) {
+ var names = getOwnPropertyNames(from);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ switch (name) {
+ case "arguments":
+ case "caller":
+ case "length":
+ case "name":
+ case "prototype":
+ case "toString":
+ continue;
+ }
+ defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+ }
+ return to;
+ }
+ function oneOf(object, propertyNames) {
+ for (var i = 0; i < propertyNames.length; i++) {
+ if (propertyNames[i] in object) return propertyNames[i];
+ }
+ }
+ var nonEnumerableDataDescriptor = {
+ value: undefined,
+ configurable: true,
+ enumerable: false,
+ writable: true
+ };
+ function defineNonEnumerableDataProperty(object, name, value) {
+ nonEnumerableDataDescriptor.value = value;
+ defineProperty(object, name, nonEnumerableDataDescriptor);
+ }
+ getOwnPropertyNames(window);
+ function getWrapperConstructor(node, opt_instance) {
+ var nativePrototype = node.__proto__ || Object.getPrototypeOf(node);
+ if (isFirefox) {
+ try {
+ getOwnPropertyNames(nativePrototype);
+ } catch (error) {
+ nativePrototype = nativePrototype.__proto__;
+ }
+ }
+ var wrapperConstructor = constructorTable.get(nativePrototype);
+ if (wrapperConstructor) return wrapperConstructor;
+ var parentWrapperConstructor = getWrapperConstructor(nativePrototype);
+ var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor);
+ registerInternal(nativePrototype, GeneratedWrapper, opt_instance);
+ return GeneratedWrapper;
+ }
+ function addForwardingProperties(nativePrototype, wrapperPrototype) {
+ installProperty(nativePrototype, wrapperPrototype, true);
+ }
+ function registerInstanceProperties(wrapperPrototype, instanceObject) {
+ installProperty(instanceObject, wrapperPrototype, false);
+ }
+ var isFirefox = /Firefox/.test(navigator.userAgent);
+ var dummyDescriptor = {
+ get: function() {},
+ set: function(v) {},
+ configurable: true,
+ enumerable: true
+ };
+ function isEventHandlerName(name) {
+ return /^on[a-z]+$/.test(name);
+ }
+ function isIdentifierName(name) {
+ return /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(name);
+ }
+ function getGetter(name) {
+ return hasEval && isIdentifierName(name) ? new Function("return this.__impl4cf1e782hg__." + name) : function() {
+ return this.__impl4cf1e782hg__[name];
+ };
+ }
+ function getSetter(name) {
+ return hasEval && isIdentifierName(name) ? new Function("v", "this.__impl4cf1e782hg__." + name + " = v") : function(v) {
+ this.__impl4cf1e782hg__[name] = v;
+ };
+ }
+ function getMethod(name) {
+ return hasEval && isIdentifierName(name) ? new Function("return this.__impl4cf1e782hg__." + name + ".apply(this.__impl4cf1e782hg__, arguments)") : function() {
+ return this.__impl4cf1e782hg__[name].apply(this.__impl4cf1e782hg__, arguments);
+ };
+ }
+ function getDescriptor(source, name) {
+ try {
+ if (source === window && name === "showModalDialog") {
+ return dummyDescriptor;
+ }
+ return Object.getOwnPropertyDescriptor(source, name);
+ } catch (ex) {
+ return dummyDescriptor;
+ }
+ }
+ var isBrokenSafari = function() {
+ var descr = Object.getOwnPropertyDescriptor(Node.prototype, "nodeType");
+ return descr && !descr.get && !descr.set;
+ }();
+ function installProperty(source, target, allowMethod, opt_blacklist) {
+ var names = getOwnPropertyNames(source);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ if (name === "polymerBlackList_") continue;
+ if (name in target) continue;
+ if (source.polymerBlackList_ && source.polymerBlackList_[name]) continue;
+ if (isFirefox) {
+ source.__lookupGetter__(name);
+ }
+ var descriptor = getDescriptor(source, name);
+ var getter, setter;
+ if (typeof descriptor.value === "function") {
+ if (allowMethod) {
+ target[name] = getMethod(name);
+ }
+ continue;
+ }
+ var isEvent = isEventHandlerName(name);
+ if (isEvent) getter = scope.getEventHandlerGetter(name); else getter = getGetter(name);
+ if (descriptor.writable || descriptor.set || isBrokenSafari) {
+ if (isEvent) setter = scope.getEventHandlerSetter(name); else setter = getSetter(name);
+ }
+ var configurable = isBrokenSafari || descriptor.configurable;
+ defineProperty(target, name, {
+ get: getter,
+ set: setter,
+ configurable: configurable,
+ enumerable: descriptor.enumerable
+ });
+ }
+ }
+ function register(nativeConstructor, wrapperConstructor, opt_instance) {
+ if (nativeConstructor == null) {
+ return;
+ }
+ var nativePrototype = nativeConstructor.prototype;
+ registerInternal(nativePrototype, wrapperConstructor, opt_instance);
+ mixinStatics(wrapperConstructor, nativeConstructor);
+ }
+ function registerInternal(nativePrototype, wrapperConstructor, opt_instance) {
+ var wrapperPrototype = wrapperConstructor.prototype;
+ assert(constructorTable.get(nativePrototype) === undefined);
+ constructorTable.set(nativePrototype, wrapperConstructor);
+ nativePrototypeTable.set(wrapperPrototype, nativePrototype);
+ addForwardingProperties(nativePrototype, wrapperPrototype);
+ if (opt_instance) registerInstanceProperties(wrapperPrototype, opt_instance);
+ defineNonEnumerableDataProperty(wrapperPrototype, "constructor", wrapperConstructor);
+ wrapperConstructor.prototype = wrapperPrototype;
+ }
+ function isWrapperFor(wrapperConstructor, nativeConstructor) {
+ return constructorTable.get(nativeConstructor.prototype) === wrapperConstructor;
+ }
+ function registerObject(object) {
+ var nativePrototype = Object.getPrototypeOf(object);
+ var superWrapperConstructor = getWrapperConstructor(nativePrototype);
+ var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor);
+ registerInternal(nativePrototype, GeneratedWrapper, object);
+ return GeneratedWrapper;
+ }
+ function createWrapperConstructor(superWrapperConstructor) {
+ function GeneratedWrapper(node) {
+ superWrapperConstructor.call(this, node);
+ }
+ var p = Object.create(superWrapperConstructor.prototype);
+ p.constructor = GeneratedWrapper;
+ GeneratedWrapper.prototype = p;
+ return GeneratedWrapper;
+ }
+ function isWrapper(object) {
+ return object && object.__impl4cf1e782hg__;
+ }
+ function isNative(object) {
+ return !isWrapper(object);
+ }
+ function wrap(impl) {
+ if (impl === null) return null;
+ assert(isNative(impl));
+ var wrapper = impl.__wrapper8e3dd93a60__;
+ if (wrapper != null) {
+ return wrapper;
+ }
+ return impl.__wrapper8e3dd93a60__ = new (getWrapperConstructor(impl, impl))(impl);
+ }
+ function unwrap(wrapper) {
+ if (wrapper === null) return null;
+ assert(isWrapper(wrapper));
+ return wrapper.__impl4cf1e782hg__;
+ }
+ function unsafeUnwrap(wrapper) {
+ return wrapper.__impl4cf1e782hg__;
+ }
+ function setWrapper(impl, wrapper) {
+ wrapper.__impl4cf1e782hg__ = impl;
+ impl.__wrapper8e3dd93a60__ = wrapper;
+ }
+ function unwrapIfNeeded(object) {
+ return object && isWrapper(object) ? unwrap(object) : object;
+ }
+ function wrapIfNeeded(object) {
+ return object && !isWrapper(object) ? wrap(object) : object;
+ }
+ function rewrap(node, wrapper) {
+ if (wrapper === null) return;
+ assert(isNative(node));
+ assert(wrapper === undefined || isWrapper(wrapper));
+ node.__wrapper8e3dd93a60__ = wrapper;
+ }
+ var getterDescriptor = {
+ get: undefined,
+ configurable: true,
+ enumerable: true
+ };
+ function defineGetter(constructor, name, getter) {
+ getterDescriptor.get = getter;
+ defineProperty(constructor.prototype, name, getterDescriptor);
+ }
+ function defineWrapGetter(constructor, name) {
+ defineGetter(constructor, name, function() {
+ return wrap(this.__impl4cf1e782hg__[name]);
+ });
+ }
+ function forwardMethodsToWrapper(constructors, names) {
+ constructors.forEach(function(constructor) {
+ names.forEach(function(name) {
+ constructor.prototype[name] = function() {
+ var w = wrapIfNeeded(this);
+ return w[name].apply(w, arguments);
+ };
+ });
+ });
+ }
+ scope.addForwardingProperties = addForwardingProperties;
+ scope.assert = assert;
+ scope.constructorTable = constructorTable;
+ scope.defineGetter = defineGetter;
+ scope.defineWrapGetter = defineWrapGetter;
+ scope.forwardMethodsToWrapper = forwardMethodsToWrapper;
+ scope.isIdentifierName = isIdentifierName;
+ scope.isWrapper = isWrapper;
+ scope.isWrapperFor = isWrapperFor;
+ scope.mixin = mixin;
+ scope.nativePrototypeTable = nativePrototypeTable;
+ scope.oneOf = oneOf;
+ scope.registerObject = registerObject;
+ scope.registerWrapper = register;
+ scope.rewrap = rewrap;
+ scope.setWrapper = setWrapper;
+ scope.unsafeUnwrap = unsafeUnwrap;
+ scope.unwrap = unwrap;
+ scope.unwrapIfNeeded = unwrapIfNeeded;
+ scope.wrap = wrap;
+ scope.wrapIfNeeded = wrapIfNeeded;
+ scope.wrappers = wrappers;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ function newSplice(index, removed, addedCount) {
+ return {
+ index: index,
+ removed: removed,
+ addedCount: addedCount
+ };
+ }
+ var EDIT_LEAVE = 0;
+ var EDIT_UPDATE = 1;
+ var EDIT_ADD = 2;
+ var EDIT_DELETE = 3;
+ function ArraySplice() {}
+ ArraySplice.prototype = {
+ calcEditDistances: function(current, currentStart, currentEnd, old, oldStart, oldEnd) {
+ var rowCount = oldEnd - oldStart + 1;
+ var columnCount = currentEnd - currentStart + 1;
+ var distances = new Array(rowCount);
+ for (var i = 0; i < rowCount; i++) {
+ distances[i] = new Array(columnCount);
+ distances[i][0] = i;
+ }
+ for (var j = 0; j < columnCount; j++) distances[0][j] = j;
+ for (var i = 1; i < rowCount; i++) {
+ for (var j = 1; j < columnCount; j++) {
+ if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) distances[i][j] = distances[i - 1][j - 1]; else {
+ var north = distances[i - 1][j] + 1;
+ var west = distances[i][j - 1] + 1;
+ distances[i][j] = north < west ? north : west;
+ }
+ }
+ }
+ return distances;
+ },
+ spliceOperationsFromEditDistances: function(distances) {
+ var i = distances.length - 1;
+ var j = distances[0].length - 1;
+ var current = distances[i][j];
+ var edits = [];
+ while (i > 0 || j > 0) {
+ if (i == 0) {
+ edits.push(EDIT_ADD);
+ j--;
+ continue;
+ }
+ if (j == 0) {
+ edits.push(EDIT_DELETE);
+ i--;
+ continue;
+ }
+ var northWest = distances[i - 1][j - 1];
+ var west = distances[i - 1][j];
+ var north = distances[i][j - 1];
+ var min;
+ if (west < north) min = west < northWest ? west : northWest; else min = north < northWest ? north : northWest;
+ if (min == northWest) {
+ if (northWest == current) {
+ edits.push(EDIT_LEAVE);
+ } else {
+ edits.push(EDIT_UPDATE);
+ current = northWest;
+ }
+ i--;
+ j--;
+ } else if (min == west) {
+ edits.push(EDIT_DELETE);
+ i--;
+ current = west;
+ } else {
+ edits.push(EDIT_ADD);
+ j--;
+ current = north;
+ }
+ }
+ edits.reverse();
+ return edits;
+ },
+ calcSplices: function(current, currentStart, currentEnd, old, oldStart, oldEnd) {
+ var prefixCount = 0;
+ var suffixCount = 0;
+ var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+ if (currentStart == 0 && oldStart == 0) prefixCount = this.sharedPrefix(current, old, minLength);
+ if (currentEnd == current.length && oldEnd == old.length) suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+ currentStart += prefixCount;
+ oldStart += prefixCount;
+ currentEnd -= suffixCount;
+ oldEnd -= suffixCount;
+ if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) return [];
+ if (currentStart == currentEnd) {
+ var splice = newSplice(currentStart, [], 0);
+ while (oldStart < oldEnd) splice.removed.push(old[oldStart++]);
+ return [ splice ];
+ } else if (oldStart == oldEnd) return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+ var ops = this.spliceOperationsFromEditDistances(this.calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
+ var splice = undefined;
+ var splices = [];
+ var index = currentStart;
+ var oldIndex = oldStart;
+ for (var i = 0; i < ops.length; i++) {
+ switch (ops[i]) {
+ case EDIT_LEAVE:
+ if (splice) {
+ splices.push(splice);
+ splice = undefined;
+ }
+ index++;
+ oldIndex++;
+ break;
+
+ case EDIT_UPDATE:
+ if (!splice) splice = newSplice(index, [], 0);
+ splice.addedCount++;
+ index++;
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+
+ case EDIT_ADD:
+ if (!splice) splice = newSplice(index, [], 0);
+ splice.addedCount++;
+ index++;
+ break;
+
+ case EDIT_DELETE:
+ if (!splice) splice = newSplice(index, [], 0);
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ }
+ }
+ if (splice) {
+ splices.push(splice);
+ }
+ return splices;
+ },
+ sharedPrefix: function(current, old, searchLength) {
+ for (var i = 0; i < searchLength; i++) if (!this.equals(current[i], old[i])) return i;
+ return searchLength;
+ },
+ sharedSuffix: function(current, old, searchLength) {
+ var index1 = current.length;
+ var index2 = old.length;
+ var count = 0;
+ while (count < searchLength && this.equals(current[--index1], old[--index2])) count++;
+ return count;
+ },
+ calculateSplices: function(current, previous) {
+ return this.calcSplices(current, 0, current.length, previous, 0, previous.length);
+ },
+ equals: function(currentValue, previousValue) {
+ return currentValue === previousValue;
+ }
+ };
+ scope.ArraySplice = ArraySplice;
+ })(window.ShadowDOMPolyfill);
+ (function(context) {
+ "use strict";
+ var OriginalMutationObserver = window.MutationObserver;
+ var callbacks = [];
+ var pending = false;
+ var timerFunc;
+ function handle() {
+ pending = false;
+ var copies = callbacks.slice(0);
+ callbacks = [];
+ for (var i = 0; i < copies.length; i++) {
+ (0, copies[i])();
+ }
+ }
+ if (OriginalMutationObserver) {
+ var counter = 1;
+ var observer = new OriginalMutationObserver(handle);
+ var textNode = document.createTextNode(counter);
+ observer.observe(textNode, {
+ characterData: true
+ });
+ timerFunc = function() {
+ counter = (counter + 1) % 2;
+ textNode.data = counter;
+ };
+ } else {
+ timerFunc = window.setTimeout;
+ }
+ function setEndOfMicrotask(func) {
+ callbacks.push(func);
+ if (pending) return;
+ pending = true;
+ timerFunc(handle, 0);
+ }
+ context.setEndOfMicrotask = setEndOfMicrotask;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var setEndOfMicrotask = scope.setEndOfMicrotask;
+ var wrapIfNeeded = scope.wrapIfNeeded;
+ var wrappers = scope.wrappers;
+ var registrationsTable = new WeakMap();
+ var globalMutationObservers = [];
+ var isScheduled = false;
+ function scheduleCallback(observer) {
+ if (observer.scheduled_) return;
+ observer.scheduled_ = true;
+ globalMutationObservers.push(observer);
+ if (isScheduled) return;
+ setEndOfMicrotask(notifyObservers);
+ isScheduled = true;
+ }
+ function notifyObservers() {
+ isScheduled = false;
+ while (globalMutationObservers.length) {
+ var notifyList = globalMutationObservers;
+ globalMutationObservers = [];
+ notifyList.sort(function(x, y) {
+ return x.uid_ - y.uid_;
+ });
+ for (var i = 0; i < notifyList.length; i++) {
+ var mo = notifyList[i];
+ mo.scheduled_ = false;
+ var queue = mo.takeRecords();
+ removeTransientObserversFor(mo);
+ if (queue.length) {
+ mo.callback_(queue, mo);
+ }
+ }
+ }
+ }
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = new wrappers.NodeList();
+ this.removedNodes = new wrappers.NodeList();
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+ function registerTransientObservers(ancestor, node) {
+ for (;ancestor; ancestor = ancestor.parentNode) {
+ var registrations = registrationsTable.get(ancestor);
+ if (!registrations) continue;
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.options.subtree) registration.addTransientObserver(node);
+ }
+ }
+ }
+ function removeTransientObserversFor(observer) {
+ for (var i = 0; i < observer.nodes_.length; i++) {
+ var node = observer.nodes_[i];
+ var registrations = registrationsTable.get(node);
+ if (!registrations) return;
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ if (registration.observer === observer) registration.removeTransientObservers();
+ }
+ }
+ }
+ function enqueueMutation(target, type, data) {
+ var interestedObservers = Object.create(null);
+ var associatedStrings = Object.create(null);
+ for (var node = target; node; node = node.parentNode) {
+ var registrations = registrationsTable.get(node);
+ if (!registrations) continue;
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ if (node !== target && !options.subtree) continue;
+ if (type === "attributes" && !options.attributes) continue;
+ if (type === "attributes" && options.attributeFilter && (data.namespace !== null || options.attributeFilter.indexOf(data.name) === -1)) {
+ continue;
+ }
+ if (type === "characterData" && !options.characterData) continue;
+ if (type === "childList" && !options.childList) continue;
+ var observer = registration.observer;
+ interestedObservers[observer.uid_] = observer;
+ if (type === "attributes" && options.attributeOldValue || type === "characterData" && options.characterDataOldValue) {
+ associatedStrings[observer.uid_] = data.oldValue;
+ }
+ }
+ }
+ for (var uid in interestedObservers) {
+ var observer = interestedObservers[uid];
+ var record = new MutationRecord(type, target);
+ if ("name" in data && "namespace" in data) {
+ record.attributeName = data.name;
+ record.attributeNamespace = data.namespace;
+ }
+ if (data.addedNodes) record.addedNodes = data.addedNodes;
+ if (data.removedNodes) record.removedNodes = data.removedNodes;
+ if (data.previousSibling) record.previousSibling = data.previousSibling;
+ if (data.nextSibling) record.nextSibling = data.nextSibling;
+ if (associatedStrings[uid] !== undefined) record.oldValue = associatedStrings[uid];
+ scheduleCallback(observer);
+ observer.records_.push(record);
+ }
+ }
+ var slice = Array.prototype.slice;
+ function MutationObserverOptions(options) {
+ this.childList = !!options.childList;
+ this.subtree = !!options.subtree;
+ if (!("attributes" in options) && ("attributeOldValue" in options || "attributeFilter" in options)) {
+ this.attributes = true;
+ } else {
+ this.attributes = !!options.attributes;
+ }
+ if ("characterDataOldValue" in options && !("characterData" in options)) this.characterData = true; else this.characterData = !!options.characterData;
+ if (!this.attributes && (options.attributeOldValue || "attributeFilter" in options) || !this.characterData && options.characterDataOldValue) {
+ throw new TypeError();
+ }
+ this.characterData = !!options.characterData;
+ this.attributeOldValue = !!options.attributeOldValue;
+ this.characterDataOldValue = !!options.characterDataOldValue;
+ if ("attributeFilter" in options) {
+ if (options.attributeFilter == null || typeof options.attributeFilter !== "object") {
+ throw new TypeError();
+ }
+ this.attributeFilter = slice.call(options.attributeFilter);
+ } else {
+ this.attributeFilter = null;
+ }
+ }
+ var uidCounter = 0;
+ function MutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+ this.scheduled_ = false;
+ }
+ MutationObserver.prototype = {
+ constructor: MutationObserver,
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+ var newOptions = new MutationObserverOptions(options);
+ var registration;
+ var registrations = registrationsTable.get(target);
+ if (!registrations) registrationsTable.set(target, registrations = []);
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ registration.removeTransientObservers();
+ registration.options = newOptions;
+ }
+ }
+ if (!registration) {
+ registration = new Registration(this, target, newOptions);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ },
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+ Registration.prototype = {
+ addTransientObserver: function(node) {
+ if (node === this.target) return;
+ scheduleCallback(this.observer);
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations) registrationsTable.set(node, registrations = []);
+ registrations.push(this);
+ },
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+ for (var i = 0; i < transientObservedNodes.length; i++) {
+ var node = transientObservedNodes[i];
+ var registrations = registrationsTable.get(node);
+ for (var j = 0; j < registrations.length; j++) {
+ if (registrations[j] === this) {
+ registrations.splice(j, 1);
+ break;
+ }
+ }
+ }
+ }
+ };
+ scope.enqueueMutation = enqueueMutation;
+ scope.registerTransientObservers = registerTransientObservers;
+ scope.wrappers.MutationObserver = MutationObserver;
+ scope.wrappers.MutationRecord = MutationRecord;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ function TreeScope(root, parent) {
+ this.root = root;
+ this.parent = parent;
+ }
+ TreeScope.prototype = {
+ get renderer() {
+ if (this.root instanceof scope.wrappers.ShadowRoot) {
+ return scope.getRendererForHost(this.root.host);
+ }
+ return null;
+ },
+ contains: function(treeScope) {
+ for (;treeScope; treeScope = treeScope.parent) {
+ if (treeScope === this) return true;
+ }
+ return false;
+ }
+ };
+ function setTreeScope(node, treeScope) {
+ if (node.treeScope_ !== treeScope) {
+ node.treeScope_ = treeScope;
+ for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) {
+ sr.treeScope_.parent = treeScope;
+ }
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ setTreeScope(child, treeScope);
+ }
+ }
+ }
+ function getTreeScope(node) {
+ if (node instanceof scope.wrappers.Window) {
+ debugger;
+ }
+ if (node.treeScope_) return node.treeScope_;
+ var parent = node.parentNode;
+ var treeScope;
+ if (parent) treeScope = getTreeScope(parent); else treeScope = new TreeScope(node, null);
+ return node.treeScope_ = treeScope;
+ }
+ scope.TreeScope = TreeScope;
+ scope.getTreeScope = getTreeScope;
+ scope.setTreeScope = setTreeScope;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+ var getTreeScope = scope.getTreeScope;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrappers = scope.wrappers;
+ var wrappedFuns = new WeakMap();
+ var listenersTable = new WeakMap();
+ var handledEventsTable = new WeakMap();
+ var currentlyDispatchingEvents = new WeakMap();
+ var targetTable = new WeakMap();
+ var currentTargetTable = new WeakMap();
+ var relatedTargetTable = new WeakMap();
+ var eventPhaseTable = new WeakMap();
+ var stopPropagationTable = new WeakMap();
+ var stopImmediatePropagationTable = new WeakMap();
+ var eventHandlersTable = new WeakMap();
+ var eventPathTable = new WeakMap();
+ function isShadowRoot(node) {
+ return node instanceof wrappers.ShadowRoot;
+ }
+ function rootOfNode(node) {
+ return getTreeScope(node).root;
+ }
+ function getEventPath(node, event) {
+ var path = [];
+ var current = node;
+ path.push(current);
+ while (current) {
+ var destinationInsertionPoints = getDestinationInsertionPoints(current);
+ if (destinationInsertionPoints && destinationInsertionPoints.length > 0) {
+ for (var i = 0; i < destinationInsertionPoints.length; i++) {
+ var insertionPoint = destinationInsertionPoints[i];
+ if (isShadowInsertionPoint(insertionPoint)) {
+ var shadowRoot = rootOfNode(insertionPoint);
+ var olderShadowRoot = shadowRoot.olderShadowRoot;
+ if (olderShadowRoot) path.push(olderShadowRoot);
+ }
+ path.push(insertionPoint);
+ }
+ current = destinationInsertionPoints[destinationInsertionPoints.length - 1];
+ } else {
+ if (isShadowRoot(current)) {
+ if (inSameTree(node, current) && eventMustBeStopped(event)) {
+ break;
+ }
+ current = current.host;
+ path.push(current);
+ } else {
+ current = current.parentNode;
+ if (current) path.push(current);
+ }
+ }
+ }
+ return path;
+ }
+ function eventMustBeStopped(event) {
+ if (!event) return false;
+ switch (event.type) {
+ case "abort":
+ case "error":
+ case "select":
+ case "change":
+ case "load":
+ case "reset":
+ case "resize":
+ case "scroll":
+ case "selectstart":
+ return true;
+ }
+ return false;
+ }
+ function isShadowInsertionPoint(node) {
+ return node instanceof HTMLShadowElement;
+ }
+ function getDestinationInsertionPoints(node) {
+ return scope.getDestinationInsertionPoints(node);
+ }
+ function eventRetargetting(path, currentTarget) {
+ if (path.length === 0) return currentTarget;
+ if (currentTarget instanceof wrappers.Window) currentTarget = currentTarget.document;
+ var currentTargetTree = getTreeScope(currentTarget);
+ var originalTarget = path[0];
+ var originalTargetTree = getTreeScope(originalTarget);
+ var relativeTargetTree = lowestCommonInclusiveAncestor(currentTargetTree, originalTargetTree);
+ for (var i = 0; i < path.length; i++) {
+ var node = path[i];
+ if (getTreeScope(node) === relativeTargetTree) return node;
+ }
+ return path[path.length - 1];
+ }
+ function getTreeScopeAncestors(treeScope) {
+ var ancestors = [];
+ for (;treeScope; treeScope = treeScope.parent) {
+ ancestors.push(treeScope);
+ }
+ return ancestors;
+ }
+ function lowestCommonInclusiveAncestor(tsA, tsB) {
+ var ancestorsA = getTreeScopeAncestors(tsA);
+ var ancestorsB = getTreeScopeAncestors(tsB);
+ var result = null;
+ while (ancestorsA.length > 0 && ancestorsB.length > 0) {
+ var a = ancestorsA.pop();
+ var b = ancestorsB.pop();
+ if (a === b) result = a; else break;
+ }
+ return result;
+ }
+ function getTreeScopeRoot(ts) {
+ if (!ts.parent) return ts;
+ return getTreeScopeRoot(ts.parent);
+ }
+ function relatedTargetResolution(event, currentTarget, relatedTarget) {
+ if (currentTarget instanceof wrappers.Window) currentTarget = currentTarget.document;
+ var currentTargetTree = getTreeScope(currentTarget);
+ var relatedTargetTree = getTreeScope(relatedTarget);
+ var relatedTargetEventPath = getEventPath(relatedTarget, event);
+ var lowestCommonAncestorTree;
+ var lowestCommonAncestorTree = lowestCommonInclusiveAncestor(currentTargetTree, relatedTargetTree);
+ if (!lowestCommonAncestorTree) lowestCommonAncestorTree = relatedTargetTree.root;
+ for (var commonAncestorTree = lowestCommonAncestorTree; commonAncestorTree; commonAncestorTree = commonAncestorTree.parent) {
+ var adjustedRelatedTarget;
+ for (var i = 0; i < relatedTargetEventPath.length; i++) {
+ var node = relatedTargetEventPath[i];
+ if (getTreeScope(node) === commonAncestorTree) return node;
+ }
+ }
+ return null;
+ }
+ function inSameTree(a, b) {
+ return getTreeScope(a) === getTreeScope(b);
+ }
+ var NONE = 0;
+ var CAPTURING_PHASE = 1;
+ var AT_TARGET = 2;
+ var BUBBLING_PHASE = 3;
+ var pendingError;
+ function dispatchOriginalEvent(originalEvent) {
+ if (handledEventsTable.get(originalEvent)) return;
+ handledEventsTable.set(originalEvent, true);
+ dispatchEvent(wrap(originalEvent), wrap(originalEvent.target));
+ if (pendingError) {
+ var err = pendingError;
+ pendingError = null;
+ throw err;
+ }
+ }
+ function isLoadLikeEvent(event) {
+ switch (event.type) {
+ case "load":
+ case "beforeunload":
+ case "unload":
+ return true;
+ }
+ return false;
+ }
+ function dispatchEvent(event, originalWrapperTarget) {
+ if (currentlyDispatchingEvents.get(event)) throw new Error("InvalidStateError");
+ currentlyDispatchingEvents.set(event, true);
+ scope.renderAllPending();
+ var eventPath;
+ var overrideTarget;
+ var win;
+ if (isLoadLikeEvent(event) && !event.bubbles) {
+ var doc = originalWrapperTarget;
+ if (doc instanceof wrappers.Document && (win = doc.defaultView)) {
+ overrideTarget = doc;
+ eventPath = [];
+ }
+ }
+ if (!eventPath) {
+ if (originalWrapperTarget instanceof wrappers.Window) {
+ win = originalWrapperTarget;
+ eventPath = [];
+ } else {
+ eventPath = getEventPath(originalWrapperTarget, event);
+ if (!isLoadLikeEvent(event)) {
+ var doc = eventPath[eventPath.length - 1];
+ if (doc instanceof wrappers.Document) win = doc.defaultView;
+ }
+ }
+ }
+ eventPathTable.set(event, eventPath);
+ if (dispatchCapturing(event, eventPath, win, overrideTarget)) {
+ if (dispatchAtTarget(event, eventPath, win, overrideTarget)) {
+ dispatchBubbling(event, eventPath, win, overrideTarget);
+ }
+ }
+ eventPhaseTable.set(event, NONE);
+ currentTargetTable.delete(event, null);
+ currentlyDispatchingEvents.delete(event);
+ return event.defaultPrevented;
+ }
+ function dispatchCapturing(event, eventPath, win, overrideTarget) {
+ var phase = CAPTURING_PHASE;
+ if (win) {
+ if (!invoke(win, event, phase, eventPath, overrideTarget)) return false;
+ }
+ for (var i = eventPath.length - 1; i > 0; i--) {
+ if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) return false;
+ }
+ return true;
+ }
+ function dispatchAtTarget(event, eventPath, win, overrideTarget) {
+ var phase = AT_TARGET;
+ var currentTarget = eventPath[0] || win;
+ return invoke(currentTarget, event, phase, eventPath, overrideTarget);
+ }
+ function dispatchBubbling(event, eventPath, win, overrideTarget) {
+ var phase = BUBBLING_PHASE;
+ for (var i = 1; i < eventPath.length; i++) {
+ if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) return;
+ }
+ if (win && eventPath.length > 0) {
+ invoke(win, event, phase, eventPath, overrideTarget);
+ }
+ }
+ function invoke(currentTarget, event, phase, eventPath, overrideTarget) {
+ var listeners = listenersTable.get(currentTarget);
+ if (!listeners) return true;
+ var target = overrideTarget || eventRetargetting(eventPath, currentTarget);
+ if (target === currentTarget) {
+ if (phase === CAPTURING_PHASE) return true;
+ if (phase === BUBBLING_PHASE) phase = AT_TARGET;
+ } else if (phase === BUBBLING_PHASE && !event.bubbles) {
+ return true;
+ }
+ if ("relatedTarget" in event) {
+ var originalEvent = unwrap(event);
+ var unwrappedRelatedTarget = originalEvent.relatedTarget;
+ if (unwrappedRelatedTarget) {
+ if (unwrappedRelatedTarget instanceof Object && unwrappedRelatedTarget.addEventListener) {
+ var relatedTarget = wrap(unwrappedRelatedTarget);
+ var adjusted = relatedTargetResolution(event, currentTarget, relatedTarget);
+ if (adjusted === target) return true;
+ } else {
+ adjusted = null;
+ }
+ relatedTargetTable.set(event, adjusted);
+ }
+ }
+ eventPhaseTable.set(event, phase);
+ var type = event.type;
+ var anyRemoved = false;
+ targetTable.set(event, target);
+ currentTargetTable.set(event, currentTarget);
+ listeners.depth++;
+ for (var i = 0, len = listeners.length; i < len; i++) {
+ var listener = listeners[i];
+ if (listener.removed) {
+ anyRemoved = true;
+ continue;
+ }
+ if (listener.type !== type || !listener.capture && phase === CAPTURING_PHASE || listener.capture && phase === BUBBLING_PHASE) {
+ continue;
+ }
+ try {
+ if (typeof listener.handler === "function") listener.handler.call(currentTarget, event); else listener.handler.handleEvent(event);
+ if (stopImmediatePropagationTable.get(event)) return false;
+ } catch (ex) {
+ if (!pendingError) pendingError = ex;
+ }
+ }
+ listeners.depth--;
+ if (anyRemoved && listeners.depth === 0) {
+ var copy = listeners.slice();
+ listeners.length = 0;
+ for (var i = 0; i < copy.length; i++) {
+ if (!copy[i].removed) listeners.push(copy[i]);
+ }
+ }
+ return !stopPropagationTable.get(event);
+ }
+ function Listener(type, handler, capture) {
+ this.type = type;
+ this.handler = handler;
+ this.capture = Boolean(capture);
+ }
+ Listener.prototype = {
+ equals: function(that) {
+ return this.handler === that.handler && this.type === that.type && this.capture === that.capture;
+ },
+ get removed() {
+ return this.handler === null;
+ },
+ remove: function() {
+ this.handler = null;
+ }
+ };
+ var OriginalEvent = window.Event;
+ OriginalEvent.prototype.polymerBlackList_ = {
+ returnValue: true,
+ keyLocation: true
+ };
+ function Event(type, options) {
+ if (type instanceof OriginalEvent) {
+ var impl = type;
+ if (!OriginalBeforeUnloadEvent && impl.type === "beforeunload" && !(this instanceof BeforeUnloadEvent)) {
+ return new BeforeUnloadEvent(impl);
+ }
+ setWrapper(impl, this);
+ } else {
+ return wrap(constructEvent(OriginalEvent, "Event", type, options));
+ }
+ }
+ Event.prototype = {
+ get target() {
+ return targetTable.get(this);
+ },
+ get currentTarget() {
+ return currentTargetTable.get(this);
+ },
+ get eventPhase() {
+ return eventPhaseTable.get(this);
+ },
+ get path() {
+ var eventPath = eventPathTable.get(this);
+ if (!eventPath) return [];
+ return eventPath.slice();
+ },
+ stopPropagation: function() {
+ stopPropagationTable.set(this, true);
+ },
+ stopImmediatePropagation: function() {
+ stopPropagationTable.set(this, true);
+ stopImmediatePropagationTable.set(this, true);
+ }
+ };
+ var supportsDefaultPrevented = function() {
+ var e = document.createEvent("Event");
+ e.initEvent("test", true, true);
+ e.preventDefault();
+ return e.defaultPrevented;
+ }();
+ if (!supportsDefaultPrevented) {
+ Event.prototype.preventDefault = function() {
+ if (!this.cancelable) return;
+ unsafeUnwrap(this).preventDefault();
+ Object.defineProperty(this, "defaultPrevented", {
+ get: function() {
+ return true;
+ },
+ configurable: true
+ });
+ };
+ }
+ registerWrapper(OriginalEvent, Event, document.createEvent("Event"));
+ function unwrapOptions(options) {
+ if (!options || !options.relatedTarget) return options;
+ return Object.create(options, {
+ relatedTarget: {
+ value: unwrap(options.relatedTarget)
+ }
+ });
+ }
+ function registerGenericEvent(name, SuperEvent, prototype) {
+ var OriginalEvent = window[name];
+ var GenericEvent = function(type, options) {
+ if (type instanceof OriginalEvent) setWrapper(type, this); else return wrap(constructEvent(OriginalEvent, name, type, options));
+ };
+ GenericEvent.prototype = Object.create(SuperEvent.prototype);
+ if (prototype) mixin(GenericEvent.prototype, prototype);
+ if (OriginalEvent) {
+ try {
+ registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent("temp"));
+ } catch (ex) {
+ registerWrapper(OriginalEvent, GenericEvent, document.createEvent(name));
+ }
+ }
+ return GenericEvent;
+ }
+ var UIEvent = registerGenericEvent("UIEvent", Event);
+ var CustomEvent = registerGenericEvent("CustomEvent", Event);
+ var relatedTargetProto = {
+ get relatedTarget() {
+ var relatedTarget = relatedTargetTable.get(this);
+ if (relatedTarget !== undefined) return relatedTarget;
+ return wrap(unwrap(this).relatedTarget);
+ }
+ };
+ function getInitFunction(name, relatedTargetIndex) {
+ return function() {
+ arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]);
+ var impl = unwrap(this);
+ impl[name].apply(impl, arguments);
+ };
+ }
+ var mouseEventProto = mixin({
+ initMouseEvent: getInitFunction("initMouseEvent", 14)
+ }, relatedTargetProto);
+ var focusEventProto = mixin({
+ initFocusEvent: getInitFunction("initFocusEvent", 5)
+ }, relatedTargetProto);
+ var MouseEvent = registerGenericEvent("MouseEvent", UIEvent, mouseEventProto);
+ var FocusEvent = registerGenericEvent("FocusEvent", UIEvent, focusEventProto);
+ var defaultInitDicts = Object.create(null);
+ var supportsEventConstructors = function() {
+ try {
+ new window.FocusEvent("focus");
+ } catch (ex) {
+ return false;
+ }
+ return true;
+ }();
+ function constructEvent(OriginalEvent, name, type, options) {
+ if (supportsEventConstructors) return new OriginalEvent(type, unwrapOptions(options));
+ var event = unwrap(document.createEvent(name));
+ var defaultDict = defaultInitDicts[name];
+ var args = [ type ];
+ Object.keys(defaultDict).forEach(function(key) {
+ var v = options != null && key in options ? options[key] : defaultDict[key];
+ if (key === "relatedTarget") v = unwrap(v);
+ args.push(v);
+ });
+ event["init" + name].apply(event, args);
+ return event;
+ }
+ if (!supportsEventConstructors) {
+ var configureEventConstructor = function(name, initDict, superName) {
+ if (superName) {
+ var superDict = defaultInitDicts[superName];
+ initDict = mixin(mixin({}, superDict), initDict);
+ }
+ defaultInitDicts[name] = initDict;
+ };
+ configureEventConstructor("Event", {
+ bubbles: false,
+ cancelable: false
+ });
+ configureEventConstructor("CustomEvent", {
+ detail: null
+ }, "Event");
+ configureEventConstructor("UIEvent", {
+ view: null,
+ detail: 0
+ }, "Event");
+ configureEventConstructor("MouseEvent", {
+ screenX: 0,
+ screenY: 0,
+ clientX: 0,
+ clientY: 0,
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false,
+ button: 0,
+ relatedTarget: null
+ }, "UIEvent");
+ configureEventConstructor("FocusEvent", {
+ relatedTarget: null
+ }, "UIEvent");
+ }
+ var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent;
+ function BeforeUnloadEvent(impl) {
+ Event.call(this, impl);
+ }
+ BeforeUnloadEvent.prototype = Object.create(Event.prototype);
+ mixin(BeforeUnloadEvent.prototype, {
+ get returnValue() {
+ return unsafeUnwrap(this).returnValue;
+ },
+ set returnValue(v) {
+ unsafeUnwrap(this).returnValue = v;
+ }
+ });
+ if (OriginalBeforeUnloadEvent) registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent);
+ function isValidListener(fun) {
+ if (typeof fun === "function") return true;
+ return fun && fun.handleEvent;
+ }
+ function isMutationEvent(type) {
+ switch (type) {
+ case "DOMAttrModified":
+ case "DOMAttributeNameChanged":
+ case "DOMCharacterDataModified":
+ case "DOMElementNameChanged":
+ case "DOMNodeInserted":
+ case "DOMNodeInsertedIntoDocument":
+ case "DOMNodeRemoved":
+ case "DOMNodeRemovedFromDocument":
+ case "DOMSubtreeModified":
+ return true;
+ }
+ return false;
+ }
+ var OriginalEventTarget = window.EventTarget;
+ function EventTarget(impl) {
+ setWrapper(impl, this);
+ }
+ var methodNames = [ "addEventListener", "removeEventListener", "dispatchEvent" ];
+ [ Node, Window ].forEach(function(constructor) {
+ var p = constructor.prototype;
+ methodNames.forEach(function(name) {
+ Object.defineProperty(p, name + "_", {
+ value: p[name]
+ });
+ });
+ });
+ function getTargetToListenAt(wrapper) {
+ if (wrapper instanceof wrappers.ShadowRoot) wrapper = wrapper.host;
+ return unwrap(wrapper);
+ }
+ EventTarget.prototype = {
+ addEventListener: function(type, fun, capture) {
+ if (!isValidListener(fun) || isMutationEvent(type)) return;
+ var listener = new Listener(type, fun, capture);
+ var listeners = listenersTable.get(this);
+ if (!listeners) {
+ listeners = [];
+ listeners.depth = 0;
+ listenersTable.set(this, listeners);
+ } else {
+ for (var i = 0; i < listeners.length; i++) {
+ if (listener.equals(listeners[i])) return;
+ }
+ }
+ listeners.push(listener);
+ var target = getTargetToListenAt(this);
+ target.addEventListener_(type, dispatchOriginalEvent, true);
+ },
+ removeEventListener: function(type, fun, capture) {
+ capture = Boolean(capture);
+ var listeners = listenersTable.get(this);
+ if (!listeners) return;
+ var count = 0, found = false;
+ for (var i = 0; i < listeners.length; i++) {
+ if (listeners[i].type === type && listeners[i].capture === capture) {
+ count++;
+ if (listeners[i].handler === fun) {
+ found = true;
+ listeners[i].remove();
+ }
+ }
+ }
+ if (found && count === 1) {
+ var target = getTargetToListenAt(this);
+ target.removeEventListener_(type, dispatchOriginalEvent, true);
+ }
+ },
+ dispatchEvent: function(event) {
+ var nativeEvent = unwrap(event);
+ var eventType = nativeEvent.type;
+ handledEventsTable.set(nativeEvent, false);
+ scope.renderAllPending();
+ var tempListener;
+ if (!hasListenerInAncestors(this, eventType)) {
+ tempListener = function() {};
+ this.addEventListener(eventType, tempListener, true);
+ }
+ try {
+ return unwrap(this).dispatchEvent_(nativeEvent);
+ } finally {
+ if (tempListener) this.removeEventListener(eventType, tempListener, true);
+ }
+ }
+ };
+ function hasListener(node, type) {
+ var listeners = listenersTable.get(node);
+ if (listeners) {
+ for (var i = 0; i < listeners.length; i++) {
+ if (!listeners[i].removed && listeners[i].type === type) return true;
+ }
+ }
+ return false;
+ }
+ function hasListenerInAncestors(target, type) {
+ for (var node = unwrap(target); node; node = node.parentNode) {
+ if (hasListener(wrap(node), type)) return true;
+ }
+ return false;
+ }
+ if (OriginalEventTarget) registerWrapper(OriginalEventTarget, EventTarget);
+ function wrapEventTargetMethods(constructors) {
+ forwardMethodsToWrapper(constructors, methodNames);
+ }
+ var originalElementFromPoint = document.elementFromPoint;
+ function elementFromPoint(self, document, x, y) {
+ scope.renderAllPending();
+ var element = wrap(originalElementFromPoint.call(unsafeUnwrap(document), x, y));
+ if (!element) return null;
+ var path = getEventPath(element, null);
+ var idx = path.lastIndexOf(self);
+ if (idx == -1) return null; else path = path.slice(0, idx);
+ return eventRetargetting(path, self);
+ }
+ function getEventHandlerGetter(name) {
+ return function() {
+ var inlineEventHandlers = eventHandlersTable.get(this);
+ return inlineEventHandlers && inlineEventHandlers[name] && inlineEventHandlers[name].value || null;
+ };
+ }
+ function getEventHandlerSetter(name) {
+ var eventType = name.slice(2);
+ return function(value) {
+ var inlineEventHandlers = eventHandlersTable.get(this);
+ if (!inlineEventHandlers) {
+ inlineEventHandlers = Object.create(null);
+ eventHandlersTable.set(this, inlineEventHandlers);
+ }
+ var old = inlineEventHandlers[name];
+ if (old) this.removeEventListener(eventType, old.wrapped, false);
+ if (typeof value === "function") {
+ var wrapped = function(e) {
+ var rv = value.call(this, e);
+ if (rv === false) e.preventDefault(); else if (name === "onbeforeunload" && typeof rv === "string") e.returnValue = rv;
+ };
+ this.addEventListener(eventType, wrapped, false);
+ inlineEventHandlers[name] = {
+ value: value,
+ wrapped: wrapped
+ };
+ }
+ };
+ }
+ scope.elementFromPoint = elementFromPoint;
+ scope.getEventHandlerGetter = getEventHandlerGetter;
+ scope.getEventHandlerSetter = getEventHandlerSetter;
+ scope.wrapEventTargetMethods = wrapEventTargetMethods;
+ scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent;
+ scope.wrappers.CustomEvent = CustomEvent;
+ scope.wrappers.Event = Event;
+ scope.wrappers.EventTarget = EventTarget;
+ scope.wrappers.FocusEvent = FocusEvent;
+ scope.wrappers.MouseEvent = MouseEvent;
+ scope.wrappers.UIEvent = UIEvent;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var UIEvent = scope.wrappers.UIEvent;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var OriginalTouchEvent = window.TouchEvent;
+ if (!OriginalTouchEvent) return;
+ var nativeEvent;
+ try {
+ nativeEvent = document.createEvent("TouchEvent");
+ } catch (ex) {
+ return;
+ }
+ var nonEnumDescriptor = {
+ enumerable: false
+ };
+ function nonEnum(obj, prop) {
+ Object.defineProperty(obj, prop, nonEnumDescriptor);
+ }
+ function Touch(impl) {
+ setWrapper(impl, this);
+ }
+ Touch.prototype = {
+ get target() {
+ return wrap(unsafeUnwrap(this).target);
+ }
+ };
+ var descr = {
+ configurable: true,
+ enumerable: true,
+ get: null
+ };
+ [ "clientX", "clientY", "screenX", "screenY", "pageX", "pageY", "identifier", "webkitRadiusX", "webkitRadiusY", "webkitRotationAngle", "webkitForce" ].forEach(function(name) {
+ descr.get = function() {
+ return unsafeUnwrap(this)[name];
+ };
+ Object.defineProperty(Touch.prototype, name, descr);
+ });
+ function TouchList() {
+ this.length = 0;
+ nonEnum(this, "length");
+ }
+ TouchList.prototype = {
+ item: function(index) {
+ return this[index];
+ }
+ };
+ function wrapTouchList(nativeTouchList) {
+ var list = new TouchList();
+ for (var i = 0; i < nativeTouchList.length; i++) {
+ list[i] = new Touch(nativeTouchList[i]);
+ }
+ list.length = i;
+ return list;
+ }
+ function TouchEvent(impl) {
+ UIEvent.call(this, impl);
+ }
+ TouchEvent.prototype = Object.create(UIEvent.prototype);
+ mixin(TouchEvent.prototype, {
+ get touches() {
+ return wrapTouchList(unsafeUnwrap(this).touches);
+ },
+ get targetTouches() {
+ return wrapTouchList(unsafeUnwrap(this).targetTouches);
+ },
+ get changedTouches() {
+ return wrapTouchList(unsafeUnwrap(this).changedTouches);
+ },
+ initTouchEvent: function() {
+ throw new Error("Not implemented");
+ }
+ });
+ registerWrapper(OriginalTouchEvent, TouchEvent, nativeEvent);
+ scope.wrappers.Touch = Touch;
+ scope.wrappers.TouchEvent = TouchEvent;
+ scope.wrappers.TouchList = TouchList;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var nonEnumDescriptor = {
+ enumerable: false
+ };
+ function nonEnum(obj, prop) {
+ Object.defineProperty(obj, prop, nonEnumDescriptor);
+ }
+ function NodeList() {
+ this.length = 0;
+ nonEnum(this, "length");
+ }
+ NodeList.prototype = {
+ item: function(index) {
+ return this[index];
+ }
+ };
+ nonEnum(NodeList.prototype, "item");
+ function wrapNodeList(list) {
+ if (list == null) return list;
+ var wrapperList = new NodeList();
+ for (var i = 0, length = list.length; i < length; i++) {
+ wrapperList[i] = wrap(list[i]);
+ }
+ wrapperList.length = length;
+ return wrapperList;
+ }
+ function addWrapNodeListMethod(wrapperConstructor, name) {
+ wrapperConstructor.prototype[name] = function() {
+ return wrapNodeList(unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments));
+ };
+ }
+ scope.wrappers.NodeList = NodeList;
+ scope.addWrapNodeListMethod = addWrapNodeListMethod;
+ scope.wrapNodeList = wrapNodeList;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ scope.wrapHTMLCollection = scope.wrapNodeList;
+ scope.wrappers.HTMLCollection = scope.wrappers.NodeList;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var EventTarget = scope.wrappers.EventTarget;
+ var NodeList = scope.wrappers.NodeList;
+ var TreeScope = scope.TreeScope;
+ var assert = scope.assert;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var enqueueMutation = scope.enqueueMutation;
+ var getTreeScope = scope.getTreeScope;
+ var isWrapper = scope.isWrapper;
+ var mixin = scope.mixin;
+ var registerTransientObservers = scope.registerTransientObservers;
+ var registerWrapper = scope.registerWrapper;
+ var setTreeScope = scope.setTreeScope;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var wrapIfNeeded = scope.wrapIfNeeded;
+ var wrappers = scope.wrappers;
+ function assertIsNodeWrapper(node) {
+ assert(node instanceof Node);
+ }
+ function createOneElementNodeList(node) {
+ var nodes = new NodeList();
+ nodes[0] = node;
+ nodes.length = 1;
+ return nodes;
+ }
+ var surpressMutations = false;
+ function enqueueRemovalForInsertedNodes(node, parent, nodes) {
+ enqueueMutation(parent, "childList", {
+ removedNodes: nodes,
+ previousSibling: node.previousSibling,
+ nextSibling: node.nextSibling
+ });
+ }
+ function enqueueRemovalForInsertedDocumentFragment(df, nodes) {
+ enqueueMutation(df, "childList", {
+ removedNodes: nodes
+ });
+ }
+ function collectNodes(node, parentNode, previousNode, nextNode) {
+ if (node instanceof DocumentFragment) {
+ var nodes = collectNodesForDocumentFragment(node);
+ surpressMutations = true;
+ for (var i = nodes.length - 1; i >= 0; i--) {
+ node.removeChild(nodes[i]);
+ nodes[i].parentNode_ = parentNode;
+ }
+ surpressMutations = false;
+ for (var i = 0; i < nodes.length; i++) {
+ nodes[i].previousSibling_ = nodes[i - 1] || previousNode;
+ nodes[i].nextSibling_ = nodes[i + 1] || nextNode;
+ }
+ if (previousNode) previousNode.nextSibling_ = nodes[0];
+ if (nextNode) nextNode.previousSibling_ = nodes[nodes.length - 1];
+ return nodes;
+ }
+ var nodes = createOneElementNodeList(node);
+ var oldParent = node.parentNode;
+ if (oldParent) {
+ oldParent.removeChild(node);
+ }
+ node.parentNode_ = parentNode;
+ node.previousSibling_ = previousNode;
+ node.nextSibling_ = nextNode;
+ if (previousNode) previousNode.nextSibling_ = node;
+ if (nextNode) nextNode.previousSibling_ = node;
+ return nodes;
+ }
+ function collectNodesNative(node) {
+ if (node instanceof DocumentFragment) return collectNodesForDocumentFragment(node);
+ var nodes = createOneElementNodeList(node);
+ var oldParent = node.parentNode;
+ if (oldParent) enqueueRemovalForInsertedNodes(node, oldParent, nodes);
+ return nodes;
+ }
+ function collectNodesForDocumentFragment(node) {
+ var nodes = new NodeList();
+ var i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ nodes[i++] = child;
+ }
+ nodes.length = i;
+ enqueueRemovalForInsertedDocumentFragment(node, nodes);
+ return nodes;
+ }
+ function snapshotNodeList(nodeList) {
+ return nodeList;
+ }
+ function nodeWasAdded(node, treeScope) {
+ setTreeScope(node, treeScope);
+ node.nodeIsInserted_();
+ }
+ function nodesWereAdded(nodes, parent) {
+ var treeScope = getTreeScope(parent);
+ for (var i = 0; i < nodes.length; i++) {
+ nodeWasAdded(nodes[i], treeScope);
+ }
+ }
+ function nodeWasRemoved(node) {
+ setTreeScope(node, new TreeScope(node, null));
+ }
+ function nodesWereRemoved(nodes) {
+ for (var i = 0; i < nodes.length; i++) {
+ nodeWasRemoved(nodes[i]);
+ }
+ }
+ function ensureSameOwnerDocument(parent, child) {
+ var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? parent : parent.ownerDocument;
+ if (ownerDoc !== child.ownerDocument) ownerDoc.adoptNode(child);
+ }
+ function adoptNodesIfNeeded(owner, nodes) {
+ if (!nodes.length) return;
+ var ownerDoc = owner.ownerDocument;
+ if (ownerDoc === nodes[0].ownerDocument) return;
+ for (var i = 0; i < nodes.length; i++) {
+ scope.adoptNodeNoRemove(nodes[i], ownerDoc);
+ }
+ }
+ function unwrapNodesForInsertion(owner, nodes) {
+ adoptNodesIfNeeded(owner, nodes);
+ var length = nodes.length;
+ if (length === 1) return unwrap(nodes[0]);
+ var df = unwrap(owner.ownerDocument.createDocumentFragment());
+ for (var i = 0; i < length; i++) {
+ df.appendChild(unwrap(nodes[i]));
+ }
+ return df;
+ }
+ function clearChildNodes(wrapper) {
+ if (wrapper.firstChild_ !== undefined) {
+ var child = wrapper.firstChild_;
+ while (child) {
+ var tmp = child;
+ child = child.nextSibling_;
+ tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined;
+ }
+ }
+ wrapper.firstChild_ = wrapper.lastChild_ = undefined;
+ }
+ function removeAllChildNodes(wrapper) {
+ if (wrapper.invalidateShadowRenderer()) {
+ var childWrapper = wrapper.firstChild;
+ while (childWrapper) {
+ assert(childWrapper.parentNode === wrapper);
+ var nextSibling = childWrapper.nextSibling;
+ var childNode = unwrap(childWrapper);
+ var parentNode = childNode.parentNode;
+ if (parentNode) originalRemoveChild.call(parentNode, childNode);
+ childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.parentNode_ = null;
+ childWrapper = nextSibling;
+ }
+ wrapper.firstChild_ = wrapper.lastChild_ = null;
+ } else {
+ var node = unwrap(wrapper);
+ var child = node.firstChild;
+ var nextSibling;
+ while (child) {
+ nextSibling = child.nextSibling;
+ originalRemoveChild.call(node, child);
+ child = nextSibling;
+ }
+ }
+ }
+ function invalidateParent(node) {
+ var p = node.parentNode;
+ return p && p.invalidateShadowRenderer();
+ }
+ function cleanupNodes(nodes) {
+ for (var i = 0, n; i < nodes.length; i++) {
+ n = nodes[i];
+ n.parentNode.removeChild(n);
+ }
+ }
+ var originalImportNode = document.importNode;
+ var originalCloneNode = window.Node.prototype.cloneNode;
+ function cloneNode(node, deep, opt_doc) {
+ var clone;
+ if (opt_doc) clone = wrap(originalImportNode.call(opt_doc, unsafeUnwrap(node), false)); else clone = wrap(originalCloneNode.call(unsafeUnwrap(node), false));
+ if (deep) {
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ clone.appendChild(cloneNode(child, true, opt_doc));
+ }
+ if (node instanceof wrappers.HTMLTemplateElement) {
+ var cloneContent = clone.content;
+ for (var child = node.content.firstChild; child; child = child.nextSibling) {
+ cloneContent.appendChild(cloneNode(child, true, opt_doc));
+ }
+ }
+ }
+ return clone;
+ }
+ function contains(self, child) {
+ if (!child || getTreeScope(self) !== getTreeScope(child)) return false;
+ for (var node = child; node; node = node.parentNode) {
+ if (node === self) return true;
+ }
+ return false;
+ }
+ var OriginalNode = window.Node;
+ function Node(original) {
+ assert(original instanceof OriginalNode);
+ EventTarget.call(this, original);
+ this.parentNode_ = undefined;
+ this.firstChild_ = undefined;
+ this.lastChild_ = undefined;
+ this.nextSibling_ = undefined;
+ this.previousSibling_ = undefined;
+ this.treeScope_ = undefined;
+ }
+ var OriginalDocumentFragment = window.DocumentFragment;
+ var originalAppendChild = OriginalNode.prototype.appendChild;
+ var originalCompareDocumentPosition = OriginalNode.prototype.compareDocumentPosition;
+ var originalIsEqualNode = OriginalNode.prototype.isEqualNode;
+ var originalInsertBefore = OriginalNode.prototype.insertBefore;
+ var originalRemoveChild = OriginalNode.prototype.removeChild;
+ var originalReplaceChild = OriginalNode.prototype.replaceChild;
+ var isIEOrEdge = /Trident|Edge/.test(navigator.userAgent);
+ var removeChildOriginalHelper = isIEOrEdge ? function(parent, child) {
+ try {
+ originalRemoveChild.call(parent, child);
+ } catch (ex) {
+ if (!(parent instanceof OriginalDocumentFragment)) throw ex;
+ }
+ } : function(parent, child) {
+ originalRemoveChild.call(parent, child);
+ };
+ Node.prototype = Object.create(EventTarget.prototype);
+ mixin(Node.prototype, {
+ appendChild: function(childWrapper) {
+ return this.insertBefore(childWrapper, null);
+ },
+ insertBefore: function(childWrapper, refWrapper) {
+ assertIsNodeWrapper(childWrapper);
+ var refNode;
+ if (refWrapper) {
+ if (isWrapper(refWrapper)) {
+ refNode = unwrap(refWrapper);
+ } else {
+ refNode = refWrapper;
+ refWrapper = wrap(refNode);
+ }
+ } else {
+ refWrapper = null;
+ refNode = null;
+ }
+ refWrapper && assert(refWrapper.parentNode === this);
+ var nodes;
+ var previousNode = refWrapper ? refWrapper.previousSibling : this.lastChild;
+ var useNative = !this.invalidateShadowRenderer() && !invalidateParent(childWrapper);
+ if (useNative) nodes = collectNodesNative(childWrapper); else nodes = collectNodes(childWrapper, this, previousNode, refWrapper);
+ if (useNative) {
+ ensureSameOwnerDocument(this, childWrapper);
+ clearChildNodes(this);
+ originalInsertBefore.call(unsafeUnwrap(this), unwrap(childWrapper), refNode);
+ } else {
+ if (!previousNode) this.firstChild_ = nodes[0];
+ if (!refWrapper) {
+ this.lastChild_ = nodes[nodes.length - 1];
+ if (this.firstChild_ === undefined) this.firstChild_ = this.firstChild;
+ }
+ var parentNode = refNode ? refNode.parentNode : unsafeUnwrap(this);
+ if (parentNode) {
+ originalInsertBefore.call(parentNode, unwrapNodesForInsertion(this, nodes), refNode);
+ } else {
+ adoptNodesIfNeeded(this, nodes);
+ }
+ }
+ enqueueMutation(this, "childList", {
+ addedNodes: nodes,
+ nextSibling: refWrapper,
+ previousSibling: previousNode
+ });
+ nodesWereAdded(nodes, this);
+ return childWrapper;
+ },
+ removeChild: function(childWrapper) {
+ assertIsNodeWrapper(childWrapper);
+ if (childWrapper.parentNode !== this) {
+ var found = false;
+ var childNodes = this.childNodes;
+ for (var ieChild = this.firstChild; ieChild; ieChild = ieChild.nextSibling) {
+ if (ieChild === childWrapper) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new Error("NotFoundError");
+ }
+ }
+ var childNode = unwrap(childWrapper);
+ var childWrapperNextSibling = childWrapper.nextSibling;
+ var childWrapperPreviousSibling = childWrapper.previousSibling;
+ if (this.invalidateShadowRenderer()) {
+ var thisFirstChild = this.firstChild;
+ var thisLastChild = this.lastChild;
+ var parentNode = childNode.parentNode;
+ if (parentNode) removeChildOriginalHelper(parentNode, childNode);
+ if (thisFirstChild === childWrapper) this.firstChild_ = childWrapperNextSibling;
+ if (thisLastChild === childWrapper) this.lastChild_ = childWrapperPreviousSibling;
+ if (childWrapperPreviousSibling) childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling;
+ if (childWrapperNextSibling) {
+ childWrapperNextSibling.previousSibling_ = childWrapperPreviousSibling;
+ }
+ childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.parentNode_ = undefined;
+ } else {
+ clearChildNodes(this);
+ removeChildOriginalHelper(unsafeUnwrap(this), childNode);
+ }
+ if (!surpressMutations) {
+ enqueueMutation(this, "childList", {
+ removedNodes: createOneElementNodeList(childWrapper),
+ nextSibling: childWrapperNextSibling,
+ previousSibling: childWrapperPreviousSibling
+ });
+ }
+ registerTransientObservers(this, childWrapper);
+ return childWrapper;
+ },
+ replaceChild: function(newChildWrapper, oldChildWrapper) {
+ assertIsNodeWrapper(newChildWrapper);
+ var oldChildNode;
+ if (isWrapper(oldChildWrapper)) {
+ oldChildNode = unwrap(oldChildWrapper);
+ } else {
+ oldChildNode = oldChildWrapper;
+ oldChildWrapper = wrap(oldChildNode);
+ }
+ if (oldChildWrapper.parentNode !== this) {
+ throw new Error("NotFoundError");
+ }
+ var nextNode = oldChildWrapper.nextSibling;
+ var previousNode = oldChildWrapper.previousSibling;
+ var nodes;
+ var useNative = !this.invalidateShadowRenderer() && !invalidateParent(newChildWrapper);
+ if (useNative) {
+ nodes = collectNodesNative(newChildWrapper);
+ } else {
+ if (nextNode === newChildWrapper) nextNode = newChildWrapper.nextSibling;
+ nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
+ }
+ if (!useNative) {
+ if (this.firstChild === oldChildWrapper) this.firstChild_ = nodes[0];
+ if (this.lastChild === oldChildWrapper) this.lastChild_ = nodes[nodes.length - 1];
+ oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = oldChildWrapper.parentNode_ = undefined;
+ if (oldChildNode.parentNode) {
+ originalReplaceChild.call(oldChildNode.parentNode, unwrapNodesForInsertion(this, nodes), oldChildNode);
+ }
+ } else {
+ ensureSameOwnerDocument(this, newChildWrapper);
+ clearChildNodes(this);
+ originalReplaceChild.call(unsafeUnwrap(this), unwrap(newChildWrapper), oldChildNode);
+ }
+ enqueueMutation(this, "childList", {
+ addedNodes: nodes,
+ removedNodes: createOneElementNodeList(oldChildWrapper),
+ nextSibling: nextNode,
+ previousSibling: previousNode
+ });
+ nodeWasRemoved(oldChildWrapper);
+ nodesWereAdded(nodes, this);
+ return oldChildWrapper;
+ },
+ nodeIsInserted_: function() {
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ child.nodeIsInserted_();
+ }
+ },
+ hasChildNodes: function() {
+ return this.firstChild !== null;
+ },
+ get parentNode() {
+ return this.parentNode_ !== undefined ? this.parentNode_ : wrap(unsafeUnwrap(this).parentNode);
+ },
+ get firstChild() {
+ return this.firstChild_ !== undefined ? this.firstChild_ : wrap(unsafeUnwrap(this).firstChild);
+ },
+ get lastChild() {
+ return this.lastChild_ !== undefined ? this.lastChild_ : wrap(unsafeUnwrap(this).lastChild);
+ },
+ get nextSibling() {
+ return this.nextSibling_ !== undefined ? this.nextSibling_ : wrap(unsafeUnwrap(this).nextSibling);
+ },
+ get previousSibling() {
+ return this.previousSibling_ !== undefined ? this.previousSibling_ : wrap(unsafeUnwrap(this).previousSibling);
+ },
+ get parentElement() {
+ var p = this.parentNode;
+ while (p && p.nodeType !== Node.ELEMENT_NODE) {
+ p = p.parentNode;
+ }
+ return p;
+ },
+ get textContent() {
+ var s = "";
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType != Node.COMMENT_NODE) {
+ s += child.textContent;
+ }
+ }
+ return s;
+ },
+ set textContent(textContent) {
+ if (textContent == null) textContent = "";
+ var removedNodes = snapshotNodeList(this.childNodes);
+ if (this.invalidateShadowRenderer()) {
+ removeAllChildNodes(this);
+ if (textContent !== "") {
+ var textNode = unsafeUnwrap(this).ownerDocument.createTextNode(textContent);
+ this.appendChild(textNode);
+ }
+ } else {
+ clearChildNodes(this);
+ unsafeUnwrap(this).textContent = textContent;
+ }
+ var addedNodes = snapshotNodeList(this.childNodes);
+ enqueueMutation(this, "childList", {
+ addedNodes: addedNodes,
+ removedNodes: removedNodes
+ });
+ nodesWereRemoved(removedNodes);
+ nodesWereAdded(addedNodes, this);
+ },
+ get childNodes() {
+ var wrapperList = new NodeList();
+ var i = 0;
+ for (var child = this.firstChild; child; child = child.nextSibling) {
+ wrapperList[i++] = child;
+ }
+ wrapperList.length = i;
+ return wrapperList;
+ },
+ cloneNode: function(deep) {
+ return cloneNode(this, deep);
+ },
+ contains: function(child) {
+ return contains(this, wrapIfNeeded(child));
+ },
+ compareDocumentPosition: function(otherNode) {
+ return originalCompareDocumentPosition.call(unsafeUnwrap(this), unwrapIfNeeded(otherNode));
+ },
+ isEqualNode: function(otherNode) {
+ return originalIsEqualNode.call(unsafeUnwrap(this), unwrapIfNeeded(otherNode));
+ },
+ normalize: function() {
+ var nodes = snapshotNodeList(this.childNodes);
+ var remNodes = [];
+ var s = "";
+ var modNode;
+ for (var i = 0, n; i < nodes.length; i++) {
+ n = nodes[i];
+ if (n.nodeType === Node.TEXT_NODE) {
+ if (!modNode && !n.data.length) this.removeChild(n); else if (!modNode) modNode = n; else {
+ s += n.data;
+ remNodes.push(n);
+ }
+ } else {
+ if (modNode && remNodes.length) {
+ modNode.data += s;
+ cleanupNodes(remNodes);
+ }
+ remNodes = [];
+ s = "";
+ modNode = null;
+ if (n.childNodes.length) n.normalize();
+ }
+ }
+ if (modNode && remNodes.length) {
+ modNode.data += s;
+ cleanupNodes(remNodes);
+ }
+ }
+ });
+ defineWrapGetter(Node, "ownerDocument");
+ registerWrapper(OriginalNode, Node, document.createDocumentFragment());
+ delete Node.prototype.querySelector;
+ delete Node.prototype.querySelectorAll;
+ Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
+ scope.cloneNode = cloneNode;
+ scope.nodeWasAdded = nodeWasAdded;
+ scope.nodeWasRemoved = nodeWasRemoved;
+ scope.nodesWereAdded = nodesWereAdded;
+ scope.nodesWereRemoved = nodesWereRemoved;
+ scope.originalInsertBefore = originalInsertBefore;
+ scope.originalRemoveChild = originalRemoveChild;
+ scope.snapshotNodeList = snapshotNodeList;
+ scope.wrappers.Node = Node;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLCollection = scope.wrappers.HTMLCollection;
+ var NodeList = scope.wrappers.NodeList;
+ var getTreeScope = scope.getTreeScope;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var originalDocumentQuerySelector = document.querySelector;
+ var originalElementQuerySelector = document.documentElement.querySelector;
+ var originalDocumentQuerySelectorAll = document.querySelectorAll;
+ var originalElementQuerySelectorAll = document.documentElement.querySelectorAll;
+ var originalDocumentGetElementsByTagName = document.getElementsByTagName;
+ var originalElementGetElementsByTagName = document.documentElement.getElementsByTagName;
+ var originalDocumentGetElementsByTagNameNS = document.getElementsByTagNameNS;
+ var originalElementGetElementsByTagNameNS = document.documentElement.getElementsByTagNameNS;
+ var OriginalElement = window.Element;
+ var OriginalDocument = window.HTMLDocument || window.Document;
+ function filterNodeList(list, index, result, deep) {
+ var wrappedItem = null;
+ var root = null;
+ for (var i = 0, length = list.length; i < length; i++) {
+ wrappedItem = wrap(list[i]);
+ if (!deep && (root = getTreeScope(wrappedItem).root)) {
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ continue;
+ }
+ }
+ result[index++] = wrappedItem;
+ }
+ return index;
+ }
+ function shimSelector(selector) {
+ return String(selector).replace(/\/deep\/|::shadow|>>>/g, " ");
+ }
+ function shimMatchesSelector(selector) {
+ return String(selector).replace(/:host\(([^\s]+)\)/g, "$1").replace(/([^\s]):host/g, "$1").replace(":host", "*").replace(/\^|\/shadow\/|\/shadow-deep\/|::shadow|\/deep\/|::content|>>>/g, " ");
+ }
+ function findOne(node, selector) {
+ var m, el = node.firstElementChild;
+ while (el) {
+ if (el.matches(selector)) return el;
+ m = findOne(el, selector);
+ if (m) return m;
+ el = el.nextElementSibling;
+ }
+ return null;
+ }
+ function matchesSelector(el, selector) {
+ return el.matches(selector);
+ }
+ var XHTML_NS = "http://www.w3.org/1999/xhtml";
+ function matchesTagName(el, localName, localNameLowerCase) {
+ var ln = el.localName;
+ return ln === localName || ln === localNameLowerCase && el.namespaceURI === XHTML_NS;
+ }
+ function matchesEveryThing() {
+ return true;
+ }
+ function matchesLocalNameOnly(el, ns, localName) {
+ return el.localName === localName;
+ }
+ function matchesNameSpace(el, ns) {
+ return el.namespaceURI === ns;
+ }
+ function matchesLocalNameNS(el, ns, localName) {
+ return el.namespaceURI === ns && el.localName === localName;
+ }
+ function findElements(node, index, result, p, arg0, arg1) {
+ var el = node.firstElementChild;
+ while (el) {
+ if (p(el, arg0, arg1)) result[index++] = el;
+ index = findElements(el, index, result, p, arg0, arg1);
+ el = el.nextElementSibling;
+ }
+ return index;
+ }
+ function querySelectorAllFiltered(p, index, result, selector, deep) {
+ var target = unsafeUnwrap(this);
+ var list;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findElements(this, index, result, p, selector, null);
+ } else if (target instanceof OriginalElement) {
+ list = originalElementQuerySelectorAll.call(target, selector);
+ } else if (target instanceof OriginalDocument) {
+ list = originalDocumentQuerySelectorAll.call(target, selector);
+ } else {
+ return findElements(this, index, result, p, selector, null);
+ }
+ return filterNodeList(list, index, result, deep);
+ }
+ var SelectorsInterface = {
+ querySelector: function(selector) {
+ var shimmed = shimSelector(selector);
+ var deep = shimmed !== selector;
+ selector = shimmed;
+ var target = unsafeUnwrap(this);
+ var wrappedItem;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findOne(this, selector);
+ } else if (target instanceof OriginalElement) {
+ wrappedItem = wrap(originalElementQuerySelector.call(target, selector));
+ } else if (target instanceof OriginalDocument) {
+ wrappedItem = wrap(originalDocumentQuerySelector.call(target, selector));
+ } else {
+ return findOne(this, selector);
+ }
+ if (!wrappedItem) {
+ return wrappedItem;
+ } else if (!deep && (root = getTreeScope(wrappedItem).root)) {
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findOne(this, selector);
+ }
+ }
+ return wrappedItem;
+ },
+ querySelectorAll: function(selector) {
+ var shimmed = shimSelector(selector);
+ var deep = shimmed !== selector;
+ selector = shimmed;
+ var result = new NodeList();
+ result.length = querySelectorAllFiltered.call(this, matchesSelector, 0, result, selector, deep);
+ return result;
+ }
+ };
+ var MatchesInterface = {
+ matches: function(selector) {
+ selector = shimMatchesSelector(selector);
+ return scope.originalMatches.call(unsafeUnwrap(this), selector);
+ }
+ };
+ function getElementsByTagNameFiltered(p, index, result, localName, lowercase) {
+ var target = unsafeUnwrap(this);
+ var list;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findElements(this, index, result, p, localName, lowercase);
+ } else if (target instanceof OriginalElement) {
+ list = originalElementGetElementsByTagName.call(target, localName, lowercase);
+ } else if (target instanceof OriginalDocument) {
+ list = originalDocumentGetElementsByTagName.call(target, localName, lowercase);
+ } else {
+ return findElements(this, index, result, p, localName, lowercase);
+ }
+ return filterNodeList(list, index, result, false);
+ }
+ function getElementsByTagNameNSFiltered(p, index, result, ns, localName) {
+ var target = unsafeUnwrap(this);
+ var list;
+ var root = getTreeScope(this).root;
+ if (root instanceof scope.wrappers.ShadowRoot) {
+ return findElements(this, index, result, p, ns, localName);
+ } else if (target instanceof OriginalElement) {
+ list = originalElementGetElementsByTagNameNS.call(target, ns, localName);
+ } else if (target instanceof OriginalDocument) {
+ list = originalDocumentGetElementsByTagNameNS.call(target, ns, localName);
+ } else {
+ return findElements(this, index, result, p, ns, localName);
+ }
+ return filterNodeList(list, index, result, false);
+ }
+ var GetElementsByInterface = {
+ getElementsByTagName: function(localName) {
+ var result = new HTMLCollection();
+ var match = localName === "*" ? matchesEveryThing : matchesTagName;
+ result.length = getElementsByTagNameFiltered.call(this, match, 0, result, localName, localName.toLowerCase());
+ return result;
+ },
+ getElementsByClassName: function(className) {
+ return this.querySelectorAll("." + className);
+ },
+ getElementsByTagNameNS: function(ns, localName) {
+ var result = new HTMLCollection();
+ var match = null;
+ if (ns === "*") {
+ match = localName === "*" ? matchesEveryThing : matchesLocalNameOnly;
+ } else {
+ match = localName === "*" ? matchesNameSpace : matchesLocalNameNS;
+ }
+ result.length = getElementsByTagNameNSFiltered.call(this, match, 0, result, ns || null, localName);
+ return result;
+ }
+ };
+ scope.GetElementsByInterface = GetElementsByInterface;
+ scope.SelectorsInterface = SelectorsInterface;
+ scope.MatchesInterface = MatchesInterface;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var NodeList = scope.wrappers.NodeList;
+ function forwardElement(node) {
+ while (node && node.nodeType !== Node.ELEMENT_NODE) {
+ node = node.nextSibling;
+ }
+ return node;
+ }
+ function backwardsElement(node) {
+ while (node && node.nodeType !== Node.ELEMENT_NODE) {
+ node = node.previousSibling;
+ }
+ return node;
+ }
+ var ParentNodeInterface = {
+ get firstElementChild() {
+ return forwardElement(this.firstChild);
+ },
+ get lastElementChild() {
+ return backwardsElement(this.lastChild);
+ },
+ get childElementCount() {
+ var count = 0;
+ for (var child = this.firstElementChild; child; child = child.nextElementSibling) {
+ count++;
+ }
+ return count;
+ },
+ get children() {
+ var wrapperList = new NodeList();
+ var i = 0;
+ for (var child = this.firstElementChild; child; child = child.nextElementSibling) {
+ wrapperList[i++] = child;
+ }
+ wrapperList.length = i;
+ return wrapperList;
+ },
+ remove: function() {
+ var p = this.parentNode;
+ if (p) p.removeChild(this);
+ }
+ };
+ var ChildNodeInterface = {
+ get nextElementSibling() {
+ return forwardElement(this.nextSibling);
+ },
+ get previousElementSibling() {
+ return backwardsElement(this.previousSibling);
+ }
+ };
+ var NonElementParentNodeInterface = {
+ getElementById: function(id) {
+ if (/[ \t\n\r\f]/.test(id)) return null;
+ return this.querySelector('[id="' + id + '"]');
+ }
+ };
+ scope.ChildNodeInterface = ChildNodeInterface;
+ scope.NonElementParentNodeInterface = NonElementParentNodeInterface;
+ scope.ParentNodeInterface = ParentNodeInterface;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var ChildNodeInterface = scope.ChildNodeInterface;
+ var Node = scope.wrappers.Node;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var OriginalCharacterData = window.CharacterData;
+ function CharacterData(node) {
+ Node.call(this, node);
+ }
+ CharacterData.prototype = Object.create(Node.prototype);
+ mixin(CharacterData.prototype, {
+ get nodeValue() {
+ return this.data;
+ },
+ set nodeValue(data) {
+ this.data = data;
+ },
+ get textContent() {
+ return this.data;
+ },
+ set textContent(value) {
+ this.data = value;
+ },
+ get data() {
+ return unsafeUnwrap(this).data;
+ },
+ set data(value) {
+ var oldValue = unsafeUnwrap(this).data;
+ enqueueMutation(this, "characterData", {
+ oldValue: oldValue
+ });
+ unsafeUnwrap(this).data = value;
+ }
+ });
+ mixin(CharacterData.prototype, ChildNodeInterface);
+ registerWrapper(OriginalCharacterData, CharacterData, document.createTextNode(""));
+ scope.wrappers.CharacterData = CharacterData;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var CharacterData = scope.wrappers.CharacterData;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ function toUInt32(x) {
+ return x >>> 0;
+ }
+ var OriginalText = window.Text;
+ function Text(node) {
+ CharacterData.call(this, node);
+ }
+ Text.prototype = Object.create(CharacterData.prototype);
+ mixin(Text.prototype, {
+ splitText: function(offset) {
+ offset = toUInt32(offset);
+ var s = this.data;
+ if (offset > s.length) throw new Error("IndexSizeError");
+ var head = s.slice(0, offset);
+ var tail = s.slice(offset);
+ this.data = head;
+ var newTextNode = this.ownerDocument.createTextNode(tail);
+ if (this.parentNode) this.parentNode.insertBefore(newTextNode, this.nextSibling);
+ return newTextNode;
+ }
+ });
+ registerWrapper(OriginalText, Text, document.createTextNode(""));
+ scope.wrappers.Text = Text;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ if (!window.DOMTokenList) {
+ console.warn("Missing DOMTokenList prototype, please include a " + "compatible classList polyfill such as http://goo.gl/uTcepH.");
+ return;
+ }
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var enqueueMutation = scope.enqueueMutation;
+ function getClass(el) {
+ return unsafeUnwrap(el).getAttribute("class");
+ }
+ function enqueueClassAttributeChange(el, oldValue) {
+ enqueueMutation(el, "attributes", {
+ name: "class",
+ namespace: null,
+ oldValue: oldValue
+ });
+ }
+ function invalidateClass(el) {
+ scope.invalidateRendererBasedOnAttribute(el, "class");
+ }
+ function changeClass(tokenList, method, args) {
+ var ownerElement = tokenList.ownerElement_;
+ if (ownerElement == null) {
+ return method.apply(tokenList, args);
+ }
+ var oldValue = getClass(ownerElement);
+ var retv = method.apply(tokenList, args);
+ if (getClass(ownerElement) !== oldValue) {
+ enqueueClassAttributeChange(ownerElement, oldValue);
+ invalidateClass(ownerElement);
+ }
+ return retv;
+ }
+ var oldAdd = DOMTokenList.prototype.add;
+ DOMTokenList.prototype.add = function() {
+ changeClass(this, oldAdd, arguments);
+ };
+ var oldRemove = DOMTokenList.prototype.remove;
+ DOMTokenList.prototype.remove = function() {
+ changeClass(this, oldRemove, arguments);
+ };
+ var oldToggle = DOMTokenList.prototype.toggle;
+ DOMTokenList.prototype.toggle = function() {
+ return changeClass(this, oldToggle, arguments);
+ };
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var ChildNodeInterface = scope.ChildNodeInterface;
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var Node = scope.wrappers.Node;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var MatchesInterface = scope.MatchesInterface;
+ var addWrapNodeListMethod = scope.addWrapNodeListMethod;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var oneOf = scope.oneOf;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrappers = scope.wrappers;
+ var OriginalElement = window.Element;
+ var matchesNames = [ "matches", "mozMatchesSelector", "msMatchesSelector", "webkitMatchesSelector" ].filter(function(name) {
+ return OriginalElement.prototype[name];
+ });
+ var matchesName = matchesNames[0];
+ var originalMatches = OriginalElement.prototype[matchesName];
+ function invalidateRendererBasedOnAttribute(element, name) {
+ var p = element.parentNode;
+ if (!p || !p.shadowRoot) return;
+ var renderer = scope.getRendererForHost(p);
+ if (renderer.dependsOnAttribute(name)) renderer.invalidate();
+ }
+ function enqueAttributeChange(element, name, oldValue) {
+ enqueueMutation(element, "attributes", {
+ name: name,
+ namespace: null,
+ oldValue: oldValue
+ });
+ }
+ var classListTable = new WeakMap();
+ function Element(node) {
+ Node.call(this, node);
+ }
+ Element.prototype = Object.create(Node.prototype);
+ mixin(Element.prototype, {
+ createShadowRoot: function() {
+ var newShadowRoot = new wrappers.ShadowRoot(this);
+ unsafeUnwrap(this).polymerShadowRoot_ = newShadowRoot;
+ var renderer = scope.getRendererForHost(this);
+ renderer.invalidate();
+ return newShadowRoot;
+ },
+ get shadowRoot() {
+ return unsafeUnwrap(this).polymerShadowRoot_ || null;
+ },
+ setAttribute: function(name, value) {
+ var oldValue = unsafeUnwrap(this).getAttribute(name);
+ unsafeUnwrap(this).setAttribute(name, value);
+ enqueAttributeChange(this, name, oldValue);
+ invalidateRendererBasedOnAttribute(this, name);
+ },
+ removeAttribute: function(name) {
+ var oldValue = unsafeUnwrap(this).getAttribute(name);
+ unsafeUnwrap(this).removeAttribute(name);
+ enqueAttributeChange(this, name, oldValue);
+ invalidateRendererBasedOnAttribute(this, name);
+ },
+ get classList() {
+ var list = classListTable.get(this);
+ if (!list) {
+ list = unsafeUnwrap(this).classList;
+ if (!list) return;
+ list.ownerElement_ = this;
+ classListTable.set(this, list);
+ }
+ return list;
+ },
+ get className() {
+ return unsafeUnwrap(this).className;
+ },
+ set className(v) {
+ this.setAttribute("class", v);
+ },
+ get id() {
+ return unsafeUnwrap(this).id;
+ },
+ set id(v) {
+ this.setAttribute("id", v);
+ }
+ });
+ matchesNames.forEach(function(name) {
+ if (name !== "matches") {
+ Element.prototype[name] = function(selector) {
+ return this.matches(selector);
+ };
+ }
+ });
+ if (OriginalElement.prototype.webkitCreateShadowRoot) {
+ Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot;
+ }
+ mixin(Element.prototype, ChildNodeInterface);
+ mixin(Element.prototype, GetElementsByInterface);
+ mixin(Element.prototype, ParentNodeInterface);
+ mixin(Element.prototype, SelectorsInterface);
+ mixin(Element.prototype, MatchesInterface);
+ registerWrapper(OriginalElement, Element, document.createElementNS(null, "x"));
+ scope.invalidateRendererBasedOnAttribute = invalidateRendererBasedOnAttribute;
+ scope.matchesNames = matchesNames;
+ scope.originalMatches = originalMatches;
+ scope.wrappers.Element = Element;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var Element = scope.wrappers.Element;
+ var defineGetter = scope.defineGetter;
+ var enqueueMutation = scope.enqueueMutation;
+ var mixin = scope.mixin;
+ var nodesWereAdded = scope.nodesWereAdded;
+ var nodesWereRemoved = scope.nodesWereRemoved;
+ var registerWrapper = scope.registerWrapper;
+ var snapshotNodeList = scope.snapshotNodeList;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrappers = scope.wrappers;
+ var escapeAttrRegExp = /[&\u00A0"]/g;
+ var escapeDataRegExp = /[&\u00A0<>]/g;
+ function escapeReplace(c) {
+ switch (c) {
+ case "&":
+ return "&amp;";
+
+ case "<":
+ return "&lt;";
+
+ case ">":
+ return "&gt;";
+
+ case '"':
+ return "&quot;";
+
+ case " ":
+ return "&nbsp;";
+ }
+ }
+ function escapeAttr(s) {
+ return s.replace(escapeAttrRegExp, escapeReplace);
+ }
+ function escapeData(s) {
+ return s.replace(escapeDataRegExp, escapeReplace);
+ }
+ function makeSet(arr) {
+ var set = {};
+ for (var i = 0; i < arr.length; i++) {
+ set[arr[i]] = true;
+ }
+ return set;
+ }
+ var voidElements = makeSet([ "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr" ]);
+ var plaintextParents = makeSet([ "style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript" ]);
+ var XHTML_NS = "http://www.w3.org/1999/xhtml";
+ function needsSelfClosingSlash(node) {
+ if (node.namespaceURI !== XHTML_NS) return true;
+ var doctype = node.ownerDocument.doctype;
+ return doctype && doctype.publicId && doctype.systemId;
+ }
+ function getOuterHTML(node, parentNode) {
+ switch (node.nodeType) {
+ case Node.ELEMENT_NODE:
+ var tagName = node.tagName.toLowerCase();
+ var s = "<" + tagName;
+ var attrs = node.attributes;
+ for (var i = 0, attr; attr = attrs[i]; i++) {
+ s += " " + attr.name + '="' + escapeAttr(attr.value) + '"';
+ }
+ if (voidElements[tagName]) {
+ if (needsSelfClosingSlash(node)) s += "/";
+ return s + ">";
+ }
+ return s + ">" + getInnerHTML(node) + "</" + tagName + ">";
+
+ case Node.TEXT_NODE:
+ var data = node.data;
+ if (parentNode && plaintextParents[parentNode.localName]) return data;
+ return escapeData(data);
+
+ case Node.COMMENT_NODE:
+ return "<!--" + node.data + "-->";
+
+ default:
+ console.error(node);
+ throw new Error("not implemented");
+ }
+ }
+ function getInnerHTML(node) {
+ if (node instanceof wrappers.HTMLTemplateElement) node = node.content;
+ var s = "";
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ s += getOuterHTML(child, node);
+ }
+ return s;
+ }
+ function setInnerHTML(node, value, opt_tagName) {
+ var tagName = opt_tagName || "div";
+ node.textContent = "";
+ var tempElement = unwrap(node.ownerDocument.createElement(tagName));
+ tempElement.innerHTML = value;
+ var firstChild;
+ while (firstChild = tempElement.firstChild) {
+ node.appendChild(wrap(firstChild));
+ }
+ }
+ var oldIe = /MSIE/.test(navigator.userAgent);
+ var OriginalHTMLElement = window.HTMLElement;
+ var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
+ function HTMLElement(node) {
+ Element.call(this, node);
+ }
+ HTMLElement.prototype = Object.create(Element.prototype);
+ mixin(HTMLElement.prototype, {
+ get innerHTML() {
+ return getInnerHTML(this);
+ },
+ set innerHTML(value) {
+ if (oldIe && plaintextParents[this.localName]) {
+ this.textContent = value;
+ return;
+ }
+ var removedNodes = snapshotNodeList(this.childNodes);
+ if (this.invalidateShadowRenderer()) {
+ if (this instanceof wrappers.HTMLTemplateElement) setInnerHTML(this.content, value); else setInnerHTML(this, value, this.tagName);
+ } else if (!OriginalHTMLTemplateElement && this instanceof wrappers.HTMLTemplateElement) {
+ setInnerHTML(this.content, value);
+ } else {
+ unsafeUnwrap(this).innerHTML = value;
+ }
+ var addedNodes = snapshotNodeList(this.childNodes);
+ enqueueMutation(this, "childList", {
+ addedNodes: addedNodes,
+ removedNodes: removedNodes
+ });
+ nodesWereRemoved(removedNodes);
+ nodesWereAdded(addedNodes, this);
+ },
+ get outerHTML() {
+ return getOuterHTML(this, this.parentNode);
+ },
+ set outerHTML(value) {
+ var p = this.parentNode;
+ if (p) {
+ p.invalidateShadowRenderer();
+ var df = frag(p, value);
+ p.replaceChild(df, this);
+ }
+ },
+ insertAdjacentHTML: function(position, text) {
+ var contextElement, refNode;
+ switch (String(position).toLowerCase()) {
+ case "beforebegin":
+ contextElement = this.parentNode;
+ refNode = this;
+ break;
+
+ case "afterend":
+ contextElement = this.parentNode;
+ refNode = this.nextSibling;
+ break;
+
+ case "afterbegin":
+ contextElement = this;
+ refNode = this.firstChild;
+ break;
+
+ case "beforeend":
+ contextElement = this;
+ refNode = null;
+ break;
+
+ default:
+ return;
+ }
+ var df = frag(contextElement, text);
+ contextElement.insertBefore(df, refNode);
+ },
+ get hidden() {
+ return this.hasAttribute("hidden");
+ },
+ set hidden(v) {
+ if (v) {
+ this.setAttribute("hidden", "");
+ } else {
+ this.removeAttribute("hidden");
+ }
+ }
+ });
+ function frag(contextElement, html) {
+ var p = unwrap(contextElement.cloneNode(false));
+ p.innerHTML = html;
+ var df = unwrap(document.createDocumentFragment());
+ var c;
+ while (c = p.firstChild) {
+ df.appendChild(c);
+ }
+ return wrap(df);
+ }
+ function getter(name) {
+ return function() {
+ scope.renderAllPending();
+ return unsafeUnwrap(this)[name];
+ };
+ }
+ function getterRequiresRendering(name) {
+ defineGetter(HTMLElement, name, getter(name));
+ }
+ [ "clientHeight", "clientLeft", "clientTop", "clientWidth", "offsetHeight", "offsetLeft", "offsetTop", "offsetWidth", "scrollHeight", "scrollWidth" ].forEach(getterRequiresRendering);
+ function getterAndSetterRequiresRendering(name) {
+ Object.defineProperty(HTMLElement.prototype, name, {
+ get: getter(name),
+ set: function(v) {
+ scope.renderAllPending();
+ unsafeUnwrap(this)[name] = v;
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+ [ "scrollLeft", "scrollTop" ].forEach(getterAndSetterRequiresRendering);
+ function methodRequiresRendering(name) {
+ Object.defineProperty(HTMLElement.prototype, name, {
+ value: function() {
+ scope.renderAllPending();
+ return unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments);
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+ [ "focus", "getBoundingClientRect", "getClientRects", "scrollIntoView" ].forEach(methodRequiresRendering);
+ registerWrapper(OriginalHTMLElement, HTMLElement, document.createElement("b"));
+ scope.wrappers.HTMLElement = HTMLElement;
+ scope.getInnerHTML = getInnerHTML;
+ scope.setInnerHTML = setInnerHTML;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLCanvasElement = window.HTMLCanvasElement;
+ function HTMLCanvasElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLCanvasElement.prototype, {
+ getContext: function() {
+ var context = unsafeUnwrap(this).getContext.apply(unsafeUnwrap(this), arguments);
+ return context && wrap(context);
+ }
+ });
+ registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement, document.createElement("canvas"));
+ scope.wrappers.HTMLCanvasElement = HTMLCanvasElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLContentElement = window.HTMLContentElement;
+ function HTMLContentElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLContentElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLContentElement.prototype, {
+ constructor: HTMLContentElement,
+ get select() {
+ return this.getAttribute("select");
+ },
+ set select(value) {
+ this.setAttribute("select", value);
+ },
+ setAttribute: function(n, v) {
+ HTMLElement.prototype.setAttribute.call(this, n, v);
+ if (String(n).toLowerCase() === "select") this.invalidateShadowRenderer(true);
+ }
+ });
+ if (OriginalHTMLContentElement) registerWrapper(OriginalHTMLContentElement, HTMLContentElement);
+ scope.wrappers.HTMLContentElement = HTMLContentElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var unwrap = scope.unwrap;
+ var OriginalHTMLFormElement = window.HTMLFormElement;
+ function HTMLFormElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLFormElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLFormElement.prototype, {
+ get elements() {
+ return wrapHTMLCollection(unwrap(this).elements);
+ }
+ });
+ registerWrapper(OriginalHTMLFormElement, HTMLFormElement, document.createElement("form"));
+ scope.wrappers.HTMLFormElement = HTMLFormElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var rewrap = scope.rewrap;
+ var OriginalHTMLImageElement = window.HTMLImageElement;
+ function HTMLImageElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLImageElement.prototype = Object.create(HTMLElement.prototype);
+ registerWrapper(OriginalHTMLImageElement, HTMLImageElement, document.createElement("img"));
+ function Image(width, height) {
+ if (!(this instanceof Image)) {
+ throw new TypeError("DOM object constructor cannot be called as a function.");
+ }
+ var node = unwrap(document.createElement("img"));
+ HTMLElement.call(this, node);
+ rewrap(node, this);
+ if (width !== undefined) node.width = width;
+ if (height !== undefined) node.height = height;
+ }
+ Image.prototype = HTMLImageElement.prototype;
+ scope.wrappers.HTMLImageElement = HTMLImageElement;
+ scope.wrappers.Image = Image;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var NodeList = scope.wrappers.NodeList;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLShadowElement = window.HTMLShadowElement;
+ function HTMLShadowElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLShadowElement.prototype = Object.create(HTMLElement.prototype);
+ HTMLShadowElement.prototype.constructor = HTMLShadowElement;
+ if (OriginalHTMLShadowElement) registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement);
+ scope.wrappers.HTMLShadowElement = HTMLShadowElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var contentTable = new WeakMap();
+ var templateContentsOwnerTable = new WeakMap();
+ function getTemplateContentsOwner(doc) {
+ if (!doc.defaultView) return doc;
+ var d = templateContentsOwnerTable.get(doc);
+ if (!d) {
+ d = doc.implementation.createHTMLDocument("");
+ while (d.lastChild) {
+ d.removeChild(d.lastChild);
+ }
+ templateContentsOwnerTable.set(doc, d);
+ }
+ return d;
+ }
+ function extractContent(templateElement) {
+ var doc = getTemplateContentsOwner(templateElement.ownerDocument);
+ var df = unwrap(doc.createDocumentFragment());
+ var child;
+ while (child = templateElement.firstChild) {
+ df.appendChild(child);
+ }
+ return df;
+ }
+ var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
+ function HTMLTemplateElement(node) {
+ HTMLElement.call(this, node);
+ if (!OriginalHTMLTemplateElement) {
+ var content = extractContent(node);
+ contentTable.set(this, wrap(content));
+ }
+ }
+ HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTemplateElement.prototype, {
+ constructor: HTMLTemplateElement,
+ get content() {
+ if (OriginalHTMLTemplateElement) return wrap(unsafeUnwrap(this).content);
+ return contentTable.get(this);
+ }
+ });
+ if (OriginalHTMLTemplateElement) registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement);
+ scope.wrappers.HTMLTemplateElement = HTMLTemplateElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLMediaElement = window.HTMLMediaElement;
+ if (!OriginalHTMLMediaElement) return;
+ function HTMLMediaElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLMediaElement.prototype = Object.create(HTMLElement.prototype);
+ registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement, document.createElement("audio"));
+ scope.wrappers.HTMLMediaElement = HTMLMediaElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLMediaElement = scope.wrappers.HTMLMediaElement;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var rewrap = scope.rewrap;
+ var OriginalHTMLAudioElement = window.HTMLAudioElement;
+ if (!OriginalHTMLAudioElement) return;
+ function HTMLAudioElement(node) {
+ HTMLMediaElement.call(this, node);
+ }
+ HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype);
+ registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement, document.createElement("audio"));
+ function Audio(src) {
+ if (!(this instanceof Audio)) {
+ throw new TypeError("DOM object constructor cannot be called as a function.");
+ }
+ var node = unwrap(document.createElement("audio"));
+ HTMLMediaElement.call(this, node);
+ rewrap(node, this);
+ node.setAttribute("preload", "auto");
+ if (src !== undefined) node.setAttribute("src", src);
+ }
+ Audio.prototype = HTMLAudioElement.prototype;
+ scope.wrappers.HTMLAudioElement = HTMLAudioElement;
+ scope.wrappers.Audio = Audio;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var rewrap = scope.rewrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLOptionElement = window.HTMLOptionElement;
+ function trimText(s) {
+ return s.replace(/\s+/g, " ").trim();
+ }
+ function HTMLOptionElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLOptionElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLOptionElement.prototype, {
+ get text() {
+ return trimText(this.textContent);
+ },
+ set text(value) {
+ this.textContent = trimText(String(value));
+ },
+ get form() {
+ return wrap(unwrap(this).form);
+ }
+ });
+ registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement, document.createElement("option"));
+ function Option(text, value, defaultSelected, selected) {
+ if (!(this instanceof Option)) {
+ throw new TypeError("DOM object constructor cannot be called as a function.");
+ }
+ var node = unwrap(document.createElement("option"));
+ HTMLElement.call(this, node);
+ rewrap(node, this);
+ if (text !== undefined) node.text = text;
+ if (value !== undefined) node.setAttribute("value", value);
+ if (defaultSelected === true) node.setAttribute("selected", "");
+ node.selected = selected === true;
+ }
+ Option.prototype = HTMLOptionElement.prototype;
+ scope.wrappers.HTMLOptionElement = HTMLOptionElement;
+ scope.wrappers.Option = Option;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLSelectElement = window.HTMLSelectElement;
+ function HTMLSelectElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLSelectElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLSelectElement.prototype, {
+ add: function(element, before) {
+ if (typeof before === "object") before = unwrap(before);
+ unwrap(this).add(unwrap(element), before);
+ },
+ remove: function(indexOrNode) {
+ if (indexOrNode === undefined) {
+ HTMLElement.prototype.remove.call(this);
+ return;
+ }
+ if (typeof indexOrNode === "object") indexOrNode = unwrap(indexOrNode);
+ unwrap(this).remove(indexOrNode);
+ },
+ get form() {
+ return wrap(unwrap(this).form);
+ }
+ });
+ registerWrapper(OriginalHTMLSelectElement, HTMLSelectElement, document.createElement("select"));
+ scope.wrappers.HTMLSelectElement = HTMLSelectElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var OriginalHTMLTableElement = window.HTMLTableElement;
+ function HTMLTableElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLTableElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTableElement.prototype, {
+ get caption() {
+ return wrap(unwrap(this).caption);
+ },
+ createCaption: function() {
+ return wrap(unwrap(this).createCaption());
+ },
+ get tHead() {
+ return wrap(unwrap(this).tHead);
+ },
+ createTHead: function() {
+ return wrap(unwrap(this).createTHead());
+ },
+ createTFoot: function() {
+ return wrap(unwrap(this).createTFoot());
+ },
+ get tFoot() {
+ return wrap(unwrap(this).tFoot);
+ },
+ get tBodies() {
+ return wrapHTMLCollection(unwrap(this).tBodies);
+ },
+ createTBody: function() {
+ return wrap(unwrap(this).createTBody());
+ },
+ get rows() {
+ return wrapHTMLCollection(unwrap(this).rows);
+ },
+ insertRow: function(index) {
+ return wrap(unwrap(this).insertRow(index));
+ }
+ });
+ registerWrapper(OriginalHTMLTableElement, HTMLTableElement, document.createElement("table"));
+ scope.wrappers.HTMLTableElement = HTMLTableElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLTableSectionElement = window.HTMLTableSectionElement;
+ function HTMLTableSectionElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLTableSectionElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTableSectionElement.prototype, {
+ constructor: HTMLTableSectionElement,
+ get rows() {
+ return wrapHTMLCollection(unwrap(this).rows);
+ },
+ insertRow: function(index) {
+ return wrap(unwrap(this).insertRow(index));
+ }
+ });
+ registerWrapper(OriginalHTMLTableSectionElement, HTMLTableSectionElement, document.createElement("thead"));
+ scope.wrappers.HTMLTableSectionElement = HTMLTableSectionElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var wrapHTMLCollection = scope.wrapHTMLCollection;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalHTMLTableRowElement = window.HTMLTableRowElement;
+ function HTMLTableRowElement(node) {
+ HTMLElement.call(this, node);
+ }
+ HTMLTableRowElement.prototype = Object.create(HTMLElement.prototype);
+ mixin(HTMLTableRowElement.prototype, {
+ get cells() {
+ return wrapHTMLCollection(unwrap(this).cells);
+ },
+ insertCell: function(index) {
+ return wrap(unwrap(this).insertCell(index));
+ }
+ });
+ registerWrapper(OriginalHTMLTableRowElement, HTMLTableRowElement, document.createElement("tr"));
+ scope.wrappers.HTMLTableRowElement = HTMLTableRowElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLContentElement = scope.wrappers.HTMLContentElement;
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+ var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalHTMLUnknownElement = window.HTMLUnknownElement;
+ function HTMLUnknownElement(node) {
+ switch (node.localName) {
+ case "content":
+ return new HTMLContentElement(node);
+
+ case "shadow":
+ return new HTMLShadowElement(node);
+
+ case "template":
+ return new HTMLTemplateElement(node);
+ }
+ HTMLElement.call(this, node);
+ }
+ HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype);
+ registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement);
+ scope.wrappers.HTMLUnknownElement = HTMLUnknownElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var Element = scope.wrappers.Element;
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var registerWrapper = scope.registerWrapper;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var mixin = scope.mixin;
+ var SVG_NS = "http://www.w3.org/2000/svg";
+ var OriginalSVGElement = window.SVGElement;
+ var svgTitleElement = document.createElementNS(SVG_NS, "title");
+ if (!("classList" in svgTitleElement)) {
+ var descr = Object.getOwnPropertyDescriptor(Element.prototype, "classList");
+ Object.defineProperty(HTMLElement.prototype, "classList", descr);
+ delete Element.prototype.classList;
+ }
+ function SVGElement(node) {
+ Element.call(this, node);
+ }
+ SVGElement.prototype = Object.create(Element.prototype);
+ mixin(SVGElement.prototype, {
+ get ownerSVGElement() {
+ return wrap(unsafeUnwrap(this).ownerSVGElement);
+ }
+ });
+ registerWrapper(OriginalSVGElement, SVGElement, document.createElementNS(SVG_NS, "title"));
+ scope.wrappers.SVGElement = SVGElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var OriginalSVGUseElement = window.SVGUseElement;
+ var SVG_NS = "http://www.w3.org/2000/svg";
+ var gWrapper = wrap(document.createElementNS(SVG_NS, "g"));
+ var useElement = document.createElementNS(SVG_NS, "use");
+ var SVGGElement = gWrapper.constructor;
+ var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype);
+ var parentInterface = parentInterfacePrototype.constructor;
+ function SVGUseElement(impl) {
+ parentInterface.call(this, impl);
+ }
+ SVGUseElement.prototype = Object.create(parentInterfacePrototype);
+ if ("instanceRoot" in useElement) {
+ mixin(SVGUseElement.prototype, {
+ get instanceRoot() {
+ return wrap(unwrap(this).instanceRoot);
+ },
+ get animatedInstanceRoot() {
+ return wrap(unwrap(this).animatedInstanceRoot);
+ }
+ });
+ }
+ registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement);
+ scope.wrappers.SVGUseElement = SVGUseElement;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var EventTarget = scope.wrappers.EventTarget;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var wrap = scope.wrap;
+ var OriginalSVGElementInstance = window.SVGElementInstance;
+ if (!OriginalSVGElementInstance) return;
+ function SVGElementInstance(impl) {
+ EventTarget.call(this, impl);
+ }
+ SVGElementInstance.prototype = Object.create(EventTarget.prototype);
+ mixin(SVGElementInstance.prototype, {
+ get correspondingElement() {
+ return wrap(unsafeUnwrap(this).correspondingElement);
+ },
+ get correspondingUseElement() {
+ return wrap(unsafeUnwrap(this).correspondingUseElement);
+ },
+ get parentNode() {
+ return wrap(unsafeUnwrap(this).parentNode);
+ },
+ get childNodes() {
+ throw new Error("Not implemented");
+ },
+ get firstChild() {
+ return wrap(unsafeUnwrap(this).firstChild);
+ },
+ get lastChild() {
+ return wrap(unsafeUnwrap(this).lastChild);
+ },
+ get previousSibling() {
+ return wrap(unsafeUnwrap(this).previousSibling);
+ },
+ get nextSibling() {
+ return wrap(unsafeUnwrap(this).nextSibling);
+ }
+ });
+ registerWrapper(OriginalSVGElementInstance, SVGElementInstance);
+ scope.wrappers.SVGElementInstance = SVGElementInstance;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
+ function CanvasRenderingContext2D(impl) {
+ setWrapper(impl, this);
+ }
+ mixin(CanvasRenderingContext2D.prototype, {
+ get canvas() {
+ return wrap(unsafeUnwrap(this).canvas);
+ },
+ drawImage: function() {
+ arguments[0] = unwrapIfNeeded(arguments[0]);
+ unsafeUnwrap(this).drawImage.apply(unsafeUnwrap(this), arguments);
+ },
+ createPattern: function() {
+ arguments[0] = unwrap(arguments[0]);
+ return unsafeUnwrap(this).createPattern.apply(unsafeUnwrap(this), arguments);
+ }
+ });
+ registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D, document.createElement("canvas").getContext("2d"));
+ scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var addForwardingProperties = scope.addForwardingProperties;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
+ if (!OriginalWebGLRenderingContext) return;
+ function WebGLRenderingContext(impl) {
+ setWrapper(impl, this);
+ }
+ mixin(WebGLRenderingContext.prototype, {
+ get canvas() {
+ return wrap(unsafeUnwrap(this).canvas);
+ },
+ texImage2D: function() {
+ arguments[5] = unwrapIfNeeded(arguments[5]);
+ unsafeUnwrap(this).texImage2D.apply(unsafeUnwrap(this), arguments);
+ },
+ texSubImage2D: function() {
+ arguments[6] = unwrapIfNeeded(arguments[6]);
+ unsafeUnwrap(this).texSubImage2D.apply(unsafeUnwrap(this), arguments);
+ }
+ });
+ var OriginalWebGLRenderingContextBase = Object.getPrototypeOf(OriginalWebGLRenderingContext.prototype);
+ if (OriginalWebGLRenderingContextBase !== Object.prototype) {
+ addForwardingProperties(OriginalWebGLRenderingContextBase, WebGLRenderingContext.prototype);
+ }
+ var instanceProperties = /WebKit/.test(navigator.userAgent) ? {
+ drawingBufferHeight: null,
+ drawingBufferWidth: null
+ } : {};
+ registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext, instanceProperties);
+ scope.wrappers.WebGLRenderingContext = WebGLRenderingContext;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var Node = scope.wrappers.Node;
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var NonElementParentNodeInterface = scope.NonElementParentNodeInterface;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var mixin = scope.mixin;
+ var registerObject = scope.registerObject;
+ var registerWrapper = scope.registerWrapper;
+ var OriginalDocumentFragment = window.DocumentFragment;
+ function DocumentFragment(node) {
+ Node.call(this, node);
+ }
+ DocumentFragment.prototype = Object.create(Node.prototype);
+ mixin(DocumentFragment.prototype, ParentNodeInterface);
+ mixin(DocumentFragment.prototype, SelectorsInterface);
+ mixin(DocumentFragment.prototype, GetElementsByInterface);
+ mixin(DocumentFragment.prototype, NonElementParentNodeInterface);
+ registerWrapper(OriginalDocumentFragment, DocumentFragment, document.createDocumentFragment());
+ scope.wrappers.DocumentFragment = DocumentFragment;
+ var Comment = registerObject(document.createComment(""));
+ scope.wrappers.Comment = Comment;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var DocumentFragment = scope.wrappers.DocumentFragment;
+ var TreeScope = scope.TreeScope;
+ var elementFromPoint = scope.elementFromPoint;
+ var getInnerHTML = scope.getInnerHTML;
+ var getTreeScope = scope.getTreeScope;
+ var mixin = scope.mixin;
+ var rewrap = scope.rewrap;
+ var setInnerHTML = scope.setInnerHTML;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var shadowHostTable = new WeakMap();
+ var nextOlderShadowTreeTable = new WeakMap();
+ function ShadowRoot(hostWrapper) {
+ var node = unwrap(unsafeUnwrap(hostWrapper).ownerDocument.createDocumentFragment());
+ DocumentFragment.call(this, node);
+ rewrap(node, this);
+ var oldShadowRoot = hostWrapper.shadowRoot;
+ nextOlderShadowTreeTable.set(this, oldShadowRoot);
+ this.treeScope_ = new TreeScope(this, getTreeScope(oldShadowRoot || hostWrapper));
+ shadowHostTable.set(this, hostWrapper);
+ }
+ ShadowRoot.prototype = Object.create(DocumentFragment.prototype);
+ mixin(ShadowRoot.prototype, {
+ constructor: ShadowRoot,
+ get innerHTML() {
+ return getInnerHTML(this);
+ },
+ set innerHTML(value) {
+ setInnerHTML(this, value);
+ this.invalidateShadowRenderer();
+ },
+ get olderShadowRoot() {
+ return nextOlderShadowTreeTable.get(this) || null;
+ },
+ get host() {
+ return shadowHostTable.get(this) || null;
+ },
+ invalidateShadowRenderer: function() {
+ return shadowHostTable.get(this).invalidateShadowRenderer();
+ },
+ elementFromPoint: function(x, y) {
+ return elementFromPoint(this, this.ownerDocument, x, y);
+ },
+ getSelection: function() {
+ return document.getSelection();
+ },
+ get activeElement() {
+ var unwrappedActiveElement = unwrap(this).ownerDocument.activeElement;
+ if (!unwrappedActiveElement || !unwrappedActiveElement.nodeType) return null;
+ var activeElement = wrap(unwrappedActiveElement);
+ while (!this.contains(activeElement)) {
+ while (activeElement.parentNode) {
+ activeElement = activeElement.parentNode;
+ }
+ if (activeElement.host) {
+ activeElement = activeElement.host;
+ } else {
+ return null;
+ }
+ }
+ return activeElement;
+ }
+ });
+ scope.wrappers.ShadowRoot = ShadowRoot;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var getTreeScope = scope.getTreeScope;
+ var OriginalRange = window.Range;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ function getHost(node) {
+ var root = getTreeScope(node).root;
+ if (root instanceof ShadowRoot) {
+ return root.host;
+ }
+ return null;
+ }
+ function hostNodeToShadowNode(refNode, offset) {
+ if (refNode.shadowRoot) {
+ offset = Math.min(refNode.childNodes.length - 1, offset);
+ var child = refNode.childNodes[offset];
+ if (child) {
+ var insertionPoint = scope.getDestinationInsertionPoints(child);
+ if (insertionPoint.length > 0) {
+ var parentNode = insertionPoint[0].parentNode;
+ if (parentNode.nodeType == Node.ELEMENT_NODE) {
+ refNode = parentNode;
+ }
+ }
+ }
+ }
+ return refNode;
+ }
+ function shadowNodeToHostNode(node) {
+ node = wrap(node);
+ return getHost(node) || node;
+ }
+ function Range(impl) {
+ setWrapper(impl, this);
+ }
+ Range.prototype = {
+ get startContainer() {
+ return shadowNodeToHostNode(unsafeUnwrap(this).startContainer);
+ },
+ get endContainer() {
+ return shadowNodeToHostNode(unsafeUnwrap(this).endContainer);
+ },
+ get commonAncestorContainer() {
+ return shadowNodeToHostNode(unsafeUnwrap(this).commonAncestorContainer);
+ },
+ setStart: function(refNode, offset) {
+ refNode = hostNodeToShadowNode(refNode, offset);
+ unsafeUnwrap(this).setStart(unwrapIfNeeded(refNode), offset);
+ },
+ setEnd: function(refNode, offset) {
+ refNode = hostNodeToShadowNode(refNode, offset);
+ unsafeUnwrap(this).setEnd(unwrapIfNeeded(refNode), offset);
+ },
+ setStartBefore: function(refNode) {
+ unsafeUnwrap(this).setStartBefore(unwrapIfNeeded(refNode));
+ },
+ setStartAfter: function(refNode) {
+ unsafeUnwrap(this).setStartAfter(unwrapIfNeeded(refNode));
+ },
+ setEndBefore: function(refNode) {
+ unsafeUnwrap(this).setEndBefore(unwrapIfNeeded(refNode));
+ },
+ setEndAfter: function(refNode) {
+ unsafeUnwrap(this).setEndAfter(unwrapIfNeeded(refNode));
+ },
+ selectNode: function(refNode) {
+ unsafeUnwrap(this).selectNode(unwrapIfNeeded(refNode));
+ },
+ selectNodeContents: function(refNode) {
+ unsafeUnwrap(this).selectNodeContents(unwrapIfNeeded(refNode));
+ },
+ compareBoundaryPoints: function(how, sourceRange) {
+ return unsafeUnwrap(this).compareBoundaryPoints(how, unwrap(sourceRange));
+ },
+ extractContents: function() {
+ return wrap(unsafeUnwrap(this).extractContents());
+ },
+ cloneContents: function() {
+ return wrap(unsafeUnwrap(this).cloneContents());
+ },
+ insertNode: function(node) {
+ unsafeUnwrap(this).insertNode(unwrapIfNeeded(node));
+ },
+ surroundContents: function(newParent) {
+ unsafeUnwrap(this).surroundContents(unwrapIfNeeded(newParent));
+ },
+ cloneRange: function() {
+ return wrap(unsafeUnwrap(this).cloneRange());
+ },
+ isPointInRange: function(node, offset) {
+ return unsafeUnwrap(this).isPointInRange(unwrapIfNeeded(node), offset);
+ },
+ comparePoint: function(node, offset) {
+ return unsafeUnwrap(this).comparePoint(unwrapIfNeeded(node), offset);
+ },
+ intersectsNode: function(node) {
+ return unsafeUnwrap(this).intersectsNode(unwrapIfNeeded(node));
+ },
+ toString: function() {
+ return unsafeUnwrap(this).toString();
+ }
+ };
+ if (OriginalRange.prototype.createContextualFragment) {
+ Range.prototype.createContextualFragment = function(html) {
+ return wrap(unsafeUnwrap(this).createContextualFragment(html));
+ };
+ }
+ registerWrapper(window.Range, Range, document.createRange());
+ scope.wrappers.Range = Range;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var Element = scope.wrappers.Element;
+ var HTMLContentElement = scope.wrappers.HTMLContentElement;
+ var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+ var Node = scope.wrappers.Node;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ var assert = scope.assert;
+ var getTreeScope = scope.getTreeScope;
+ var mixin = scope.mixin;
+ var oneOf = scope.oneOf;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var ArraySplice = scope.ArraySplice;
+ function updateWrapperUpAndSideways(wrapper) {
+ wrapper.previousSibling_ = wrapper.previousSibling;
+ wrapper.nextSibling_ = wrapper.nextSibling;
+ wrapper.parentNode_ = wrapper.parentNode;
+ }
+ function updateWrapperDown(wrapper) {
+ wrapper.firstChild_ = wrapper.firstChild;
+ wrapper.lastChild_ = wrapper.lastChild;
+ }
+ function updateAllChildNodes(parentNodeWrapper) {
+ assert(parentNodeWrapper instanceof Node);
+ for (var childWrapper = parentNodeWrapper.firstChild; childWrapper; childWrapper = childWrapper.nextSibling) {
+ updateWrapperUpAndSideways(childWrapper);
+ }
+ updateWrapperDown(parentNodeWrapper);
+ }
+ function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
+ var parentNode = unwrap(parentNodeWrapper);
+ var newChild = unwrap(newChildWrapper);
+ var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
+ remove(newChildWrapper);
+ updateWrapperUpAndSideways(newChildWrapper);
+ if (!refChildWrapper) {
+ parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
+ if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
+ var lastChildWrapper = wrap(parentNode.lastChild);
+ if (lastChildWrapper) lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
+ } else {
+ if (parentNodeWrapper.firstChild === refChildWrapper) parentNodeWrapper.firstChild_ = refChildWrapper;
+ refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
+ }
+ scope.originalInsertBefore.call(parentNode, newChild, refChild);
+ }
+ function remove(nodeWrapper) {
+ var node = unwrap(nodeWrapper);
+ var parentNode = node.parentNode;
+ if (!parentNode) return;
+ var parentNodeWrapper = wrap(parentNode);
+ updateWrapperUpAndSideways(nodeWrapper);
+ if (nodeWrapper.previousSibling) nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
+ if (nodeWrapper.nextSibling) nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
+ if (parentNodeWrapper.lastChild === nodeWrapper) parentNodeWrapper.lastChild_ = nodeWrapper;
+ if (parentNodeWrapper.firstChild === nodeWrapper) parentNodeWrapper.firstChild_ = nodeWrapper;
+ scope.originalRemoveChild.call(parentNode, node);
+ }
+ var distributedNodesTable = new WeakMap();
+ var destinationInsertionPointsTable = new WeakMap();
+ var rendererForHostTable = new WeakMap();
+ function resetDistributedNodes(insertionPoint) {
+ distributedNodesTable.set(insertionPoint, []);
+ }
+ function getDistributedNodes(insertionPoint) {
+ var rv = distributedNodesTable.get(insertionPoint);
+ if (!rv) distributedNodesTable.set(insertionPoint, rv = []);
+ return rv;
+ }
+ function getChildNodesSnapshot(node) {
+ var result = [], i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ result[i++] = child;
+ }
+ return result;
+ }
+ var request = oneOf(window, [ "requestAnimationFrame", "mozRequestAnimationFrame", "webkitRequestAnimationFrame", "setTimeout" ]);
+ var pendingDirtyRenderers = [];
+ var renderTimer;
+ function renderAllPending() {
+ for (var i = 0; i < pendingDirtyRenderers.length; i++) {
+ var renderer = pendingDirtyRenderers[i];
+ var parentRenderer = renderer.parentRenderer;
+ if (parentRenderer && parentRenderer.dirty) continue;
+ renderer.render();
+ }
+ pendingDirtyRenderers = [];
+ }
+ function handleRequestAnimationFrame() {
+ renderTimer = null;
+ renderAllPending();
+ }
+ function getRendererForHost(host) {
+ var renderer = rendererForHostTable.get(host);
+ if (!renderer) {
+ renderer = new ShadowRenderer(host);
+ rendererForHostTable.set(host, renderer);
+ }
+ return renderer;
+ }
+ function getShadowRootAncestor(node) {
+ var root = getTreeScope(node).root;
+ if (root instanceof ShadowRoot) return root;
+ return null;
+ }
+ function getRendererForShadowRoot(shadowRoot) {
+ return getRendererForHost(shadowRoot.host);
+ }
+ var spliceDiff = new ArraySplice();
+ spliceDiff.equals = function(renderNode, rawNode) {
+ return unwrap(renderNode.node) === rawNode;
+ };
+ function RenderNode(node) {
+ this.skip = false;
+ this.node = node;
+ this.childNodes = [];
+ }
+ RenderNode.prototype = {
+ append: function(node) {
+ var rv = new RenderNode(node);
+ this.childNodes.push(rv);
+ return rv;
+ },
+ sync: function(opt_added) {
+ if (this.skip) return;
+ var nodeWrapper = this.node;
+ var newChildren = this.childNodes;
+ var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
+ var added = opt_added || new WeakMap();
+ var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
+ var newIndex = 0, oldIndex = 0;
+ var lastIndex = 0;
+ for (var i = 0; i < splices.length; i++) {
+ var splice = splices[i];
+ for (;lastIndex < splice.index; lastIndex++) {
+ oldIndex++;
+ newChildren[newIndex++].sync(added);
+ }
+ var removedCount = splice.removed.length;
+ for (var j = 0; j < removedCount; j++) {
+ var wrapper = wrap(oldChildren[oldIndex++]);
+ if (!added.get(wrapper)) remove(wrapper);
+ }
+ var addedCount = splice.addedCount;
+ var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
+ for (var j = 0; j < addedCount; j++) {
+ var newChildRenderNode = newChildren[newIndex++];
+ var newChildWrapper = newChildRenderNode.node;
+ insertBefore(nodeWrapper, newChildWrapper, refNode);
+ added.set(newChildWrapper, true);
+ newChildRenderNode.sync(added);
+ }
+ lastIndex += addedCount;
+ }
+ for (var i = lastIndex; i < newChildren.length; i++) {
+ newChildren[i].sync(added);
+ }
+ }
+ };
+ function ShadowRenderer(host) {
+ this.host = host;
+ this.dirty = false;
+ this.invalidateAttributes();
+ this.associateNode(host);
+ }
+ ShadowRenderer.prototype = {
+ render: function(opt_renderNode) {
+ if (!this.dirty) return;
+ this.invalidateAttributes();
+ var host = this.host;
+ this.distribution(host);
+ var renderNode = opt_renderNode || new RenderNode(host);
+ this.buildRenderTree(renderNode, host);
+ var topMostRenderer = !opt_renderNode;
+ if (topMostRenderer) renderNode.sync();
+ this.dirty = false;
+ },
+ get parentRenderer() {
+ return getTreeScope(this.host).renderer;
+ },
+ invalidate: function() {
+ if (!this.dirty) {
+ this.dirty = true;
+ var parentRenderer = this.parentRenderer;
+ if (parentRenderer) parentRenderer.invalidate();
+ pendingDirtyRenderers.push(this);
+ if (renderTimer) return;
+ renderTimer = window[request](handleRequestAnimationFrame, 0);
+ }
+ },
+ distribution: function(root) {
+ this.resetAllSubtrees(root);
+ this.distributionResolution(root);
+ },
+ resetAll: function(node) {
+ if (isInsertionPoint(node)) resetDistributedNodes(node); else resetDestinationInsertionPoints(node);
+ this.resetAllSubtrees(node);
+ },
+ resetAllSubtrees: function(node) {
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.resetAll(child);
+ }
+ if (node.shadowRoot) this.resetAll(node.shadowRoot);
+ if (node.olderShadowRoot) this.resetAll(node.olderShadowRoot);
+ },
+ distributionResolution: function(node) {
+ if (isShadowHost(node)) {
+ var shadowHost = node;
+ var pool = poolPopulation(shadowHost);
+ var shadowTrees = getShadowTrees(shadowHost);
+ for (var i = 0; i < shadowTrees.length; i++) {
+ this.poolDistribution(shadowTrees[i], pool);
+ }
+ for (var i = shadowTrees.length - 1; i >= 0; i--) {
+ var shadowTree = shadowTrees[i];
+ var shadow = getShadowInsertionPoint(shadowTree);
+ if (shadow) {
+ var olderShadowRoot = shadowTree.olderShadowRoot;
+ if (olderShadowRoot) {
+ pool = poolPopulation(olderShadowRoot);
+ }
+ for (var j = 0; j < pool.length; j++) {
+ destributeNodeInto(pool[j], shadow);
+ }
+ }
+ this.distributionResolution(shadowTree);
+ }
+ }
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.distributionResolution(child);
+ }
+ },
+ poolDistribution: function(node, pool) {
+ if (node instanceof HTMLShadowElement) return;
+ if (node instanceof HTMLContentElement) {
+ var content = node;
+ this.updateDependentAttributes(content.getAttribute("select"));
+ var anyDistributed = false;
+ for (var i = 0; i < pool.length; i++) {
+ var node = pool[i];
+ if (!node) continue;
+ if (matches(node, content)) {
+ destributeNodeInto(node, content);
+ pool[i] = undefined;
+ anyDistributed = true;
+ }
+ }
+ if (!anyDistributed) {
+ for (var child = content.firstChild; child; child = child.nextSibling) {
+ destributeNodeInto(child, content);
+ }
+ }
+ return;
+ }
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.poolDistribution(child, pool);
+ }
+ },
+ buildRenderTree: function(renderNode, node) {
+ var children = this.compose(node);
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ var childRenderNode = renderNode.append(child);
+ this.buildRenderTree(childRenderNode, child);
+ }
+ if (isShadowHost(node)) {
+ var renderer = getRendererForHost(node);
+ renderer.dirty = false;
+ }
+ },
+ compose: function(node) {
+ var children = [];
+ var p = node.shadowRoot || node;
+ for (var child = p.firstChild; child; child = child.nextSibling) {
+ if (isInsertionPoint(child)) {
+ this.associateNode(p);
+ var distributedNodes = getDistributedNodes(child);
+ for (var j = 0; j < distributedNodes.length; j++) {
+ var distributedNode = distributedNodes[j];
+ if (isFinalDestination(child, distributedNode)) children.push(distributedNode);
+ }
+ } else {
+ children.push(child);
+ }
+ }
+ return children;
+ },
+ invalidateAttributes: function() {
+ this.attributes = Object.create(null);
+ },
+ updateDependentAttributes: function(selector) {
+ if (!selector) return;
+ var attributes = this.attributes;
+ if (/\.\w+/.test(selector)) attributes["class"] = true;
+ if (/#\w+/.test(selector)) attributes["id"] = true;
+ selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) {
+ attributes[name] = true;
+ });
+ },
+ dependsOnAttribute: function(name) {
+ return this.attributes[name];
+ },
+ associateNode: function(node) {
+ unsafeUnwrap(node).polymerShadowRenderer_ = this;
+ }
+ };
+ function poolPopulation(node) {
+ var pool = [];
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ if (isInsertionPoint(child)) {
+ pool.push.apply(pool, getDistributedNodes(child));
+ } else {
+ pool.push(child);
+ }
+ }
+ return pool;
+ }
+ function getShadowInsertionPoint(node) {
+ if (node instanceof HTMLShadowElement) return node;
+ if (node instanceof HTMLContentElement) return null;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ var res = getShadowInsertionPoint(child);
+ if (res) return res;
+ }
+ return null;
+ }
+ function destributeNodeInto(child, insertionPoint) {
+ getDistributedNodes(insertionPoint).push(child);
+ var points = destinationInsertionPointsTable.get(child);
+ if (!points) destinationInsertionPointsTable.set(child, [ insertionPoint ]); else points.push(insertionPoint);
+ }
+ function getDestinationInsertionPoints(node) {
+ return destinationInsertionPointsTable.get(node);
+ }
+ function resetDestinationInsertionPoints(node) {
+ destinationInsertionPointsTable.set(node, undefined);
+ }
+ var selectorStartCharRe = /^(:not\()?[*.#[a-zA-Z_|]/;
+ function matches(node, contentElement) {
+ var select = contentElement.getAttribute("select");
+ if (!select) return true;
+ select = select.trim();
+ if (!select) return true;
+ if (!(node instanceof Element)) return false;
+ if (!selectorStartCharRe.test(select)) return false;
+ try {
+ return node.matches(select);
+ } catch (ex) {
+ return false;
+ }
+ }
+ function isFinalDestination(insertionPoint, node) {
+ var points = getDestinationInsertionPoints(node);
+ return points && points[points.length - 1] === insertionPoint;
+ }
+ function isInsertionPoint(node) {
+ return node instanceof HTMLContentElement || node instanceof HTMLShadowElement;
+ }
+ function isShadowHost(shadowHost) {
+ return shadowHost.shadowRoot;
+ }
+ function getShadowTrees(host) {
+ var trees = [];
+ for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) {
+ trees.push(tree);
+ }
+ return trees;
+ }
+ function render(host) {
+ new ShadowRenderer(host).render();
+ }
+ Node.prototype.invalidateShadowRenderer = function(force) {
+ var renderer = unsafeUnwrap(this).polymerShadowRenderer_;
+ if (renderer) {
+ renderer.invalidate();
+ return true;
+ }
+ return false;
+ };
+ HTMLContentElement.prototype.getDistributedNodes = HTMLShadowElement.prototype.getDistributedNodes = function() {
+ renderAllPending();
+ return getDistributedNodes(this);
+ };
+ Element.prototype.getDestinationInsertionPoints = function() {
+ renderAllPending();
+ return getDestinationInsertionPoints(this) || [];
+ };
+ HTMLContentElement.prototype.nodeIsInserted_ = HTMLShadowElement.prototype.nodeIsInserted_ = function() {
+ this.invalidateShadowRenderer();
+ var shadowRoot = getShadowRootAncestor(this);
+ var renderer;
+ if (shadowRoot) renderer = getRendererForShadowRoot(shadowRoot);
+ unsafeUnwrap(this).polymerShadowRenderer_ = renderer;
+ if (renderer) renderer.invalidate();
+ };
+ scope.getRendererForHost = getRendererForHost;
+ scope.getShadowTrees = getShadowTrees;
+ scope.renderAllPending = renderAllPending;
+ scope.getDestinationInsertionPoints = getDestinationInsertionPoints;
+ scope.visual = {
+ insertBefore: insertBefore,
+ remove: remove
+ };
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var HTMLElement = scope.wrappers.HTMLElement;
+ var assert = scope.assert;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var elementsWithFormProperty = [ "HTMLButtonElement", "HTMLFieldSetElement", "HTMLInputElement", "HTMLKeygenElement", "HTMLLabelElement", "HTMLLegendElement", "HTMLObjectElement", "HTMLOutputElement", "HTMLTextAreaElement" ];
+ function createWrapperConstructor(name) {
+ if (!window[name]) return;
+ assert(!scope.wrappers[name]);
+ var GeneratedWrapper = function(node) {
+ HTMLElement.call(this, node);
+ };
+ GeneratedWrapper.prototype = Object.create(HTMLElement.prototype);
+ mixin(GeneratedWrapper.prototype, {
+ get form() {
+ return wrap(unwrap(this).form);
+ }
+ });
+ registerWrapper(window[name], GeneratedWrapper, document.createElement(name.slice(4, -7)));
+ scope.wrappers[name] = GeneratedWrapper;
+ }
+ elementsWithFormProperty.forEach(createWrapperConstructor);
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalSelection = window.Selection;
+ function Selection(impl) {
+ setWrapper(impl, this);
+ }
+ Selection.prototype = {
+ get anchorNode() {
+ return wrap(unsafeUnwrap(this).anchorNode);
+ },
+ get focusNode() {
+ return wrap(unsafeUnwrap(this).focusNode);
+ },
+ addRange: function(range) {
+ unsafeUnwrap(this).addRange(unwrapIfNeeded(range));
+ },
+ collapse: function(node, index) {
+ unsafeUnwrap(this).collapse(unwrapIfNeeded(node), index);
+ },
+ containsNode: function(node, allowPartial) {
+ return unsafeUnwrap(this).containsNode(unwrapIfNeeded(node), allowPartial);
+ },
+ getRangeAt: function(index) {
+ return wrap(unsafeUnwrap(this).getRangeAt(index));
+ },
+ removeRange: function(range) {
+ unsafeUnwrap(this).removeRange(unwrap(range));
+ },
+ selectAllChildren: function(node) {
+ unsafeUnwrap(this).selectAllChildren(node instanceof ShadowRoot ? unsafeUnwrap(node.host) : unwrapIfNeeded(node));
+ },
+ toString: function() {
+ return unsafeUnwrap(this).toString();
+ }
+ };
+ if (OriginalSelection.prototype.extend) {
+ Selection.prototype.extend = function(node, offset) {
+ unsafeUnwrap(this).extend(unwrapIfNeeded(node), offset);
+ };
+ }
+ registerWrapper(window.Selection, Selection, window.getSelection());
+ scope.wrappers.Selection = Selection;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalTreeWalker = window.TreeWalker;
+ function TreeWalker(impl) {
+ setWrapper(impl, this);
+ }
+ TreeWalker.prototype = {
+ get root() {
+ return wrap(unsafeUnwrap(this).root);
+ },
+ get currentNode() {
+ return wrap(unsafeUnwrap(this).currentNode);
+ },
+ set currentNode(node) {
+ unsafeUnwrap(this).currentNode = unwrapIfNeeded(node);
+ },
+ get filter() {
+ return unsafeUnwrap(this).filter;
+ },
+ parentNode: function() {
+ return wrap(unsafeUnwrap(this).parentNode());
+ },
+ firstChild: function() {
+ return wrap(unsafeUnwrap(this).firstChild());
+ },
+ lastChild: function() {
+ return wrap(unsafeUnwrap(this).lastChild());
+ },
+ previousSibling: function() {
+ return wrap(unsafeUnwrap(this).previousSibling());
+ },
+ previousNode: function() {
+ return wrap(unsafeUnwrap(this).previousNode());
+ },
+ nextNode: function() {
+ return wrap(unsafeUnwrap(this).nextNode());
+ }
+ };
+ registerWrapper(OriginalTreeWalker, TreeWalker);
+ scope.wrappers.TreeWalker = TreeWalker;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var GetElementsByInterface = scope.GetElementsByInterface;
+ var Node = scope.wrappers.Node;
+ var ParentNodeInterface = scope.ParentNodeInterface;
+ var NonElementParentNodeInterface = scope.NonElementParentNodeInterface;
+ var Selection = scope.wrappers.Selection;
+ var SelectorsInterface = scope.SelectorsInterface;
+ var ShadowRoot = scope.wrappers.ShadowRoot;
+ var TreeScope = scope.TreeScope;
+ var cloneNode = scope.cloneNode;
+ var defineGetter = scope.defineGetter;
+ var defineWrapGetter = scope.defineWrapGetter;
+ var elementFromPoint = scope.elementFromPoint;
+ var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+ var matchesNames = scope.matchesNames;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var renderAllPending = scope.renderAllPending;
+ var rewrap = scope.rewrap;
+ var setWrapper = scope.setWrapper;
+ var unsafeUnwrap = scope.unsafeUnwrap;
+ var unwrap = scope.unwrap;
+ var wrap = scope.wrap;
+ var wrapEventTargetMethods = scope.wrapEventTargetMethods;
+ var wrapNodeList = scope.wrapNodeList;
+ var implementationTable = new WeakMap();
+ function Document(node) {
+ Node.call(this, node);
+ this.treeScope_ = new TreeScope(this, null);
+ }
+ Document.prototype = Object.create(Node.prototype);
+ defineWrapGetter(Document, "documentElement");
+ defineWrapGetter(Document, "body");
+ defineWrapGetter(Document, "head");
+ defineGetter(Document, "activeElement", function() {
+ var unwrappedActiveElement = unwrap(this).activeElement;
+ if (!unwrappedActiveElement || !unwrappedActiveElement.nodeType) return null;
+ var activeElement = wrap(unwrappedActiveElement);
+ while (!this.contains(activeElement)) {
+ while (activeElement.parentNode) {
+ activeElement = activeElement.parentNode;
+ }
+ if (activeElement.host) {
+ activeElement = activeElement.host;
+ } else {
+ return null;
+ }
+ }
+ return activeElement;
+ });
+ function wrapMethod(name) {
+ var original = document[name];
+ Document.prototype[name] = function() {
+ return wrap(original.apply(unsafeUnwrap(this), arguments));
+ };
+ }
+ [ "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEvent", "createEventNS", "createRange", "createTextNode" ].forEach(wrapMethod);
+ var originalAdoptNode = document.adoptNode;
+ function adoptNodeNoRemove(node, doc) {
+ originalAdoptNode.call(unsafeUnwrap(doc), unwrap(node));
+ adoptSubtree(node, doc);
+ }
+ function adoptSubtree(node, doc) {
+ if (node.shadowRoot) doc.adoptNode(node.shadowRoot);
+ if (node instanceof ShadowRoot) adoptOlderShadowRoots(node, doc);
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ adoptSubtree(child, doc);
+ }
+ }
+ function adoptOlderShadowRoots(shadowRoot, doc) {
+ var oldShadowRoot = shadowRoot.olderShadowRoot;
+ if (oldShadowRoot) doc.adoptNode(oldShadowRoot);
+ }
+ var originalGetSelection = document.getSelection;
+ mixin(Document.prototype, {
+ adoptNode: function(node) {
+ if (node.parentNode) node.parentNode.removeChild(node);
+ adoptNodeNoRemove(node, this);
+ return node;
+ },
+ elementFromPoint: function(x, y) {
+ return elementFromPoint(this, this, x, y);
+ },
+ importNode: function(node, deep) {
+ return cloneNode(node, deep, unsafeUnwrap(this));
+ },
+ getSelection: function() {
+ renderAllPending();
+ return new Selection(originalGetSelection.call(unwrap(this)));
+ },
+ getElementsByName: function(name) {
+ return SelectorsInterface.querySelectorAll.call(this, "[name=" + JSON.stringify(String(name)) + "]");
+ }
+ });
+ var originalCreateTreeWalker = document.createTreeWalker;
+ var TreeWalkerWrapper = scope.wrappers.TreeWalker;
+ Document.prototype.createTreeWalker = function(root, whatToShow, filter, expandEntityReferences) {
+ var newFilter = null;
+ if (filter) {
+ if (filter.acceptNode && typeof filter.acceptNode === "function") {
+ newFilter = {
+ acceptNode: function(node) {
+ return filter.acceptNode(wrap(node));
+ }
+ };
+ } else if (typeof filter === "function") {
+ newFilter = function(node) {
+ return filter(wrap(node));
+ };
+ }
+ }
+ return new TreeWalkerWrapper(originalCreateTreeWalker.call(unwrap(this), unwrap(root), whatToShow, newFilter, expandEntityReferences));
+ };
+ if (document.registerElement) {
+ var originalRegisterElement = document.registerElement;
+ Document.prototype.registerElement = function(tagName, object) {
+ var prototype, extendsOption;
+ if (object !== undefined) {
+ prototype = object.prototype;
+ extendsOption = object.extends;
+ }
+ if (!prototype) prototype = Object.create(HTMLElement.prototype);
+ if (scope.nativePrototypeTable.get(prototype)) {
+ throw new Error("NotSupportedError");
+ }
+ var proto = Object.getPrototypeOf(prototype);
+ var nativePrototype;
+ var prototypes = [];
+ while (proto) {
+ nativePrototype = scope.nativePrototypeTable.get(proto);
+ if (nativePrototype) break;
+ prototypes.push(proto);
+ proto = Object.getPrototypeOf(proto);
+ }
+ if (!nativePrototype) {
+ throw new Error("NotSupportedError");
+ }
+ var newPrototype = Object.create(nativePrototype);
+ for (var i = prototypes.length - 1; i >= 0; i--) {
+ newPrototype = Object.create(newPrototype);
+ }
+ [ "createdCallback", "attachedCallback", "detachedCallback", "attributeChangedCallback" ].forEach(function(name) {
+ var f = prototype[name];
+ if (!f) return;
+ newPrototype[name] = function() {
+ if (!(wrap(this) instanceof CustomElementConstructor)) {
+ rewrap(this);
+ }
+ f.apply(wrap(this), arguments);
+ };
+ });
+ var p = {
+ prototype: newPrototype
+ };
+ if (extendsOption) p.extends = extendsOption;
+ function CustomElementConstructor(node) {
+ if (!node) {
+ if (extendsOption) {
+ return document.createElement(extendsOption, tagName);
+ } else {
+ return document.createElement(tagName);
+ }
+ }
+ setWrapper(node, this);
+ }
+ CustomElementConstructor.prototype = prototype;
+ CustomElementConstructor.prototype.constructor = CustomElementConstructor;
+ scope.constructorTable.set(newPrototype, CustomElementConstructor);
+ scope.nativePrototypeTable.set(prototype, newPrototype);
+ var nativeConstructor = originalRegisterElement.call(unwrap(this), tagName, p);
+ return CustomElementConstructor;
+ };
+ forwardMethodsToWrapper([ window.HTMLDocument || window.Document ], [ "registerElement" ]);
+ }
+ forwardMethodsToWrapper([ window.HTMLBodyElement, window.HTMLDocument || window.Document, window.HTMLHeadElement, window.HTMLHtmlElement ], [ "appendChild", "compareDocumentPosition", "contains", "getElementsByClassName", "getElementsByTagName", "getElementsByTagNameNS", "insertBefore", "querySelector", "querySelectorAll", "removeChild", "replaceChild" ]);
+ forwardMethodsToWrapper([ window.HTMLBodyElement, window.HTMLHeadElement, window.HTMLHtmlElement ], matchesNames);
+ forwardMethodsToWrapper([ window.HTMLDocument || window.Document ], [ "adoptNode", "importNode", "contains", "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEvent", "createEventNS", "createRange", "createTextNode", "createTreeWalker", "elementFromPoint", "getElementById", "getElementsByName", "getSelection" ]);
+ mixin(Document.prototype, GetElementsByInterface);
+ mixin(Document.prototype, ParentNodeInterface);
+ mixin(Document.prototype, SelectorsInterface);
+ mixin(Document.prototype, NonElementParentNodeInterface);
+ mixin(Document.prototype, {
+ get implementation() {
+ var implementation = implementationTable.get(this);
+ if (implementation) return implementation;
+ implementation = new DOMImplementation(unwrap(this).implementation);
+ implementationTable.set(this, implementation);
+ return implementation;
+ },
+ get defaultView() {
+ return wrap(unwrap(this).defaultView);
+ }
+ });
+ registerWrapper(window.Document, Document, document.implementation.createHTMLDocument(""));
+ if (window.HTMLDocument) registerWrapper(window.HTMLDocument, Document);
+ wrapEventTargetMethods([ window.HTMLBodyElement, window.HTMLDocument || window.Document, window.HTMLHeadElement ]);
+ function DOMImplementation(impl) {
+ setWrapper(impl, this);
+ }
+ var originalCreateDocument = document.implementation.createDocument;
+ DOMImplementation.prototype.createDocument = function() {
+ arguments[2] = unwrap(arguments[2]);
+ return wrap(originalCreateDocument.apply(unsafeUnwrap(this), arguments));
+ };
+ function wrapImplMethod(constructor, name) {
+ var original = document.implementation[name];
+ constructor.prototype[name] = function() {
+ return wrap(original.apply(unsafeUnwrap(this), arguments));
+ };
+ }
+ function forwardImplMethod(constructor, name) {
+ var original = document.implementation[name];
+ constructor.prototype[name] = function() {
+ return original.apply(unsafeUnwrap(this), arguments);
+ };
+ }
+ wrapImplMethod(DOMImplementation, "createDocumentType");
+ wrapImplMethod(DOMImplementation, "createHTMLDocument");
+ forwardImplMethod(DOMImplementation, "hasFeature");
+ registerWrapper(window.DOMImplementation, DOMImplementation);
+ forwardMethodsToWrapper([ window.DOMImplementation ], [ "createDocument", "createDocumentType", "createHTMLDocument", "hasFeature" ]);
+ scope.adoptNodeNoRemove = adoptNodeNoRemove;
+ scope.wrappers.DOMImplementation = DOMImplementation;
+ scope.wrappers.Document = Document;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var EventTarget = scope.wrappers.EventTarget;
+ var Selection = scope.wrappers.Selection;
+ var mixin = scope.mixin;
+ var registerWrapper = scope.registerWrapper;
+ var renderAllPending = scope.renderAllPending;
+ var unwrap = scope.unwrap;
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var wrap = scope.wrap;
+ var OriginalWindow = window.Window;
+ var originalGetComputedStyle = window.getComputedStyle;
+ var originalGetDefaultComputedStyle = window.getDefaultComputedStyle;
+ var originalGetSelection = window.getSelection;
+ function Window(impl) {
+ EventTarget.call(this, impl);
+ }
+ Window.prototype = Object.create(EventTarget.prototype);
+ OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
+ return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo);
+ };
+ if (originalGetDefaultComputedStyle) {
+ OriginalWindow.prototype.getDefaultComputedStyle = function(el, pseudo) {
+ return wrap(this || window).getDefaultComputedStyle(unwrapIfNeeded(el), pseudo);
+ };
+ }
+ OriginalWindow.prototype.getSelection = function() {
+ return wrap(this || window).getSelection();
+ };
+ delete window.getComputedStyle;
+ delete window.getDefaultComputedStyle;
+ delete window.getSelection;
+ [ "addEventListener", "removeEventListener", "dispatchEvent" ].forEach(function(name) {
+ OriginalWindow.prototype[name] = function() {
+ var w = wrap(this || window);
+ return w[name].apply(w, arguments);
+ };
+ delete window[name];
+ });
+ mixin(Window.prototype, {
+ getComputedStyle: function(el, pseudo) {
+ renderAllPending();
+ return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), pseudo);
+ },
+ getSelection: function() {
+ renderAllPending();
+ return new Selection(originalGetSelection.call(unwrap(this)));
+ },
+ get document() {
+ return wrap(unwrap(this).document);
+ }
+ });
+ if (originalGetDefaultComputedStyle) {
+ Window.prototype.getDefaultComputedStyle = function(el, pseudo) {
+ renderAllPending();
+ return originalGetDefaultComputedStyle.call(unwrap(this), unwrapIfNeeded(el), pseudo);
+ };
+ }
+ registerWrapper(OriginalWindow, Window, window);
+ scope.wrappers.Window = Window;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var unwrap = scope.unwrap;
+ var OriginalDataTransfer = window.DataTransfer || window.Clipboard;
+ var OriginalDataTransferSetDragImage = OriginalDataTransfer.prototype.setDragImage;
+ if (OriginalDataTransferSetDragImage) {
+ OriginalDataTransfer.prototype.setDragImage = function(image, x, y) {
+ OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y);
+ };
+ }
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var registerWrapper = scope.registerWrapper;
+ var setWrapper = scope.setWrapper;
+ var unwrap = scope.unwrap;
+ var OriginalFormData = window.FormData;
+ if (!OriginalFormData) return;
+ function FormData(formElement) {
+ var impl;
+ if (formElement instanceof OriginalFormData) {
+ impl = formElement;
+ } else {
+ impl = new OriginalFormData(formElement && unwrap(formElement));
+ }
+ setWrapper(impl, this);
+ }
+ registerWrapper(OriginalFormData, FormData, new OriginalFormData());
+ scope.wrappers.FormData = FormData;
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var unwrapIfNeeded = scope.unwrapIfNeeded;
+ var originalSend = XMLHttpRequest.prototype.send;
+ XMLHttpRequest.prototype.send = function(obj) {
+ return originalSend.call(this, unwrapIfNeeded(obj));
+ };
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ "use strict";
+ var isWrapperFor = scope.isWrapperFor;
+ var elements = {
+ a: "HTMLAnchorElement",
+ area: "HTMLAreaElement",
+ audio: "HTMLAudioElement",
+ base: "HTMLBaseElement",
+ body: "HTMLBodyElement",
+ br: "HTMLBRElement",
+ button: "HTMLButtonElement",
+ canvas: "HTMLCanvasElement",
+ caption: "HTMLTableCaptionElement",
+ col: "HTMLTableColElement",
+ content: "HTMLContentElement",
+ data: "HTMLDataElement",
+ datalist: "HTMLDataListElement",
+ del: "HTMLModElement",
+ dir: "HTMLDirectoryElement",
+ div: "HTMLDivElement",
+ dl: "HTMLDListElement",
+ embed: "HTMLEmbedElement",
+ fieldset: "HTMLFieldSetElement",
+ font: "HTMLFontElement",
+ form: "HTMLFormElement",
+ frame: "HTMLFrameElement",
+ frameset: "HTMLFrameSetElement",
+ h1: "HTMLHeadingElement",
+ head: "HTMLHeadElement",
+ hr: "HTMLHRElement",
+ html: "HTMLHtmlElement",
+ iframe: "HTMLIFrameElement",
+ img: "HTMLImageElement",
+ input: "HTMLInputElement",
+ keygen: "HTMLKeygenElement",
+ label: "HTMLLabelElement",
+ legend: "HTMLLegendElement",
+ li: "HTMLLIElement",
+ link: "HTMLLinkElement",
+ map: "HTMLMapElement",
+ marquee: "HTMLMarqueeElement",
+ menu: "HTMLMenuElement",
+ menuitem: "HTMLMenuItemElement",
+ meta: "HTMLMetaElement",
+ meter: "HTMLMeterElement",
+ object: "HTMLObjectElement",
+ ol: "HTMLOListElement",
+ optgroup: "HTMLOptGroupElement",
+ option: "HTMLOptionElement",
+ output: "HTMLOutputElement",
+ p: "HTMLParagraphElement",
+ param: "HTMLParamElement",
+ pre: "HTMLPreElement",
+ progress: "HTMLProgressElement",
+ q: "HTMLQuoteElement",
+ script: "HTMLScriptElement",
+ select: "HTMLSelectElement",
+ shadow: "HTMLShadowElement",
+ source: "HTMLSourceElement",
+ span: "HTMLSpanElement",
+ style: "HTMLStyleElement",
+ table: "HTMLTableElement",
+ tbody: "HTMLTableSectionElement",
+ template: "HTMLTemplateElement",
+ textarea: "HTMLTextAreaElement",
+ thead: "HTMLTableSectionElement",
+ time: "HTMLTimeElement",
+ title: "HTMLTitleElement",
+ tr: "HTMLTableRowElement",
+ track: "HTMLTrackElement",
+ ul: "HTMLUListElement",
+ video: "HTMLVideoElement"
+ };
+ function overrideConstructor(tagName) {
+ var nativeConstructorName = elements[tagName];
+ var nativeConstructor = window[nativeConstructorName];
+ if (!nativeConstructor) return;
+ var element = document.createElement(tagName);
+ var wrapperConstructor = element.constructor;
+ window[nativeConstructorName] = wrapperConstructor;
+ }
+ Object.keys(elements).forEach(overrideConstructor);
+ Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) {
+ window[name] = scope.wrappers[name];
+ });
+ })(window.ShadowDOMPolyfill);
+ (function(scope) {
+ var ShadowCSS = {
+ strictStyling: false,
+ registry: {},
+ shimStyling: function(root, name, extendsName) {
+ var scopeStyles = this.prepareRoot(root, name, extendsName);
+ var typeExtension = this.isTypeExtension(extendsName);
+ var scopeSelector = this.makeScopeSelector(name, typeExtension);
+ var cssText = stylesToCssText(scopeStyles, true);
+ cssText = this.scopeCssText(cssText, scopeSelector);
+ if (root) {
+ root.shimmedStyle = cssText;
+ }
+ this.addCssToDocument(cssText, name);
+ },
+ shimStyle: function(style, selector) {
+ return this.shimCssText(style.textContent, selector);
+ },
+ shimCssText: function(cssText, selector) {
+ cssText = this.insertDirectives(cssText);
+ return this.scopeCssText(cssText, selector);
+ },
+ makeScopeSelector: function(name, typeExtension) {
+ if (name) {
+ return typeExtension ? "[is=" + name + "]" : name;
+ }
+ return "";
+ },
+ isTypeExtension: function(extendsName) {
+ return extendsName && extendsName.indexOf("-") < 0;
+ },
+ prepareRoot: function(root, name, extendsName) {
+ var def = this.registerRoot(root, name, extendsName);
+ this.replaceTextInStyles(def.rootStyles, this.insertDirectives);
+ this.removeStyles(root, def.rootStyles);
+ if (this.strictStyling) {
+ this.applyScopeToContent(root, name);
+ }
+ return def.scopeStyles;
+ },
+ removeStyles: function(root, styles) {
+ for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
+ s.parentNode.removeChild(s);
+ }
+ },
+ registerRoot: function(root, name, extendsName) {
+ var def = this.registry[name] = {
+ root: root,
+ name: name,
+ extendsName: extendsName
+ };
+ var styles = this.findStyles(root);
+ def.rootStyles = styles;
+ def.scopeStyles = def.rootStyles;
+ var extendee = this.registry[def.extendsName];
+ if (extendee) {
+ def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles);
+ }
+ return def;
+ },
+ findStyles: function(root) {
+ if (!root) {
+ return [];
+ }
+ var styles = root.querySelectorAll("style");
+ return Array.prototype.filter.call(styles, function(s) {
+ return !s.hasAttribute(NO_SHIM_ATTRIBUTE);
+ });
+ },
+ applyScopeToContent: function(root, name) {
+ if (root) {
+ Array.prototype.forEach.call(root.querySelectorAll("*"), function(node) {
+ node.setAttribute(name, "");
+ });
+ Array.prototype.forEach.call(root.querySelectorAll("template"), function(template) {
+ this.applyScopeToContent(template.content, name);
+ }, this);
+ }
+ },
+ insertDirectives: function(cssText) {
+ cssText = this.insertPolyfillDirectivesInCssText(cssText);
+ return this.insertPolyfillRulesInCssText(cssText);
+ },
+ insertPolyfillDirectivesInCssText: function(cssText) {
+ cssText = cssText.replace(cssCommentNextSelectorRe, function(match, p1) {
+ return p1.slice(0, -2) + "{";
+ });
+ return cssText.replace(cssContentNextSelectorRe, function(match, p1) {
+ return p1 + " {";
+ });
+ },
+ insertPolyfillRulesInCssText: function(cssText) {
+ cssText = cssText.replace(cssCommentRuleRe, function(match, p1) {
+ return p1.slice(0, -1);
+ });
+ return cssText.replace(cssContentRuleRe, function(match, p1, p2, p3) {
+ var rule = match.replace(p1, "").replace(p2, "");
+ return p3 + rule;
+ });
+ },
+ scopeCssText: function(cssText, scopeSelector) {
+ var unscoped = this.extractUnscopedRulesFromCssText(cssText);
+ cssText = this.insertPolyfillHostInCssText(cssText);
+ cssText = this.convertColonHost(cssText);
+ cssText = this.convertColonHostContext(cssText);
+ cssText = this.convertShadowDOMSelectors(cssText);
+ if (scopeSelector) {
+ var self = this, cssText;
+ withCssRules(cssText, function(rules) {
+ cssText = self.scopeRules(rules, scopeSelector);
+ });
+ }
+ cssText = cssText + "\n" + unscoped;
+ return cssText.trim();
+ },
+ extractUnscopedRulesFromCssText: function(cssText) {
+ var r = "", m;
+ while (m = cssCommentUnscopedRuleRe.exec(cssText)) {
+ r += m[1].slice(0, -1) + "\n\n";
+ }
+ while (m = cssContentUnscopedRuleRe.exec(cssText)) {
+ r += m[0].replace(m[2], "").replace(m[1], m[3]) + "\n\n";
+ }
+ return r;
+ },
+ convertColonHost: function(cssText) {
+ return this.convertColonRule(cssText, cssColonHostRe, this.colonHostPartReplacer);
+ },
+ convertColonHostContext: function(cssText) {
+ return this.convertColonRule(cssText, cssColonHostContextRe, this.colonHostContextPartReplacer);
+ },
+ convertColonRule: function(cssText, regExp, partReplacer) {
+ return cssText.replace(regExp, function(m, p1, p2, p3) {
+ p1 = polyfillHostNoCombinator;
+ if (p2) {
+ var parts = p2.split(","), r = [];
+ for (var i = 0, l = parts.length, p; i < l && (p = parts[i]); i++) {
+ p = p.trim();
+ r.push(partReplacer(p1, p, p3));
+ }
+ return r.join(",");
+ } else {
+ return p1 + p3;
+ }
+ });
+ },
+ colonHostContextPartReplacer: function(host, part, suffix) {
+ if (part.match(polyfillHost)) {
+ return this.colonHostPartReplacer(host, part, suffix);
+ } else {
+ return host + part + suffix + ", " + part + " " + host + suffix;
+ }
+ },
+ colonHostPartReplacer: function(host, part, suffix) {
+ return host + part.replace(polyfillHost, "") + suffix;
+ },
+ convertShadowDOMSelectors: function(cssText) {
+ for (var i = 0; i < shadowDOMSelectorsRe.length; i++) {
+ cssText = cssText.replace(shadowDOMSelectorsRe[i], " ");
+ }
+ return cssText;
+ },
+ scopeRules: function(cssRules, scopeSelector) {
+ var cssText = "";
+ if (cssRules) {
+ Array.prototype.forEach.call(cssRules, function(rule) {
+ if (rule.selectorText && (rule.style && rule.style.cssText !== undefined)) {
+ cssText += this.scopeSelector(rule.selectorText, scopeSelector, this.strictStyling) + " {\n\t";
+ cssText += this.propertiesFromRule(rule) + "\n}\n\n";
+ } else if (rule.type === CSSRule.MEDIA_RULE) {
+ cssText += "@media " + rule.media.mediaText + " {\n";
+ cssText += this.scopeRules(rule.cssRules, scopeSelector);
+ cssText += "\n}\n\n";
+ } else {
+ try {
+ if (rule.cssText) {
+ cssText += rule.cssText + "\n\n";
+ }
+ } catch (x) {
+ if (rule.type === CSSRule.KEYFRAMES_RULE && rule.cssRules) {
+ cssText += this.ieSafeCssTextFromKeyFrameRule(rule);
+ }
+ }
+ }
+ }, this);
+ }
+ return cssText;
+ },
+ ieSafeCssTextFromKeyFrameRule: function(rule) {
+ var cssText = "@keyframes " + rule.name + " {";
+ Array.prototype.forEach.call(rule.cssRules, function(rule) {
+ cssText += " " + rule.keyText + " {" + rule.style.cssText + "}";
+ });
+ cssText += " }";
+ return cssText;
+ },
+ scopeSelector: function(selector, scopeSelector, strict) {
+ var r = [], parts = selector.split(",");
+ parts.forEach(function(p) {
+ p = p.trim();
+ if (this.selectorNeedsScoping(p, scopeSelector)) {
+ p = strict && !p.match(polyfillHostNoCombinator) ? this.applyStrictSelectorScope(p, scopeSelector) : this.applySelectorScope(p, scopeSelector);
+ }
+ r.push(p);
+ }, this);
+ return r.join(", ");
+ },
+ selectorNeedsScoping: function(selector, scopeSelector) {
+ if (Array.isArray(scopeSelector)) {
+ return true;
+ }
+ var re = this.makeScopeMatcher(scopeSelector);
+ return !selector.match(re);
+ },
+ makeScopeMatcher: function(scopeSelector) {
+ scopeSelector = scopeSelector.replace(/\[/g, "\\[").replace(/\]/g, "\\]");
+ return new RegExp("^(" + scopeSelector + ")" + selectorReSuffix, "m");
+ },
+ applySelectorScope: function(selector, selectorScope) {
+ return Array.isArray(selectorScope) ? this.applySelectorScopeList(selector, selectorScope) : this.applySimpleSelectorScope(selector, selectorScope);
+ },
+ applySelectorScopeList: function(selector, scopeSelectorList) {
+ var r = [];
+ for (var i = 0, s; s = scopeSelectorList[i]; i++) {
+ r.push(this.applySimpleSelectorScope(selector, s));
+ }
+ return r.join(", ");
+ },
+ applySimpleSelectorScope: function(selector, scopeSelector) {
+ if (selector.match(polyfillHostRe)) {
+ selector = selector.replace(polyfillHostNoCombinator, scopeSelector);
+ return selector.replace(polyfillHostRe, scopeSelector + " ");
+ } else {
+ return scopeSelector + " " + selector;
+ }
+ },
+ applyStrictSelectorScope: function(selector, scopeSelector) {
+ scopeSelector = scopeSelector.replace(/\[is=([^\]]*)\]/g, "$1");
+ var splits = [ " ", ">", "+", "~" ], scoped = selector, attrName = "[" + scopeSelector + "]";
+ splits.forEach(function(sep) {
+ var parts = scoped.split(sep);
+ scoped = parts.map(function(p) {
+ var t = p.trim().replace(polyfillHostRe, "");
+ if (t && splits.indexOf(t) < 0 && t.indexOf(attrName) < 0) {
+ p = t.replace(/([^:]*)(:*)(.*)/, "$1" + attrName + "$2$3");
+ }
+ return p;
+ }).join(sep);
+ });
+ return scoped;
+ },
+ insertPolyfillHostInCssText: function(selector) {
+ return selector.replace(colonHostContextRe, polyfillHostContext).replace(colonHostRe, polyfillHost);
+ },
+ propertiesFromRule: function(rule) {
+ var cssText = rule.style.cssText;
+ if (rule.style.content && !rule.style.content.match(/['"]+|attr/)) {
+ cssText = cssText.replace(/content:[^;]*;/g, "content: '" + rule.style.content + "';");
+ }
+ var style = rule.style;
+ for (var i in style) {
+ if (style[i] === "initial") {
+ cssText += i + ": initial; ";
+ }
+ }
+ return cssText;
+ },
+ replaceTextInStyles: function(styles, action) {
+ if (styles && action) {
+ if (!(styles instanceof Array)) {
+ styles = [ styles ];
+ }
+ Array.prototype.forEach.call(styles, function(s) {
+ s.textContent = action.call(this, s.textContent);
+ }, this);
+ }
+ },
+ addCssToDocument: function(cssText, name) {
+ if (cssText.match("@import")) {
+ addOwnSheet(cssText, name);
+ } else {
+ addCssToDocument(cssText);
+ }
+ }
+ };
+ var selectorRe = /([^{]*)({[\s\S]*?})/gim, cssCommentRe = /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim, cssCommentNextSelectorRe = /\/\*\s*@polyfill ([^*]*\*+([^\/*][^*]*\*+)*\/)([^{]*?){/gim, cssContentNextSelectorRe = /polyfill-next-selector[^}]*content\:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim, cssCommentRuleRe = /\/\*\s@polyfill-rule([^*]*\*+([^\/*][^*]*\*+)*)\//gim, cssContentRuleRe = /(polyfill-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim, cssCommentUnscopedRuleRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^\/*][^*]*\*+)*)\//gim, cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim, cssPseudoRe = /::(x-[^\s{,(]*)/gim, cssPartRe = /::part\(([^)]*)\)/gim, polyfillHost = "-shadowcsshost", polyfillHostContext = "-shadowcsscontext", parenSuffix = ")(?:\\((" + "(?:\\([^)(]*\\)|[^)(]*)+?" + ")\\))?([^,{]*)";
+ var cssColonHostRe = new RegExp("(" + polyfillHost + parenSuffix, "gim"), cssColonHostContextRe = new RegExp("(" + polyfillHostContext + parenSuffix, "gim"), selectorReSuffix = "([>\\s~+[.,{:][\\s\\S]*)?$", colonHostRe = /\:host/gim, colonHostContextRe = /\:host-context/gim, polyfillHostNoCombinator = polyfillHost + "-no-combinator", polyfillHostRe = new RegExp(polyfillHost, "gim"), polyfillHostContextRe = new RegExp(polyfillHostContext, "gim"), shadowDOMSelectorsRe = [ />>>/g, /::shadow/g, /::content/g, /\/deep\//g, /\/shadow\//g, /\/shadow-deep\//g, /\^\^/g, /\^(?!=)/g ];
+ function stylesToCssText(styles, preserveComments) {
+ var cssText = "";
+ Array.prototype.forEach.call(styles, function(s) {
+ cssText += s.textContent + "\n\n";
+ });
+ if (!preserveComments) {
+ cssText = cssText.replace(cssCommentRe, "");
+ }
+ return cssText;
+ }
+ function cssTextToStyle(cssText) {
+ var style = document.createElement("style");
+ style.textContent = cssText;
+ return style;
+ }
+ function cssToRules(cssText) {
+ var style = cssTextToStyle(cssText);
+ document.head.appendChild(style);
+ var rules = [];
+ if (style.sheet) {
+ try {
+ rules = style.sheet.cssRules;
+ } catch (e) {}
+ } else {
+ console.warn("sheet not found", style);
+ }
+ style.parentNode.removeChild(style);
+ return rules;
+ }
+ var frame = document.createElement("iframe");
+ frame.style.display = "none";
+ function initFrame() {
+ frame.initialized = true;
+ document.body.appendChild(frame);
+ var doc = frame.contentDocument;
+ var base = doc.createElement("base");
+ base.href = document.baseURI;
+ doc.head.appendChild(base);
+ }
+ function inFrame(fn) {
+ if (!frame.initialized) {
+ initFrame();
+ }
+ document.body.appendChild(frame);
+ fn(frame.contentDocument);
+ document.body.removeChild(frame);
+ }
+ var isChrome = navigator.userAgent.match("Chrome");
+ function withCssRules(cssText, callback) {
+ if (!callback) {
+ return;
+ }
+ var rules;
+ if (cssText.match("@import") && isChrome) {
+ var style = cssTextToStyle(cssText);
+ inFrame(function(doc) {
+ doc.head.appendChild(style.impl);
+ rules = Array.prototype.slice.call(style.sheet.cssRules, 0);
+ callback(rules);
+ });
+ } else {
+ rules = cssToRules(cssText);
+ callback(rules);
+ }
+ }
+ function rulesToCss(cssRules) {
+ for (var i = 0, css = []; i < cssRules.length; i++) {
+ css.push(cssRules[i].cssText);
+ }
+ return css.join("\n\n");
+ }
+ function addCssToDocument(cssText) {
+ if (cssText) {
+ getSheet().appendChild(document.createTextNode(cssText));
+ }
+ }
+ function addOwnSheet(cssText, name) {
+ var style = cssTextToStyle(cssText);
+ style.setAttribute(name, "");
+ style.setAttribute(SHIMMED_ATTRIBUTE, "");
+ document.head.appendChild(style);
+ }
+ var SHIM_ATTRIBUTE = "shim-shadowdom";
+ var SHIMMED_ATTRIBUTE = "shim-shadowdom-css";
+ var NO_SHIM_ATTRIBUTE = "no-shim";
+ var sheet;
+ function getSheet() {
+ if (!sheet) {
+ sheet = document.createElement("style");
+ sheet.setAttribute(SHIMMED_ATTRIBUTE, "");
+ sheet[SHIMMED_ATTRIBUTE] = true;
+ }
+ return sheet;
+ }
+ if (window.ShadowDOMPolyfill) {
+ addCssToDocument("style { display: none !important; }\n");
+ var doc = ShadowDOMPolyfill.wrap(document);
+ var head = doc.querySelector("head");
+ head.insertBefore(getSheet(), head.childNodes[0]);
+ document.addEventListener("DOMContentLoaded", function() {
+ var urlResolver = scope.urlResolver;
+ if (window.HTMLImports && !HTMLImports.useNative) {
+ var SHIM_SHEET_SELECTOR = "link[rel=stylesheet]" + "[" + SHIM_ATTRIBUTE + "]";
+ var SHIM_STYLE_SELECTOR = "style[" + SHIM_ATTRIBUTE + "]";
+ HTMLImports.importer.documentPreloadSelectors += "," + SHIM_SHEET_SELECTOR;
+ HTMLImports.importer.importsPreloadSelectors += "," + SHIM_SHEET_SELECTOR;
+ HTMLImports.parser.documentSelectors = [ HTMLImports.parser.documentSelectors, SHIM_SHEET_SELECTOR, SHIM_STYLE_SELECTOR ].join(",");
+ var originalParseGeneric = HTMLImports.parser.parseGeneric;
+ HTMLImports.parser.parseGeneric = function(elt) {
+ if (elt[SHIMMED_ATTRIBUTE]) {
+ return;
+ }
+ var style = elt.__importElement || elt;
+ if (!style.hasAttribute(SHIM_ATTRIBUTE)) {
+ originalParseGeneric.call(this, elt);
+ return;
+ }
+ if (elt.__resource) {
+ style = elt.ownerDocument.createElement("style");
+ style.textContent = elt.__resource;
+ }
+ HTMLImports.path.resolveUrlsInStyle(style, elt.href);
+ style.textContent = ShadowCSS.shimStyle(style);
+ style.removeAttribute(SHIM_ATTRIBUTE, "");
+ style.setAttribute(SHIMMED_ATTRIBUTE, "");
+ style[SHIMMED_ATTRIBUTE] = true;
+ if (style.parentNode !== head) {
+ if (elt.parentNode === head) {
+ head.replaceChild(style, elt);
+ } else {
+ this.addElementToDocument(style);
+ }
+ }
+ style.__importParsed = true;
+ this.markParsingComplete(elt);
+ this.parseNext();
+ };
+ var hasResource = HTMLImports.parser.hasResource;
+ HTMLImports.parser.hasResource = function(node) {
+ if (node.localName === "link" && node.rel === "stylesheet" && node.hasAttribute(SHIM_ATTRIBUTE)) {
+ return node.__resource;
+ } else {
+ return hasResource.call(this, node);
+ }
+ };
+ }
+ });
+ }
+ scope.ShadowCSS = ShadowCSS;
+ })(window.WebComponents);
+}
+
+(function(scope) {
+ if (window.ShadowDOMPolyfill) {
+ window.wrap = ShadowDOMPolyfill.wrapIfNeeded;
+ window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded;
+ } else {
+ window.wrap = window.unwrap = function(n) {
+ return n;
+ };
+ }
+})(window.WebComponents);
+
+(function(scope) {
+ "use strict";
+ var hasWorkingUrl = false;
+ if (!scope.forceJURL) {
+ try {
+ var u = new URL("b", "http://a");
+ u.pathname = "c%20d";
+ hasWorkingUrl = u.href === "http://a/c%20d";
+ } catch (e) {}
+ }
+ if (hasWorkingUrl) return;
+ var relative = Object.create(null);
+ relative["ftp"] = 21;
+ relative["file"] = 0;
+ relative["gopher"] = 70;
+ relative["http"] = 80;
+ relative["https"] = 443;
+ relative["ws"] = 80;
+ relative["wss"] = 443;
+ var relativePathDotMapping = Object.create(null);
+ relativePathDotMapping["%2e"] = ".";
+ relativePathDotMapping[".%2e"] = "..";
+ relativePathDotMapping["%2e."] = "..";
+ relativePathDotMapping["%2e%2e"] = "..";
+ function isRelativeScheme(scheme) {
+ return relative[scheme] !== undefined;
+ }
+ function invalid() {
+ clear.call(this);
+ this._isInvalid = true;
+ }
+ function IDNAToASCII(h) {
+ if ("" == h) {
+ invalid.call(this);
+ }
+ return h.toLowerCase();
+ }
+ function percentEscape(c) {
+ var unicode = c.charCodeAt(0);
+ if (unicode > 32 && unicode < 127 && [ 34, 35, 60, 62, 63, 96 ].indexOf(unicode) == -1) {
+ return c;
+ }
+ return encodeURIComponent(c);
+ }
+ function percentEscapeQuery(c) {
+ var unicode = c.charCodeAt(0);
+ if (unicode > 32 && unicode < 127 && [ 34, 35, 60, 62, 96 ].indexOf(unicode) == -1) {
+ return c;
+ }
+ return encodeURIComponent(c);
+ }
+ var EOF = undefined, ALPHA = /[a-zA-Z]/, ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
+ function parse(input, stateOverride, base) {
+ function err(message) {
+ errors.push(message);
+ }
+ var state = stateOverride || "scheme start", cursor = 0, buffer = "", seenAt = false, seenBracket = false, errors = [];
+ loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
+ var c = input[cursor];
+ switch (state) {
+ case "scheme start":
+ if (c && ALPHA.test(c)) {
+ buffer += c.toLowerCase();
+ state = "scheme";
+ } else if (!stateOverride) {
+ buffer = "";
+ state = "no scheme";
+ continue;
+ } else {
+ err("Invalid scheme.");
+ break loop;
+ }
+ break;
+
+ case "scheme":
+ if (c && ALPHANUMERIC.test(c)) {
+ buffer += c.toLowerCase();
+ } else if (":" == c) {
+ this._scheme = buffer;
+ buffer = "";
+ if (stateOverride) {
+ break loop;
+ }
+ if (isRelativeScheme(this._scheme)) {
+ this._isRelative = true;
+ }
+ if ("file" == this._scheme) {
+ state = "relative";
+ } else if (this._isRelative && base && base._scheme == this._scheme) {
+ state = "relative or authority";
+ } else if (this._isRelative) {
+ state = "authority first slash";
+ } else {
+ state = "scheme data";
+ }
+ } else if (!stateOverride) {
+ buffer = "";
+ cursor = 0;
+ state = "no scheme";
+ continue;
+ } else if (EOF == c) {
+ break loop;
+ } else {
+ err("Code point not allowed in scheme: " + c);
+ break loop;
+ }
+ break;
+
+ case "scheme data":
+ if ("?" == c) {
+ this._query = "?";
+ state = "query";
+ } else if ("#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ } else {
+ if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._schemeData += percentEscape(c);
+ }
+ }
+ break;
+
+ case "no scheme":
+ if (!base || !isRelativeScheme(base._scheme)) {
+ err("Missing scheme.");
+ invalid.call(this);
+ } else {
+ state = "relative";
+ continue;
+ }
+ break;
+
+ case "relative or authority":
+ if ("/" == c && "/" == input[cursor + 1]) {
+ state = "authority ignore slashes";
+ } else {
+ err("Expected /, got: " + c);
+ state = "relative";
+ continue;
+ }
+ break;
+
+ case "relative":
+ this._isRelative = true;
+ if ("file" != this._scheme) this._scheme = base._scheme;
+ if (EOF == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = base._query;
+ this._username = base._username;
+ this._password = base._password;
+ break loop;
+ } else if ("/" == c || "\\" == c) {
+ if ("\\" == c) err("\\ is an invalid code point.");
+ state = "relative slash";
+ } else if ("?" == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = "?";
+ this._username = base._username;
+ this._password = base._password;
+ state = "query";
+ } else if ("#" == c) {
+ this._host = base._host;
+ this._port = base._port;
+ this._path = base._path.slice();
+ this._query = base._query;
+ this._fragment = "#";
+ this._username = base._username;
+ this._password = base._password;
+ state = "fragment";
+ } else {
+ var nextC = input[cursor + 1];
+ var nextNextC = input[cursor + 2];
+ if ("file" != this._scheme || !ALPHA.test(c) || nextC != ":" && nextC != "|" || EOF != nextNextC && "/" != nextNextC && "\\" != nextNextC && "?" != nextNextC && "#" != nextNextC) {
+ this._host = base._host;
+ this._port = base._port;
+ this._username = base._username;
+ this._password = base._password;
+ this._path = base._path.slice();
+ this._path.pop();
+ }
+ state = "relative path";
+ continue;
+ }
+ break;
+
+ case "relative slash":
+ if ("/" == c || "\\" == c) {
+ if ("\\" == c) {
+ err("\\ is an invalid code point.");
+ }
+ if ("file" == this._scheme) {
+ state = "file host";
+ } else {
+ state = "authority ignore slashes";
+ }
+ } else {
+ if ("file" != this._scheme) {
+ this._host = base._host;
+ this._port = base._port;
+ this._username = base._username;
+ this._password = base._password;
+ }
+ state = "relative path";
+ continue;
+ }
+ break;
+
+ case "authority first slash":
+ if ("/" == c) {
+ state = "authority second slash";
+ } else {
+ err("Expected '/', got: " + c);
+ state = "authority ignore slashes";
+ continue;
+ }
+ break;
+
+ case "authority second slash":
+ state = "authority ignore slashes";
+ if ("/" != c) {
+ err("Expected '/', got: " + c);
+ continue;
+ }
+ break;
+
+ case "authority ignore slashes":
+ if ("/" != c && "\\" != c) {
+ state = "authority";
+ continue;
+ } else {
+ err("Expected authority, got: " + c);
+ }
+ break;
+
+ case "authority":
+ if ("@" == c) {
+ if (seenAt) {
+ err("@ already seen.");
+ buffer += "%40";
+ }
+ seenAt = true;
+ for (var i = 0; i < buffer.length; i++) {
+ var cp = buffer[i];
+ if ("\t" == cp || "\n" == cp || "\r" == cp) {
+ err("Invalid whitespace in authority.");
+ continue;
+ }
+ if (":" == cp && null === this._password) {
+ this._password = "";
+ continue;
+ }
+ var tempC = percentEscape(cp);
+ null !== this._password ? this._password += tempC : this._username += tempC;
+ }
+ buffer = "";
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ cursor -= buffer.length;
+ buffer = "";
+ state = "host";
+ continue;
+ } else {
+ buffer += c;
+ }
+ break;
+
+ case "file host":
+ if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ":" || buffer[1] == "|")) {
+ state = "relative path";
+ } else if (buffer.length == 0) {
+ state = "relative path start";
+ } else {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "relative path start";
+ }
+ continue;
+ } else if ("\t" == c || "\n" == c || "\r" == c) {
+ err("Invalid whitespace in file host.");
+ } else {
+ buffer += c;
+ }
+ break;
+
+ case "host":
+ case "hostname":
+ if (":" == c && !seenBracket) {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "port";
+ if ("hostname" == stateOverride) {
+ break loop;
+ }
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
+ this._host = IDNAToASCII.call(this, buffer);
+ buffer = "";
+ state = "relative path start";
+ if (stateOverride) {
+ break loop;
+ }
+ continue;
+ } else if ("\t" != c && "\n" != c && "\r" != c) {
+ if ("[" == c) {
+ seenBracket = true;
+ } else if ("]" == c) {
+ seenBracket = false;
+ }
+ buffer += c;
+ } else {
+ err("Invalid code point in host/hostname: " + c);
+ }
+ break;
+
+ case "port":
+ if (/[0-9]/.test(c)) {
+ buffer += c;
+ } else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c || stateOverride) {
+ if ("" != buffer) {
+ var temp = parseInt(buffer, 10);
+ if (temp != relative[this._scheme]) {
+ this._port = temp + "";
+ }
+ buffer = "";
+ }
+ if (stateOverride) {
+ break loop;
+ }
+ state = "relative path start";
+ continue;
+ } else if ("\t" == c || "\n" == c || "\r" == c) {
+ err("Invalid code point in port: " + c);
+ } else {
+ invalid.call(this);
+ }
+ break;
+
+ case "relative path start":
+ if ("\\" == c) err("'\\' not allowed in path.");
+ state = "relative path";
+ if ("/" != c && "\\" != c) {
+ continue;
+ }
+ break;
+
+ case "relative path":
+ if (EOF == c || "/" == c || "\\" == c || !stateOverride && ("?" == c || "#" == c)) {
+ if ("\\" == c) {
+ err("\\ not allowed in relative path.");
+ }
+ var tmp;
+ if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
+ buffer = tmp;
+ }
+ if (".." == buffer) {
+ this._path.pop();
+ if ("/" != c && "\\" != c) {
+ this._path.push("");
+ }
+ } else if ("." == buffer && "/" != c && "\\" != c) {
+ this._path.push("");
+ } else if ("." != buffer) {
+ if ("file" == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == "|") {
+ buffer = buffer[0] + ":";
+ }
+ this._path.push(buffer);
+ }
+ buffer = "";
+ if ("?" == c) {
+ this._query = "?";
+ state = "query";
+ } else if ("#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ }
+ } else if ("\t" != c && "\n" != c && "\r" != c) {
+ buffer += percentEscape(c);
+ }
+ break;
+
+ case "query":
+ if (!stateOverride && "#" == c) {
+ this._fragment = "#";
+ state = "fragment";
+ } else if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._query += percentEscapeQuery(c);
+ }
+ break;
+
+ case "fragment":
+ if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
+ this._fragment += c;
+ }
+ break;
+ }
+ cursor++;
+ }
+ }
+ function clear() {
+ this._scheme = "";
+ this._schemeData = "";
+ this._username = "";
+ this._password = null;
+ this._host = "";
+ this._port = "";
+ this._path = [];
+ this._query = "";
+ this._fragment = "";
+ this._isInvalid = false;
+ this._isRelative = false;
+ }
+ function jURL(url, base) {
+ if (base !== undefined && !(base instanceof jURL)) base = new jURL(String(base));
+ this._url = url;
+ clear.call(this);
+ var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, "");
+ parse.call(this, input, null, base);
+ }
+ jURL.prototype = {
+ toString: function() {
+ return this.href;
+ },
+ get href() {
+ if (this._isInvalid) return this._url;
+ var authority = "";
+ if ("" != this._username || null != this._password) {
+ authority = this._username + (null != this._password ? ":" + this._password : "") + "@";
+ }
+ return this.protocol + (this._isRelative ? "//" + authority + this.host : "") + this.pathname + this._query + this._fragment;
+ },
+ set href(href) {
+ clear.call(this);
+ parse.call(this, href);
+ },
+ get protocol() {
+ return this._scheme + ":";
+ },
+ set protocol(protocol) {
+ if (this._isInvalid) return;
+ parse.call(this, protocol + ":", "scheme start");
+ },
+ get host() {
+ return this._isInvalid ? "" : this._port ? this._host + ":" + this._port : this._host;
+ },
+ set host(host) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, host, "host");
+ },
+ get hostname() {
+ return this._host;
+ },
+ set hostname(hostname) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, hostname, "hostname");
+ },
+ get port() {
+ return this._port;
+ },
+ set port(port) {
+ if (this._isInvalid || !this._isRelative) return;
+ parse.call(this, port, "port");
+ },
+ get pathname() {
+ return this._isInvalid ? "" : this._isRelative ? "/" + this._path.join("/") : this._schemeData;
+ },
+ set pathname(pathname) {
+ if (this._isInvalid || !this._isRelative) return;
+ this._path = [];
+ parse.call(this, pathname, "relative path start");
+ },
+ get search() {
+ return this._isInvalid || !this._query || "?" == this._query ? "" : this._query;
+ },
+ set search(search) {
+ if (this._isInvalid || !this._isRelative) return;
+ this._query = "?";
+ if ("?" == search[0]) search = search.slice(1);
+ parse.call(this, search, "query");
+ },
+ get hash() {
+ return this._isInvalid || !this._fragment || "#" == this._fragment ? "" : this._fragment;
+ },
+ set hash(hash) {
+ if (this._isInvalid) return;
+ this._fragment = "#";
+ if ("#" == hash[0]) hash = hash.slice(1);
+ parse.call(this, hash, "fragment");
+ },
+ get origin() {
+ var host;
+ if (this._isInvalid || !this._scheme) {
+ return "";
+ }
+ switch (this._scheme) {
+ case "data":
+ case "file":
+ case "javascript":
+ case "mailto":
+ return "null";
+ }
+ host = this.host;
+ if (!host) {
+ return "";
+ }
+ return this._scheme + "://" + host;
+ }
+ };
+ var OriginalURL = scope.URL;
+ if (OriginalURL) {
+ jURL.createObjectURL = function(blob) {
+ return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
+ };
+ jURL.revokeObjectURL = function(url) {
+ OriginalURL.revokeObjectURL(url);
+ };
+ }
+ scope.URL = jURL;
+})(self);
+
+(function(global) {
+ if (global.JsMutationObserver) {
+ return;
+ }
+ var registrationsTable = new WeakMap();
+ var setImmediate;
+ if (/Trident|Edge/.test(navigator.userAgent)) {
+ setImmediate = setTimeout;
+ } else if (window.setImmediate) {
+ setImmediate = window.setImmediate;
+ } else {
+ var setImmediateQueue = [];
+ var sentinel = String(Math.random());
+ window.addEventListener("message", function(e) {
+ if (e.data === sentinel) {
+ var queue = setImmediateQueue;
+ setImmediateQueue = [];
+ queue.forEach(function(func) {
+ func();
+ });
+ }
+ });
+ setImmediate = function(func) {
+ setImmediateQueue.push(func);
+ window.postMessage(sentinel, "*");
+ };
+ }
+ var isScheduled = false;
+ var scheduledObservers = [];
+ function scheduleCallback(observer) {
+ scheduledObservers.push(observer);
+ if (!isScheduled) {
+ isScheduled = true;
+ setImmediate(dispatchCallbacks);
+ }
+ }
+ function wrapIfNeeded(node) {
+ return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
+ }
+ function dispatchCallbacks() {
+ isScheduled = false;
+ var observers = scheduledObservers;
+ scheduledObservers = [];
+ observers.sort(function(o1, o2) {
+ return o1.uid_ - o2.uid_;
+ });
+ var anyNonEmpty = false;
+ observers.forEach(function(observer) {
+ var queue = observer.takeRecords();
+ removeTransientObserversFor(observer);
+ if (queue.length) {
+ observer.callback_(queue, observer);
+ anyNonEmpty = true;
+ }
+ });
+ if (anyNonEmpty) dispatchCallbacks();
+ }
+ function removeTransientObserversFor(observer) {
+ observer.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ if (!registrations) return;
+ registrations.forEach(function(registration) {
+ if (registration.observer === observer) registration.removeTransientObservers();
+ });
+ });
+ }
+ function forEachAncestorAndObserverEnqueueRecord(target, callback) {
+ for (var node = target; node; node = node.parentNode) {
+ var registrations = registrationsTable.get(node);
+ if (registrations) {
+ for (var j = 0; j < registrations.length; j++) {
+ var registration = registrations[j];
+ var options = registration.options;
+ if (node !== target && !options.subtree) continue;
+ var record = callback(options);
+ if (record) registration.enqueue(record);
+ }
+ }
+ }
+ }
+ var uidCounter = 0;
+ function JsMutationObserver(callback) {
+ this.callback_ = callback;
+ this.nodes_ = [];
+ this.records_ = [];
+ this.uid_ = ++uidCounter;
+ }
+ JsMutationObserver.prototype = {
+ observe: function(target, options) {
+ target = wrapIfNeeded(target);
+ if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
+ throw new SyntaxError();
+ }
+ var registrations = registrationsTable.get(target);
+ if (!registrations) registrationsTable.set(target, registrations = []);
+ var registration;
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i].observer === this) {
+ registration = registrations[i];
+ registration.removeListeners();
+ registration.options = options;
+ break;
+ }
+ }
+ if (!registration) {
+ registration = new Registration(this, target, options);
+ registrations.push(registration);
+ this.nodes_.push(target);
+ }
+ registration.addListeners();
+ },
+ disconnect: function() {
+ this.nodes_.forEach(function(node) {
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ var registration = registrations[i];
+ if (registration.observer === this) {
+ registration.removeListeners();
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ this.records_ = [];
+ },
+ takeRecords: function() {
+ var copyOfRecords = this.records_;
+ this.records_ = [];
+ return copyOfRecords;
+ }
+ };
+ function MutationRecord(type, target) {
+ this.type = type;
+ this.target = target;
+ this.addedNodes = [];
+ this.removedNodes = [];
+ this.previousSibling = null;
+ this.nextSibling = null;
+ this.attributeName = null;
+ this.attributeNamespace = null;
+ this.oldValue = null;
+ }
+ function copyMutationRecord(original) {
+ var record = new MutationRecord(original.type, original.target);
+ record.addedNodes = original.addedNodes.slice();
+ record.removedNodes = original.removedNodes.slice();
+ record.previousSibling = original.previousSibling;
+ record.nextSibling = original.nextSibling;
+ record.attributeName = original.attributeName;
+ record.attributeNamespace = original.attributeNamespace;
+ record.oldValue = original.oldValue;
+ return record;
+ }
+ var currentRecord, recordWithOldValue;
+ function getRecord(type, target) {
+ return currentRecord = new MutationRecord(type, target);
+ }
+ function getRecordWithOldValue(oldValue) {
+ if (recordWithOldValue) return recordWithOldValue;
+ recordWithOldValue = copyMutationRecord(currentRecord);
+ recordWithOldValue.oldValue = oldValue;
+ return recordWithOldValue;
+ }
+ function clearRecords() {
+ currentRecord = recordWithOldValue = undefined;
+ }
+ function recordRepresentsCurrentMutation(record) {
+ return record === recordWithOldValue || record === currentRecord;
+ }
+ function selectRecord(lastRecord, newRecord) {
+ if (lastRecord === newRecord) return lastRecord;
+ if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
+ return null;
+ }
+ function Registration(observer, target, options) {
+ this.observer = observer;
+ this.target = target;
+ this.options = options;
+ this.transientObservedNodes = [];
+ }
+ Registration.prototype = {
+ enqueue: function(record) {
+ var records = this.observer.records_;
+ var length = records.length;
+ if (records.length > 0) {
+ var lastRecord = records[length - 1];
+ var recordToReplaceLast = selectRecord(lastRecord, record);
+ if (recordToReplaceLast) {
+ records[length - 1] = recordToReplaceLast;
+ return;
+ }
+ } else {
+ scheduleCallback(this.observer);
+ }
+ records[length] = record;
+ },
+ addListeners: function() {
+ this.addListeners_(this.target);
+ },
+ addListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
+ },
+ removeListeners: function() {
+ this.removeListeners_(this.target);
+ },
+ removeListeners_: function(node) {
+ var options = this.options;
+ if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
+ if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
+ if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
+ if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
+ },
+ addTransientObserver: function(node) {
+ if (node === this.target) return;
+ this.addListeners_(node);
+ this.transientObservedNodes.push(node);
+ var registrations = registrationsTable.get(node);
+ if (!registrations) registrationsTable.set(node, registrations = []);
+ registrations.push(this);
+ },
+ removeTransientObservers: function() {
+ var transientObservedNodes = this.transientObservedNodes;
+ this.transientObservedNodes = [];
+ transientObservedNodes.forEach(function(node) {
+ this.removeListeners_(node);
+ var registrations = registrationsTable.get(node);
+ for (var i = 0; i < registrations.length; i++) {
+ if (registrations[i] === this) {
+ registrations.splice(i, 1);
+ break;
+ }
+ }
+ }, this);
+ },
+ handleEvent: function(e) {
+ e.stopImmediatePropagation();
+ switch (e.type) {
+ case "DOMAttrModified":
+ var name = e.attrName;
+ var namespace = e.relatedNode.namespaceURI;
+ var target = e.target;
+ var record = new getRecord("attributes", target);
+ record.attributeName = name;
+ record.attributeNamespace = namespace;
+ var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.attributes) return;
+ if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
+ return;
+ }
+ if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMCharacterDataModified":
+ var target = e.target;
+ var record = getRecord("characterData", target);
+ var oldValue = e.prevValue;
+ forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+ if (!options.characterData) return;
+ if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
+ return record;
+ });
+ break;
+
+ case "DOMNodeRemoved":
+ this.addTransientObserver(e.target);
+
+ case "DOMNodeInserted":
+ var changedNode = e.target;
+ var addedNodes, removedNodes;
+ if (e.type === "DOMNodeInserted") {
+ addedNodes = [ changedNode ];
+ removedNodes = [];
+ } else {
+ addedNodes = [];
+ removedNodes = [ changedNode ];
+ }
+ var previousSibling = changedNode.previousSibling;
+ var nextSibling = changedNode.nextSibling;
+ var record = getRecord("childList", e.target.parentNode);
+ record.addedNodes = addedNodes;
+ record.removedNodes = removedNodes;
+ record.previousSibling = previousSibling;
+ record.nextSibling = nextSibling;
+ forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
+ if (!options.childList) return;
+ return record;
+ });
+ }
+ clearRecords();
+ }
+ };
+ global.JsMutationObserver = JsMutationObserver;
+ if (!global.MutationObserver) {
+ global.MutationObserver = JsMutationObserver;
+ JsMutationObserver._isPolyfilled = true;
+ }
+})(self);
+
+(function(scope) {
+ "use strict";
+ if (!(window.performance && window.performance.now)) {
+ var start = Date.now();
+ window.performance = {
+ now: function() {
+ return Date.now() - start;
+ }
+ };
+ }
+ if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = function() {
+ var nativeRaf = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
+ return nativeRaf ? function(callback) {
+ return nativeRaf(function() {
+ callback(performance.now());
+ });
+ } : function(callback) {
+ return window.setTimeout(callback, 1e3 / 60);
+ };
+ }();
+ }
+ if (!window.cancelAnimationFrame) {
+ window.cancelAnimationFrame = function() {
+ return window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function(id) {
+ clearTimeout(id);
+ };
+ }();
+ }
+ var workingDefaultPrevented = function() {
+ var e = document.createEvent("Event");
+ e.initEvent("foo", true, true);
+ e.preventDefault();
+ return e.defaultPrevented;
+ }();
+ if (!workingDefaultPrevented) {
+ var origPreventDefault = Event.prototype.preventDefault;
+ Event.prototype.preventDefault = function() {
+ if (!this.cancelable) {
+ return;
+ }
+ origPreventDefault.call(this);
+ Object.defineProperty(this, "defaultPrevented", {
+ get: function() {
+ return true;
+ },
+ configurable: true
+ });
+ };
+ }
+ var isIE = /Trident/.test(navigator.userAgent);
+ if (!window.CustomEvent || isIE && typeof window.CustomEvent !== "function") {
+ window.CustomEvent = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("CustomEvent");
+ e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
+ return e;
+ };
+ window.CustomEvent.prototype = window.Event.prototype;
+ }
+ if (!window.Event || isIE && typeof window.Event !== "function") {
+ var origEvent = window.Event;
+ window.Event = function(inType, params) {
+ params = params || {};
+ var e = document.createEvent("Event");
+ e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable));
+ return e;
+ };
+ window.Event.prototype = origEvent.prototype;
+ }
+})(window.WebComponents);
+
+window.HTMLImports = window.HTMLImports || {
+ flags: {}
+};
+
+(function(scope) {
+ var IMPORT_LINK_TYPE = "import";
+ var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement("link"));
+ var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
+ var wrap = function(node) {
+ return hasShadowDOMPolyfill ? window.ShadowDOMPolyfill.wrapIfNeeded(node) : node;
+ };
+ var rootDocument = wrap(document);
+ var currentScriptDescriptor = {
+ get: function() {
+ var script = window.HTMLImports.currentScript || document.currentScript || (document.readyState !== "complete" ? document.scripts[document.scripts.length - 1] : null);
+ return wrap(script);
+ },
+ configurable: true
+ };
+ Object.defineProperty(document, "_currentScript", currentScriptDescriptor);
+ Object.defineProperty(rootDocument, "_currentScript", currentScriptDescriptor);
+ var isIE = /Trident/.test(navigator.userAgent);
+ function whenReady(callback, doc) {
+ doc = doc || rootDocument;
+ whenDocumentReady(function() {
+ watchImportsLoad(callback, doc);
+ }, doc);
+ }
+ var requiredReadyState = isIE ? "complete" : "interactive";
+ var READY_EVENT = "readystatechange";
+ function isDocumentReady(doc) {
+ return doc.readyState === "complete" || doc.readyState === requiredReadyState;
+ }
+ function whenDocumentReady(callback, doc) {
+ if (!isDocumentReady(doc)) {
+ var checkReady = function() {
+ if (doc.readyState === "complete" || doc.readyState === requiredReadyState) {
+ doc.removeEventListener(READY_EVENT, checkReady);
+ whenDocumentReady(callback, doc);
+ }
+ };
+ doc.addEventListener(READY_EVENT, checkReady);
+ } else if (callback) {
+ callback();
+ }
+ }
+ function markTargetLoaded(event) {
+ event.target.__loaded = true;
+ }
+ function watchImportsLoad(callback, doc) {
+ var imports = doc.querySelectorAll("link[rel=import]");
+ var parsedCount = 0, importCount = imports.length, newImports = [], errorImports = [];
+ function checkDone() {
+ if (parsedCount == importCount && callback) {
+ callback({
+ allImports: imports,
+ loadedImports: newImports,
+ errorImports: errorImports
+ });
+ }
+ }
+ function loadedImport(e) {
+ markTargetLoaded(e);
+ newImports.push(this);
+ parsedCount++;
+ checkDone();
+ }
+ function errorLoadingImport(e) {
+ errorImports.push(this);
+ parsedCount++;
+ checkDone();
+ }
+ if (importCount) {
+ for (var i = 0, imp; i < importCount && (imp = imports[i]); i++) {
+ if (isImportLoaded(imp)) {
+ newImports.push(this);
+ parsedCount++;
+ checkDone();
+ } else {
+ imp.addEventListener("load", loadedImport);
+ imp.addEventListener("error", errorLoadingImport);
+ }
+ }
+ } else {
+ checkDone();
+ }
+ }
+ function isImportLoaded(link) {
+ return useNative ? link.__loaded || link.import && link.import.readyState !== "loading" : link.__importParsed;
+ }
+ if (useNative) {
+ new MutationObserver(function(mxns) {
+ for (var i = 0, l = mxns.length, m; i < l && (m = mxns[i]); i++) {
+ if (m.addedNodes) {
+ handleImports(m.addedNodes);
+ }
+ }
+ }).observe(document.head, {
+ childList: true
+ });
+ function handleImports(nodes) {
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (isImport(n)) {
+ handleImport(n);
+ }
+ }
+ }
+ function isImport(element) {
+ return element.localName === "link" && element.rel === "import";
+ }
+ function handleImport(element) {
+ var loaded = element.import;
+ if (loaded) {
+ markTargetLoaded({
+ target: element
+ });
+ } else {
+ element.addEventListener("load", markTargetLoaded);
+ element.addEventListener("error", markTargetLoaded);
+ }
+ }
+ (function() {
+ if (document.readyState === "loading") {
+ var imports = document.querySelectorAll("link[rel=import]");
+ for (var i = 0, l = imports.length, imp; i < l && (imp = imports[i]); i++) {
+ handleImport(imp);
+ }
+ }
+ })();
+ }
+ whenReady(function(detail) {
+ window.HTMLImports.ready = true;
+ window.HTMLImports.readyTime = new Date().getTime();
+ var evt = rootDocument.createEvent("CustomEvent");
+ evt.initCustomEvent("HTMLImportsLoaded", true, true, detail);
+ rootDocument.dispatchEvent(evt);
+ });
+ scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
+ scope.useNative = useNative;
+ scope.rootDocument = rootDocument;
+ scope.whenReady = whenReady;
+ scope.isIE = isIE;
+})(window.HTMLImports);
+
+(function(scope) {
+ var modules = [];
+ var addModule = function(module) {
+ modules.push(module);
+ };
+ var initializeModules = function() {
+ modules.forEach(function(module) {
+ module(scope);
+ });
+ };
+ scope.addModule = addModule;
+ scope.initializeModules = initializeModules;
+})(window.HTMLImports);
+
+window.HTMLImports.addModule(function(scope) {
+ var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
+ var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
+ var path = {
+ resolveUrlsInStyle: function(style, linkUrl) {
+ var doc = style.ownerDocument;
+ var resolver = doc.createElement("a");
+ style.textContent = this.resolveUrlsInCssText(style.textContent, linkUrl, resolver);
+ return style;
+ },
+ resolveUrlsInCssText: function(cssText, linkUrl, urlObj) {
+ var r = this.replaceUrls(cssText, urlObj, linkUrl, CSS_URL_REGEXP);
+ r = this.replaceUrls(r, urlObj, linkUrl, CSS_IMPORT_REGEXP);
+ return r;
+ },
+ replaceUrls: function(text, urlObj, linkUrl, regexp) {
+ return text.replace(regexp, function(m, pre, url, post) {
+ var urlPath = url.replace(/["']/g, "");
+ if (linkUrl) {
+ urlPath = new URL(urlPath, linkUrl).href;
+ }
+ urlObj.href = urlPath;
+ urlPath = urlObj.href;
+ return pre + "'" + urlPath + "'" + post;
+ });
+ }
+ };
+ scope.path = path;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var xhr = {
+ async: true,
+ ok: function(request) {
+ return request.status >= 200 && request.status < 300 || request.status === 304 || request.status === 0;
+ },
+ load: function(url, next, nextContext) {
+ var request = new XMLHttpRequest();
+ if (scope.flags.debug || scope.flags.bust) {
+ url += "?" + Math.random();
+ }
+ request.open("GET", url, xhr.async);
+ request.addEventListener("readystatechange", function(e) {
+ if (request.readyState === 4) {
+ var redirectedUrl = null;
+ try {
+ var locationHeader = request.getResponseHeader("Location");
+ if (locationHeader) {
+ redirectedUrl = locationHeader.substr(0, 1) === "/" ? location.origin + locationHeader : locationHeader;
+ }
+ } catch (e) {
+ console.error(e.message);
+ }
+ next.call(nextContext, !xhr.ok(request) && request, request.response || request.responseText, redirectedUrl);
+ }
+ });
+ request.send();
+ return request;
+ },
+ loadDocument: function(url, next, nextContext) {
+ this.load(url, next, nextContext).responseType = "document";
+ }
+ };
+ scope.xhr = xhr;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var xhr = scope.xhr;
+ var flags = scope.flags;
+ var Loader = function(onLoad, onComplete) {
+ this.cache = {};
+ this.onload = onLoad;
+ this.oncomplete = onComplete;
+ this.inflight = 0;
+ this.pending = {};
+ };
+ Loader.prototype = {
+ addNodes: function(nodes) {
+ this.inflight += nodes.length;
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ this.require(n);
+ }
+ this.checkDone();
+ },
+ addNode: function(node) {
+ this.inflight++;
+ this.require(node);
+ this.checkDone();
+ },
+ require: function(elt) {
+ var url = elt.src || elt.href;
+ elt.__nodeUrl = url;
+ if (!this.dedupe(url, elt)) {
+ this.fetch(url, elt);
+ }
+ },
+ dedupe: function(url, elt) {
+ if (this.pending[url]) {
+ this.pending[url].push(elt);
+ return true;
+ }
+ var resource;
+ if (this.cache[url]) {
+ this.onload(url, elt, this.cache[url]);
+ this.tail();
+ return true;
+ }
+ this.pending[url] = [ elt ];
+ return false;
+ },
+ fetch: function(url, elt) {
+ flags.load && console.log("fetch", url, elt);
+ if (!url) {
+ setTimeout(function() {
+ this.receive(url, elt, {
+ error: "href must be specified"
+ }, null);
+ }.bind(this), 0);
+ } else if (url.match(/^data:/)) {
+ var pieces = url.split(",");
+ var header = pieces[0];
+ var body = pieces[1];
+ if (header.indexOf(";base64") > -1) {
+ body = atob(body);
+ } else {
+ body = decodeURIComponent(body);
+ }
+ setTimeout(function() {
+ this.receive(url, elt, null, body);
+ }.bind(this), 0);
+ } else {
+ var receiveXhr = function(err, resource, redirectedUrl) {
+ this.receive(url, elt, err, resource, redirectedUrl);
+ }.bind(this);
+ xhr.load(url, receiveXhr);
+ }
+ },
+ receive: function(url, elt, err, resource, redirectedUrl) {
+ this.cache[url] = resource;
+ var $p = this.pending[url];
+ for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+ this.onload(url, p, resource, err, redirectedUrl);
+ this.tail();
+ }
+ this.pending[url] = null;
+ },
+ tail: function() {
+ --this.inflight;
+ this.checkDone();
+ },
+ checkDone: function() {
+ if (!this.inflight) {
+ this.oncomplete();
+ }
+ }
+ };
+ scope.Loader = Loader;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var Observer = function(addCallback) {
+ this.addCallback = addCallback;
+ this.mo = new MutationObserver(this.handler.bind(this));
+ };
+ Observer.prototype = {
+ handler: function(mutations) {
+ for (var i = 0, l = mutations.length, m; i < l && (m = mutations[i]); i++) {
+ if (m.type === "childList" && m.addedNodes.length) {
+ this.addedNodes(m.addedNodes);
+ }
+ }
+ },
+ addedNodes: function(nodes) {
+ if (this.addCallback) {
+ this.addCallback(nodes);
+ }
+ for (var i = 0, l = nodes.length, n, loading; i < l && (n = nodes[i]); i++) {
+ if (n.children && n.children.length) {
+ this.addedNodes(n.children);
+ }
+ }
+ },
+ observe: function(root) {
+ this.mo.observe(root, {
+ childList: true,
+ subtree: true
+ });
+ }
+ };
+ scope.Observer = Observer;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var path = scope.path;
+ var rootDocument = scope.rootDocument;
+ var flags = scope.flags;
+ var isIE = scope.isIE;
+ var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+ var IMPORT_SELECTOR = "link[rel=" + IMPORT_LINK_TYPE + "]";
+ var importParser = {
+ documentSelectors: IMPORT_SELECTOR,
+ importsSelectors: [ IMPORT_SELECTOR, "link[rel=stylesheet]:not([type])", "style:not([type])", "script:not([type])", 'script[type="application/javascript"]', 'script[type="text/javascript"]' ].join(","),
+ map: {
+ link: "parseLink",
+ script: "parseScript",
+ style: "parseStyle"
+ },
+ dynamicElements: [],
+ parseNext: function() {
+ var next = this.nextToParse();
+ if (next) {
+ this.parse(next);
+ }
+ },
+ parse: function(elt) {
+ if (this.isParsed(elt)) {
+ flags.parse && console.log("[%s] is already parsed", elt.localName);
+ return;
+ }
+ var fn = this[this.map[elt.localName]];
+ if (fn) {
+ this.markParsing(elt);
+ fn.call(this, elt);
+ }
+ },
+ parseDynamic: function(elt, quiet) {
+ this.dynamicElements.push(elt);
+ if (!quiet) {
+ this.parseNext();
+ }
+ },
+ markParsing: function(elt) {
+ flags.parse && console.log("parsing", elt);
+ this.parsingElement = elt;
+ },
+ markParsingComplete: function(elt) {
+ elt.__importParsed = true;
+ this.markDynamicParsingComplete(elt);
+ if (elt.__importElement) {
+ elt.__importElement.__importParsed = true;
+ this.markDynamicParsingComplete(elt.__importElement);
+ }
+ this.parsingElement = null;
+ flags.parse && console.log("completed", elt);
+ },
+ markDynamicParsingComplete: function(elt) {
+ var i = this.dynamicElements.indexOf(elt);
+ if (i >= 0) {
+ this.dynamicElements.splice(i, 1);
+ }
+ },
+ parseImport: function(elt) {
+ elt.import = elt.__doc;
+ if (window.HTMLImports.__importsParsingHook) {
+ window.HTMLImports.__importsParsingHook(elt);
+ }
+ if (elt.import) {
+ elt.import.__importParsed = true;
+ }
+ this.markParsingComplete(elt);
+ if (elt.__resource && !elt.__error) {
+ elt.dispatchEvent(new CustomEvent("load", {
+ bubbles: false
+ }));
+ } else {
+ elt.dispatchEvent(new CustomEvent("error", {
+ bubbles: false
+ }));
+ }
+ if (elt.__pending) {
+ var fn;
+ while (elt.__pending.length) {
+ fn = elt.__pending.shift();
+ if (fn) {
+ fn({
+ target: elt
+ });
+ }
+ }
+ }
+ this.parseNext();
+ },
+ parseLink: function(linkElt) {
+ if (nodeIsImport(linkElt)) {
+ this.parseImport(linkElt);
+ } else {
+ linkElt.href = linkElt.href;
+ this.parseGeneric(linkElt);
+ }
+ },
+ parseStyle: function(elt) {
+ var src = elt;
+ elt = cloneStyle(elt);
+ src.__appliedElement = elt;
+ elt.__importElement = src;
+ this.parseGeneric(elt);
+ },
+ parseGeneric: function(elt) {
+ this.trackElement(elt);
+ this.addElementToDocument(elt);
+ },
+ rootImportForElement: function(elt) {
+ var n = elt;
+ while (n.ownerDocument.__importLink) {
+ n = n.ownerDocument.__importLink;
+ }
+ return n;
+ },
+ addElementToDocument: function(elt) {
+ var port = this.rootImportForElement(elt.__importElement || elt);
+ port.parentNode.insertBefore(elt, port);
+ },
+ trackElement: function(elt, callback) {
+ var self = this;
+ var done = function(e) {
+ elt.removeEventListener("load", done);
+ elt.removeEventListener("error", done);
+ if (callback) {
+ callback(e);
+ }
+ self.markParsingComplete(elt);
+ self.parseNext();
+ };
+ elt.addEventListener("load", done);
+ elt.addEventListener("error", done);
+ if (isIE && elt.localName === "style") {
+ var fakeLoad = false;
+ if (elt.textContent.indexOf("@import") == -1) {
+ fakeLoad = true;
+ } else if (elt.sheet) {
+ fakeLoad = true;
+ var csr = elt.sheet.cssRules;
+ var len = csr ? csr.length : 0;
+ for (var i = 0, r; i < len && (r = csr[i]); i++) {
+ if (r.type === CSSRule.IMPORT_RULE) {
+ fakeLoad = fakeLoad && Boolean(r.styleSheet);
+ }
+ }
+ }
+ if (fakeLoad) {
+ setTimeout(function() {
+ elt.dispatchEvent(new CustomEvent("load", {
+ bubbles: false
+ }));
+ });
+ }
+ }
+ },
+ parseScript: function(scriptElt) {
+ var script = document.createElement("script");
+ script.__importElement = scriptElt;
+ script.src = scriptElt.src ? scriptElt.src : generateScriptDataUrl(scriptElt);
+ scope.currentScript = scriptElt;
+ this.trackElement(script, function(e) {
+ if (script.parentNode) {
+ script.parentNode.removeChild(script);
+ }
+ scope.currentScript = null;
+ });
+ this.addElementToDocument(script);
+ },
+ nextToParse: function() {
+ this._mayParse = [];
+ return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || this.nextToParseDynamic());
+ },
+ nextToParseInDoc: function(doc, link) {
+ if (doc && this._mayParse.indexOf(doc) < 0) {
+ this._mayParse.push(doc);
+ var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc));
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (!this.isParsed(n)) {
+ if (this.hasResource(n)) {
+ return nodeIsImport(n) ? this.nextToParseInDoc(n.__doc, n) : n;
+ } else {
+ return;
+ }
+ }
+ }
+ }
+ return link;
+ },
+ nextToParseDynamic: function() {
+ return this.dynamicElements[0];
+ },
+ parseSelectorsForNode: function(node) {
+ var doc = node.ownerDocument || node;
+ return doc === rootDocument ? this.documentSelectors : this.importsSelectors;
+ },
+ isParsed: function(node) {
+ return node.__importParsed;
+ },
+ needsDynamicParsing: function(elt) {
+ return this.dynamicElements.indexOf(elt) >= 0;
+ },
+ hasResource: function(node) {
+ if (nodeIsImport(node) && node.__doc === undefined) {
+ return false;
+ }
+ return true;
+ }
+ };
+ function nodeIsImport(elt) {
+ return elt.localName === "link" && elt.rel === IMPORT_LINK_TYPE;
+ }
+ function generateScriptDataUrl(script) {
+ var scriptContent = generateScriptContent(script);
+ return "data:text/javascript;charset=utf-8," + encodeURIComponent(scriptContent);
+ }
+ function generateScriptContent(script) {
+ return script.textContent + generateSourceMapHint(script);
+ }
+ function generateSourceMapHint(script) {
+ var owner = script.ownerDocument;
+ owner.__importedScripts = owner.__importedScripts || 0;
+ var moniker = script.ownerDocument.baseURI;
+ var num = owner.__importedScripts ? "-" + owner.__importedScripts : "";
+ owner.__importedScripts++;
+ return "\n//# sourceURL=" + moniker + num + ".js\n";
+ }
+ function cloneStyle(style) {
+ var clone = style.ownerDocument.createElement("style");
+ clone.textContent = style.textContent;
+ path.resolveUrlsInStyle(clone);
+ return clone;
+ }
+ scope.parser = importParser;
+ scope.IMPORT_SELECTOR = IMPORT_SELECTOR;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var flags = scope.flags;
+ var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+ var IMPORT_SELECTOR = scope.IMPORT_SELECTOR;
+ var rootDocument = scope.rootDocument;
+ var Loader = scope.Loader;
+ var Observer = scope.Observer;
+ var parser = scope.parser;
+ var importer = {
+ documents: {},
+ documentPreloadSelectors: IMPORT_SELECTOR,
+ importsPreloadSelectors: [ IMPORT_SELECTOR ].join(","),
+ loadNode: function(node) {
+ importLoader.addNode(node);
+ },
+ loadSubtree: function(parent) {
+ var nodes = this.marshalNodes(parent);
+ importLoader.addNodes(nodes);
+ },
+ marshalNodes: function(parent) {
+ return parent.querySelectorAll(this.loadSelectorsForNode(parent));
+ },
+ loadSelectorsForNode: function(node) {
+ var doc = node.ownerDocument || node;
+ return doc === rootDocument ? this.documentPreloadSelectors : this.importsPreloadSelectors;
+ },
+ loaded: function(url, elt, resource, err, redirectedUrl) {
+ flags.load && console.log("loaded", url, elt);
+ elt.__resource = resource;
+ elt.__error = err;
+ if (isImportLink(elt)) {
+ var doc = this.documents[url];
+ if (doc === undefined) {
+ doc = err ? null : makeDocument(resource, redirectedUrl || url);
+ if (doc) {
+ doc.__importLink = elt;
+ this.bootDocument(doc);
+ }
+ this.documents[url] = doc;
+ }
+ elt.__doc = doc;
+ }
+ parser.parseNext();
+ },
+ bootDocument: function(doc) {
+ this.loadSubtree(doc);
+ this.observer.observe(doc);
+ parser.parseNext();
+ },
+ loadedAll: function() {
+ parser.parseNext();
+ }
+ };
+ var importLoader = new Loader(importer.loaded.bind(importer), importer.loadedAll.bind(importer));
+ importer.observer = new Observer();
+ function isImportLink(elt) {
+ return isLinkRel(elt, IMPORT_LINK_TYPE);
+ }
+ function isLinkRel(elt, rel) {
+ return elt.localName === "link" && elt.getAttribute("rel") === rel;
+ }
+ function hasBaseURIAccessor(doc) {
+ return !!Object.getOwnPropertyDescriptor(doc, "baseURI");
+ }
+ function makeDocument(resource, url) {
+ var doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
+ doc._URL = url;
+ var base = doc.createElement("base");
+ base.setAttribute("href", url);
+ if (!doc.baseURI && !hasBaseURIAccessor(doc)) {
+ Object.defineProperty(doc, "baseURI", {
+ value: url
+ });
+ }
+ var meta = doc.createElement("meta");
+ meta.setAttribute("charset", "utf-8");
+ doc.head.appendChild(meta);
+ doc.head.appendChild(base);
+ doc.body.innerHTML = resource;
+ if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
+ HTMLTemplateElement.bootstrap(doc);
+ }
+ return doc;
+ }
+ if (!document.baseURI) {
+ var baseURIDescriptor = {
+ get: function() {
+ var base = document.querySelector("base");
+ return base ? base.href : window.location.href;
+ },
+ configurable: true
+ };
+ Object.defineProperty(document, "baseURI", baseURIDescriptor);
+ Object.defineProperty(rootDocument, "baseURI", baseURIDescriptor);
+ }
+ scope.importer = importer;
+ scope.importLoader = importLoader;
+});
+
+window.HTMLImports.addModule(function(scope) {
+ var parser = scope.parser;
+ var importer = scope.importer;
+ var dynamic = {
+ added: function(nodes) {
+ var owner, parsed, loading;
+ for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+ if (!owner) {
+ owner = n.ownerDocument;
+ parsed = parser.isParsed(owner);
+ }
+ loading = this.shouldLoadNode(n);
+ if (loading) {
+ importer.loadNode(n);
+ }
+ if (this.shouldParseNode(n) && parsed) {
+ parser.parseDynamic(n, loading);
+ }
+ }
+ },
+ shouldLoadNode: function(node) {
+ return node.nodeType === 1 && matches.call(node, importer.loadSelectorsForNode(node));
+ },
+ shouldParseNode: function(node) {
+ return node.nodeType === 1 && matches.call(node, parser.parseSelectorsForNode(node));
+ }
+ };
+ importer.observer.addCallback = dynamic.added.bind(dynamic);
+ var matches = HTMLElement.prototype.matches || HTMLElement.prototype.matchesSelector || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector || HTMLElement.prototype.msMatchesSelector;
+});
+
+(function(scope) {
+ var initializeModules = scope.initializeModules;
+ var isIE = scope.isIE;
+ if (scope.useNative) {
+ return;
+ }
+ initializeModules();
+ var rootDocument = scope.rootDocument;
+ function bootstrap() {
+ window.HTMLImports.importer.bootDocument(rootDocument);
+ }
+ if (document.readyState === "complete" || document.readyState === "interactive" && !window.attachEvent) {
+ bootstrap();
+ } else {
+ document.addEventListener("DOMContentLoaded", bootstrap);
+ }
+})(window.HTMLImports);
+
+window.CustomElements = window.CustomElements || {
+ flags: {}
+};
+
+(function(scope) {
+ var flags = scope.flags;
+ var modules = [];
+ var addModule = function(module) {
+ modules.push(module);
+ };
+ var initializeModules = function() {
+ modules.forEach(function(module) {
+ module(scope);
+ });
+ };
+ scope.addModule = addModule;
+ scope.initializeModules = initializeModules;
+ scope.hasNative = Boolean(document.registerElement);
+ scope.isIE = /Trident/.test(navigator.userAgent);
+ scope.useNative = !flags.register && scope.hasNative && !window.ShadowDOMPolyfill && (!window.HTMLImports || window.HTMLImports.useNative);
+})(window.CustomElements);
+
+window.CustomElements.addModule(function(scope) {
+ var IMPORT_LINK_TYPE = window.HTMLImports ? window.HTMLImports.IMPORT_LINK_TYPE : "none";
+ function forSubtree(node, cb) {
+ findAllElements(node, function(e) {
+ if (cb(e)) {
+ return true;
+ }
+ forRoots(e, cb);
+ });
+ forRoots(node, cb);
+ }
+ function findAllElements(node, find, data) {
+ var e = node.firstElementChild;
+ if (!e) {
+ e = node.firstChild;
+ while (e && e.nodeType !== Node.ELEMENT_NODE) {
+ e = e.nextSibling;
+ }
+ }
+ while (e) {
+ if (find(e, data) !== true) {
+ findAllElements(e, find, data);
+ }
+ e = e.nextElementSibling;
+ }
+ return null;
+ }
+ function forRoots(node, cb) {
+ var root = node.shadowRoot;
+ while (root) {
+ forSubtree(root, cb);
+ root = root.olderShadowRoot;
+ }
+ }
+ function forDocumentTree(doc, cb) {
+ _forDocumentTree(doc, cb, []);
+ }
+ function _forDocumentTree(doc, cb, processingDocuments) {
+ doc = window.wrap(doc);
+ if (processingDocuments.indexOf(doc) >= 0) {
+ return;
+ }
+ processingDocuments.push(doc);
+ var imports = doc.querySelectorAll("link[rel=" + IMPORT_LINK_TYPE + "]");
+ for (var i = 0, l = imports.length, n; i < l && (n = imports[i]); i++) {
+ if (n.import) {
+ _forDocumentTree(n.import, cb, processingDocuments);
+ }
+ }
+ cb(doc);
+ }
+ scope.forDocumentTree = forDocumentTree;
+ scope.forSubtree = forSubtree;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var flags = scope.flags;
+ var forSubtree = scope.forSubtree;
+ var forDocumentTree = scope.forDocumentTree;
+ function addedNode(node, isAttached) {
+ return added(node, isAttached) || addedSubtree(node, isAttached);
+ }
+ function added(node, isAttached) {
+ if (scope.upgrade(node, isAttached)) {
+ return true;
+ }
+ if (isAttached) {
+ attached(node);
+ }
+ }
+ function addedSubtree(node, isAttached) {
+ forSubtree(node, function(e) {
+ if (added(e, isAttached)) {
+ return true;
+ }
+ });
+ }
+ var hasThrottledAttached = window.MutationObserver._isPolyfilled && flags["throttle-attached"];
+ scope.hasPolyfillMutations = hasThrottledAttached;
+ scope.hasThrottledAttached = hasThrottledAttached;
+ var isPendingMutations = false;
+ var pendingMutations = [];
+ function deferMutation(fn) {
+ pendingMutations.push(fn);
+ if (!isPendingMutations) {
+ isPendingMutations = true;
+ setTimeout(takeMutations);
+ }
+ }
+ function takeMutations() {
+ isPendingMutations = false;
+ var $p = pendingMutations;
+ for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+ p();
+ }
+ pendingMutations = [];
+ }
+ function attached(element) {
+ if (hasThrottledAttached) {
+ deferMutation(function() {
+ _attached(element);
+ });
+ } else {
+ _attached(element);
+ }
+ }
+ function _attached(element) {
+ if (element.__upgraded__ && !element.__attached) {
+ element.__attached = true;
+ if (element.attachedCallback) {
+ element.attachedCallback();
+ }
+ }
+ }
+ function detachedNode(node) {
+ detached(node);
+ forSubtree(node, function(e) {
+ detached(e);
+ });
+ }
+ function detached(element) {
+ if (hasThrottledAttached) {
+ deferMutation(function() {
+ _detached(element);
+ });
+ } else {
+ _detached(element);
+ }
+ }
+ function _detached(element) {
+ if (element.__upgraded__ && element.__attached) {
+ element.__attached = false;
+ if (element.detachedCallback) {
+ element.detachedCallback();
+ }
+ }
+ }
+ function inDocument(element) {
+ var p = element;
+ var doc = window.wrap(document);
+ while (p) {
+ if (p == doc) {
+ return true;
+ }
+ p = p.parentNode || p.nodeType === Node.DOCUMENT_FRAGMENT_NODE && p.host;
+ }
+ }
+ function watchShadow(node) {
+ if (node.shadowRoot && !node.shadowRoot.__watched) {
+ flags.dom && console.log("watching shadow-root for: ", node.localName);
+ var root = node.shadowRoot;
+ while (root) {
+ observe(root);
+ root = root.olderShadowRoot;
+ }
+ }
+ }
+ function handler(root, mutations) {
+ if (flags.dom) {
+ var mx = mutations[0];
+ if (mx && mx.type === "childList" && mx.addedNodes) {
+ if (mx.addedNodes) {
+ var d = mx.addedNodes[0];
+ while (d && d !== document && !d.host) {
+ d = d.parentNode;
+ }
+ var u = d && (d.URL || d._URL || d.host && d.host.localName) || "";
+ u = u.split("/?").shift().split("/").pop();
+ }
+ }
+ console.group("mutations (%d) [%s]", mutations.length, u || "");
+ }
+ var isAttached = inDocument(root);
+ mutations.forEach(function(mx) {
+ if (mx.type === "childList") {
+ forEach(mx.addedNodes, function(n) {
+ if (!n.localName) {
+ return;
+ }
+ addedNode(n, isAttached);
+ });
+ forEach(mx.removedNodes, function(n) {
+ if (!n.localName) {
+ return;
+ }
+ detachedNode(n);
+ });
+ }
+ });
+ flags.dom && console.groupEnd();
+ }
+ function takeRecords(node) {
+ node = window.wrap(node);
+ if (!node) {
+ node = window.wrap(document);
+ }
+ while (node.parentNode) {
+ node = node.parentNode;
+ }
+ var observer = node.__observer;
+ if (observer) {
+ handler(node, observer.takeRecords());
+ takeMutations();
+ }
+ }
+ var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+ function observe(inRoot) {
+ if (inRoot.__observer) {
+ return;
+ }
+ var observer = new MutationObserver(handler.bind(this, inRoot));
+ observer.observe(inRoot, {
+ childList: true,
+ subtree: true
+ });
+ inRoot.__observer = observer;
+ }
+ function upgradeDocument(doc) {
+ doc = window.wrap(doc);
+ flags.dom && console.group("upgradeDocument: ", doc.baseURI.split("/").pop());
+ var isMainDocument = doc === window.wrap(document);
+ addedNode(doc, isMainDocument);
+ observe(doc);
+ flags.dom && console.groupEnd();
+ }
+ function upgradeDocumentTree(doc) {
+ forDocumentTree(doc, upgradeDocument);
+ }
+ var originalCreateShadowRoot = Element.prototype.createShadowRoot;
+ if (originalCreateShadowRoot) {
+ Element.prototype.createShadowRoot = function() {
+ var root = originalCreateShadowRoot.call(this);
+ window.CustomElements.watchShadow(this);
+ return root;
+ };
+ }
+ scope.watchShadow = watchShadow;
+ scope.upgradeDocumentTree = upgradeDocumentTree;
+ scope.upgradeDocument = upgradeDocument;
+ scope.upgradeSubtree = addedSubtree;
+ scope.upgradeAll = addedNode;
+ scope.attached = attached;
+ scope.takeRecords = takeRecords;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var flags = scope.flags;
+ function upgrade(node, isAttached) {
+ if (node.localName === "template") {
+ if (window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
+ HTMLTemplateElement.decorate(node);
+ }
+ }
+ if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) {
+ var is = node.getAttribute("is");
+ var definition = scope.getRegisteredDefinition(node.localName) || scope.getRegisteredDefinition(is);
+ if (definition) {
+ if (is && definition.tag == node.localName || !is && !definition.extends) {
+ return upgradeWithDefinition(node, definition, isAttached);
+ }
+ }
+ }
+ }
+ function upgradeWithDefinition(element, definition, isAttached) {
+ flags.upgrade && console.group("upgrade:", element.localName);
+ if (definition.is) {
+ element.setAttribute("is", definition.is);
+ }
+ implementPrototype(element, definition);
+ element.__upgraded__ = true;
+ created(element);
+ if (isAttached) {
+ scope.attached(element);
+ }
+ scope.upgradeSubtree(element, isAttached);
+ flags.upgrade && console.groupEnd();
+ return element;
+ }
+ function implementPrototype(element, definition) {
+ if (Object.__proto__) {
+ element.__proto__ = definition.prototype;
+ } else {
+ customMixin(element, definition.prototype, definition.native);
+ element.__proto__ = definition.prototype;
+ }
+ }
+ function customMixin(inTarget, inSrc, inNative) {
+ var used = {};
+ var p = inSrc;
+ while (p !== inNative && p !== HTMLElement.prototype) {
+ var keys = Object.getOwnPropertyNames(p);
+ for (var i = 0, k; k = keys[i]; i++) {
+ if (!used[k]) {
+ Object.defineProperty(inTarget, k, Object.getOwnPropertyDescriptor(p, k));
+ used[k] = 1;
+ }
+ }
+ p = Object.getPrototypeOf(p);
+ }
+ }
+ function created(element) {
+ if (element.createdCallback) {
+ element.createdCallback();
+ }
+ }
+ scope.upgrade = upgrade;
+ scope.upgradeWithDefinition = upgradeWithDefinition;
+ scope.implementPrototype = implementPrototype;
+});
+
+window.CustomElements.addModule(function(scope) {
+ var isIE = scope.isIE;
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
+ var upgradeAll = scope.upgradeAll;
+ var upgradeWithDefinition = scope.upgradeWithDefinition;
+ var implementPrototype = scope.implementPrototype;
+ var useNative = scope.useNative;
+ function register(name, options) {
+ var definition = options || {};
+ if (!name) {
+ throw new Error("document.registerElement: first argument `name` must not be empty");
+ }
+ if (name.indexOf("-") < 0) {
+ throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '" + String(name) + "'.");
+ }
+ if (isReservedTag(name)) {
+ throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '" + String(name) + "'. The type name is invalid.");
+ }
+ if (getRegisteredDefinition(name)) {
+ throw new Error("DuplicateDefinitionError: a type with name '" + String(name) + "' is already registered");
+ }
+ if (!definition.prototype) {
+ definition.prototype = Object.create(HTMLElement.prototype);
+ }
+ definition.__name = name.toLowerCase();
+ if (definition.extends) {
+ definition.extends = definition.extends.toLowerCase();
+ }
+ definition.lifecycle = definition.lifecycle || {};
+ definition.ancestry = ancestry(definition.extends);
+ resolveTagName(definition);
+ resolvePrototypeChain(definition);
+ overrideAttributeApi(definition.prototype);
+ registerDefinition(definition.__name, definition);
+ definition.ctor = generateConstructor(definition);
+ definition.ctor.prototype = definition.prototype;
+ definition.prototype.constructor = definition.ctor;
+ if (scope.ready) {
+ upgradeDocumentTree(document);
+ }
+ return definition.ctor;
+ }
+ function overrideAttributeApi(prototype) {
+ if (prototype.setAttribute._polyfilled) {
+ return;
+ }
+ var setAttribute = prototype.setAttribute;
+ prototype.setAttribute = function(name, value) {
+ changeAttribute.call(this, name, value, setAttribute);
+ };
+ var removeAttribute = prototype.removeAttribute;
+ prototype.removeAttribute = function(name) {
+ changeAttribute.call(this, name, null, removeAttribute);
+ };
+ prototype.setAttribute._polyfilled = true;
+ }
+ function changeAttribute(name, value, operation) {
+ name = name.toLowerCase();
+ var oldValue = this.getAttribute(name);
+ operation.apply(this, arguments);
+ var newValue = this.getAttribute(name);
+ if (this.attributeChangedCallback && newValue !== oldValue) {
+ this.attributeChangedCallback(name, oldValue, newValue);
+ }
+ }
+ function isReservedTag(name) {
+ for (var i = 0; i < reservedTagList.length; i++) {
+ if (name === reservedTagList[i]) {
+ return true;
+ }
+ }
+ }
+ var reservedTagList = [ "annotation-xml", "color-profile", "font-face", "font-face-src", "font-face-uri", "font-face-format", "font-face-name", "missing-glyph" ];
+ function ancestry(extnds) {
+ var extendee = getRegisteredDefinition(extnds);
+ if (extendee) {
+ return ancestry(extendee.extends).concat([ extendee ]);
+ }
+ return [];
+ }
+ function resolveTagName(definition) {
+ var baseTag = definition.extends;
+ for (var i = 0, a; a = definition.ancestry[i]; i++) {
+ baseTag = a.is && a.tag;
+ }
+ definition.tag = baseTag || definition.__name;
+ if (baseTag) {
+ definition.is = definition.__name;
+ }
+ }
+ function resolvePrototypeChain(definition) {
+ if (!Object.__proto__) {
+ var nativePrototype = HTMLElement.prototype;
+ if (definition.is) {
+ var inst = document.createElement(definition.tag);
+ nativePrototype = Object.getPrototypeOf(inst);
+ }
+ var proto = definition.prototype, ancestor;
+ var foundPrototype = false;
+ while (proto) {
+ if (proto == nativePrototype) {
+ foundPrototype = true;
+ }
+ ancestor = Object.getPrototypeOf(proto);
+ if (ancestor) {
+ proto.__proto__ = ancestor;
+ }
+ proto = ancestor;
+ }
+ if (!foundPrototype) {
+ console.warn(definition.tag + " prototype not found in prototype chain for " + definition.is);
+ }
+ definition.native = nativePrototype;
+ }
+ }
+ function instantiate(definition) {
+ return upgradeWithDefinition(domCreateElement(definition.tag), definition);
+ }
+ var registry = {};
+ function getRegisteredDefinition(name) {
+ if (name) {
+ return registry[name.toLowerCase()];
+ }
+ }
+ function registerDefinition(name, definition) {
+ registry[name] = definition;
+ }
+ function generateConstructor(definition) {
+ return function() {
+ return instantiate(definition);
+ };
+ }
+ var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+ function createElementNS(namespace, tag, typeExtension) {
+ if (namespace === HTML_NAMESPACE) {
+ return createElement(tag, typeExtension);
+ } else {
+ return domCreateElementNS(namespace, tag);
+ }
+ }
+ function createElement(tag, typeExtension) {
+ if (tag) {
+ tag = tag.toLowerCase();
+ }
+ if (typeExtension) {
+ typeExtension = typeExtension.toLowerCase();
+ }
+ var definition = getRegisteredDefinition(typeExtension || tag);
+ if (definition) {
+ if (tag == definition.tag && typeExtension == definition.is) {
+ return new definition.ctor();
+ }
+ if (!typeExtension && !definition.is) {
+ return new definition.ctor();
+ }
+ }
+ var element;
+ if (typeExtension) {
+ element = createElement(tag);
+ element.setAttribute("is", typeExtension);
+ return element;
+ }
+ element = domCreateElement(tag);
+ if (tag.indexOf("-") >= 0) {
+ implementPrototype(element, HTMLElement);
+ }
+ return element;
+ }
+ var domCreateElement = document.createElement.bind(document);
+ var domCreateElementNS = document.createElementNS.bind(document);
+ var isInstance;
+ if (!Object.__proto__ && !useNative) {
+ isInstance = function(obj, ctor) {
+ if (obj instanceof ctor) {
+ return true;
+ }
+ var p = obj;
+ while (p) {
+ if (p === ctor.prototype) {
+ return true;
+ }
+ p = p.__proto__;
+ }
+ return false;
+ };
+ } else {
+ isInstance = function(obj, base) {
+ return obj instanceof base;
+ };
+ }
+ function wrapDomMethodToForceUpgrade(obj, methodName) {
+ var orig = obj[methodName];
+ obj[methodName] = function() {
+ var n = orig.apply(this, arguments);
+ upgradeAll(n);
+ return n;
+ };
+ }
+ wrapDomMethodToForceUpgrade(Node.prototype, "cloneNode");
+ wrapDomMethodToForceUpgrade(document, "importNode");
+ document.registerElement = register;
+ document.createElement = createElement;
+ document.createElementNS = createElementNS;
+ scope.registry = registry;
+ scope.instanceof = isInstance;
+ scope.reservedTagList = reservedTagList;
+ scope.getRegisteredDefinition = getRegisteredDefinition;
+ document.register = document.registerElement;
+});
+
+(function(scope) {
+ var useNative = scope.useNative;
+ var initializeModules = scope.initializeModules;
+ var isIE = scope.isIE;
+ if (useNative) {
+ var nop = function() {};
+ scope.watchShadow = nop;
+ scope.upgrade = nop;
+ scope.upgradeAll = nop;
+ scope.upgradeDocumentTree = nop;
+ scope.upgradeSubtree = nop;
+ scope.takeRecords = nop;
+ scope.instanceof = function(obj, base) {
+ return obj instanceof base;
+ };
+ } else {
+ initializeModules();
+ }
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
+ var upgradeDocument = scope.upgradeDocument;
+ if (!window.wrap) {
+ if (window.ShadowDOMPolyfill) {
+ window.wrap = window.ShadowDOMPolyfill.wrapIfNeeded;
+ window.unwrap = window.ShadowDOMPolyfill.unwrapIfNeeded;
+ } else {
+ window.wrap = window.unwrap = function(node) {
+ return node;
+ };
+ }
+ }
+ if (window.HTMLImports) {
+ window.HTMLImports.__importsParsingHook = function(elt) {
+ if (elt.import) {
+ upgradeDocument(wrap(elt.import));
+ }
+ };
+ }
+ function bootstrap() {
+ upgradeDocumentTree(window.wrap(document));
+ window.CustomElements.ready = true;
+ var requestAnimationFrame = window.requestAnimationFrame || function(f) {
+ setTimeout(f, 16);
+ };
+ requestAnimationFrame(function() {
+ setTimeout(function() {
+ window.CustomElements.readyTime = Date.now();
+ if (window.HTMLImports) {
+ window.CustomElements.elapsed = window.CustomElements.readyTime - window.HTMLImports.readyTime;
+ }
+ document.dispatchEvent(new CustomEvent("WebComponentsReady", {
+ bubbles: true
+ }));
+ });
+ });
+ }
+ if (document.readyState === "complete" || scope.flags.eager) {
+ bootstrap();
+ } else if (document.readyState === "interactive" && !window.attachEvent && (!window.HTMLImports || window.HTMLImports.ready)) {
+ bootstrap();
+ } else {
+ var loadEvent = window.HTMLImports && !window.HTMLImports.ready ? "HTMLImportsLoaded" : "DOMContentLoaded";
+ window.addEventListener(loadEvent, bootstrap);
+ }
+})(window.CustomElements);
+
+(function(scope) {
+ if (!Function.prototype.bind) {
+ Function.prototype.bind = function(scope) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function() {
+ var args2 = args.slice();
+ args2.push.apply(args2, arguments);
+ return self.apply(scope, args2);
+ };
+ };
+ }
+})(window.WebComponents);
+
+(function(scope) {
+ var style = document.createElement("style");
+ style.textContent = "" + "body {" + "transition: opacity ease-in 0.2s;" + " } \n" + "body[unresolved] {" + "opacity: 0; display: block; overflow: hidden; position: relative;" + " } \n";
+ var head = document.querySelector("head");
+ head.insertBefore(style, head.firstChild);
+})(window.WebComponents);
+
+(function(scope) {
+ window.Platform = scope;
+})(window.WebComponents); \ No newline at end of file
diff --git a/catapult/third_party/polymer/components/webcomponentsjs/webcomponents.min.js b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents.min.js
new file mode 100644
index 00000000..ad8196bc
--- /dev/null
+++ b/catapult/third_party/polymer/components/webcomponentsjs/webcomponents.min.js
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright (c) 2014 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
+ */
+// @version 0.7.24
+!function(){window.WebComponents=window.WebComponents||{flags:{}};var e="webcomponents.js",t=document.querySelector('script[src*="'+e+'"]'),n={};if(!n.noOpts){if(location.search.slice(1).split("&").forEach(function(e){var t,r=e.split("=");r[0]&&(t=r[0].match(/wc-(.+)/))&&(n[t[1]]=r[1]||!0)}),t)for(var r,o=0;r=t.attributes[o];o++)"src"!==r.name&&(n[r.name]=r.value||!0);if(n.log&&n.log.split){var i=n.log.split(",");n.log={},i.forEach(function(e){n.log[e]=!0})}else n.log={}}n.shadow=n.shadow||n.shadowdom||n.polyfill,"native"===n.shadow?n.shadow=!1:n.shadow=n.shadow||!HTMLElement.prototype.createShadowRoot,n.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=n.register),WebComponents.flags=n}(),WebComponents.flags.shadow&&("undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var r=t[this.name];return r&&r[0]===t?r[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),window.ShadowDOMPolyfill={},function(e){"use strict";function t(){if("undefined"!=typeof chrome&&chrome.app&&chrome.app.runtime)return!1;if(navigator.getDeviceStorage)return!1;try{var e=new Function("return true;");return e()}catch(t){return!1}}function n(e){if(!e)throw new Error("Assertion failed")}function r(e,t){for(var n=W(t),r=0;r<n.length;r++){var o=n[r];A(e,o,F(t,o))}return e}function o(e,t){for(var n=W(t),r=0;r<n.length;r++){var o=n[r];switch(o){case"arguments":case"caller":case"length":case"name":case"prototype":case"toString":continue}A(e,o,F(t,o))}return e}function i(e,t){for(var n=0;n<t.length;n++)if(t[n]in e)return t[n]}function a(e,t,n){U.value=n,A(e,t,U)}function s(e,t){var n=e.__proto__||Object.getPrototypeOf(e);if(q)try{W(n)}catch(r){n=n.__proto__}var o=R.get(n);if(o)return o;var i=s(n),a=E(i);return g(n,a,t),a}function c(e,t){w(e,t,!0)}function l(e,t){w(t,e,!1)}function u(e){return/^on[a-z]+$/.test(e)}function d(e){return/^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(e)}function p(e){return k&&d(e)?new Function("return this.__impl4cf1e782hg__."+e):function(){return this.__impl4cf1e782hg__[e]}}function h(e){return k&&d(e)?new Function("v","this.__impl4cf1e782hg__."+e+" = v"):function(t){this.__impl4cf1e782hg__[e]=t}}function f(e){return k&&d(e)?new Function("return this.__impl4cf1e782hg__."+e+".apply(this.__impl4cf1e782hg__, arguments)"):function(){return this.__impl4cf1e782hg__[e].apply(this.__impl4cf1e782hg__,arguments)}}function m(e,t){try{return e===window&&"showModalDialog"===t?B:Object.getOwnPropertyDescriptor(e,t)}catch(n){return B}}function w(t,n,r,o){for(var i=W(t),a=0;a<i.length;a++){var s=i[a];if("polymerBlackList_"!==s&&!(s in n||t.polymerBlackList_&&t.polymerBlackList_[s])){q&&t.__lookupGetter__(s);var c,l,d=m(t,s);if("function"!=typeof d.value){var w=u(s);c=w?e.getEventHandlerGetter(s):p(s),(d.writable||d.set||V)&&(l=w?e.getEventHandlerSetter(s):h(s));var v=V||d.configurable;A(n,s,{get:c,set:l,configurable:v,enumerable:d.enumerable})}else r&&(n[s]=f(s))}}}function v(e,t,n){if(null!=e){var r=e.prototype;g(r,t,n),o(t,e)}}function g(e,t,r){var o=t.prototype;n(void 0===R.get(e)),R.set(e,t),I.set(o,e),c(e,o),r&&l(o,r),a(o,"constructor",t),t.prototype=o}function b(e,t){return R.get(t.prototype)===e}function y(e){var t=Object.getPrototypeOf(e),n=s(t),r=E(n);return g(t,r,e),r}function E(e){function t(t){e.call(this,t)}var n=Object.create(e.prototype);return n.constructor=t,t.prototype=n,t}function _(e){return e&&e.__impl4cf1e782hg__}function S(e){return!_(e)}function T(e){if(null===e)return null;n(S(e));var t=e.__wrapper8e3dd93a60__;return null!=t?t:e.__wrapper8e3dd93a60__=new(s(e,e))(e)}function M(e){return null===e?null:(n(_(e)),e.__impl4cf1e782hg__)}function O(e){return e.__impl4cf1e782hg__}function L(e,t){t.__impl4cf1e782hg__=e,e.__wrapper8e3dd93a60__=t}function N(e){return e&&_(e)?M(e):e}function C(e){return e&&!_(e)?T(e):e}function j(e,t){null!==t&&(n(S(e)),n(void 0===t||_(t)),e.__wrapper8e3dd93a60__=t)}function D(e,t,n){G.get=n,A(e.prototype,t,G)}function H(e,t){D(e,t,function(){return T(this.__impl4cf1e782hg__[t])})}function x(e,t){e.forEach(function(e){t.forEach(function(t){e.prototype[t]=function(){var e=C(this);return e[t].apply(e,arguments)}})})}var R=new WeakMap,I=new WeakMap,P=Object.create(null),k=t(),A=Object.defineProperty,W=Object.getOwnPropertyNames,F=Object.getOwnPropertyDescriptor,U={value:void 0,configurable:!0,enumerable:!1,writable:!0};W(window);var q=/Firefox/.test(navigator.userAgent),B={get:function(){},set:function(e){},configurable:!0,enumerable:!0},V=function(){var e=Object.getOwnPropertyDescriptor(Node.prototype,"nodeType");return e&&!e.get&&!e.set}(),G={get:void 0,configurable:!0,enumerable:!0};e.addForwardingProperties=c,e.assert=n,e.constructorTable=R,e.defineGetter=D,e.defineWrapGetter=H,e.forwardMethodsToWrapper=x,e.isIdentifierName=d,e.isWrapper=_,e.isWrapperFor=b,e.mixin=r,e.nativePrototypeTable=I,e.oneOf=i,e.registerObject=y,e.registerWrapper=v,e.rewrap=j,e.setWrapper=L,e.unsafeUnwrap=O,e.unwrap=M,e.unwrapIfNeeded=N,e.wrap=T,e.wrapIfNeeded=C,e.wrappers=P}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t,n){return{index:e,removed:t,addedCount:n}}function n(){}var r=0,o=1,i=2,a=3;n.prototype={calcEditDistances:function(e,t,n,r,o,i){for(var a=i-o+1,s=n-t+1,c=new Array(a),l=0;l<a;l++)c[l]=new Array(s),c[l][0]=l;for(var u=0;u<s;u++)c[0][u]=u;for(var l=1;l<a;l++)for(var u=1;u<s;u++)if(this.equals(e[t+u-1],r[o+l-1]))c[l][u]=c[l-1][u-1];else{var d=c[l-1][u]+1,p=c[l][u-1]+1;c[l][u]=d<p?d:p}return c},spliceOperationsFromEditDistances:function(e){for(var t=e.length-1,n=e[0].length-1,s=e[t][n],c=[];t>0||n>0;)if(0!=t)if(0!=n){var l,u=e[t-1][n-1],d=e[t-1][n],p=e[t][n-1];l=d<p?d<u?d:u:p<u?p:u,l==u?(u==s?c.push(r):(c.push(o),s=u),t--,n--):l==d?(c.push(a),t--,s=d):(c.push(i),n--,s=p)}else c.push(a),t--;else c.push(i),n--;return c.reverse(),c},calcSplices:function(e,n,s,c,l,u){var d=0,p=0,h=Math.min(s-n,u-l);if(0==n&&0==l&&(d=this.sharedPrefix(e,c,h)),s==e.length&&u==c.length&&(p=this.sharedSuffix(e,c,h-d)),n+=d,l+=d,s-=p,u-=p,s-n==0&&u-l==0)return[];if(n==s){for(var f=t(n,[],0);l<u;)f.removed.push(c[l++]);return[f]}if(l==u)return[t(n,[],s-n)];for(var m=this.spliceOperationsFromEditDistances(this.calcEditDistances(e,n,s,c,l,u)),f=void 0,w=[],v=n,g=l,b=0;b<m.length;b++)switch(m[b]){case r:f&&(w.push(f),f=void 0),v++,g++;break;case o:f||(f=t(v,[],0)),f.addedCount++,v++,f.removed.push(c[g]),g++;break;case i:f||(f=t(v,[],0)),f.addedCount++,v++;break;case a:f||(f=t(v,[],0)),f.removed.push(c[g]),g++}return f&&w.push(f),w},sharedPrefix:function(e,t,n){for(var r=0;r<n;r++)if(!this.equals(e[r],t[r]))return r;return n},sharedSuffix:function(e,t,n){for(var r=e.length,o=t.length,i=0;i<n&&this.equals(e[--r],t[--o]);)i++;return i},calculateSplices:function(e,t){return this.calcSplices(e,0,e.length,t,0,t.length)},equals:function(e,t){return e===t}},e.ArraySplice=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(){a=!1;var e=i.slice(0);i=[];for(var t=0;t<e.length;t++)(0,e[t])()}function n(e){i.push(e),a||(a=!0,r(t,0))}var r,o=window.MutationObserver,i=[],a=!1;if(o){var s=1,c=new o(t),l=document.createTextNode(s);c.observe(l,{characterData:!0}),r=function(){s=(s+1)%2,l.data=s}}else r=window.setTimeout;e.setEndOfMicrotask=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.scheduled_||(e.scheduled_=!0,f.push(e),m||(u(n),m=!0))}function n(){for(m=!1;f.length;){var e=f;f=[],e.sort(function(e,t){return e.uid_-t.uid_});for(var t=0;t<e.length;t++){var n=e[t];n.scheduled_=!1;var r=n.takeRecords();i(n),r.length&&n.callback_(r,n)}}}function r(e,t){this.type=e,this.target=t,this.addedNodes=new p.NodeList,this.removedNodes=new p.NodeList,this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function o(e,t){for(;e;e=e.parentNode){var n=h.get(e);if(n)for(var r=0;r<n.length;r++){var o=n[r];o.options.subtree&&o.addTransientObserver(t)}}}function i(e){for(var t=0;t<e.nodes_.length;t++){var n=e.nodes_[t],r=h.get(n);if(!r)return;for(var o=0;o<r.length;o++){var i=r[o];i.observer===e&&i.removeTransientObservers()}}}function a(e,n,o){for(var i=Object.create(null),a=Object.create(null),s=e;s;s=s.parentNode){var c=h.get(s);if(c)for(var l=0;l<c.length;l++){var u=c[l],d=u.options;if((s===e||d.subtree)&&("attributes"!==n||d.attributes)&&("attributes"!==n||!d.attributeFilter||null===o.namespace&&d.attributeFilter.indexOf(o.name)!==-1)&&("characterData"!==n||d.characterData)&&("childList"!==n||d.childList)){var p=u.observer;i[p.uid_]=p,("attributes"===n&&d.attributeOldValue||"characterData"===n&&d.characterDataOldValue)&&(a[p.uid_]=o.oldValue)}}}for(var f in i){var p=i[f],m=new r(n,e);"name"in o&&"namespace"in o&&(m.attributeName=o.name,m.attributeNamespace=o.namespace),o.addedNodes&&(m.addedNodes=o.addedNodes),o.removedNodes&&(m.removedNodes=o.removedNodes),o.previousSibling&&(m.previousSibling=o.previousSibling),o.nextSibling&&(m.nextSibling=o.nextSibling),void 0!==a[f]&&(m.oldValue=a[f]),t(p),p.records_.push(m)}}function s(e){if(this.childList=!!e.childList,this.subtree=!!e.subtree,"attributes"in e||!("attributeOldValue"in e||"attributeFilter"in e)?this.attributes=!!e.attributes:this.attributes=!0,"characterDataOldValue"in e&&!("characterData"in e)?this.characterData=!0:this.characterData=!!e.characterData,!this.attributes&&(e.attributeOldValue||"attributeFilter"in e)||!this.characterData&&e.characterDataOldValue)throw new TypeError;if(this.characterData=!!e.characterData,this.attributeOldValue=!!e.attributeOldValue,this.characterDataOldValue=!!e.characterDataOldValue,"attributeFilter"in e){if(null==e.attributeFilter||"object"!=typeof e.attributeFilter)throw new TypeError;this.attributeFilter=w.call(e.attributeFilter)}else this.attributeFilter=null}function c(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++v,this.scheduled_=!1}function l(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}var u=e.setEndOfMicrotask,d=e.wrapIfNeeded,p=e.wrappers,h=new WeakMap,f=[],m=!1,w=Array.prototype.slice,v=0;c.prototype={constructor:c,observe:function(e,t){e=d(e);var n,r=new s(t),o=h.get(e);o||h.set(e,o=[]);for(var i=0;i<o.length;i++)o[i].observer===this&&(n=o[i],n.removeTransientObservers(),n.options=r);n||(n=new l(this,e,r),o.push(n),this.nodes_.push(e))},disconnect:function(){this.nodes_.forEach(function(e){for(var t=h.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}},l.prototype={addTransientObserver:function(e){if(e!==this.target){t(this.observer),this.transientObservedNodes.push(e);var n=h.get(e);n||h.set(e,n=[]),n.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[];for(var t=0;t<e.length;t++)for(var n=e[t],r=h.get(n),o=0;o<r.length;o++)if(r[o]===this){r.splice(o,1);break}}},e.enqueueMutation=a,e.registerTransientObservers=o,e.wrappers.MutationObserver=c,e.wrappers.MutationRecord=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){this.root=e,this.parent=t}function n(e,t){if(e.treeScope_!==t){e.treeScope_=t;for(var r=e.shadowRoot;r;r=r.olderShadowRoot)r.treeScope_.parent=t;for(var o=e.firstChild;o;o=o.nextSibling)n(o,t)}}function r(n){if(n instanceof e.wrappers.Window,n.treeScope_)return n.treeScope_;var o,i=n.parentNode;return o=i?r(i):new t(n,null),n.treeScope_=o}t.prototype={get renderer(){return this.root instanceof e.wrappers.ShadowRoot?e.getRendererForHost(this.root.host):null},contains:function(e){for(;e;e=e.parent)if(e===this)return!0;return!1}},e.TreeScope=t,e.getTreeScope=r,e.setTreeScope=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e instanceof G.ShadowRoot}function n(e){return A(e).root}function r(e,r){var s=[],c=e;for(s.push(c);c;){var l=a(c);if(l&&l.length>0){for(var u=0;u<l.length;u++){var p=l[u];if(i(p)){var h=n(p),f=h.olderShadowRoot;f&&s.push(f)}s.push(p)}c=l[l.length-1]}else if(t(c)){if(d(e,c)&&o(r))break;c=c.host,s.push(c)}else c=c.parentNode,c&&s.push(c)}return s}function o(e){if(!e)return!1;switch(e.type){case"abort":case"error":case"select":case"change":case"load":case"reset":case"resize":case"scroll":case"selectstart":return!0}return!1}function i(e){return e instanceof HTMLShadowElement}function a(t){return e.getDestinationInsertionPoints(t)}function s(e,t){if(0===e.length)return t;t instanceof G.Window&&(t=t.document);for(var n=A(t),r=e[0],o=A(r),i=l(n,o),a=0;a<e.length;a++){var s=e[a];if(A(s)===i)return s}return e[e.length-1]}function c(e){for(var t=[];e;e=e.parent)t.push(e);return t}function l(e,t){for(var n=c(e),r=c(t),o=null;n.length>0&&r.length>0;){var i=n.pop(),a=r.pop();if(i!==a)break;o=i}return o}function u(e,t,n){t instanceof G.Window&&(t=t.document);var o,i=A(t),a=A(n),s=r(n,e),o=l(i,a);o||(o=a.root);for(var c=o;c;c=c.parent)for(var u=0;u<s.length;u++){var d=s[u];if(A(d)===c)return d}return null}function d(e,t){return A(e)===A(t)}function p(e){if(!K.get(e)&&(K.set(e,!0),f(V(e),V(e.target)),P)){var t=P;throw P=null,t}}function h(e){switch(e.type){case"load":case"beforeunload":case"unload":return!0}return!1}function f(t,n){if($.get(t))throw new Error("InvalidStateError");$.set(t,!0),e.renderAllPending();var o,i,a;if(h(t)&&!t.bubbles){var s=n;s instanceof G.Document&&(a=s.defaultView)&&(i=s,o=[])}if(!o)if(n instanceof G.Window)a=n,o=[];else if(o=r(n,t),!h(t)){var s=o[o.length-1];s instanceof G.Document&&(a=s.defaultView)}return ne.set(t,o),m(t,o,a,i)&&w(t,o,a,i)&&v(t,o,a,i),J.set(t,re),Y["delete"](t,null),$["delete"](t),t.defaultPrevented}function m(e,t,n,r){var o=oe;if(n&&!g(n,e,o,t,r))return!1;for(var i=t.length-1;i>0;i--)if(!g(t[i],e,o,t,r))return!1;return!0}function w(e,t,n,r){var o=ie,i=t[0]||n;return g(i,e,o,t,r)}function v(e,t,n,r){for(var o=ae,i=1;i<t.length;i++)if(!g(t[i],e,o,t,r))return;n&&t.length>0&&g(n,e,o,t,r)}function g(e,t,n,r,o){var i=z.get(e);if(!i)return!0;var a=o||s(r,e);if(a===e){if(n===oe)return!0;n===ae&&(n=ie)}else if(n===ae&&!t.bubbles)return!0;if("relatedTarget"in t){var c=B(t),l=c.relatedTarget;if(l){if(l instanceof Object&&l.addEventListener){var d=V(l),p=u(t,e,d);if(p===a)return!0}else p=null;Z.set(t,p)}}J.set(t,n);var h=t.type,f=!1;X.set(t,a),Y.set(t,e),i.depth++;for(var m=0,w=i.length;m<w;m++){var v=i[m];if(v.removed)f=!0;else if(!(v.type!==h||!v.capture&&n===oe||v.capture&&n===ae))try{if("function"==typeof v.handler?v.handler.call(e,t):v.handler.handleEvent(t),ee.get(t))return!1}catch(g){P||(P=g)}}if(i.depth--,f&&0===i.depth){var b=i.slice();i.length=0;for(var m=0;m<b.length;m++)b[m].removed||i.push(b[m])}return!Q.get(t)}function b(e,t,n){this.type=e,this.handler=t,this.capture=Boolean(n)}function y(e,t){if(!(e instanceof se))return V(T(se,"Event",e,t));var n=e;return be||"beforeunload"!==n.type||this instanceof M?void U(n,this):new M(n)}function E(e){return e&&e.relatedTarget?Object.create(e,{relatedTarget:{value:B(e.relatedTarget)}}):e}function _(e,t,n){var r=window[e],o=function(t,n){return t instanceof r?void U(t,this):V(T(r,e,t,n))};if(o.prototype=Object.create(t.prototype),n&&W(o.prototype,n),r)try{F(r,o,new r("temp"))}catch(i){F(r,o,document.createEvent(e))}return o}function S(e,t){return function(){arguments[t]=B(arguments[t]);var n=B(this);n[e].apply(n,arguments)}}function T(e,t,n,r){if(ve)return new e(n,E(r));var o=B(document.createEvent(t)),i=we[t],a=[n];return Object.keys(i).forEach(function(e){var t=null!=r&&e in r?r[e]:i[e];"relatedTarget"===e&&(t=B(t)),a.push(t)}),o["init"+t].apply(o,a),o}function M(e){y.call(this,e)}function O(e){return"function"==typeof e||e&&e.handleEvent}function L(e){switch(e){case"DOMAttrModified":case"DOMAttributeNameChanged":case"DOMCharacterDataModified":case"DOMElementNameChanged":case"DOMNodeInserted":case"DOMNodeInsertedIntoDocument":case"DOMNodeRemoved":case"DOMNodeRemovedFromDocument":case"DOMSubtreeModified":return!0}return!1}function N(e){U(e,this)}function C(e){return e instanceof G.ShadowRoot&&(e=e.host),B(e)}function j(e,t){var n=z.get(e);if(n)for(var r=0;r<n.length;r++)if(!n[r].removed&&n[r].type===t)return!0;return!1}function D(e,t){for(var n=B(e);n;n=n.parentNode)if(j(V(n),t))return!0;return!1}function H(e){k(e,Ee)}function x(t,n,o,i){e.renderAllPending();var a=V(_e.call(q(n),o,i));if(!a)return null;var c=r(a,null),l=c.lastIndexOf(t);return l==-1?null:(c=c.slice(0,l),s(c,t))}function R(e){return function(){var t=te.get(this);return t&&t[e]&&t[e].value||null}}function I(e){var t=e.slice(2);return function(n){var r=te.get(this);r||(r=Object.create(null),te.set(this,r));var o=r[e];if(o&&this.removeEventListener(t,o.wrapped,!1),"function"==typeof n){var i=function(t){var r=n.call(this,t);r===!1?t.preventDefault():"onbeforeunload"===e&&"string"==typeof r&&(t.returnValue=r)};this.addEventListener(t,i,!1),r[e]={value:n,wrapped:i}}}}var P,k=e.forwardMethodsToWrapper,A=e.getTreeScope,W=e.mixin,F=e.registerWrapper,U=e.setWrapper,q=e.unsafeUnwrap,B=e.unwrap,V=e.wrap,G=e.wrappers,z=(new WeakMap,new WeakMap),K=new WeakMap,$=new WeakMap,X=new WeakMap,Y=new WeakMap,Z=new WeakMap,J=new WeakMap,Q=new WeakMap,ee=new WeakMap,te=new WeakMap,ne=new WeakMap,re=0,oe=1,ie=2,ae=3;b.prototype={equals:function(e){return this.handler===e.handler&&this.type===e.type&&this.capture===e.capture},get removed(){return null===this.handler},remove:function(){this.handler=null}};var se=window.Event;se.prototype.polymerBlackList_={returnValue:!0,keyLocation:!0},y.prototype={get target(){return X.get(this)},get currentTarget(){return Y.get(this)},get eventPhase(){return J.get(this)},get path(){var e=ne.get(this);return e?e.slice():[]},stopPropagation:function(){Q.set(this,!0)},stopImmediatePropagation:function(){Q.set(this,!0),ee.set(this,!0)}};var ce=function(){var e=document.createEvent("Event");return e.initEvent("test",!0,!0),e.preventDefault(),e.defaultPrevented}();ce||(y.prototype.preventDefault=function(){this.cancelable&&(q(this).preventDefault(),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}),F(se,y,document.createEvent("Event"));var le=_("UIEvent",y),ue=_("CustomEvent",y),de={get relatedTarget(){var e=Z.get(this);return void 0!==e?e:V(B(this).relatedTarget)}},pe=W({initMouseEvent:S("initMouseEvent",14)},de),he=W({initFocusEvent:S("initFocusEvent",5)},de),fe=_("MouseEvent",le,pe),me=_("FocusEvent",le,he),we=Object.create(null),ve=function(){try{new window.FocusEvent("focus")}catch(e){return!1}return!0}();if(!ve){var ge=function(e,t,n){if(n){var r=we[n];t=W(W({},r),t)}we[e]=t};ge("Event",{bubbles:!1,cancelable:!1}),ge("CustomEvent",{detail:null},"Event"),ge("UIEvent",{view:null,detail:0},"Event"),ge("MouseEvent",{screenX:0,screenY:0,clientX:0,clientY:0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:null},"UIEvent"),ge("FocusEvent",{relatedTarget:null},"UIEvent")}var be=window.BeforeUnloadEvent;M.prototype=Object.create(y.prototype),W(M.prototype,{get returnValue(){return q(this).returnValue},set returnValue(e){q(this).returnValue=e}}),be&&F(be,M);var ye=window.EventTarget,Ee=["addEventListener","removeEventListener","dispatchEvent"];[Node,Window].forEach(function(e){var t=e.prototype;Ee.forEach(function(e){Object.defineProperty(t,e+"_",{value:t[e]})})}),N.prototype={addEventListener:function(e,t,n){if(O(t)&&!L(e)){var r=new b(e,t,n),o=z.get(this);if(o){for(var i=0;i<o.length;i++)if(r.equals(o[i]))return}else o=[],o.depth=0,z.set(this,o);o.push(r);var a=C(this);a.addEventListener_(e,p,!0)}},removeEventListener:function(e,t,n){n=Boolean(n);var r=z.get(this);if(r){for(var o=0,i=!1,a=0;a<r.length;a++)r[a].type===e&&r[a].capture===n&&(o++,r[a].handler===t&&(i=!0,r[a].remove()));if(i&&1===o){var s=C(this);s.removeEventListener_(e,p,!0)}}},dispatchEvent:function(t){var n=B(t),r=n.type;K.set(n,!1),e.renderAllPending();var o;D(this,r)||(o=function(){},this.addEventListener(r,o,!0));try{return B(this).dispatchEvent_(n)}finally{o&&this.removeEventListener(r,o,!0)}}},ye&&F(ye,N);var _e=document.elementFromPoint;e.elementFromPoint=x,e.getEventHandlerGetter=R,e.getEventHandlerSetter=I,e.wrapEventTargetMethods=H,e.wrappers.BeforeUnloadEvent=M,e.wrappers.CustomEvent=ue,e.wrappers.Event=y,e.wrappers.EventTarget=N,e.wrappers.FocusEvent=me,e.wrappers.MouseEvent=fe,e.wrappers.UIEvent=le}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,m)}function n(e){l(e,this)}function r(){this.length=0,t(this,"length")}function o(e){for(var t=new r,o=0;o<e.length;o++)t[o]=new n(e[o]);return t.length=o,t}function i(e){a.call(this,e)}var a=e.wrappers.UIEvent,s=e.mixin,c=e.registerWrapper,l=e.setWrapper,u=e.unsafeUnwrap,d=e.wrap,p=window.TouchEvent;if(p){var h;try{h=document.createEvent("TouchEvent")}catch(f){return}var m={enumerable:!1};n.prototype={get target(){return d(u(this).target)}};var w={configurable:!0,enumerable:!0,get:null};["clientX","clientY","screenX","screenY","pageX","pageY","identifier","webkitRadiusX","webkitRadiusY","webkitRotationAngle","webkitForce"].forEach(function(e){w.get=function(){return u(this)[e]},Object.defineProperty(n.prototype,e,w)}),r.prototype={item:function(e){return this[e]}},i.prototype=Object.create(a.prototype),s(i.prototype,{get touches(){return o(u(this).touches)},get targetTouches(){return o(u(this).targetTouches)},get changedTouches(){return o(u(this).changedTouches)},initTouchEvent:function(){throw new Error("Not implemented")}}),c(p,i,h),e.wrappers.Touch=n,e.wrappers.TouchEvent=i,e.wrappers.TouchList=r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,s)}function n(){this.length=0,t(this,"length")}function r(e){if(null==e)return e;for(var t=new n,r=0,o=e.length;r<o;r++)t[r]=a(e[r]);return t.length=o,t}function o(e,t){e.prototype[t]=function(){return r(i(this)[t].apply(i(this),arguments))}}var i=e.unsafeUnwrap,a=e.wrap,s={enumerable:!1};n.prototype={item:function(e){return this[e]}},t(n.prototype,"item"),e.wrappers.NodeList=n,e.addWrapNodeListMethod=o,e.wrapNodeList=r}(window.ShadowDOMPolyfill),function(e){"use strict";e.wrapHTMLCollection=e.wrapNodeList,e.wrappers.HTMLCollection=e.wrappers.NodeList}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){O(e instanceof _)}function n(e){var t=new T;return t[0]=e,t.length=1,t}function r(e,t,n){N(t,"childList",{removedNodes:n,previousSibling:e.previousSibling,nextSibling:e.nextSibling})}function o(e,t){N(e,"childList",{removedNodes:t})}function i(e,t,r,o){if(e instanceof DocumentFragment){var i=s(e);U=!0;for(var a=i.length-1;a>=0;a--)e.removeChild(i[a]),i[a].parentNode_=t;U=!1;for(var a=0;a<i.length;a++)i[a].previousSibling_=i[a-1]||r,i[a].nextSibling_=i[a+1]||o;return r&&(r.nextSibling_=i[0]),o&&(o.previousSibling_=i[i.length-1]),i}var i=n(e),c=e.parentNode;return c&&c.removeChild(e),e.parentNode_=t,e.previousSibling_=r,e.nextSibling_=o,r&&(r.nextSibling_=e),o&&(o.previousSibling_=e),i}function a(e){if(e instanceof DocumentFragment)return s(e);var t=n(e),o=e.parentNode;return o&&r(e,o,t),t}function s(e){for(var t=new T,n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t.length=n,o(e,t),t}function c(e){return e}function l(e,t){R(e,t),e.nodeIsInserted_()}function u(e,t){for(var n=C(t),r=0;r<e.length;r++)l(e[r],n)}function d(e){R(e,new M(e,null))}function p(e){for(var t=0;t<e.length;t++)d(e[t])}function h(e,t){var n=e.nodeType===_.DOCUMENT_NODE?e:e.ownerDocument;n!==t.ownerDocument&&n.adoptNode(t)}function f(t,n){if(n.length){var r=t.ownerDocument;if(r!==n[0].ownerDocument)for(var o=0;o<n.length;o++)e.adoptNodeNoRemove(n[o],r)}}function m(e,t){f(e,t);var n=t.length;if(1===n)return P(t[0]);for(var r=P(e.ownerDocument.createDocumentFragment()),o=0;o<n;o++)r.appendChild(P(t[o]));return r}function w(e){if(void 0!==e.firstChild_)for(var t=e.firstChild_;t;){var n=t;t=t.nextSibling_,n.parentNode_=n.previousSibling_=n.nextSibling_=void 0}e.firstChild_=e.lastChild_=void 0}function v(e){if(e.invalidateShadowRenderer()){for(var t=e.firstChild;t;){O(t.parentNode===e);var n=t.nextSibling,r=P(t),o=r.parentNode;o&&X.call(o,r),t.previousSibling_=t.nextSibling_=t.parentNode_=null,t=n}e.firstChild_=e.lastChild_=null}else for(var n,i=P(e),a=i.firstChild;a;)n=a.nextSibling,X.call(i,a),a=n}function g(e){var t=e.parentNode;return t&&t.invalidateShadowRenderer()}function b(e){for(var t,n=0;n<e.length;n++)t=e[n],t.parentNode.removeChild(t)}function y(e,t,n){var r;if(r=A(n?q.call(n,I(e),!1):B.call(I(e),!1)),t){for(var o=e.firstChild;o;o=o.nextSibling)r.appendChild(y(o,!0,n));if(e instanceof F.HTMLTemplateElement)for(var i=r.content,o=e.content.firstChild;o;o=o.nextSibling)i.appendChild(y(o,!0,n))}return r}function E(e,t){if(!t||C(e)!==C(t))return!1;for(var n=t;n;n=n.parentNode)if(n===e)return!0;return!1}function _(e){O(e instanceof V),S.call(this,e),this.parentNode_=void 0,this.firstChild_=void 0,this.lastChild_=void 0,this.nextSibling_=void 0,this.previousSibling_=void 0,this.treeScope_=void 0}var S=e.wrappers.EventTarget,T=e.wrappers.NodeList,M=e.TreeScope,O=e.assert,L=e.defineWrapGetter,N=e.enqueueMutation,C=e.getTreeScope,j=e.isWrapper,D=e.mixin,H=e.registerTransientObservers,x=e.registerWrapper,R=e.setTreeScope,I=e.unsafeUnwrap,P=e.unwrap,k=e.unwrapIfNeeded,A=e.wrap,W=e.wrapIfNeeded,F=e.wrappers,U=!1,q=document.importNode,B=window.Node.prototype.cloneNode,V=window.Node,G=window.DocumentFragment,z=(V.prototype.appendChild,V.prototype.compareDocumentPosition),K=V.prototype.isEqualNode,$=V.prototype.insertBefore,X=V.prototype.removeChild,Y=V.prototype.replaceChild,Z=/Trident|Edge/.test(navigator.userAgent),J=Z?function(e,t){try{X.call(e,t)}catch(n){if(!(e instanceof G))throw n}}:function(e,t){X.call(e,t)};_.prototype=Object.create(S.prototype),D(_.prototype,{appendChild:function(e){return this.insertBefore(e,null)},insertBefore:function(e,n){t(e);var r;n?j(n)?r=P(n):(r=n,n=A(r)):(n=null,r=null),n&&O(n.parentNode===this);var o,s=n?n.previousSibling:this.lastChild,c=!this.invalidateShadowRenderer()&&!g(e);if(o=c?a(e):i(e,this,s,n),c)h(this,e),w(this),$.call(I(this),P(e),r);else{s||(this.firstChild_=o[0]),n||(this.lastChild_=o[o.length-1],void 0===this.firstChild_&&(this.firstChild_=this.firstChild));var l=r?r.parentNode:I(this);l?$.call(l,m(this,o),r):f(this,o)}return N(this,"childList",{addedNodes:o,nextSibling:n,previousSibling:s}),u(o,this),e},removeChild:function(e){if(t(e),e.parentNode!==this){for(var r=!1,o=(this.childNodes,this.firstChild);o;o=o.nextSibling)if(o===e){r=!0;break}if(!r)throw new Error("NotFoundError")}var i=P(e),a=e.nextSibling,s=e.previousSibling;if(this.invalidateShadowRenderer()){var c=this.firstChild,l=this.lastChild,u=i.parentNode;u&&J(u,i),c===e&&(this.firstChild_=a),l===e&&(this.lastChild_=s),s&&(s.nextSibling_=a),a&&(a.previousSibling_=s),e.previousSibling_=e.nextSibling_=e.parentNode_=void 0}else w(this),J(I(this),i);return U||N(this,"childList",{removedNodes:n(e),nextSibling:a,previousSibling:s}),H(this,e),e},replaceChild:function(e,r){t(e);var o;if(j(r)?o=P(r):(o=r,r=A(o)),r.parentNode!==this)throw new Error("NotFoundError");var s,c=r.nextSibling,l=r.previousSibling,p=!this.invalidateShadowRenderer()&&!g(e);return p?s=a(e):(c===e&&(c=e.nextSibling),s=i(e,this,l,c)),p?(h(this,e),w(this),Y.call(I(this),P(e),o)):(this.firstChild===r&&(this.firstChild_=s[0]),this.lastChild===r&&(this.lastChild_=s[s.length-1]),r.previousSibling_=r.nextSibling_=r.parentNode_=void 0,o.parentNode&&Y.call(o.parentNode,m(this,s),o)),N(this,"childList",{addedNodes:s,removedNodes:n(r),nextSibling:c,previousSibling:l}),d(r),u(s,this),r},nodeIsInserted_:function(){for(var e=this.firstChild;e;e=e.nextSibling)e.nodeIsInserted_()},hasChildNodes:function(){return null!==this.firstChild},get parentNode(){return void 0!==this.parentNode_?this.parentNode_:A(I(this).parentNode)},get firstChild(){return void 0!==this.firstChild_?this.firstChild_:A(I(this).firstChild)},get lastChild(){return void 0!==this.lastChild_?this.lastChild_:A(I(this).lastChild)},get nextSibling(){return void 0!==this.nextSibling_?this.nextSibling_:A(I(this).nextSibling)},get previousSibling(){return void 0!==this.previousSibling_?this.previousSibling_:A(I(this).previousSibling)},get parentElement(){for(var e=this.parentNode;e&&e.nodeType!==_.ELEMENT_NODE;)e=e.parentNode;return e},get textContent(){for(var e="",t=this.firstChild;t;t=t.nextSibling)t.nodeType!=_.COMMENT_NODE&&(e+=t.textContent);return e},set textContent(e){null==e&&(e="");var t=c(this.childNodes);if(this.invalidateShadowRenderer()){if(v(this),""!==e){var n=I(this).ownerDocument.createTextNode(e);this.appendChild(n)}}else w(this),I(this).textContent=e;var r=c(this.childNodes);N(this,"childList",{addedNodes:r,removedNodes:t}),p(t),u(r,this)},get childNodes(){for(var e=new T,t=0,n=this.firstChild;n;n=n.nextSibling)e[t++]=n;return e.length=t,e},cloneNode:function(e){return y(this,e)},contains:function(e){return E(this,W(e))},compareDocumentPosition:function(e){return z.call(I(this),k(e))},isEqualNode:function(e){return K.call(I(this),k(e))},normalize:function(){for(var e,t,n=c(this.childNodes),r=[],o="",i=0;i<n.length;i++)t=n[i],t.nodeType===_.TEXT_NODE?e||t.data.length?e?(o+=t.data,r.push(t)):e=t:this.removeChild(t):(e&&r.length&&(e.data+=o,b(r)),r=[],o="",e=null,t.childNodes.length&&t.normalize());e&&r.length&&(e.data+=o,b(r))}}),L(_,"ownerDocument"),x(V,_,document.createDocumentFragment()),delete _.prototype.querySelector,delete _.prototype.querySelectorAll,_.prototype=D(Object.create(S.prototype),_.prototype),e.cloneNode=y,e.nodeWasAdded=l,e.nodeWasRemoved=d,e.nodesWereAdded=u,e.nodesWereRemoved=p,e.originalInsertBefore=$,e.originalRemoveChild=X,e.snapshotNodeList=c,e.wrappers.Node=_}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n,r,o){for(var i=null,a=null,s=0,c=t.length;s<c;s++)i=b(t[s]),!o&&(a=v(i).root)&&a instanceof e.wrappers.ShadowRoot||(r[n++]=i);return n}function n(e){return String(e).replace(/\/deep\/|::shadow|>>>/g," ")}function r(e){return String(e).replace(/:host\(([^\s]+)\)/g,"$1").replace(/([^\s]):host/g,"$1").replace(":host","*").replace(/\^|\/shadow\/|\/shadow-deep\/|::shadow|\/deep\/|::content|>>>/g," ")}function o(e,t){for(var n,r=e.firstElementChild;r;){if(r.matches(t))return r;if(n=o(r,t))return n;r=r.nextElementSibling}return null}function i(e,t){return e.matches(t)}function a(e,t,n){var r=e.localName;return r===t||r===n&&e.namespaceURI===j}function s(){return!0}function c(e,t,n){return e.localName===n}function l(e,t){return e.namespaceURI===t}function u(e,t,n){return e.namespaceURI===t&&e.localName===n}function d(e,t,n,r,o,i){for(var a=e.firstElementChild;a;)r(a,o,i)&&(n[t++]=a),t=d(a,t,n,r,o,i),a=a.nextElementSibling;return t}function p(n,r,o,i,a){var s,c=g(this),l=v(this).root;if(l instanceof e.wrappers.ShadowRoot)return d(this,r,o,n,i,null);if(c instanceof N)s=S.call(c,i);else{if(!(c instanceof C))return d(this,r,o,n,i,null);s=_.call(c,i)}return t(s,r,o,a)}function h(n,r,o,i,a){var s,c=g(this),l=v(this).root;if(l instanceof e.wrappers.ShadowRoot)return d(this,r,o,n,i,a);if(c instanceof N)s=M.call(c,i,a);else{if(!(c instanceof C))return d(this,r,o,n,i,a);s=T.call(c,i,a)}return t(s,r,o,!1)}function f(n,r,o,i,a){var s,c=g(this),l=v(this).root;if(l instanceof e.wrappers.ShadowRoot)return d(this,r,o,n,i,a);if(c instanceof N)s=L.call(c,i,a);else{if(!(c instanceof C))return d(this,r,o,n,i,a);s=O.call(c,i,a)}return t(s,r,o,!1)}var m=e.wrappers.HTMLCollection,w=e.wrappers.NodeList,v=e.getTreeScope,g=e.unsafeUnwrap,b=e.wrap,y=document.querySelector,E=document.documentElement.querySelector,_=document.querySelectorAll,S=document.documentElement.querySelectorAll,T=document.getElementsByTagName,M=document.documentElement.getElementsByTagName,O=document.getElementsByTagNameNS,L=document.documentElement.getElementsByTagNameNS,N=window.Element,C=window.HTMLDocument||window.Document,j="http://www.w3.org/1999/xhtml",D={
+querySelector:function(t){var r=n(t),i=r!==t;t=r;var a,s=g(this),c=v(this).root;if(c instanceof e.wrappers.ShadowRoot)return o(this,t);if(s instanceof N)a=b(E.call(s,t));else{if(!(s instanceof C))return o(this,t);a=b(y.call(s,t))}return a&&!i&&(c=v(a).root)&&c instanceof e.wrappers.ShadowRoot?o(this,t):a},querySelectorAll:function(e){var t=n(e),r=t!==e;e=t;var o=new w;return o.length=p.call(this,i,0,o,e,r),o}},H={matches:function(t){return t=r(t),e.originalMatches.call(g(this),t)}},x={getElementsByTagName:function(e){var t=new m,n="*"===e?s:a;return t.length=h.call(this,n,0,t,e,e.toLowerCase()),t},getElementsByClassName:function(e){return this.querySelectorAll("."+e)},getElementsByTagNameNS:function(e,t){var n=new m,r=null;return r="*"===e?"*"===t?s:c:"*"===t?l:u,n.length=f.call(this,r,0,n,e||null,t),n}};e.GetElementsByInterface=x,e.SelectorsInterface=D,e.MatchesInterface=H}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.nextSibling;return e}function n(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.previousSibling;return e}var r=e.wrappers.NodeList,o={get firstElementChild(){return t(this.firstChild)},get lastElementChild(){return n(this.lastChild)},get childElementCount(){for(var e=0,t=this.firstElementChild;t;t=t.nextElementSibling)e++;return e},get children(){for(var e=new r,t=0,n=this.firstElementChild;n;n=n.nextElementSibling)e[t++]=n;return e.length=t,e},remove:function(){var e=this.parentNode;e&&e.removeChild(this)}},i={get nextElementSibling(){return t(this.nextSibling)},get previousElementSibling(){return n(this.previousSibling)}},a={getElementById:function(e){return/[ \t\n\r\f]/.test(e)?null:this.querySelector('[id="'+e+'"]')}};e.ChildNodeInterface=i,e.NonElementParentNodeInterface=a,e.ParentNodeInterface=o}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}var n=e.ChildNodeInterface,r=e.wrappers.Node,o=e.enqueueMutation,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=window.CharacterData;t.prototype=Object.create(r.prototype),i(t.prototype,{get nodeValue(){return this.data},set nodeValue(e){this.data=e},get textContent(){return this.data},set textContent(e){this.data=e},get data(){return s(this).data},set data(e){var t=s(this).data;o(this,"characterData",{oldValue:t}),s(this).data=e}}),i(t.prototype,n),a(c,t,document.createTextNode("")),e.wrappers.CharacterData=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e>>>0}function n(e){r.call(this,e)}var r=e.wrappers.CharacterData,o=(e.enqueueMutation,e.mixin),i=e.registerWrapper,a=window.Text;n.prototype=Object.create(r.prototype),o(n.prototype,{splitText:function(e){e=t(e);var n=this.data;if(e>n.length)throw new Error("IndexSizeError");var r=n.slice(0,e),o=n.slice(e);this.data=r;var i=this.ownerDocument.createTextNode(o);return this.parentNode&&this.parentNode.insertBefore(i,this.nextSibling),i}}),i(a,n,document.createTextNode("")),e.wrappers.Text=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return i(e).getAttribute("class")}function n(e,t){a(e,"attributes",{name:"class",namespace:null,oldValue:t})}function r(t){e.invalidateRendererBasedOnAttribute(t,"class")}function o(e,o,i){var a=e.ownerElement_;if(null==a)return o.apply(e,i);var s=t(a),c=o.apply(e,i);return t(a)!==s&&(n(a,s),r(a)),c}if(!window.DOMTokenList)return void console.warn("Missing DOMTokenList prototype, please include a compatible classList polyfill such as http://goo.gl/uTcepH.");var i=e.unsafeUnwrap,a=e.enqueueMutation,s=DOMTokenList.prototype.add;DOMTokenList.prototype.add=function(){o(this,s,arguments)};var c=DOMTokenList.prototype.remove;DOMTokenList.prototype.remove=function(){o(this,c,arguments)};var l=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(){return o(this,l,arguments)}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n){var r=t.parentNode;if(r&&r.shadowRoot){var o=e.getRendererForHost(r);o.dependsOnAttribute(n)&&o.invalidate()}}function n(e,t,n){u(e,"attributes",{name:t,namespace:null,oldValue:n})}function r(e){a.call(this,e)}var o=e.ChildNodeInterface,i=e.GetElementsByInterface,a=e.wrappers.Node,s=e.ParentNodeInterface,c=e.SelectorsInterface,l=e.MatchesInterface,u=(e.addWrapNodeListMethod,e.enqueueMutation),d=e.mixin,p=(e.oneOf,e.registerWrapper),h=e.unsafeUnwrap,f=e.wrappers,m=window.Element,w=["matches","mozMatchesSelector","msMatchesSelector","webkitMatchesSelector"].filter(function(e){return m.prototype[e]}),v=w[0],g=m.prototype[v],b=new WeakMap;r.prototype=Object.create(a.prototype),d(r.prototype,{createShadowRoot:function(){var t=new f.ShadowRoot(this);h(this).polymerShadowRoot_=t;var n=e.getRendererForHost(this);return n.invalidate(),t},get shadowRoot(){return h(this).polymerShadowRoot_||null},setAttribute:function(e,r){var o=h(this).getAttribute(e);h(this).setAttribute(e,r),n(this,e,o),t(this,e)},removeAttribute:function(e){var r=h(this).getAttribute(e);h(this).removeAttribute(e),n(this,e,r),t(this,e)},get classList(){var e=b.get(this);if(!e){if(e=h(this).classList,!e)return;e.ownerElement_=this,b.set(this,e)}return e},get className(){return h(this).className},set className(e){this.setAttribute("class",e)},get id(){return h(this).id},set id(e){this.setAttribute("id",e)}}),w.forEach(function(e){"matches"!==e&&(r.prototype[e]=function(e){return this.matches(e)})}),m.prototype.webkitCreateShadowRoot&&(r.prototype.webkitCreateShadowRoot=r.prototype.createShadowRoot),d(r.prototype,o),d(r.prototype,i),d(r.prototype,s),d(r.prototype,c),d(r.prototype,l),p(m,r,document.createElementNS(null,"x")),e.invalidateRendererBasedOnAttribute=t,e.matchesNames=w,e.originalMatches=g,e.wrappers.Element=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";case" ":return"&nbsp;"}}function n(e){return e.replace(L,t)}function r(e){return e.replace(N,t)}function o(e){for(var t={},n=0;n<e.length;n++)t[e[n]]=!0;return t}function i(e){if(e.namespaceURI!==D)return!0;var t=e.ownerDocument.doctype;return t&&t.publicId&&t.systemId}function a(e,t){switch(e.nodeType){case Node.ELEMENT_NODE:for(var o,a=e.tagName.toLowerCase(),c="<"+a,l=e.attributes,u=0;o=l[u];u++)c+=" "+o.name+'="'+n(o.value)+'"';return C[a]?(i(e)&&(c+="/"),c+">"):c+">"+s(e)+"</"+a+">";case Node.TEXT_NODE:var d=e.data;return t&&j[t.localName]?d:r(d);case Node.COMMENT_NODE:return"<!--"+e.data+"-->";default:throw console.error(e),new Error("not implemented")}}function s(e){e instanceof O.HTMLTemplateElement&&(e=e.content);for(var t="",n=e.firstChild;n;n=n.nextSibling)t+=a(n,e);return t}function c(e,t,n){var r=n||"div";e.textContent="";var o=T(e.ownerDocument.createElement(r));o.innerHTML=t;for(var i;i=o.firstChild;)e.appendChild(M(i))}function l(e){m.call(this,e)}function u(e,t){var n=T(e.cloneNode(!1));n.innerHTML=t;for(var r,o=T(document.createDocumentFragment());r=n.firstChild;)o.appendChild(r);return M(o)}function d(t){return function(){return e.renderAllPending(),S(this)[t]}}function p(e){w(l,e,d(e))}function h(t){Object.defineProperty(l.prototype,t,{get:d(t),set:function(n){e.renderAllPending(),S(this)[t]=n},configurable:!0,enumerable:!0})}function f(t){Object.defineProperty(l.prototype,t,{value:function(){return e.renderAllPending(),S(this)[t].apply(S(this),arguments)},configurable:!0,enumerable:!0})}var m=e.wrappers.Element,w=e.defineGetter,v=e.enqueueMutation,g=e.mixin,b=e.nodesWereAdded,y=e.nodesWereRemoved,E=e.registerWrapper,_=e.snapshotNodeList,S=e.unsafeUnwrap,T=e.unwrap,M=e.wrap,O=e.wrappers,L=/[&\u00A0"]/g,N=/[&\u00A0<>]/g,C=o(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),j=o(["style","script","xmp","iframe","noembed","noframes","plaintext","noscript"]),D="http://www.w3.org/1999/xhtml",H=/MSIE/.test(navigator.userAgent),x=window.HTMLElement,R=window.HTMLTemplateElement;l.prototype=Object.create(m.prototype),g(l.prototype,{get innerHTML(){return s(this)},set innerHTML(e){if(H&&j[this.localName])return void(this.textContent=e);var t=_(this.childNodes);this.invalidateShadowRenderer()?this instanceof O.HTMLTemplateElement?c(this.content,e):c(this,e,this.tagName):!R&&this instanceof O.HTMLTemplateElement?c(this.content,e):S(this).innerHTML=e;var n=_(this.childNodes);v(this,"childList",{addedNodes:n,removedNodes:t}),y(t),b(n,this)},get outerHTML(){return a(this,this.parentNode)},set outerHTML(e){var t=this.parentNode;if(t){t.invalidateShadowRenderer();var n=u(t,e);t.replaceChild(n,this)}},insertAdjacentHTML:function(e,t){var n,r;switch(String(e).toLowerCase()){case"beforebegin":n=this.parentNode,r=this;break;case"afterend":n=this.parentNode,r=this.nextSibling;break;case"afterbegin":n=this,r=this.firstChild;break;case"beforeend":n=this,r=null;break;default:return}var o=u(n,t);n.insertBefore(o,r)},get hidden(){return this.hasAttribute("hidden")},set hidden(e){e?this.setAttribute("hidden",""):this.removeAttribute("hidden")}}),["clientHeight","clientLeft","clientTop","clientWidth","offsetHeight","offsetLeft","offsetTop","offsetWidth","scrollHeight","scrollWidth"].forEach(p),["scrollLeft","scrollTop"].forEach(h),["focus","getBoundingClientRect","getClientRects","scrollIntoView"].forEach(f),E(x,l,document.createElement("b")),e.wrappers.HTMLElement=l,e.getInnerHTML=s,e.setInnerHTML=c}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.HTMLCanvasElement;t.prototype=Object.create(n.prototype),r(t.prototype,{getContext:function(){var e=i(this).getContext.apply(i(this),arguments);return e&&a(e)}}),o(s,t,document.createElement("canvas")),e.wrappers.HTMLCanvasElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=window.HTMLContentElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get select(){return this.getAttribute("select")},set select(e){this.setAttribute("select",e)},setAttribute:function(e,t){n.prototype.setAttribute.call(this,e,t),"select"===String(e).toLowerCase()&&this.invalidateShadowRenderer(!0)}}),i&&o(i,t),e.wrappers.HTMLContentElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=window.HTMLFormElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get elements(){return i(a(this).elements)}}),o(s,t,document.createElement("form")),e.wrappers.HTMLFormElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e,t){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var o=i(document.createElement("img"));r.call(this,o),a(o,this),void 0!==e&&(o.width=e),void 0!==t&&(o.height=t)}var r=e.wrappers.HTMLElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLImageElement;t.prototype=Object.create(r.prototype),o(s,t,document.createElement("img")),n.prototype=t.prototype,e.wrappers.HTMLImageElement=t,e.wrappers.Image=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=(e.mixin,e.wrappers.NodeList,e.registerWrapper),o=window.HTMLShadowElement;t.prototype=Object.create(n.prototype),t.prototype.constructor=t,o&&r(o,t),e.wrappers.HTMLShadowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){if(!e.defaultView)return e;var t=d.get(e);if(!t){for(t=e.implementation.createHTMLDocument("");t.lastChild;)t.removeChild(t.lastChild);d.set(e,t)}return t}function n(e){for(var n,r=t(e.ownerDocument),o=c(r.createDocumentFragment());n=e.firstChild;)o.appendChild(n);return o}function r(e){if(o.call(this,e),!p){var t=n(e);u.set(this,l(t))}}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=e.unwrap,l=e.wrap,u=new WeakMap,d=new WeakMap,p=window.HTMLTemplateElement;r.prototype=Object.create(o.prototype),i(r.prototype,{constructor:r,get content(){return p?l(s(this).content):u.get(this)}}),p&&a(p,r),e.wrappers.HTMLTemplateElement=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.registerWrapper,o=window.HTMLMediaElement;o&&(t.prototype=Object.create(n.prototype),r(o,t,document.createElement("audio")),e.wrappers.HTMLMediaElement=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var t=i(document.createElement("audio"));r.call(this,t),a(t,this),t.setAttribute("preload","auto"),void 0!==e&&t.setAttribute("src",e)}var r=e.wrappers.HTMLMediaElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLAudioElement;s&&(t.prototype=Object.create(r.prototype),o(s,t,document.createElement("audio")),n.prototype=t.prototype,e.wrappers.HTMLAudioElement=t,e.wrappers.Audio=n)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e.replace(/\s+/g," ").trim()}function n(e){o.call(this,e)}function r(e,t,n,i){if(!(this instanceof r))throw new TypeError("DOM object constructor cannot be called as a function.");var a=c(document.createElement("option"));o.call(this,a),s(a,this),void 0!==e&&(a.text=e),void 0!==t&&a.setAttribute("value",t),n===!0&&a.setAttribute("selected",""),a.selected=i===!0}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.rewrap,c=e.unwrap,l=e.wrap,u=window.HTMLOptionElement;n.prototype=Object.create(o.prototype),i(n.prototype,{get text(){return t(this.textContent)},set text(e){this.textContent=t(String(e))},get form(){return l(c(this).form)}}),a(u,n,document.createElement("option")),r.prototype=n.prototype,e.wrappers.HTMLOptionElement=n,e.wrappers.Option=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=window.HTMLSelectElement;t.prototype=Object.create(n.prototype),r(t.prototype,{add:function(e,t){"object"==typeof t&&(t=i(t)),i(this).add(i(e),t)},remove:function(e){return void 0===e?void n.prototype.remove.call(this):("object"==typeof e&&(e=i(e)),void i(this).remove(e))},get form(){return a(i(this).form)}}),o(s,t,document.createElement("select")),e.wrappers.HTMLSelectElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=e.wrapHTMLCollection,c=window.HTMLTableElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get caption(){return a(i(this).caption)},createCaption:function(){return a(i(this).createCaption())},get tHead(){return a(i(this).tHead)},createTHead:function(){return a(i(this).createTHead())},createTFoot:function(){return a(i(this).createTFoot())},get tFoot(){return a(i(this).tFoot)},get tBodies(){return s(i(this).tBodies)},createTBody:function(){return a(i(this).createTBody())},get rows(){return s(i(this).rows)},insertRow:function(e){return a(i(this).insertRow(e))}}),o(c,t,document.createElement("table")),e.wrappers.HTMLTableElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableSectionElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get rows(){return i(a(this).rows)},insertRow:function(e){return s(a(this).insertRow(e))}}),o(c,t,document.createElement("thead")),e.wrappers.HTMLTableSectionElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableRowElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get cells(){return i(a(this).cells)},insertCell:function(e){return s(a(this).insertCell(e))}}),o(c,t,document.createElement("tr")),e.wrappers.HTMLTableRowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e.localName){case"content":return new n(e);case"shadow":return new o(e);case"template":return new i(e)}r.call(this,e)}var n=e.wrappers.HTMLContentElement,r=e.wrappers.HTMLElement,o=e.wrappers.HTMLShadowElement,i=e.wrappers.HTMLTemplateElement,a=(e.mixin,e.registerWrapper),s=window.HTMLUnknownElement;t.prototype=Object.create(r.prototype),a(s,t),e.wrappers.HTMLUnknownElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.Element,r=e.wrappers.HTMLElement,o=e.registerWrapper,i=(e.defineWrapGetter,e.unsafeUnwrap),a=e.wrap,s=e.mixin,c="http://www.w3.org/2000/svg",l=window.SVGElement,u=document.createElementNS(c,"title");if(!("classList"in u)){var d=Object.getOwnPropertyDescriptor(n.prototype,"classList");Object.defineProperty(r.prototype,"classList",d),delete n.prototype.classList}t.prototype=Object.create(n.prototype),s(t.prototype,{get ownerSVGElement(){return a(i(this).ownerSVGElement)}}),o(l,t,document.createElementNS(c,"title")),e.wrappers.SVGElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){p.call(this,e)}var n=e.mixin,r=e.registerWrapper,o=e.unwrap,i=e.wrap,a=window.SVGUseElement,s="http://www.w3.org/2000/svg",c=i(document.createElementNS(s,"g")),l=document.createElementNS(s,"use"),u=c.constructor,d=Object.getPrototypeOf(u.prototype),p=d.constructor;t.prototype=Object.create(d),"instanceRoot"in l&&n(t.prototype,{get instanceRoot(){return i(o(this).instanceRoot)},get animatedInstanceRoot(){return i(o(this).animatedInstanceRoot)}}),r(a,t,l),e.wrappers.SVGUseElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.SVGElementInstance;s&&(t.prototype=Object.create(n.prototype),r(t.prototype,{get correspondingElement(){return a(i(this).correspondingElement)},get correspondingUseElement(){return a(i(this).correspondingUseElement)},get parentNode(){return a(i(this).parentNode)},get childNodes(){throw new Error("Not implemented")},get firstChild(){return a(i(this).firstChild)},get lastChild(){return a(i(this).lastChild)},get previousSibling(){return a(i(this).previousSibling)},get nextSibling(){return a(i(this).nextSibling)}}),o(s,t),e.wrappers.SVGElementInstance=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){o(e,this)}var n=e.mixin,r=e.registerWrapper,o=e.setWrapper,i=e.unsafeUnwrap,a=e.unwrap,s=e.unwrapIfNeeded,c=e.wrap,l=window.CanvasRenderingContext2D;n(t.prototype,{get canvas(){return c(i(this).canvas)},drawImage:function(){arguments[0]=s(arguments[0]),i(this).drawImage.apply(i(this),arguments)},createPattern:function(){return arguments[0]=a(arguments[0]),i(this).createPattern.apply(i(this),arguments)}}),r(l,t,document.createElement("canvas").getContext("2d")),e.wrappers.CanvasRenderingContext2D=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){i(e,this)}var n=e.addForwardingProperties,r=e.mixin,o=e.registerWrapper,i=e.setWrapper,a=e.unsafeUnwrap,s=e.unwrapIfNeeded,c=e.wrap,l=window.WebGLRenderingContext;if(l){r(t.prototype,{get canvas(){return c(a(this).canvas)},texImage2D:function(){arguments[5]=s(arguments[5]),a(this).texImage2D.apply(a(this),arguments)},texSubImage2D:function(){arguments[6]=s(arguments[6]),a(this).texSubImage2D.apply(a(this),arguments)}});var u=Object.getPrototypeOf(l.prototype);u!==Object.prototype&&n(u,t.prototype);var d=/WebKit/.test(navigator.userAgent)?{drawingBufferHeight:null,drawingBufferWidth:null}:{};o(l,t,d),e.wrappers.WebGLRenderingContext=t}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.Node,r=e.GetElementsByInterface,o=e.NonElementParentNodeInterface,i=e.ParentNodeInterface,a=e.SelectorsInterface,s=e.mixin,c=e.registerObject,l=e.registerWrapper,u=window.DocumentFragment;t.prototype=Object.create(n.prototype),s(t.prototype,i),s(t.prototype,a),s(t.prototype,r),s(t.prototype,o),l(u,t,document.createDocumentFragment()),e.wrappers.DocumentFragment=t;var d=c(document.createComment(""));e.wrappers.Comment=d}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=d(u(e).ownerDocument.createDocumentFragment());n.call(this,t),c(t,this);var o=e.shadowRoot;f.set(this,o),this.treeScope_=new r(this,a(o||e)),h.set(this,e)}var n=e.wrappers.DocumentFragment,r=e.TreeScope,o=e.elementFromPoint,i=e.getInnerHTML,a=e.getTreeScope,s=e.mixin,c=e.rewrap,l=e.setInnerHTML,u=e.unsafeUnwrap,d=e.unwrap,p=e.wrap,h=new WeakMap,f=new WeakMap;t.prototype=Object.create(n.prototype),s(t.prototype,{constructor:t,get innerHTML(){return i(this)},set innerHTML(e){l(this,e),this.invalidateShadowRenderer()},get olderShadowRoot(){return f.get(this)||null},get host(){return h.get(this)||null},invalidateShadowRenderer:function(){return h.get(this).invalidateShadowRenderer()},elementFromPoint:function(e,t){return o(this,this.ownerDocument,e,t)},getSelection:function(){return document.getSelection()},get activeElement(){var e=d(this).ownerDocument.activeElement;if(!e||!e.nodeType)return null;for(var t=p(e);!this.contains(t);){for(;t.parentNode;)t=t.parentNode;if(!t.host)return null;t=t.host}return t}}),e.wrappers.ShadowRoot=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=d(e).root;return t instanceof h?t.host:null}function n(t,n){if(t.shadowRoot){n=Math.min(t.childNodes.length-1,n);var r=t.childNodes[n];if(r){var o=e.getDestinationInsertionPoints(r);if(o.length>0){var i=o[0].parentNode;i.nodeType==Node.ELEMENT_NODE&&(t=i)}}}return t}function r(e){return e=u(e),t(e)||e}function o(e){a(e,this)}var i=e.registerWrapper,a=e.setWrapper,s=e.unsafeUnwrap,c=e.unwrap,l=e.unwrapIfNeeded,u=e.wrap,d=e.getTreeScope,p=window.Range,h=e.wrappers.ShadowRoot;o.prototype={get startContainer(){return r(s(this).startContainer)},get endContainer(){return r(s(this).endContainer)},get commonAncestorContainer(){return r(s(this).commonAncestorContainer)},setStart:function(e,t){e=n(e,t),s(this).setStart(l(e),t)},setEnd:function(e,t){e=n(e,t),s(this).setEnd(l(e),t)},setStartBefore:function(e){s(this).setStartBefore(l(e))},setStartAfter:function(e){s(this).setStartAfter(l(e))},setEndBefore:function(e){s(this).setEndBefore(l(e))},setEndAfter:function(e){s(this).setEndAfter(l(e))},selectNode:function(e){s(this).selectNode(l(e))},selectNodeContents:function(e){s(this).selectNodeContents(l(e))},compareBoundaryPoints:function(e,t){return s(this).compareBoundaryPoints(e,c(t))},extractContents:function(){return u(s(this).extractContents())},cloneContents:function(){return u(s(this).cloneContents())},insertNode:function(e){s(this).insertNode(l(e))},surroundContents:function(e){s(this).surroundContents(l(e))},cloneRange:function(){return u(s(this).cloneRange())},isPointInRange:function(e,t){return s(this).isPointInRange(l(e),t)},comparePoint:function(e,t){return s(this).comparePoint(l(e),t)},intersectsNode:function(e){return s(this).intersectsNode(l(e))},toString:function(){return s(this).toString()}},p.prototype.createContextualFragment&&(o.prototype.createContextualFragment=function(e){return u(s(this).createContextualFragment(e))}),i(window.Range,o,document.createRange()),e.wrappers.Range=o}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.previousSibling_=e.previousSibling,e.nextSibling_=e.nextSibling,e.parentNode_=e.parentNode}function n(n,o,i){var a=x(n),s=x(o),c=i?x(i):null;if(r(o),t(o),i)n.firstChild===i&&(n.firstChild_=i),i.previousSibling_=i.previousSibling;else{n.lastChild_=n.lastChild,n.lastChild===n.firstChild&&(n.firstChild_=n.firstChild);var l=R(a.lastChild);l&&(l.nextSibling_=l.nextSibling)}e.originalInsertBefore.call(a,s,c)}function r(n){var r=x(n),o=r.parentNode;if(o){var i=R(o);t(n),n.previousSibling&&(n.previousSibling.nextSibling_=n),n.nextSibling&&(n.nextSibling.previousSibling_=n),i.lastChild===n&&(i.lastChild_=n),i.firstChild===n&&(i.firstChild_=n),e.originalRemoveChild.call(o,r)}}function o(e){P.set(e,[])}function i(e){var t=P.get(e);return t||P.set(e,t=[]),t}function a(e){for(var t=[],n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t}function s(){for(var e=0;e<F.length;e++){var t=F[e],n=t.parentRenderer;n&&n.dirty||t.render()}F=[]}function c(){T=null,s()}function l(e){var t=A.get(e);return t||(t=new h(e),A.set(e,t)),t}function u(e){var t=j(e).root;return t instanceof C?t:null}function d(e){return l(e.host)}function p(e){this.skip=!1,this.node=e,this.childNodes=[]}function h(e){this.host=e,this.dirty=!1,this.invalidateAttributes(),this.associateNode(e)}function f(e){for(var t=[],n=e.firstChild;n;n=n.nextSibling)E(n)?t.push.apply(t,i(n)):t.push(n);return t}function m(e){if(e instanceof L)return e;if(e instanceof O)return null;for(var t=e.firstChild;t;t=t.nextSibling){var n=m(t);if(n)return n}return null}function w(e,t){i(t).push(e);var n=k.get(e);n?n.push(t):k.set(e,[t])}function v(e){return k.get(e)}function g(e){k.set(e,void 0)}function b(e,t){var n=t.getAttribute("select");if(!n)return!0;if(n=n.trim(),!n)return!0;if(!(e instanceof M))return!1;if(!q.test(n))return!1;try{return e.matches(n)}catch(r){return!1}}function y(e,t){var n=v(t);return n&&n[n.length-1]===e}function E(e){return e instanceof O||e instanceof L}function _(e){return e.shadowRoot}function S(e){for(var t=[],n=e.shadowRoot;n;n=n.olderShadowRoot)t.push(n);return t}var T,M=e.wrappers.Element,O=e.wrappers.HTMLContentElement,L=e.wrappers.HTMLShadowElement,N=e.wrappers.Node,C=e.wrappers.ShadowRoot,j=(e.assert,e.getTreeScope),D=(e.mixin,e.oneOf),H=e.unsafeUnwrap,x=e.unwrap,R=e.wrap,I=e.ArraySplice,P=new WeakMap,k=new WeakMap,A=new WeakMap,W=D(window,["requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","setTimeout"]),F=[],U=new I;U.equals=function(e,t){return x(e.node)===t},p.prototype={append:function(e){var t=new p(e);return this.childNodes.push(t),t},sync:function(e){if(!this.skip){for(var t=this.node,o=this.childNodes,i=a(x(t)),s=e||new WeakMap,c=U.calculateSplices(o,i),l=0,u=0,d=0,p=0;p<c.length;p++){for(var h=c[p];d<h.index;d++)u++,o[l++].sync(s);for(var f=h.removed.length,m=0;m<f;m++){var w=R(i[u++]);s.get(w)||r(w)}for(var v=h.addedCount,g=i[u]&&R(i[u]),m=0;m<v;m++){var b=o[l++],y=b.node;n(t,y,g),s.set(y,!0),b.sync(s)}d+=v}for(var p=d;p<o.length;p++)o[p].sync(s)}}},h.prototype={render:function(e){if(this.dirty){this.invalidateAttributes();var t=this.host;this.distribution(t);var n=e||new p(t);this.buildRenderTree(n,t);var r=!e;r&&n.sync(),this.dirty=!1}},get parentRenderer(){return j(this.host).renderer},invalidate:function(){if(!this.dirty){this.dirty=!0;var e=this.parentRenderer;if(e&&e.invalidate(),F.push(this),T)return;T=window[W](c,0)}},distribution:function(e){this.resetAllSubtrees(e),this.distributionResolution(e)},resetAll:function(e){E(e)?o(e):g(e),this.resetAllSubtrees(e)},resetAllSubtrees:function(e){for(var t=e.firstChild;t;t=t.nextSibling)this.resetAll(t);e.shadowRoot&&this.resetAll(e.shadowRoot),e.olderShadowRoot&&this.resetAll(e.olderShadowRoot)},distributionResolution:function(e){if(_(e)){for(var t=e,n=f(t),r=S(t),o=0;o<r.length;o++)this.poolDistribution(r[o],n);for(var o=r.length-1;o>=0;o--){var i=r[o],a=m(i);if(a){var s=i.olderShadowRoot;s&&(n=f(s));for(var c=0;c<n.length;c++)w(n[c],a)}this.distributionResolution(i)}}for(var l=e.firstChild;l;l=l.nextSibling)this.distributionResolution(l)},poolDistribution:function(e,t){if(!(e instanceof L))if(e instanceof O){var n=e;this.updateDependentAttributes(n.getAttribute("select"));for(var r=!1,o=0;o<t.length;o++){var e=t[o];e&&b(e,n)&&(w(e,n),t[o]=void 0,r=!0)}if(!r)for(var i=n.firstChild;i;i=i.nextSibling)w(i,n)}else for(var i=e.firstChild;i;i=i.nextSibling)this.poolDistribution(i,t)},buildRenderTree:function(e,t){for(var n=this.compose(t),r=0;r<n.length;r++){var o=n[r],i=e.append(o);this.buildRenderTree(i,o)}if(_(t)){var a=l(t);a.dirty=!1}},compose:function(e){for(var t=[],n=e.shadowRoot||e,r=n.firstChild;r;r=r.nextSibling)if(E(r)){this.associateNode(n);for(var o=i(r),a=0;a<o.length;a++){var s=o[a];y(r,s)&&t.push(s)}}else t.push(r);return t},invalidateAttributes:function(){this.attributes=Object.create(null)},updateDependentAttributes:function(e){if(e){var t=this.attributes;/\.\w+/.test(e)&&(t["class"]=!0),/#\w+/.test(e)&&(t.id=!0),e.replace(/\[\s*([^\s=\|~\]]+)/g,function(e,n){t[n]=!0})}},dependsOnAttribute:function(e){return this.attributes[e]},associateNode:function(e){H(e).polymerShadowRenderer_=this}};var q=/^(:not\()?[*.#[a-zA-Z_|]/;N.prototype.invalidateShadowRenderer=function(e){var t=H(this).polymerShadowRenderer_;return!!t&&(t.invalidate(),!0)},O.prototype.getDistributedNodes=L.prototype.getDistributedNodes=function(){return s(),i(this)},M.prototype.getDestinationInsertionPoints=function(){return s(),v(this)||[]},O.prototype.nodeIsInserted_=L.prototype.nodeIsInserted_=function(){this.invalidateShadowRenderer();var e,t=u(this);t&&(e=d(t)),H(this).polymerShadowRenderer_=e,e&&e.invalidate()},e.getRendererForHost=l,e.getShadowTrees=S,e.renderAllPending=s,e.getDestinationInsertionPoints=v,e.visual={insertBefore:n,remove:r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t){if(window[t]){r(!e.wrappers[t]);var c=function(e){n.call(this,e)};c.prototype=Object.create(n.prototype),o(c.prototype,{get form(){return s(a(this).form)}}),i(window[t],c,document.createElement(t.slice(4,-7))),e.wrappers[t]=c}}var n=e.wrappers.HTMLElement,r=e.assert,o=e.mixin,i=e.registerWrapper,a=e.unwrap,s=e.wrap,c=["HTMLButtonElement","HTMLFieldSetElement","HTMLInputElement","HTMLKeygenElement","HTMLLabelElement","HTMLLegendElement","HTMLObjectElement","HTMLOutputElement","HTMLTextAreaElement"];c.forEach(t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrap,a=e.unwrapIfNeeded,s=e.wrap,c=window.Selection;t.prototype={get anchorNode(){return s(o(this).anchorNode)},get focusNode(){return s(o(this).focusNode)},addRange:function(e){o(this).addRange(a(e))},collapse:function(e,t){o(this).collapse(a(e),t)},containsNode:function(e,t){return o(this).containsNode(a(e),t)},getRangeAt:function(e){return s(o(this).getRangeAt(e))},removeRange:function(e){o(this).removeRange(i(e))},selectAllChildren:function(e){o(this).selectAllChildren(e instanceof ShadowRoot?o(e.host):a(e))},toString:function(){return o(this).toString()}},c.prototype.extend&&(t.prototype.extend=function(e,t){o(this).extend(a(e),t)}),n(window.Selection,t,window.getSelection()),e.wrappers.Selection=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrapIfNeeded,a=e.wrap,s=window.TreeWalker;t.prototype={get root(){return a(o(this).root)},get currentNode(){return a(o(this).currentNode)},set currentNode(e){o(this).currentNode=i(e)},get filter(){return o(this).filter},parentNode:function(){return a(o(this).parentNode())},firstChild:function(){return a(o(this).firstChild())},lastChild:function(){return a(o(this).lastChild())},previousSibling:function(){return a(o(this).previousSibling())},previousNode:function(){return a(o(this).previousNode())},nextNode:function(){return a(o(this).nextNode())}},n(s,t),e.wrappers.TreeWalker=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){u.call(this,e),this.treeScope_=new w(this,null)}function n(e){var n=document[e];t.prototype[e]=function(){return j(n.apply(N(this),arguments))}}function r(e,t){x.call(N(t),C(e)),o(e,t)}function o(e,t){e.shadowRoot&&t.adoptNode(e.shadowRoot),e instanceof m&&i(e,t);for(var n=e.firstChild;n;n=n.nextSibling)o(n,t)}function i(e,t){var n=e.olderShadowRoot;n&&t.adoptNode(n)}function a(e){L(e,this)}function s(e,t){var n=document.implementation[t];e.prototype[t]=function(){
+return j(n.apply(N(this),arguments))}}function c(e,t){var n=document.implementation[t];e.prototype[t]=function(){return n.apply(N(this),arguments)}}var l=e.GetElementsByInterface,u=e.wrappers.Node,d=e.ParentNodeInterface,p=e.NonElementParentNodeInterface,h=e.wrappers.Selection,f=e.SelectorsInterface,m=e.wrappers.ShadowRoot,w=e.TreeScope,v=e.cloneNode,g=e.defineGetter,b=e.defineWrapGetter,y=e.elementFromPoint,E=e.forwardMethodsToWrapper,_=e.matchesNames,S=e.mixin,T=e.registerWrapper,M=e.renderAllPending,O=e.rewrap,L=e.setWrapper,N=e.unsafeUnwrap,C=e.unwrap,j=e.wrap,D=e.wrapEventTargetMethods,H=(e.wrapNodeList,new WeakMap);t.prototype=Object.create(u.prototype),b(t,"documentElement"),b(t,"body"),b(t,"head"),g(t,"activeElement",function(){var e=C(this).activeElement;if(!e||!e.nodeType)return null;for(var t=j(e);!this.contains(t);){for(;t.parentNode;)t=t.parentNode;if(!t.host)return null;t=t.host}return t}),["createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode"].forEach(n);var x=document.adoptNode,R=document.getSelection;S(t.prototype,{adoptNode:function(e){return e.parentNode&&e.parentNode.removeChild(e),r(e,this),e},elementFromPoint:function(e,t){return y(this,this,e,t)},importNode:function(e,t){return v(e,t,N(this))},getSelection:function(){return M(),new h(R.call(C(this)))},getElementsByName:function(e){return f.querySelectorAll.call(this,"[name="+JSON.stringify(String(e))+"]")}});var I=document.createTreeWalker,P=e.wrappers.TreeWalker;if(t.prototype.createTreeWalker=function(e,t,n,r){var o=null;return n&&(n.acceptNode&&"function"==typeof n.acceptNode?o={acceptNode:function(e){return n.acceptNode(j(e))}}:"function"==typeof n&&(o=function(e){return n(j(e))})),new P(I.call(C(this),C(e),t,o,r))},document.registerElement){var k=document.registerElement;t.prototype.registerElement=function(t,n){function r(e){return e?void L(e,this):i?document.createElement(i,t):document.createElement(t)}var o,i;if(void 0!==n&&(o=n.prototype,i=n["extends"]),o||(o=Object.create(HTMLElement.prototype)),e.nativePrototypeTable.get(o))throw new Error("NotSupportedError");for(var a,s=Object.getPrototypeOf(o),c=[];s&&!(a=e.nativePrototypeTable.get(s));)c.push(s),s=Object.getPrototypeOf(s);if(!a)throw new Error("NotSupportedError");for(var l=Object.create(a),u=c.length-1;u>=0;u--)l=Object.create(l);["createdCallback","attachedCallback","detachedCallback","attributeChangedCallback"].forEach(function(e){var t=o[e];t&&(l[e]=function(){j(this)instanceof r||O(this),t.apply(j(this),arguments)})});var d={prototype:l};i&&(d["extends"]=i),r.prototype=o,r.prototype.constructor=r,e.constructorTable.set(l,r),e.nativePrototypeTable.set(o,l);k.call(C(this),t,d);return r},E([window.HTMLDocument||window.Document],["registerElement"])}E([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement,window.HTMLHtmlElement],["appendChild","compareDocumentPosition","contains","getElementsByClassName","getElementsByTagName","getElementsByTagNameNS","insertBefore","querySelector","querySelectorAll","removeChild","replaceChild"]),E([window.HTMLBodyElement,window.HTMLHeadElement,window.HTMLHtmlElement],_),E([window.HTMLDocument||window.Document],["adoptNode","importNode","contains","createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","createTreeWalker","elementFromPoint","getElementById","getElementsByName","getSelection"]),S(t.prototype,l),S(t.prototype,d),S(t.prototype,f),S(t.prototype,p),S(t.prototype,{get implementation(){var e=H.get(this);return e?e:(e=new a(C(this).implementation),H.set(this,e),e)},get defaultView(){return j(C(this).defaultView)}}),T(window.Document,t,document.implementation.createHTMLDocument("")),window.HTMLDocument&&T(window.HTMLDocument,t),D([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement]);var A=document.implementation.createDocument;a.prototype.createDocument=function(){return arguments[2]=C(arguments[2]),j(A.apply(N(this),arguments))},s(a,"createDocumentType"),s(a,"createHTMLDocument"),c(a,"hasFeature"),T(window.DOMImplementation,a),E([window.DOMImplementation],["createDocument","createDocumentType","createHTMLDocument","hasFeature"]),e.adoptNodeNoRemove=r,e.wrappers.DOMImplementation=a,e.wrappers.Document=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.wrappers.Selection,o=e.mixin,i=e.registerWrapper,a=e.renderAllPending,s=e.unwrap,c=e.unwrapIfNeeded,l=e.wrap,u=window.Window,d=window.getComputedStyle,p=window.getDefaultComputedStyle,h=window.getSelection;t.prototype=Object.create(n.prototype),u.prototype.getComputedStyle=function(e,t){return l(this||window).getComputedStyle(c(e),t)},p&&(u.prototype.getDefaultComputedStyle=function(e,t){return l(this||window).getDefaultComputedStyle(c(e),t)}),u.prototype.getSelection=function(){return l(this||window).getSelection()},delete window.getComputedStyle,delete window.getDefaultComputedStyle,delete window.getSelection,["addEventListener","removeEventListener","dispatchEvent"].forEach(function(e){u.prototype[e]=function(){var t=l(this||window);return t[e].apply(t,arguments)},delete window[e]}),o(t.prototype,{getComputedStyle:function(e,t){return a(),d.call(s(this),c(e),t)},getSelection:function(){return a(),new r(h.call(s(this)))},get document(){return l(s(this).document)}}),p&&(t.prototype.getDefaultComputedStyle=function(e,t){return a(),p.call(s(this),c(e),t)}),i(u,t,window),e.wrappers.Window=t}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrap,n=window.DataTransfer||window.Clipboard,r=n.prototype.setDragImage;r&&(n.prototype.setDragImage=function(e,n,o){r.call(this,t(e),n,o)})}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t;t=e instanceof i?e:new i(e&&o(e)),r(t,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unwrap,i=window.FormData;i&&(n(i,t,new i),e.wrappers.FormData=t)}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrapIfNeeded,n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(e){return n.call(this,t(e))}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=n[e],r=window[t];if(r){var o=document.createElement(e),i=o.constructor;window[t]=i}}var n=(e.isWrapperFor,{a:"HTMLAnchorElement",area:"HTMLAreaElement",audio:"HTMLAudioElement",base:"HTMLBaseElement",body:"HTMLBodyElement",br:"HTMLBRElement",button:"HTMLButtonElement",canvas:"HTMLCanvasElement",caption:"HTMLTableCaptionElement",col:"HTMLTableColElement",content:"HTMLContentElement",data:"HTMLDataElement",datalist:"HTMLDataListElement",del:"HTMLModElement",dir:"HTMLDirectoryElement",div:"HTMLDivElement",dl:"HTMLDListElement",embed:"HTMLEmbedElement",fieldset:"HTMLFieldSetElement",font:"HTMLFontElement",form:"HTMLFormElement",frame:"HTMLFrameElement",frameset:"HTMLFrameSetElement",h1:"HTMLHeadingElement",head:"HTMLHeadElement",hr:"HTMLHRElement",html:"HTMLHtmlElement",iframe:"HTMLIFrameElement",img:"HTMLImageElement",input:"HTMLInputElement",keygen:"HTMLKeygenElement",label:"HTMLLabelElement",legend:"HTMLLegendElement",li:"HTMLLIElement",link:"HTMLLinkElement",map:"HTMLMapElement",marquee:"HTMLMarqueeElement",menu:"HTMLMenuElement",menuitem:"HTMLMenuItemElement",meta:"HTMLMetaElement",meter:"HTMLMeterElement",object:"HTMLObjectElement",ol:"HTMLOListElement",optgroup:"HTMLOptGroupElement",option:"HTMLOptionElement",output:"HTMLOutputElement",p:"HTMLParagraphElement",param:"HTMLParamElement",pre:"HTMLPreElement",progress:"HTMLProgressElement",q:"HTMLQuoteElement",script:"HTMLScriptElement",select:"HTMLSelectElement",shadow:"HTMLShadowElement",source:"HTMLSourceElement",span:"HTMLSpanElement",style:"HTMLStyleElement",table:"HTMLTableElement",tbody:"HTMLTableSectionElement",template:"HTMLTemplateElement",textarea:"HTMLTextAreaElement",thead:"HTMLTableSectionElement",time:"HTMLTimeElement",title:"HTMLTitleElement",tr:"HTMLTableRowElement",track:"HTMLTrackElement",ul:"HTMLUListElement",video:"HTMLVideoElement"});Object.keys(n).forEach(t),Object.getOwnPropertyNames(e.wrappers).forEach(function(t){window[t]=e.wrappers[t]})}(window.ShadowDOMPolyfill),function(e){function t(e,t){var n="";return Array.prototype.forEach.call(e,function(e){n+=e.textContent+"\n\n"}),t||(n=n.replace(d,"")),n}function n(e){var t=document.createElement("style");return t.textContent=e,t}function r(e){var t=n(e);document.head.appendChild(t);var r=[];if(t.sheet)try{r=t.sheet.cssRules}catch(o){}else console.warn("sheet not found",t);return t.parentNode.removeChild(t),r}function o(){C.initialized=!0,document.body.appendChild(C);var e=C.contentDocument,t=e.createElement("base");t.href=document.baseURI,e.head.appendChild(t)}function i(e){C.initialized||o(),document.body.appendChild(C),e(C.contentDocument),document.body.removeChild(C)}function a(e,t){if(t){var o;if(e.match("@import")&&D){var a=n(e);i(function(e){e.head.appendChild(a.impl),o=Array.prototype.slice.call(a.sheet.cssRules,0),t(o)})}else o=r(e),t(o)}}function s(e){e&&l().appendChild(document.createTextNode(e))}function c(e,t){var r=n(e);r.setAttribute(t,""),r.setAttribute(x,""),document.head.appendChild(r)}function l(){return j||(j=document.createElement("style"),j.setAttribute(x,""),j[x]=!0),j}var u={strictStyling:!1,registry:{},shimStyling:function(e,n,r){var o=this.prepareRoot(e,n,r),i=this.isTypeExtension(r),a=this.makeScopeSelector(n,i),s=t(o,!0);s=this.scopeCssText(s,a),e&&(e.shimmedStyle=s),this.addCssToDocument(s,n)},shimStyle:function(e,t){return this.shimCssText(e.textContent,t)},shimCssText:function(e,t){return e=this.insertDirectives(e),this.scopeCssText(e,t)},makeScopeSelector:function(e,t){return e?t?"[is="+e+"]":e:""},isTypeExtension:function(e){return e&&e.indexOf("-")<0},prepareRoot:function(e,t,n){var r=this.registerRoot(e,t,n);return this.replaceTextInStyles(r.rootStyles,this.insertDirectives),this.removeStyles(e,r.rootStyles),this.strictStyling&&this.applyScopeToContent(e,t),r.scopeStyles},removeStyles:function(e,t){for(var n,r=0,o=t.length;r<o&&(n=t[r]);r++)n.parentNode.removeChild(n)},registerRoot:function(e,t,n){var r=this.registry[t]={root:e,name:t,extendsName:n},o=this.findStyles(e);r.rootStyles=o,r.scopeStyles=r.rootStyles;var i=this.registry[r.extendsName];return i&&(r.scopeStyles=i.scopeStyles.concat(r.scopeStyles)),r},findStyles:function(e){if(!e)return[];var t=e.querySelectorAll("style");return Array.prototype.filter.call(t,function(e){return!e.hasAttribute(R)})},applyScopeToContent:function(e,t){e&&(Array.prototype.forEach.call(e.querySelectorAll("*"),function(e){e.setAttribute(t,"")}),Array.prototype.forEach.call(e.querySelectorAll("template"),function(e){this.applyScopeToContent(e.content,t)},this))},insertDirectives:function(e){return e=this.insertPolyfillDirectivesInCssText(e),this.insertPolyfillRulesInCssText(e)},insertPolyfillDirectivesInCssText:function(e){return e=e.replace(p,function(e,t){return t.slice(0,-2)+"{"}),e.replace(h,function(e,t){return t+" {"})},insertPolyfillRulesInCssText:function(e){return e=e.replace(f,function(e,t){return t.slice(0,-1)}),e.replace(m,function(e,t,n,r){var o=e.replace(t,"").replace(n,"");return r+o})},scopeCssText:function(e,t){var n=this.extractUnscopedRulesFromCssText(e);if(e=this.insertPolyfillHostInCssText(e),e=this.convertColonHost(e),e=this.convertColonHostContext(e),e=this.convertShadowDOMSelectors(e),t){var e,r=this;a(e,function(n){e=r.scopeRules(n,t)})}return e=e+"\n"+n,e.trim()},extractUnscopedRulesFromCssText:function(e){for(var t,n="";t=w.exec(e);)n+=t[1].slice(0,-1)+"\n\n";for(;t=v.exec(e);)n+=t[0].replace(t[2],"").replace(t[1],t[3])+"\n\n";return n},convertColonHost:function(e){return this.convertColonRule(e,E,this.colonHostPartReplacer)},convertColonHostContext:function(e){return this.convertColonRule(e,_,this.colonHostContextPartReplacer)},convertColonRule:function(e,t,n){return e.replace(t,function(e,t,r,o){if(t=O,r){for(var i,a=r.split(","),s=[],c=0,l=a.length;c<l&&(i=a[c]);c++)i=i.trim(),s.push(n(t,i,o));return s.join(",")}return t+o})},colonHostContextPartReplacer:function(e,t,n){return t.match(g)?this.colonHostPartReplacer(e,t,n):e+t+n+", "+t+" "+e+n},colonHostPartReplacer:function(e,t,n){return e+t.replace(g,"")+n},convertShadowDOMSelectors:function(e){for(var t=0;t<N.length;t++)e=e.replace(N[t]," ");return e},scopeRules:function(e,t){var n="";return e&&Array.prototype.forEach.call(e,function(e){if(e.selectorText&&e.style&&void 0!==e.style.cssText)n+=this.scopeSelector(e.selectorText,t,this.strictStyling)+" {\n\t",n+=this.propertiesFromRule(e)+"\n}\n\n";else if(e.type===CSSRule.MEDIA_RULE)n+="@media "+e.media.mediaText+" {\n",n+=this.scopeRules(e.cssRules,t),n+="\n}\n\n";else try{e.cssText&&(n+=e.cssText+"\n\n")}catch(r){e.type===CSSRule.KEYFRAMES_RULE&&e.cssRules&&(n+=this.ieSafeCssTextFromKeyFrameRule(e))}},this),n},ieSafeCssTextFromKeyFrameRule:function(e){var t="@keyframes "+e.name+" {";return Array.prototype.forEach.call(e.cssRules,function(e){t+=" "+e.keyText+" {"+e.style.cssText+"}"}),t+=" }"},scopeSelector:function(e,t,n){var r=[],o=e.split(",");return o.forEach(function(e){e=e.trim(),this.selectorNeedsScoping(e,t)&&(e=n&&!e.match(O)?this.applyStrictSelectorScope(e,t):this.applySelectorScope(e,t)),r.push(e)},this),r.join(", ")},selectorNeedsScoping:function(e,t){if(Array.isArray(t))return!0;var n=this.makeScopeMatcher(t);return!e.match(n)},makeScopeMatcher:function(e){return e=e.replace(/\[/g,"\\[").replace(/\]/g,"\\]"),new RegExp("^("+e+")"+S,"m")},applySelectorScope:function(e,t){return Array.isArray(t)?this.applySelectorScopeList(e,t):this.applySimpleSelectorScope(e,t)},applySelectorScopeList:function(e,t){for(var n,r=[],o=0;n=t[o];o++)r.push(this.applySimpleSelectorScope(e,n));return r.join(", ")},applySimpleSelectorScope:function(e,t){return e.match(L)?(e=e.replace(O,t),e.replace(L,t+" ")):t+" "+e},applyStrictSelectorScope:function(e,t){t=t.replace(/\[is=([^\]]*)\]/g,"$1");var n=[" ",">","+","~"],r=e,o="["+t+"]";return n.forEach(function(e){var t=r.split(e);r=t.map(function(e){var t=e.trim().replace(L,"");return t&&n.indexOf(t)<0&&t.indexOf(o)<0&&(e=t.replace(/([^:]*)(:*)(.*)/,"$1"+o+"$2$3")),e}).join(e)}),r},insertPolyfillHostInCssText:function(e){return e.replace(M,b).replace(T,g)},propertiesFromRule:function(e){var t=e.style.cssText;e.style.content&&!e.style.content.match(/['"]+|attr/)&&(t=t.replace(/content:[^;]*;/g,"content: '"+e.style.content+"';"));var n=e.style;for(var r in n)"initial"===n[r]&&(t+=r+": initial; ");return t},replaceTextInStyles:function(e,t){e&&t&&(e instanceof Array||(e=[e]),Array.prototype.forEach.call(e,function(e){e.textContent=t.call(this,e.textContent)},this))},addCssToDocument:function(e,t){e.match("@import")?c(e,t):s(e)}},d=/\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,p=/\/\*\s*@polyfill ([^*]*\*+([^\/*][^*]*\*+)*\/)([^{]*?){/gim,h=/polyfill-next-selector[^}]*content\:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim,f=/\/\*\s@polyfill-rule([^*]*\*+([^\/*][^*]*\*+)*)\//gim,m=/(polyfill-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim,w=/\/\*\s@polyfill-unscoped-rule([^*]*\*+([^\/*][^*]*\*+)*)\//gim,v=/(polyfill-unscoped-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim,g="-shadowcsshost",b="-shadowcsscontext",y=")(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)",E=new RegExp("("+g+y,"gim"),_=new RegExp("("+b+y,"gim"),S="([>\\s~+[.,{:][\\s\\S]*)?$",T=/\:host/gim,M=/\:host-context/gim,O=g+"-no-combinator",L=new RegExp(g,"gim"),N=(new RegExp(b,"gim"),[/>>>/g,/::shadow/g,/::content/g,/\/deep\//g,/\/shadow\//g,/\/shadow-deep\//g,/\^\^/g,/\^(?!=)/g]),C=document.createElement("iframe");C.style.display="none";var j,D=navigator.userAgent.match("Chrome"),H="shim-shadowdom",x="shim-shadowdom-css",R="no-shim";if(window.ShadowDOMPolyfill){s("style { display: none !important; }\n");var I=ShadowDOMPolyfill.wrap(document),P=I.querySelector("head");P.insertBefore(l(),P.childNodes[0]),document.addEventListener("DOMContentLoaded",function(){e.urlResolver;if(window.HTMLImports&&!HTMLImports.useNative){var t="link[rel=stylesheet]["+H+"]",n="style["+H+"]";HTMLImports.importer.documentPreloadSelectors+=","+t,HTMLImports.importer.importsPreloadSelectors+=","+t,HTMLImports.parser.documentSelectors=[HTMLImports.parser.documentSelectors,t,n].join(",");var r=HTMLImports.parser.parseGeneric;HTMLImports.parser.parseGeneric=function(e){if(!e[x]){var t=e.__importElement||e;if(!t.hasAttribute(H))return void r.call(this,e);e.__resource&&(t=e.ownerDocument.createElement("style"),t.textContent=e.__resource),HTMLImports.path.resolveUrlsInStyle(t,e.href),t.textContent=u.shimStyle(t),t.removeAttribute(H,""),t.setAttribute(x,""),t[x]=!0,t.parentNode!==P&&(e.parentNode===P?P.replaceChild(t,e):this.addElementToDocument(t)),t.__importParsed=!0,this.markParsingComplete(e),this.parseNext()}};var o=HTMLImports.parser.hasResource;HTMLImports.parser.hasResource=function(e){return"link"===e.localName&&"stylesheet"===e.rel&&e.hasAttribute(H)?e.__resource:o.call(this,e)}}})}e.ShadowCSS=u}(window.WebComponents)),function(e){window.ShadowDOMPolyfill?(window.wrap=ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}}(window.WebComponents),function(e){"use strict";function t(e){return void 0!==p[e]}function n(){s.call(this),this._isInvalid=!0}function r(e){return""==e&&n.call(this),e.toLowerCase()}function o(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,63,96].indexOf(t)==-1?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,96].indexOf(t)==-1?e:encodeURIComponent(e)}function a(e,a,s){function c(e){b.push(e)}var l=a||"scheme start",u=0,d="",v=!1,g=!1,b=[];e:for(;(e[u-1]!=f||0==u)&&!this._isInvalid;){var y=e[u];switch(l){case"scheme start":if(!y||!m.test(y)){if(a){c("Invalid scheme.");break e}d="",l="no scheme";continue}d+=y.toLowerCase(),l="scheme";break;case"scheme":if(y&&w.test(y))d+=y.toLowerCase();else{if(":"!=y){if(a){if(f==y)break e;c("Code point not allowed in scheme: "+y);break e}d="",u=0,l="no scheme";continue}if(this._scheme=d,d="",a)break e;t(this._scheme)&&(this._isRelative=!0),l="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==y?(this._query="?",l="query"):"#"==y?(this._fragment="#",l="fragment"):f!=y&&"\t"!=y&&"\n"!=y&&"\r"!=y&&(this._schemeData+=o(y));break;case"no scheme":if(s&&t(s._scheme)){l="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=y||"/"!=e[u+1]){c("Expected /, got: "+y),l="relative";continue}l="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),f==y){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==y||"\\"==y)"\\"==y&&c("\\ is an invalid code point."),l="relative slash";else if("?"==y)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,l="query";else{if("#"!=y){var E=e[u+1],_=e[u+2];("file"!=this._scheme||!m.test(y)||":"!=E&&"|"!=E||f!=_&&"/"!=_&&"\\"!=_&&"?"!=_&&"#"!=_)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),l="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,l="fragment"}break;case"relative slash":if("/"!=y&&"\\"!=y){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),l="relative path";continue}"\\"==y&&c("\\ is an invalid code point."),l="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=y){c("Expected '/', got: "+y),l="authority ignore slashes";continue}l="authority second slash";break;case"authority second slash":if(l="authority ignore slashes","/"!=y){c("Expected '/', got: "+y);continue}break;case"authority ignore slashes":if("/"!=y&&"\\"!=y){l="authority";continue}c("Expected authority, got: "+y);break;case"authority":if("@"==y){v&&(c("@ already seen."),d+="%40"),v=!0;for(var S=0;S<d.length;S++){var T=d[S];if("\t"!=T&&"\n"!=T&&"\r"!=T)if(":"!=T||null!==this._password){var M=o(T);null!==this._password?this._password+=M:this._username+=M}else this._password="";else c("Invalid whitespace in authority.")}d=""}else{if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y){u-=d.length,d="",l="host";continue}d+=y}break;case"file host":if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y){2!=d.length||!m.test(d[0])||":"!=d[1]&&"|"!=d[1]?0==d.length?l="relative path start":(this._host=r.call(this,d),d="",l="relative path start"):l="relative path";continue}"\t"==y||"\n"==y||"\r"==y?c("Invalid whitespace in file host."):d+=y;break;case"host":case"hostname":if(":"!=y||g){if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y){if(this._host=r.call(this,d),d="",l="relative path start",a)break e;continue}"\t"!=y&&"\n"!=y&&"\r"!=y?("["==y?g=!0:"]"==y&&(g=!1),d+=y):c("Invalid code point in host/hostname: "+y)}else if(this._host=r.call(this,d),d="",l="port","hostname"==a)break e;break;case"port":if(/[0-9]/.test(y))d+=y;else{if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y||a){if(""!=d){var O=parseInt(d,10);O!=p[this._scheme]&&(this._port=O+""),d=""}if(a)break e;l="relative path start";continue}"\t"==y||"\n"==y||"\r"==y?c("Invalid code point in port: "+y):n.call(this)}break;case"relative path start":if("\\"==y&&c("'\\' not allowed in path."),l="relative path","/"!=y&&"\\"!=y)continue;break;case"relative path":if(f!=y&&"/"!=y&&"\\"!=y&&(a||"?"!=y&&"#"!=y))"\t"!=y&&"\n"!=y&&"\r"!=y&&(d+=o(y));else{"\\"==y&&c("\\ not allowed in relative path.");var L;(L=h[d.toLowerCase()])&&(d=L),".."==d?(this._path.pop(),"/"!=y&&"\\"!=y&&this._path.push("")):"."==d&&"/"!=y&&"\\"!=y?this._path.push(""):"."!=d&&("file"==this._scheme&&0==this._path.length&&2==d.length&&m.test(d[0])&&"|"==d[1]&&(d=d[0]+":"),this._path.push(d)),d="","?"==y?(this._query="?",l="query"):"#"==y&&(this._fragment="#",l="fragment")}break;case"query":a||"#"!=y?f!=y&&"\t"!=y&&"\n"!=y&&"\r"!=y&&(this._query+=i(y)):(this._fragment="#",l="fragment");break;case"fragment":f!=y&&"\t"!=y&&"\n"!=y&&"\r"!=y&&(this._fragment+=y)}u++}}function s(){this._scheme="",this._schemeData="",this._username="",this._password=null,this._host="",this._port="",this._path=[],this._query="",this._fragment="",this._isInvalid=!1,this._isRelative=!1}function c(e,t){void 0===t||t instanceof c||(t=new c(String(t))),this._url=e,s.call(this);var n=e.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g,"");a.call(this,n,null,t)}var l=!1;if(!e.forceJURL)try{var u=new URL("b","http://a");u.pathname="c%20d",l="http://a/c%20d"===u.href}catch(d){}if(!l){var p=Object.create(null);p.ftp=21,p.file=0,p.gopher=70,p.http=80,p.https=443,p.ws=80,p.wss=443;var h=Object.create(null);h["%2e"]=".",h[".%2e"]="..",h["%2e."]="..",h["%2e%2e"]="..";var f=void 0,m=/[a-zA-Z]/,w=/[a-zA-Z0-9\+\-\.]/;c.prototype={toString:function(){return this.href},get href(){if(this._isInvalid)return this._url;var e="";return""==this._username&&null==this._password||(e=this._username+(null!=this._password?":"+this._password:"")+"@"),this.protocol+(this._isRelative?"//"+e+this.host:"")+this.pathname+this._query+this._fragment},set href(e){s.call(this),a.call(this,e)},get protocol(){return this._scheme+":"},set protocol(e){this._isInvalid||a.call(this,e+":","scheme start")},get host(){return this._isInvalid?"":this._port?this._host+":"+this._port:this._host},set host(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"host")},get hostname(){return this._host},set hostname(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"hostname")},get port(){return this._port},set port(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"port")},get pathname(){return this._isInvalid?"":this._isRelative?"/"+this._path.join("/"):this._schemeData},set pathname(e){!this._isInvalid&&this._isRelative&&(this._path=[],a.call(this,e,"relative path start"))},get search(){return this._isInvalid||!this._query||"?"==this._query?"":this._query},set search(e){!this._isInvalid&&this._isRelative&&(this._query="?","?"==e[0]&&(e=e.slice(1)),a.call(this,e,"query"))},get hash(){return this._isInvalid||!this._fragment||"#"==this._fragment?"":this._fragment},set hash(e){this._isInvalid||(this._fragment="#","#"==e[0]&&(e=e.slice(1)),a.call(this,e,"fragment"))},get origin(){var e;if(this._isInvalid||!this._scheme)return"";switch(this._scheme){case"data":case"file":case"javascript":case"mailto":return"null"}return e=this.host,e?this._scheme+"://"+e:""}};var v=e.URL;v&&(c.createObjectURL=function(e){return v.createObjectURL.apply(v,arguments)},c.revokeObjectURL=function(e){v.revokeObjectURL(e)}),e.URL=c}}(self),function(e){function t(e){y.push(e),b||(b=!0,m(r))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function r(){b=!1;var e=y;y=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();o(e),n.length&&(e.callback_(n,e),t=!0)}),t&&r()}function o(e){e.nodes_.forEach(function(t){var n=w.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var r=w.get(n);if(r)for(var o=0;o<r.length;o++){var i=r[o],a=i.options;if(n===e||a.subtree){var s=t(a);s&&i.enqueue(s)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++E}function s(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function c(e){var t=new s(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function l(e,t){return _=new s(e,t)}function u(e){return S?S:(S=c(_),S.oldValue=e,S)}function d(){_=S=void 0}function p(e){return e===S||e===_}function h(e,t){return e===t?e:S&&p(e)?S:null}function f(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var m,w=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))m=setTimeout;else if(window.setImmediate)m=window.setImmediate;else{var v=[],g=String(Math.random());window.addEventListener("message",function(e){if(e.data===g){var t=v;v=[],t.forEach(function(e){e()})}}),m=function(e){v.push(e),window.postMessage(g,"*")}}var b=!1,y=[],E=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var r=w.get(e);r||w.set(e,r=[]);for(var o,i=0;i<r.length;i++)if(r[i].observer===this){o=r[i],o.removeListeners(),o.options=t;break}o||(o=new f(this,e,t),r.push(o),this.nodes_.push(e)),o.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=w.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){r.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var _,S;f.prototype={enqueue:function(e){var n=this.observer.records_,r=n.length;if(n.length>0){var o=n[r-1],i=h(o,e);if(i)return void(n[r-1]=i)}else t(this.observer);n[r]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=w.get(e);t||w.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=w.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,r=e.target,o=new l("attributes",r);o.attributeName=t,o.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(r,function(e){if(e.attributes&&(!e.attributeFilter||!e.attributeFilter.length||e.attributeFilter.indexOf(t)!==-1||e.attributeFilter.indexOf(n)!==-1))return e.attributeOldValue?u(a):o});break;case"DOMCharacterDataModified":var r=e.target,o=l("characterData",r),a=e.prevValue;i(r,function(e){if(e.characterData)return e.characterDataOldValue?u(a):o});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var s,c,p=e.target;"DOMNodeInserted"===e.type?(s=[p],c=[]):(s=[],c=[p]);var h=p.previousSibling,f=p.nextSibling,o=l("childList",e.target.parentNode);o.addedNodes=s,o.removedNodes=c,o.previousSibling=h,o.nextSibling=f,i(e.relatedNode,function(e){if(e.childList)return o})}d()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(e){"use strict";if(!window.performance||!window.performance.now){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var r=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(r.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var o=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||o&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||o&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||f,r(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===v}function r(e,t){if(n(t))e&&e();else{var o=function(){"complete"!==t.readyState&&t.readyState!==v||(t.removeEventListener(g,o),r(e,t))};t.addEventListener(g,o)}}function o(e){e.target.__loaded=!0}function i(e,t){function n(){c==l&&e&&e({allImports:s,loadedImports:u,errorImports:d})}function r(e){o(e),u.push(this),c++,n()}function i(e){
+d.push(this),c++,n()}var s=t.querySelectorAll("link[rel=import]"),c=0,l=s.length,u=[],d=[];if(l)for(var p,h=0;h<l&&(p=s[h]);h++)a(p)?(u.push(this),c++,n()):(p.addEventListener("load",r),p.addEventListener("error",i));else n()}function a(e){return d?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)c(t)&&l(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function l(e){var t=e["import"];t?o({target:e}):(e.addEventListener("load",o),e.addEventListener("error",o))}var u="import",d=Boolean(u in document.createElement("link")),p=Boolean(window.ShadowDOMPolyfill),h=function(e){return p?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},f=h(document),m={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return h(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(f,"_currentScript",m);var w=/Trident/.test(navigator.userAgent),v=w?"complete":"interactive",g="readystatechange";d&&(new MutationObserver(function(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,r=t.length;n<r&&(e=t[n]);n++)l(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=f.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),f.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=u,e.useNative=d,e.rootDocument=f,e.whenReady=t,e.isIE=w}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},r=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=r}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,r={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,r=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,r),e},resolveUrlsInCssText:function(e,r,o){var i=this.replaceUrls(e,o,r,t);return i=this.replaceUrls(i,o,r,n)},replaceUrls:function(e,t,n,r){return e.replace(r,function(e,r,o,i){var a=o.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,r+"'"+a+"'"+i})}};e.path=r}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,r,o){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}r.call(o,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,r=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};r.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,r){if(n.load&&console.log("fetch",e,r),e)if(e.match(/^data:/)){var o=e.split(","),i=o[0],a=o[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,r,null,a)}.bind(this),0)}else{var s=function(t,n,o){this.receive(e,r,t,n,o)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,r,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,r,o){this.cache[e]=r;for(var i,a=this.pending[e],s=0,c=a.length;s<c&&(i=a[s]);s++)this.onload(e,i,r,n,o),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=r}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===u}function n(e){var t=r(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function r(e){return e.textContent+o(e)}function o(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,r=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+r+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,l=e.isIE,u=e.IMPORT_LINK_TYPE,d="link[rel="+u+"]",p={documentSelectors:d,importsSelectors:[d,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,r=function(o){e.removeEventListener("load",r),e.removeEventListener("error",r),t&&t(o),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",r),e.addEventListener("error",r),l&&"style"===e.localName){var o=!1;if(e.textContent.indexOf("@import")==-1)o=!0;else if(e.sheet){o=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;c<s&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(o=o&&Boolean(i.styleSheet))}o&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var r=document.createElement("script");r.__importElement=t,r.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(r,function(t){r.parentNode&&r.parentNode.removeChild(r),e.currentScript=null}),this.addElementToDocument(r)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var r,o=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=o.length;i<a&&(r=o[i]);i++)if(!this.isParsed(r))return this.hasResource(r)?t(r)?this.nextToParseInDoc(r.__doc,r):r:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return!t(e)||void 0!==e.__doc}};e.parser=p,e.IMPORT_SELECTOR=d}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function r(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function o(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var o=n.createElement("base");o.setAttribute("href",t),n.baseURI||r(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(o),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,l=e.Loader,u=e.Observer,d=e.parser,p={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){h.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);h.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,r,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=r,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:o(r,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}d.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),d.parseNext()},loadedAll:function(){d.parseNext()}},h=new l(p.loaded.bind(p),p.loadedAll.bind(p));if(p.observer=new u,!document.baseURI){var f={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",f),Object.defineProperty(c,"baseURI",f)}e.importer=p,e.importLoader=h}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,r={added:function(e){for(var r,o,i,a,s=0,c=e.length;s<c&&(a=e[s]);s++)r||(r=a.ownerDocument,o=t.isParsed(r)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&o&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&o.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&o.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=r.added.bind(r);var o=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(r)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var r=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],r=function(e){n.push(e)},o=function(){n.forEach(function(t){t(e)})};e.addModule=r,e.initializeModules=o,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return!!t(e)||void r(e,t)}),r(e,t)}function n(e,t,r){var o=e.firstElementChild;if(!o)for(o=e.firstChild;o&&o.nodeType!==Node.ELEMENT_NODE;)o=o.nextSibling;for(;o;)t(o,r)!==!0&&n(o,t,r),o=o.nextElementSibling;return null}function r(e,n){for(var r=e.shadowRoot;r;)t(r,n),r=r.olderShadowRoot}function o(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var r,o=e.querySelectorAll("link[rel="+a+"]"),s=0,c=o.length;s<c&&(r=o[s]);s++)r["import"]&&i(r["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=o,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||r(e,t)}function n(t,n){return!!e.upgrade(t,n)||void(n&&a(t))}function r(e,t){b(e,function(e){if(n(e,t))return!0})}function o(e){S.push(e),_||(_=!0,setTimeout(i))}function i(){_=!1;for(var e,t=S,n=0,r=t.length;n<r&&(e=t[n]);n++)e();S=[]}function a(e){E?o(function(){s(e)}):s(e)}function s(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function c(e){l(e),b(e,function(e){l(e)})}function l(e){E?o(function(){u(e)}):u(e)}function u(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function d(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function p(e){if(e.shadowRoot&&!e.shadowRoot.__watched){g.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function h(e,n){if(g.dom){var r=n[0];if(r&&"childList"===r.type&&r.addedNodes&&r.addedNodes){for(var o=r.addedNodes[0];o&&o!==document&&!o.host;)o=o.parentNode;var i=o&&(o.URL||o._URL||o.host&&o.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=d(e);n.forEach(function(e){"childList"===e.type&&(T(e.addedNodes,function(e){e.localName&&t(e,a)}),T(e.removedNodes,function(e){e.localName&&c(e)}))}),g.dom&&console.groupEnd()}function f(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(h(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(h.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function w(e){e=window.wrap(e),g.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document);t(e,n),m(e),g.dom&&console.groupEnd()}function v(e){y(e,w)}var g=e.flags,b=e.forSubtree,y=e.forDocumentTree,E=window.MutationObserver._isPolyfilled&&g["throttle-attached"];e.hasPolyfillMutations=E,e.hasThrottledAttached=E;var _=!1,S=[],T=Array.prototype.forEach.call.bind(Array.prototype.forEach),M=Element.prototype.createShadowRoot;M&&(Element.prototype.createShadowRoot=function(){var e=M.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=p,e.upgradeDocumentTree=v,e.upgradeDocument=w,e.upgradeSubtree=r,e.upgradeAll=t,e.attached=a,e.takeRecords=f}),window.CustomElements.addModule(function(e){function t(t,r){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var o=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(o);if(i&&(o&&i.tag==t.localName||!o&&!i["extends"]))return n(t,i,r)}}function n(t,n,o){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),r(t,n),t.__upgraded__=!0,i(t),o&&e.attached(t),e.upgradeSubtree(t,o),a.upgrade&&console.groupEnd(),t}function r(e,t){Object.__proto__?e.__proto__=t.prototype:(o(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function o(e,t,n){for(var r={},o=t;o!==n&&o!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(o),s=0;i=a[s];s++)r[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(o,i)),r[i]=1);o=Object.getPrototypeOf(o)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=r}),window.CustomElements.addModule(function(e){function t(t,r){var c=r||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(o(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(l(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c["extends"]&&(c["extends"]=c["extends"].toLowerCase()),c.lifecycle=c.lifecycle||{},c.ancestry=i(c["extends"]),a(c),s(c),n(c.prototype),u(c.__name,c),c.ctor=d(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&w(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){r.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){r.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function r(e,t,n){e=e.toLowerCase();var r=this.getAttribute(e);n.apply(this,arguments);var o=this.getAttribute(e);this.attributeChangedCallback&&o!==r&&this.attributeChangedCallback(e,r,o)}function o(e){for(var t=0;t<E.length;t++)if(e===E[t])return!0}function i(e){var t=l(e);return t?i(t["extends"]).concat([t]):[]}function a(e){for(var t,n=e["extends"],r=0;t=e.ancestry[r];r++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function s(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag);t=Object.getPrototypeOf(n)}for(var r,o=e.prototype,i=!1;o;)o==t&&(i=!0),r=Object.getPrototypeOf(o),r&&(o.__proto__=r),o=r;i||console.warn(e.tag+" prototype not found in prototype chain for "+e.is),e["native"]=t}}function c(e){return g(T(e.tag),e)}function l(e){if(e)return _[e.toLowerCase()]}function u(e,t){_[e]=t}function d(e){return function(){return c(e)}}function p(e,t,n){return e===S?h(t,n):M(e,t)}function h(e,t){e&&(e=e.toLowerCase()),t&&(t=t.toLowerCase());var n=l(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var r;return t?(r=h(e),r.setAttribute("is",t),r):(r=T(e),e.indexOf("-")>=0&&b(r,HTMLElement),r)}function f(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return v(e),e}}var m,w=(e.isIE,e.upgradeDocumentTree),v=e.upgradeAll,g=e.upgradeWithDefinition,b=e.implementPrototype,y=e.useNative,E=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],_={},S="http://www.w3.org/1999/xhtml",T=document.createElement.bind(document),M=document.createElementNS.bind(document);m=Object.__proto__||y?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},f(Node.prototype,"cloneNode"),f(document,"importNode"),document.registerElement=t,document.createElement=h,document.createElementNS=p,e.registry=_,e["instanceof"]=m,e.reservedTagList=E,e.getRegisteredDefinition=l,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,r=e.initializeModules;e.isIE;if(n){var o=function(){};e.watchShadow=o,e.upgrade=o,e.upgradeAll=o,e.upgradeDocumentTree=o,e.upgradeSubtree=o,e.takeRecords=o,e["instanceof"]=function(e,t){return e instanceof t}}else r();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){Function.prototype.bind||(Function.prototype.bind=function(e){var t=this,n=Array.prototype.slice.call(arguments,1);return function(){var r=n.slice();return r.push.apply(r,arguments),t.apply(e,r)}})}(window.WebComponents),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents),function(e){window.Platform=e}(window.WebComponents); \ No newline at end of file
diff --git a/catapult/tracing/tracing/trace_data/trace_data.py b/catapult/tracing/tracing/trace_data/trace_data.py
index ae658a22..e599502c 100644
--- a/catapult/tracing/tracing/trace_data/trace_data.py
+++ b/catapult/tracing/tracing/trace_data/trace_data.py
@@ -10,10 +10,12 @@ import gzip
import json
import logging
import os
+import platform
import shutil
import subprocess
import tempfile
import time
+import traceback
import six
@@ -59,6 +61,7 @@ CHROME_TRACE_PART = TraceDataPart('traceEvents')
CPU_TRACE_DATA = TraceDataPart('cpuSnapshots')
TELEMETRY_PART = TraceDataPart('telemetry')
WALT_TRACE_PART = TraceDataPart('waltTraceEvents')
+CGROUP_TRACE_PART = TraceDataPart('cgroupDump')
ALL_TRACE_PARTS = {ANDROID_PROCESS_DATA_PART,
ATRACE_PART,
@@ -92,7 +95,11 @@ class _TraceData(object):
_TraceItem = collections.namedtuple(
- '_TraceItem', ['part_name', 'handle', 'compressed'])
+ '_TraceItem', ['part_name', 'handle'])
+
+
+class TraceDataException(Exception):
+ """Exception raised by TraceDataBuilder via RecordTraceDataException()."""
class TraceDataBuilder(object):
@@ -111,6 +118,7 @@ class TraceDataBuilder(object):
self._traces = []
self._frozen = False
self._temp_dir = tempfile.mkdtemp()
+ self._exceptions = []
def __enter__(self):
return self
@@ -118,14 +126,14 @@ class TraceDataBuilder(object):
def __exit__(self, *args):
self.CleanUpTraceData()
- def OpenTraceHandleFor(self, part, compressed=False):
+ def OpenTraceHandleFor(self, part, suffix):
"""Open a file handle for writing trace data into it.
Args:
part: A TraceDataPart instance.
- compressed: An optional Boolean, indicates whether the written data is
- gzipped. Note, this information is currently only used by the AsData()
- method in order to be able to open and read the written data.
+ suffix: A string used as file extension and identifier for the format
+ of the trace contents, e.g. '.json'. Can also append '.gz' to
+ indicate gzipped content, e.g. '.json.gz'.
"""
if not isinstance(part, TraceDataPart):
raise TypeError('part must be a TraceDataPart instance')
@@ -133,8 +141,8 @@ class TraceDataBuilder(object):
raise RuntimeError('trace data builder is no longer open for writing')
trace = _TraceItem(
part_name=part.raw_field_name,
- handle=tempfile.NamedTemporaryFile(delete=False, dir=self._temp_dir),
- compressed=compressed)
+ handle=tempfile.NamedTemporaryFile(
+ delete=False, dir=self._temp_dir, suffix=suffix))
self._traces.append(trace)
return trace.handle
@@ -151,7 +159,8 @@ class TraceDataBuilder(object):
source file will no longer exist after calling this method; and the
lifetime of the trace data will thereafter be managed by this builder.
"""
- with self.OpenTraceHandleFor(part) as handle:
+ _, suffix = os.path.splitext(trace_file)
+ with self.OpenTraceHandleFor(part, suffix) as handle:
pass
if os.name == 'nt':
# On windows os.rename won't overwrite, so the destination path needs to
@@ -175,11 +184,13 @@ class TraceDataBuilder(object):
if not allow_unstructured:
raise ValueError('must pass allow_unstructured=True for text data')
do_write = lambda d, f: f.write(d)
+ suffix = '.txt' # Used for atrace and systrace data.
elif isinstance(data, dict):
do_write = json.dump
+ suffix = '.json'
else:
raise TypeError('invalid trace data type')
- with self.OpenTraceHandleFor(part) as handle:
+ with self.OpenTraceHandleFor(part, suffix) as handle:
do_write(data, handle)
def Freeze(self):
@@ -188,7 +199,11 @@ class TraceDataBuilder(object):
return self
def CleanUpTraceData(self):
- """Clean up resources used by the data builder."""
+ """Clean up resources used by the data builder.
+
+ Will also re-raise any exceptions previously added by
+ RecordTraceCollectionException().
+ """
if self._traces is None:
return # Already cleaned up.
self.Freeze()
@@ -200,6 +215,11 @@ class TraceDataBuilder(object):
self._temp_dir = None
self._traces = None
+ if self._exceptions:
+ raise TraceDataException(
+ 'Exceptions raised during trace data collection:\n' +
+ '\n'.join(self._exceptions))
+
def Serialize(self, file_path, trace_title=None):
"""Serialize the trace data to a file in HTML format."""
self.Freeze()
@@ -227,10 +247,15 @@ class TraceDataBuilder(object):
raw_data = {}
for trace in self._traces:
- traces_for_part = raw_data.setdefault(trace.part_name, [])
- opener = gzip.open if trace.compressed else open
- with opener(trace.handle.name, 'rb') as f:
- traces_for_part.append(json.load(f))
+ is_compressed_json = trace.handle.name.endswith('.json.gz')
+ is_json = trace.handle.name.endswith('.json') or is_compressed_json
+ if is_json:
+ traces_for_part = raw_data.setdefault(trace.part_name, [])
+ opener = gzip.open if is_compressed_json else open
+ with opener(trace.handle.name, 'rb') as f:
+ traces_for_part.append(json.load(f))
+ else:
+ logging.info('Skipping over non-json trace: %s', trace.handle.name)
return _TraceData(raw_data)
def IterTraceParts(self):
@@ -241,6 +266,22 @@ class TraceDataBuilder(object):
for trace in self._traces:
yield trace.part_name, trace.handle.name
+ def RecordTraceDataException(self):
+ """Records the most recent exception to be re-raised during cleanup.
+
+ Exceptions raised during trace data collection can be stored temporarily
+ in the builder. They will be re-raised when the builder is cleaned up later.
+ This way, any collected trace data can still be retained before the
+ benchmark is aborted.
+
+ This method is intended to be called from within an "except" handler, e.g.:
+ try:
+ # Collect trace data.
+ except Exception: # pylint: disable=broad-except
+ builder.RecordTraceDataException()
+ """
+ self._exceptions.append(traceback.format_exc())
+
def CreateTestTrace(number=1):
"""Convenient helper method to create trace data objects for testing.
@@ -283,7 +324,15 @@ def SerializeAsHtml(trace_files, html_file, trace_title=None):
input_size = sum(os.path.getsize(trace_file) for trace_file in trace_files)
- cmd = ['python', _TRACE2HTML_PATH]
+ cmd = []
+ if platform.system() == 'Windows':
+ version_cmd = ['python', '-c',
+ 'import sys\nprint(sys.version_info.major)']
+ version = subprocess.check_output(version_cmd)
+ if version.strip() == '3':
+ raise RuntimeError('trace2html cannot run with python 3.')
+ cmd.append('python')
+ cmd.append(_TRACE2HTML_PATH)
cmd.extend(trace_files)
cmd.extend(['--output', html_file])
if trace_title is not None:
diff --git a/catapult/tracing/tracing/trace_data/trace_data_unittest.py b/catapult/tracing/tracing/trace_data/trace_data_unittest.py
index fdc48e2c..37874841 100644
--- a/catapult/tracing/tracing/trace_data/trace_data_unittest.py
+++ b/catapult/tracing/tracing/trace_data/trace_data_unittest.py
@@ -11,7 +11,6 @@ import unittest
from py_utils import tempfile_ext
from tracing.trace_data import trace_data
-
class TraceDataTest(unittest.TestCase):
def testHasTracesForChrome(self):
d = trace_data.CreateFromRawChromeEvents([{'ph': 'B'}])
@@ -60,7 +59,7 @@ class TraceDataBuilderTest(unittest.TestCase):
def testAddTraceFileFor(self):
original_data = {'msg': 'The answer is 42'}
- with tempfile.NamedTemporaryFile(delete=False) as source:
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.json') as source:
json.dump(original_data, source)
with trace_data.TraceDataBuilder() as builder:
builder.AddTraceFileFor(trace_data.CHROME_TRACE_PART, source.name)
@@ -72,7 +71,8 @@ class TraceDataBuilderTest(unittest.TestCase):
def testOpenTraceHandleFor(self):
original_data = {'msg': 'The answer is 42'}
with trace_data.TraceDataBuilder() as builder:
- with builder.OpenTraceHandleFor(trace_data.CHROME_TRACE_PART) as handle:
+ with builder.OpenTraceHandleFor(
+ trace_data.CHROME_TRACE_PART, suffix='.json') as handle:
handle.write(json.dumps(original_data))
out_data = builder.AsData().GetTraceFor(trace_data.CHROME_TRACE_PART)
@@ -87,7 +87,7 @@ class TraceDataBuilderTest(unittest.TestCase):
'H4sIAIDMblwAA6tWyi1OV7JSUArJSFVIzCsuTy1SyCxWMDFSquUCAA4QMtscAAAA')
with trace_data.TraceDataBuilder() as builder:
with builder.OpenTraceHandleFor(
- trace_data.CHROME_TRACE_PART, compressed=True) as handle:
+ trace_data.CHROME_TRACE_PART, suffix='.json.gz') as handle:
handle.write(compressed_data)
out_data = builder.AsData().GetTraceFor(trace_data.CHROME_TRACE_PART)
@@ -104,6 +104,15 @@ class TraceDataBuilderTest(unittest.TestCase):
builder.AddTraceFor(trace_data.CHROME_TRACE_PART,
{'traceEvents': [1, 2, 3]})
+ def testCleanupReraisesExceptions(self):
+ with trace_data.TraceDataBuilder() as builder:
+ try:
+ raise Exception("test exception") # pylint: disable=broad-except
+ except Exception: # pylint: disable=broad-except
+ builder.RecordTraceDataException()
+ with self.assertRaises(trace_data.TraceDataException):
+ builder.CleanUpTraceData()
+
def testCantWriteAfterFreeze(self):
with trace_data.TraceDataBuilder() as builder:
builder.AddTraceFor(trace_data.CHROME_TRACE_PART,
diff --git a/catapult/tracing/tracing_project.py b/catapult/tracing/tracing_project.py
index b4475b92..ce198ce7 100644
--- a/catapult/tracing/tracing_project.py
+++ b/catapult/tracing/tracing_project.py
@@ -25,6 +25,7 @@ def GetDependencyPaths():
p.catapult_path,
p.py_vulcanize_path,
p.vinn_path,
+ p.protobuf_path,
os.path.join(p.catapult_third_party_path, 'WebOb'),
os.path.join(p.catapult_third_party_path, 'Paste'),
os.path.join(p.catapult_third_party_path, 'six'),
@@ -77,6 +78,11 @@ class TracingProject(object):
catapult_third_party_path = os.path.join(catapult_path, 'third_party')
polymer_path = os.path.join(catapult_third_party_path, 'polymer')
+ # We need google.protobuf for tests that use the proto. The dashboard uses
+ # the gae_ts_mon protobuf lib for its tests, so let's do the same for tracing.
+ protobuf_path = os.path.join(
+ catapult_third_party_path, 'gae_ts_mon', 'gae_ts_mon', 'protobuf')
+
tracing_third_party_path = os.path.join(tracing_root_path, 'third_party')
py_vulcanize_path = os.path.join(common_root_path, 'py_vulcanize')
vinn_path = os.path.join(catapult_third_party_path, 'vinn')
diff --git a/update.py b/update.py
index e54ffbe0..2e44e985 100755
--- a/update.py
+++ b/update.py
@@ -14,6 +14,7 @@ PACKAGE_DIRS = [
'systrace',
'third_party/pyserial',
'third_party/zipfile',
+ 'third_party/polymer',
'tracing/tracing/trace_data',
]
PACKAGE_FILES = [